Statistics
| Branch: | Tag: | Revision:

root / autotools / convert-constants @ d31193c3

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