Statistics
| Branch: | Tag: | Revision:

root / autotools / convert-constants @ cefd4a4a

History | View | Annotate | Download (10.7 kB)

1 d99d1e36 Iustin Pop
#!/usr/bin/python
2 d99d1e36 Iustin Pop
#
3 d99d1e36 Iustin Pop
4 09dc9a02 Iustin Pop
# Copyright (C) 2011, 2012 Google Inc.
5 d99d1e36 Iustin Pop
#
6 d99d1e36 Iustin Pop
# This program is free software; you can redistribute it and/or modify
7 d99d1e36 Iustin Pop
# it under the terms of the GNU General Public License as published by
8 d99d1e36 Iustin Pop
# the Free Software Foundation; either version 2 of the License, or
9 d99d1e36 Iustin Pop
# (at your option) any later version.
10 d99d1e36 Iustin Pop
#
11 d99d1e36 Iustin Pop
# This program is distributed in the hope that it will be useful, but
12 d99d1e36 Iustin Pop
# WITHOUT ANY WARRANTY; without even the implied warranty of
13 d99d1e36 Iustin Pop
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 d99d1e36 Iustin Pop
# General Public License for more details.
15 d99d1e36 Iustin Pop
#
16 d99d1e36 Iustin Pop
# You should have received a copy of the GNU General Public License
17 d99d1e36 Iustin Pop
# along with this program; if not, write to the Free Software
18 d99d1e36 Iustin Pop
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19 d99d1e36 Iustin Pop
# 02110-1301, USA.
20 d99d1e36 Iustin Pop
21 d99d1e36 Iustin Pop
"""Script for converting Python constants to Haskell code fragments.
22 d99d1e36 Iustin Pop
23 d99d1e36 Iustin Pop
"""
24 d99d1e36 Iustin Pop
25 d99d1e36 Iustin Pop
import re
26 09dc9a02 Iustin Pop
import types
27 d99d1e36 Iustin Pop
28 d5cc16d7 Iustin Pop
from ganeti import _autoconf
29 2325bfcf Iustin Pop
from ganeti import compat
30 09dc9a02 Iustin Pop
from ganeti import constants
31 d5cc16d7 Iustin Pop
from ganeti import errors
32 09dc9a02 Iustin Pop
from ganeti import luxi
33 9b773665 Iustin Pop
from ganeti import opcodes
34 31d1d442 Iustin Pop
from ganeti import qlang
35 9eeb0aa5 Michael Hanselmann
36 d99d1e36 Iustin Pop
37 26fce8df Iustin Pop
#: Constant name regex
38 8751c041 Iustin Pop
CONSTANT_RE = re.compile("^[A-Z][A-Z0-9_-]+$")
39 d99d1e36 Iustin Pop
40 09dc9a02 Iustin Pop
#: Private name regex
41 09dc9a02 Iustin Pop
PRIVATE_RE = re.compile("^__.+__$")
42 09dc9a02 Iustin Pop
43 26fce8df Iustin Pop
#: The type of regex objects
44 26fce8df Iustin Pop
RE_TYPE = type(CONSTANT_RE)
45 26fce8df Iustin Pop
46 806aa124 Iustin Pop
#: Keys which do not declare a value (manually maintained). By adding
47 806aa124 Iustin Pop
# values here, we can make more lists use the actual names; otherwise
48 806aa124 Iustin Pop
# we'll have (e.g.) both DEFAULT_ENABLED_HYPERVISOR and HT_XEN_PVM
49 806aa124 Iustin Pop
# declare the same value, and thus the list of valid hypervisors will
50 806aa124 Iustin Pop
# have strings instead of easily looked-up names.
51 806aa124 Iustin Pop
IGNORED_DECL_NAMES = ["DEFAULT_ENABLED_HYPERVISOR"]
52 d99d1e36 Iustin Pop
53 9eeb0aa5 Michael Hanselmann
54 d99d1e36 Iustin Pop
def NameRules(name):
55 d99d1e36 Iustin Pop
  """Converts the upper-cased Python name to Haskell camelCase.
56 d99d1e36 Iustin Pop
57 d99d1e36 Iustin Pop
  """
