Statistics
| Branch: | Tag: | Revision:

root / autotools / convert-constants @ 88ac4075

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