4 # Copyright (C) 2011 Google Inc.
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.
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.
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
21 """Script for converting Python constants to Haskell code fragments.
27 from ganeti import constants
28 from ganeti import compat
30 #: Constant name regex
31 CONSTANT_RE = re.compile("^[A-Z][A-Z0-9_-]+$")
33 #: The type of regex objects
34 RE_TYPE = type(CONSTANT_RE)
38 """Converts the upper-cased Python name to Haskell camelCase.
41 name = name.replace("-", "_")
42 elems = name.split("_")
43 return elems[0].lower() + "".join(e.capitalize() for e in elems[1:])
46 def StringValueRules(value):
47 """Converts a string value from Python to Haskell.
50 value = value.encode("string_escape") # escapes backslashes
51 value = value.replace("\"", "\\\"")
55 def DictKeyName(dict_name, key_name):
56 """Converts a dict plus key name to a full name.
59 return"%s_%s" % (dict_name, str(key_name).upper())
62 def HaskellTypeVal(value):
63 """Returns the Haskell type and value for a Python value.
65 Note that this only work for 'plain' Python types.
67 @returns: (string, string) or None, if we can't determine the type.
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)
82 def ConvertVariable(name, value):
83 """Converts a given variable to Haskell code.
85 @param name: the Python name
86 @param value: the value
87 @return: a list of Haskell code 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):
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))
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)))
133 lines.append("-- | Skipped list/set %s, is not homogeneous" % name)
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]))
143 lines.append("-- Skipped %s, %s not handled" % (name, type(value)))
148 """Converts the constants to Haskell.
153 all_names = dir(constants)
155 for name in all_names:
156 value = getattr(constants, name)
157 lines.extend(ConvertVariable(name, value))
160 return "\n".join(lines)
167 if __name__ == "__main__":