58 8751c041 Iustin Pop
  name = name.replace("-", "_")
59 d99d1e36 Iustin Pop
  elems = name.split("_")
60 d99d1e36 Iustin Pop
  return elems[0].lower() + "".join(e.capitalize() for e in elems[1:])
61 d99d1e36 Iustin Pop
62 d99d1e36 Iustin Pop
63 d99d1e36 Iustin Pop
def StringValueRules(value):
64 d99d1e36 Iustin Pop
  """Converts a string value from Python to Haskell.
65 d99d1e36 Iustin Pop
66 d99d1e36 Iustin Pop
  """
67 d99d1e36 Iustin Pop
  value = value.encode("string_escape") # escapes backslashes
68 d99d1e36 Iustin Pop
  value = value.replace("\"", "\\\"")
69 d99d1e36 Iustin Pop
  return value
70 d99d1e36 Iustin Pop
71 d99d1e36 Iustin Pop
72 8751c041 Iustin Pop
def DictKeyName(dict_name, key_name):
73 8751c041 Iustin Pop
  """Converts a dict plus key name to a full name.
74 8751c041 Iustin Pop
75 8751c041 Iustin Pop
  """
76 8751c041 Iustin Pop
  return"%s_%s" % (dict_name, str(key_name).upper())
77 8751c041 Iustin Pop
78 8751c041 Iustin Pop
79 df41d855 Iustin Pop
def HaskellTypeVal(value):
80 df41d855 Iustin Pop
  """Returns the Haskell type and value for a Python value.
81 df41d855 Iustin Pop
82 df41d855 Iustin Pop
  Note that this only work for 'plain' Python types.
83 df41d855 Iustin Pop
84 df41d855 Iustin Pop
  @returns: (string, string) or None, if we can't determine the type.
85 df41d855 Iustin Pop
86 df41d855 Iustin Pop
  """
87 df41d855 Iustin Pop
  if isinstance(value, basestring):
88 df41d855 Iustin Pop
    return ("String", "\"%s\"" % StringValueRules(value))
89 df41d855 Iustin Pop
  elif isinstance(value, int):
90 df41d855 Iustin Pop
    return ("Int", "%d" % value)
91 df41d855 Iustin Pop
  elif isinstance(value, long):
92 df41d855 Iustin Pop
    return ("Integer", "%d" % value)
93 df41d855 Iustin Pop
  elif isinstance(value, float):
94 df41d855 Iustin Pop
    return ("Double", "%f" % value)
95 df41d855 Iustin Pop
  else:
96 df41d855 Iustin Pop
    return None
97 df41d855 Iustin Pop
98 df41d855 Iustin Pop
99 79a04823 Iustin Pop
def IdentifyOrigin(all_items, value):
100 79a04823 Iustin Pop
  """Tries to identify a constant name from a constant's value.
101 79a04823 Iustin Pop
102 79a04823 Iustin Pop
  This uses a simple algorithm: is there a constant (and only one)
103 79a04823 Iustin Pop
  with the same value? If so, then it returns that constants' name.
104 79a04823 Iustin Pop
105 79a04823 Iustin Pop
  @note: it is recommended to use this only for tuples/lists/sets, and
106 79a04823 Iustin Pop
      not for individual (top-level) values
107 79a04823 Iustin Pop
  @param all_items: a dictionary of name/values for the current module
108 79a04823 Iustin Pop
  @param value: the value for which we try to find an origin
109 79a04823 Iustin Pop
110 79a04823 Iustin Pop
  """
111 806aa124 Iustin Pop
  found = [name for (name, v) in all_items.items()
112 806aa124 Iustin Pop
           if v is value and name not in IGNORED_DECL_NAMES]
113 79a04823 Iustin Pop
  if len(found) == 1:
114 79a04823 Iustin Pop
    return found[0]
115 79a04823 Iustin Pop
  else:
116 79a04823 Iustin Pop
    return None
