Statistics
| Branch: | Tag: | Revision:

root / autotools / convert-constants @ 36c70d4d

History | View | Annotate | Download (5.1 kB)

1
#!/usr/bin/python
2
#
3

    
4
# Copyright (C) 2011 Google Inc.
5
#
6
# This program is free software; you can redistribute it and/or modify
7
# it under the terms of the GNU General Public License as published by
8
# the Free Software Foundation; either version 2 of the License, or
9
# (at your option) any later version.
10
#
11
# This program is distributed in the hope that it will be useful, but
12
# WITHOUT ANY WARRANTY; without even the implied warranty of
13
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14
# General Public License for more details.
15
#
16
# You should have received a copy of the GNU General Public License
17
# along with this program; if not, write to the Free Software
18
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19
# 02110-1301, USA.
20

    
21
"""Script for converting Python constants to Haskell code fragments.
22

    
23
"""
24

    
25
import re
26

    
27
from ganeti import constants
28
from ganeti import compat
29

    
30
#: Constant name regex
31
CONSTANT_RE = re.compile("^[A-Z][A-Z0-9_-]+$")
32

    
33
#: The type of regex objects
34
RE_TYPE = type(CONSTANT_RE)
35

    
36

    
37
def NameRules(name):
38
  """Converts the upper-cased Python name to Haskell camelCase.
39

    
40
  """
41
  name = name.replace("-", "_")
42
  elems = name.split("_")
43
  return elems[0].lower() + "".join(e.capitalize() for e in elems[1:])
44

    
45

    
46
def StringValueRules(value):
47
  """Converts a string value from Python to Haskell.
48

    
49
  """
50
  value = value.encode("string_escape") # escapes backslashes
51
  value = value.replace("\"", "\\\"")
52
  return value
53

    
54

    
55
def DictKeyName(dict_name, key_name):
56
  """Converts a dict plus key name to a full name.
57

    
58
  """
59
  return"%s_%s" % (dict_name, str(key_name).upper())
60

    
61

    
62
def HaskellTypeVal(value):
63
  """Returns the Haskell type and value for a Python value.
64

    
65
  Note that this only work for 'plain' Python types.
66

    
67
  @returns: (string, string) or None, if we can't determine the type.
68

    
69
  """
70
  if isinstance(value, basestring):
71
    return ("String", "\"%s\"" % StringValueRules(value))
72
  elif isinstance(value, int):
73
    return ("Int", "%d" % value)
74
  elif isinstance(value, long):
75
    return ("Integer", "%d" % value)
76
  elif isinstance(value, float):
77
    return ("Double", "%f" % value)
78
  else:
79
    return None
80

    
81

    
82
def ConvertVariable(name, value):
83
  """Converts a given variable to Haskell code.
84

    
85
  @param name: the Python name
86
  @param value: the value
87
  @return: a list of Haskell code lines
88

    
89
  """
90
  lines = []
91
  hs_name = NameRules(name)
92
  hs_typeval = HaskellTypeVal(value)
93
  if not CONSTANT_RE.match(name):
94
    lines.append("-- Skipped %s, not constant" % name)
95
  elif hs_typeval is not None:
96
    # this is a simple value
97
    (hs_type, hs_val) = hs_typeval
98
    lines.append("-- | Converted from Python constant %s" % name)
99
    lines.append("%s :: %s" % (hs_name, hs_type))
100
    lines.append("%s = %s" % (hs_name, hs_val))
101
  elif isinstance(value, dict):
102
    if value:
103
      lines.append("-- Following lines come from dictionary %s" % name)
104
      for k in sorted(value.keys()):
105
        lines.extend(ConvertVariable(DictKeyName(name, k), value[k]))
106
  elif isinstance(value, tuple):
107
    tvs = [HaskellTypeVal(elem) for elem in value]
108
    if compat.all(e is not None for e in tvs):
109
      ttypes = ", ".join(e[0] for e in tvs)
110
      tvals = ", ".join(e[1] for e in tvs)
111
      lines.append("-- | Converted from Python tuple %s" % name)
112
      lines.append("%s :: (%s)" % (hs_name, ttypes))
113
      lines.append("%s = (%s)" % (hs_name, tvals))
114
    else:
115
      lines.append("-- Skipped tuple %s, cannot convert all elements" % name)
116
  elif isinstance(value, (list, set, frozenset)):
117
    # Lists and frozensets are handled the same in Haskell: as lists,
118
    # since lists are immutable and we don't need for constants the
119
    # high-speed of an actual Set type. However, we can only convert
120
    # them if they have the same type for all elements (which is a
121
    # normal expectation for constants, our code should be well
122
    # behaved); note that this is different from the tuples case,
123
    # where we always (for some values of always) can convert
124
    tvs = [HaskellTypeVal(elem) for elem in value]
125
    if compat.all(e is not None for e in tvs):
126
      ttypes, tvals = zip(*tvs)
127
      uniq_types = set(ttypes)
128
      if len(uniq_types) == 1:
129
        lines.append("-- | Converted from Python list or set %s" % name)
130
        lines.append("%s :: [%s]" % (hs_name, uniq_types.pop()))
131
        lines.append("%s = [%s]" % (hs_name, ", ".join(tvals)))
132
      else:
133
        lines.append("-- | Skipped list/set %s, is not homogeneous" % name)
134
    else:
135
      lines.append("-- | Skipped list/set %s, cannot convert all elems" % name)
136
  elif isinstance(value, RE_TYPE):
137
    tvs = HaskellTypeVal(value.pattern)
138
    assert tvs is not None
139
    lines.append("-- | Converted from Python RE object %s" % name)
140
    lines.append("%s :: %s" % (hs_name, tvs[0]))
141
    lines.append("%s = %s" % (hs_name, tvs[1]))
142
  else:
143
    lines.append("-- Skipped %s, %s not handled" % (name, type(value)))
144
  return lines
145

    
146

    
147
def Convert():
148
  """Converts the constants to Haskell.
149

    
150
  """
151
  lines = [""]
152

    
153
  all_names = dir(constants)
154

    
155
  for name in all_names:
156
    value = getattr(constants, name)
157
    lines.extend(ConvertVariable(name, value))
158
    lines.append("")
159

    
160
  return "\n".join(lines)
161

    
162

    
163
def main():
164
  print Convert()
165

    
166

    
167
if __name__ == "__main__":
168
  main()