Statistics
| Branch: | Tag: | Revision:

root / autotools / convert-constants @ 24476fa0

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