117 79a04823 Iustin Pop
118 79a04823 Iustin Pop
119 79a04823 Iustin Pop
def FormatListElems(all_items, pfx_name, ovals, tvals):
120 79a04823 Iustin Pop
  """Formats a list's elements.
121 79a04823 Iustin Pop
122 79a04823 Iustin Pop
  This formats the elements as either values or, if we find all
123 79a04823 Iustin Pop
  origins, as names.
124 79a04823 Iustin Pop
125 79a04823 Iustin Pop
  @param all_items: a dictionary of name/values for the current module
126 79a04823 Iustin Pop
  @param pfx_name: the prefix name currently used
127 79a04823 Iustin Pop
  @param ovals: the list of actual (Python) values
128 79a04823 Iustin Pop
  @param tvals: the list of values we want to format in the Haskell form
129 79a04823 Iustin Pop
130 79a04823 Iustin Pop
  """
131 79a04823 Iustin Pop
  origins = [IdentifyOrigin(all_items, v) for v in ovals]
132 79a04823 Iustin Pop
  if compat.all(x is not None for x in origins):
133 79a04823 Iustin Pop
    values = [NameRules(pfx_name + origin) for origin in origins]
134 79a04823 Iustin Pop
  else:
135 79a04823 Iustin Pop
    values = tvals
136 79a04823 Iustin Pop
  return ", ".join(values)
137 79a04823 Iustin Pop
138 79a04823 Iustin Pop
139 8c957eb3 Iustin Pop
def FormatDict(all_items, pfx_name, py_name, hs_name, mydict):
140 8c957eb3 Iustin Pop
  """Converts a dictionary to a Haskell association list ([(k, v)]),
141 8c957eb3 Iustin Pop
  if possible.
142 8c957eb3 Iustin Pop
143 8c957eb3 Iustin Pop
  @param all_items: a dictionary of name/values for the current module
144 8c957eb3 Iustin Pop
  @param pfx_name: the prefix name currently used
145 8c957eb3 Iustin Pop
  @param py_name: the Python name
146 8c957eb3 Iustin Pop
  @param hs_name: the Haskell name
147 8c957eb3 Iustin Pop
  @param mydict: a dictonary, unknown yet if homogenous or not
148 8c957eb3 Iustin Pop
149 8c957eb3 Iustin Pop
  """
150 8c957eb3 Iustin Pop
  # need this for ordering
151 8c957eb3 Iustin Pop
  orig_list = mydict.items()
152 8c957eb3 Iustin Pop
  list_form = [(HaskellTypeVal(k), HaskellTypeVal(v)) for k, v in orig_list]
153 8c957eb3 Iustin Pop
  if compat.any(v is None or k is None for k, v in list_form):
154 8c957eb3 Iustin Pop
    # type not known
155 8c957eb3 Iustin Pop
    return []
156 8c957eb3 Iustin Pop
  all_keys = [k for k, _ in list_form]
157 8c957eb3 Iustin Pop
  all_vals = [v for _, v in list_form]
158 8c957eb3 Iustin Pop
  key_types = set(k[0] for k in all_keys)
159 8c957eb3 Iustin Pop
  val_types = set(v[0] for v in all_vals)
160 8c957eb3 Iustin Pop
  if not(len(key_types) == 1 and len(val_types) == 1):
161 8c957eb3 Iustin Pop
    # multiple types
162 8c957eb3 Iustin Pop
    return []
163 8c957eb3 Iustin Pop
  # record the key and value Haskell types
164 8c957eb3 Iustin Pop
  key_type = key_types.pop()
165 8c957eb3 Iustin Pop
  val_type = val_types.pop()
166 8c957eb3 Iustin Pop
167 8c957eb3 Iustin Pop
  # now try to find names for the keys, instead of raw values
168 8c957eb3 Iustin Pop
  key_origins = [IdentifyOrigin(all_items, k) for k, _ in orig_list]
169 8c957eb3 Iustin Pop
  if compat.all(x is not None for x in key_origins):
170 8c957eb3 Iustin Pop
    key_v = [NameRules(pfx_name + origin) for origin in key_origins]
