Statistics
| Branch: | Tag: | Revision:

root / autotools / convert-constants @ 7b3cbe02

History | View | Annotate | Download (10.8 kB)

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