Fix relocation test ClusterAllocRelocate
[ganeti-local] / autotools / convert-constants
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()