171 8c957eb3 Iustin Pop
  else:
172 8c957eb3 Iustin Pop
    key_v = [k[1] for k in all_keys]
173 8c957eb3 Iustin Pop
  # ... and for values
174 8c957eb3 Iustin Pop
  val_origins = [IdentifyOrigin(all_items, v) for _, v in orig_list]
175 8c957eb3 Iustin Pop
  if compat.all(x is not None for x in val_origins):
176 8c957eb3 Iustin Pop
    val_v = [NameRules(pfx_name + origin) for origin in val_origins]
177 8c957eb3 Iustin Pop
  else:
178 8c957eb3 Iustin Pop
    val_v = [v[1] for v in all_vals]
179 8c957eb3 Iustin Pop
180 8c957eb3 Iustin Pop
  # finally generate the output
181 8c957eb3 Iustin Pop
  kv_pairs = ["(%s, %s)" % (k, v) for k, v in zip(key_v, val_v)]
182 8c957eb3 Iustin Pop
  return ["-- | Converted from Python dictionary %s" % py_name,
183 8c957eb3 Iustin Pop
          "%s :: [(%s, %s)]" % (hs_name, key_type, val_type),
184 8c957eb3 Iustin Pop
          "%s = [%s]" % (hs_name, ", ".join(kv_pairs)),
185 8c957eb3 Iustin Pop
          ]
186 8c957eb3 Iustin Pop
187 8c957eb3 Iustin Pop
188 79a04823 Iustin Pop
def ConvertVariable(prefix, name, value, all_items):
189 8751c041 Iustin Pop
  """Converts a given variable to Haskell code.
190 8751c041 Iustin Pop
191 09dc9a02 Iustin Pop
  @param prefix: a prefix for the Haskell name (useful for module
192 09dc9a02 Iustin Pop
      identification)
193 8751c041 Iustin Pop
  @param name: the Python name
194 8751c041 Iustin Pop
  @param value: the value
195 79a04823 Iustin Pop
  @param all_items: a dictionary of name/value for the module being
196 79a04823 Iustin Pop
      processed
197 8751c041 Iustin Pop
  @return: a list of Haskell code lines
198 8751c041 Iustin Pop
199 8751c041 Iustin Pop
  """
200 8751c041 Iustin Pop
  lines = []
201 09dc9a02 Iustin Pop
  if prefix:
202 09dc9a02 Iustin Pop
    pfx_name = prefix + "_"
203 09dc9a02 Iustin Pop
    fqn = prefix + "." + name
204 09dc9a02 Iustin Pop
  else:
205 09dc9a02 Iustin Pop
    pfx_name = ""
206 09dc9a02 Iustin Pop
    fqn = name
207 09dc9a02 Iustin Pop
  hs_name = NameRules(pfx_name + name)
208 df41d855 Iustin Pop
  hs_typeval = HaskellTypeVal(value)
209 09dc9a02 Iustin Pop
  if (isinstance(value, types.ModuleType) or callable(value) or
210 09dc9a02 Iustin Pop
      PRIVATE_RE.match(name)):
211 09dc9a02 Iustin Pop
    # no sense in marking these, as we don't _want_ to convert them; the
212 09dc9a02 Iustin Pop
    # message in the next if block is for datatypes we don't _know_
213 09dc9a02 Iustin Pop
    # (yet) how to convert
214 09dc9a02 Iustin Pop
    pass
215 09dc9a02 Iustin Pop
  elif not CONSTANT_RE.match(name):
216 09dc9a02 Iustin Pop
    lines.append("-- Skipped %s %s, not constant" % (fqn, type(value)))
217 df41d855 Iustin Pop
  elif hs_typeval is not None:
218 df41d855 Iustin Pop
    # this is a simple value
219 df41d855 Iustin Pop
    (hs_type, hs_val) = hs_typeval
220 09dc9a02 Iustin Pop
    lines.append("-- | Converted from Python constant %s" % fqn)
221 df41d855 Iustin Pop
    lines.append("%s :: %s" % (hs_name, hs_type))
222 df41d855 Iustin Pop
    lines.append("%s = %s" % (hs_name, hs_val))
223 8751c041 Iustin Pop
  elif isinstance(value, dict):
224 8751c041 Iustin Pop
    if value:
225 09dc9a02 Iustin Pop
      lines.append("-- Following lines come from dictionary %s" % fqn)
226 8c957eb3 Iustin Pop
      # try to build a real map here, if all keys have same type, and
227 8c957eb3 Iustin Pop
      # all values too (i.e. we have a homogeneous dictionary)
228 8c957eb3 Iustin Pop
      lines.extend(FormatDict(all_items, pfx_name, fqn, hs_name, value))
229 8c957eb3 Iustin Pop
      # and now create individual names
230 8751c041 Iustin Pop
      for k in sorted(value.keys()):
231 79a04823 Iustin Pop
        lines.extend(ConvertVariable(prefix, DictKeyName(name, k),
232 79a04823 Iustin Pop
                                     value[k], all_items))
233 2325bfcf Iustin Pop
  elif isinstance(value, tuple):
234 2325bfcf Iustin Pop
    tvs = [HaskellTypeVal(elem) for elem in value]
235 9ba02574 Iustin Pop
    # Custom rule for special cluster verify error tuples
236 9ba02574 Iustin Pop
    if name.startswith("CV_E") and len(value) == 3 and tvs[1][0] is not None:
237 9ba02574 Iustin Pop
      cv_ename = hs_name + "Code"
238 9ba02574 Iustin Pop
      lines.append("-- | Special cluster verify code %s" % name)
239 9ba02574 Iustin Pop
      lines.append("%s :: %s" % (cv_ename, tvs[1][0]))
240 9ba02574 Iustin Pop
      lines.append("%s = %s" % (cv_ename, tvs[1][1]))
241 9ba02574 Iustin Pop
      lines.append("")
242 2325bfcf Iustin Pop
    if compat.all(e is not None for e in tvs):
243 2325bfcf Iustin Pop
      ttypes = ", ".join(e[0] for e in tvs)
244 79a04823 Iustin Pop
      tvals = FormatListElems(all_items, pfx_name, value, [e[1] for e in tvs])
245 09dc9a02 Iustin Pop
      lines.append("-- | Converted from Python tuple %s" % fqn)
246 2325bfcf Iustin Pop
      lines.append("%s :: (%s)" % (hs_name, ttypes))
247 2325bfcf Iustin Pop
      lines.append("%s = (%s)" % (hs_name, tvals))
248 2325bfcf Iustin Pop
    else:
249 09dc9a02 Iustin Pop
      lines.append("-- Skipped tuple %s, cannot convert all elements" % fqn)
250 cf57f778 Iustin Pop
  elif isinstance(value, (list, set, frozenset)):
251 cf57f778 Iustin Pop
    # Lists and frozensets are handled the same in Haskell: as lists,
252 cf57f778 Iustin Pop
    # since lists are immutable and we don't need for constants the
253 cf57f778 Iustin Pop
    # high-speed of an actual Set type. However, we can only convert
254 cf57f778 Iustin Pop
    # them if they have the same type for all elements (which is a
255 cf57f778 Iustin Pop
    # normal expectation for constants, our code should be well
256 cf57f778 Iustin Pop
    # behaved); note that this is different from the tuples case,
257 cf57f778 Iustin Pop
    # where we always (for some values of always) can convert
258 cf57f778 Iustin Pop
    tvs = [HaskellTypeVal(elem) for elem in value]
259 cf57f778 Iustin Pop
    if compat.all(e is not None for e in tvs):
260 cf57f778 Iustin Pop
      ttypes, tvals = zip(*tvs)
261 cf57f778 Iustin Pop
      uniq_types = set(ttypes)
262 cf57f778 Iustin Pop
      if len(uniq_types) == 1:
263 79a04823 Iustin Pop
        values = FormatListElems(all_items, pfx_name, value, tvals)
264 09dc9a02 Iustin Pop
        lines.append("-- | Converted from Python list or set %s" % fqn)
265 cf57f778 Iustin Pop
        lines.append("%s :: [%s]" % (hs_name, uniq_types.pop()))
266 79a04823 Iustin Pop
        lines.append("%s = [%s]" % (hs_name, values))
267 cf57f778 Iustin Pop
      else:
268 09dc9a02 Iustin Pop
        lines.append("-- | Skipped list/set %s, is not homogeneous" % fqn)
269 cf57f778 Iustin Pop
    else:
270 09dc9a02 Iustin Pop
      lines.append("-- | Skipped list/set %s, cannot convert all elems" % fqn)
271 26fce8df Iustin Pop
  elif isinstance(value, RE_TYPE):
272 26fce8df Iustin Pop
    tvs = HaskellTypeVal(value.pattern)
273 26fce8df Iustin Pop
    assert tvs is not None
274 09dc9a02 Iustin Pop
    lines.append("-- | Converted from Python RE object %s" % fqn)
275 26fce8df Iustin Pop
    lines.append("%s :: %s" % (hs_name, tvs[0]))
276 26fce8df Iustin Pop
    lines.append("%s = %s" % (hs_name, tvs[1]))
277 8751c041 Iustin Pop
  else:
278 09dc9a02 Iustin Pop
    lines.append("-- Skipped %s, %s not handled" % (fqn, type(value)))
279 8751c041 Iustin Pop
  return lines
280 8751c041 Iustin Pop
281 8751c041 Iustin Pop
282 09dc9a02 Iustin Pop
def Convert(module, prefix):
283 d99d1e36 Iustin Pop
  """Converts the constants to Haskell.
284 d99d1e36 Iustin Pop
285 d99d1e36 Iustin Pop
  """
286 d99d1e36 Iustin Pop
  lines = [""]
287 d99d1e36 Iustin Pop
288 79a04823 Iustin Pop
  all_items = dict((name, getattr(module, name)) for name in dir(module))
289 d99d1e36 Iustin Pop
290 79a04823 Iustin Pop
  for name in sorted(all_items.keys()):
291 79a04823 Iustin Pop
    value = all_items[name]
292 79a04823 Iustin Pop
    new_lines = ConvertVariable(prefix, name, value, all_items)
293 09dc9a02 Iustin Pop
    if new_lines:
294 09dc9a02 Iustin Pop
      lines.extend(new_lines)
295 09dc9a02 Iustin Pop
      lines.append("")
296 d99d1e36 Iustin Pop
297 d99d1e36 Iustin Pop
  return "\n".join(lines)
298 d99d1e36 Iustin Pop
299 d99d1e36 Iustin Pop
300 9b773665 Iustin Pop
def ConvertMisc():
301 9b773665 Iustin Pop
  """Convert some extra computed-values to Haskell.
302 9b773665 Iustin Pop
303 9b773665 Iustin Pop
  """
304 9b773665 Iustin Pop
  lines = [""]
305 9b773665 Iustin Pop
  lines.extend(ConvertVariable("opcodes", "OP_IDS",
306 9b773665 Iustin Pop
                               opcodes.OP_MAPPING.keys(), {}))
307 9b773665 Iustin Pop
  return "\n".join(lines)
308 9b773665 Iustin Pop
309 9b773665 Iustin Pop
310 d99d1e36 Iustin Pop
def main():
311 09dc9a02 Iustin Pop
  print Convert(constants, "")
312 09dc9a02 Iustin Pop
  print Convert(luxi, "luxi")
313 31d1d442 Iustin Pop
  print Convert(qlang, "qlang")
314 9eeb0aa5 Michael Hanselmann
  print Convert(_autoconf, "autoconf")
315 d5cc16d7 Iustin Pop
  print Convert(errors, "errors")
316 9b773665 Iustin Pop
  print ConvertMisc()
317 d99d1e36 Iustin Pop
318 d99d1e36 Iustin Pop
319 d99d1e36 Iustin Pop
if __name__ == "__main__":
320 d99d1e36 Iustin Pop
  main()