Statistics
| Branch: | Tag: | Revision:

root / autotools / convert-constants @ 79a04823

History | View | Annotate | Download (7.4 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 2325bfcf Iustin Pop
from ganeti import compat
29 09dc9a02 Iustin Pop
from ganeti import constants
30 09dc9a02 Iustin Pop
from ganeti import luxi
31 d99d1e36 Iustin Pop
32 26fce8df Iustin Pop
#: Constant name regex
33 8751c041 Iustin Pop
CONSTANT_RE = re.compile("^[A-Z][A-Z0-9_-]+$")
34 d99d1e36 Iustin Pop
35 09dc9a02 Iustin Pop
#: Private name regex
36 09dc9a02 Iustin Pop
PRIVATE_RE = re.compile("^__.+__$")
37 09dc9a02 Iustin Pop
38 26fce8df Iustin Pop
#: The type of regex objects
39 26fce8df Iustin Pop
RE_TYPE = type(CONSTANT_RE)
40 26fce8df Iustin Pop
41 d99d1e36 Iustin Pop
42 d99d1e36 Iustin Pop
def NameRules(name):
43 d99d1e36 Iustin Pop
  """Converts the upper-cased Python name to Haskell camelCase.
44 d99d1e36 Iustin Pop
45 d99d1e36 Iustin Pop
  """
46 8751c041 Iustin Pop
  name = name.replace("-", "_")
47 d99d1e36 Iustin Pop
  elems = name.split("_")
48 d99d1e36 Iustin Pop
  return elems[0].lower() + "".join(e.capitalize() for e in elems[1:])
49 d99d1e36 Iustin Pop
50 d99d1e36 Iustin Pop
51 d99d1e36 Iustin Pop
def StringValueRules(value):
52 d99d1e36 Iustin Pop
  """Converts a string value from Python to Haskell.
53 d99d1e36 Iustin Pop
54 d99d1e36 Iustin Pop
  """
55 d99d1e36 Iustin Pop
  value = value.encode("string_escape") # escapes backslashes
56 d99d1e36 Iustin Pop
  value = value.replace("\"", "\\\"")
57 d99d1e36 Iustin Pop
  return value
58 d99d1e36 Iustin Pop
59 d99d1e36 Iustin Pop
60 8751c041 Iustin Pop
def DictKeyName(dict_name, key_name):
61 8751c041 Iustin Pop
  """Converts a dict plus key name to a full name.
62 8751c041 Iustin Pop
63 8751c041 Iustin Pop
  """
64 8751c041 Iustin Pop
  return"%s_%s" % (dict_name, str(key_name).upper())
65 8751c041 Iustin Pop
66 8751c041 Iustin Pop
67 df41d855 Iustin Pop
def HaskellTypeVal(value):
68 df41d855 Iustin Pop
  """Returns the Haskell type and value for a Python value.
69 df41d855 Iustin Pop
70 df41d855 Iustin Pop
  Note that this only work for 'plain' Python types.
71 df41d855 Iustin Pop
72 df41d855 Iustin Pop
  @returns: (string, string) or None, if we can't determine the type.
73 df41d855 Iustin Pop
74 df41d855 Iustin Pop
  """
75 df41d855 Iustin Pop
  if isinstance(value, basestring):
76 df41d855 Iustin Pop
    return ("String", "\"%s\"" % StringValueRules(value))
77 df41d855 Iustin Pop
  elif isinstance(value, int):
78 df41d855 Iustin Pop
    return ("Int", "%d" % value)
79 df41d855 Iustin Pop
  elif isinstance(value, long):
80 df41d855 Iustin Pop
    return ("Integer", "%d" % value)
81 df41d855 Iustin Pop
  elif isinstance(value, float):
82 df41d855 Iustin Pop
    return ("Double", "%f" % value)
83 df41d855 Iustin Pop
  else:
84 df41d855 Iustin Pop
    return None
85 df41d855 Iustin Pop
86 df41d855 Iustin Pop
87 79a04823 Iustin Pop
def IdentifyOrigin(all_items, value):
88 79a04823 Iustin Pop
  """Tries to identify a constant name from a constant's value.
89 79a04823 Iustin Pop
90 79a04823 Iustin Pop
  This uses a simple algorithm: is there a constant (and only one)
91 79a04823 Iustin Pop
  with the same value? If so, then it returns that constants' name.
92 79a04823 Iustin Pop
93 79a04823 Iustin Pop
  @note: it is recommended to use this only for tuples/lists/sets, and
94 79a04823 Iustin Pop
      not for individual (top-level) values
95 79a04823 Iustin Pop
  @param all_items: a dictionary of name/values for the current module
96 79a04823 Iustin Pop
  @param value: the value for which we try to find an origin
97 79a04823 Iustin Pop
98 79a04823 Iustin Pop
  """
99 79a04823 Iustin Pop
  found = [name for (name, v) in all_items.items() if v is value]
100 79a04823 Iustin Pop
  if len(found) == 1:
101 79a04823 Iustin Pop
    return found[0]
102 79a04823 Iustin Pop
  else:
103 79a04823 Iustin Pop
    return None
104 79a04823 Iustin Pop
105 79a04823 Iustin Pop
106 79a04823 Iustin Pop
def FormatListElems(all_items, pfx_name, ovals, tvals):
107 79a04823 Iustin Pop
  """Formats a list's elements.
108 79a04823 Iustin Pop
109 79a04823 Iustin Pop
  This formats the elements as either values or, if we find all
110 79a04823 Iustin Pop
  origins, as names.
111 79a04823 Iustin Pop
112 79a04823 Iustin Pop
  @param all_items: a dictionary of name/values for the current module
113 79a04823 Iustin Pop
  @param pfx_name: the prefix name currently used
114 79a04823 Iustin Pop
  @param ovals: the list of actual (Python) values
115 79a04823 Iustin Pop
  @param tvals: the list of values we want to format in the Haskell form
116 79a04823 Iustin Pop
117 79a04823 Iustin Pop
  """
118 79a04823 Iustin Pop
  origins = [IdentifyOrigin(all_items, v) for v in ovals]
119 79a04823 Iustin Pop
  if compat.all(x is not None for x in origins):
120 79a04823 Iustin Pop
    values = [NameRules(pfx_name + origin) for origin in origins]
121 79a04823 Iustin Pop
  else:
122 79a04823 Iustin Pop
    values = tvals
123 79a04823 Iustin Pop
  return ", ".join(values)
124 79a04823 Iustin Pop
125 79a04823 Iustin Pop
126 79a04823 Iustin Pop
def ConvertVariable(prefix, name, value, all_items):
127 8751c041 Iustin Pop
  """Converts a given variable to Haskell code.
128 8751c041 Iustin Pop
129 09dc9a02 Iustin Pop
  @param prefix: a prefix for the Haskell name (useful for module
130 09dc9a02 Iustin Pop
      identification)
131 8751c041 Iustin Pop
  @param name: the Python name
132 8751c041 Iustin Pop
  @param value: the value
133 79a04823 Iustin Pop
  @param all_items: a dictionary of name/value for the module being
134 79a04823 Iustin Pop
      processed
135 8751c041 Iustin Pop
  @return: a list of Haskell code lines
136 8751c041 Iustin Pop
137 8751c041 Iustin Pop
  """
138 8751c041 Iustin Pop
  lines = []
139 09dc9a02 Iustin Pop
  if prefix:
140 09dc9a02 Iustin Pop
    pfx_name = prefix + "_"
141 09dc9a02 Iustin Pop
    fqn = prefix + "." + name
142 09dc9a02 Iustin Pop
  else:
143 09dc9a02 Iustin Pop
    pfx_name = ""
144 09dc9a02 Iustin Pop
    fqn = name
145 09dc9a02 Iustin Pop
  hs_name = NameRules(pfx_name + name)
146 df41d855 Iustin Pop
  hs_typeval = HaskellTypeVal(value)
147 09dc9a02 Iustin Pop
  if (isinstance(value, types.ModuleType) or callable(value) or
148 09dc9a02 Iustin Pop
      PRIVATE_RE.match(name)):
149 09dc9a02 Iustin Pop
    # no sense in marking these, as we don't _want_ to convert them; the
150 09dc9a02 Iustin Pop
    # message in the next if block is for datatypes we don't _know_
151 09dc9a02 Iustin Pop
    # (yet) how to convert
152 09dc9a02 Iustin Pop
    pass
153 09dc9a02 Iustin Pop
  elif not CONSTANT_RE.match(name):
154 09dc9a02 Iustin Pop
    lines.append("-- Skipped %s %s, not constant" % (fqn, type(value)))
155 df41d855 Iustin Pop
  elif hs_typeval is not None:
156 df41d855 Iustin Pop
    # this is a simple value
157 df41d855 Iustin Pop
    (hs_type, hs_val) = hs_typeval
158 09dc9a02 Iustin Pop
    lines.append("-- | Converted from Python constant %s" % fqn)
159 df41d855 Iustin Pop
    lines.append("%s :: %s" % (hs_name, hs_type))
160 df41d855 Iustin Pop
    lines.append("%s = %s" % (hs_name, hs_val))
161 8751c041 Iustin Pop
  elif isinstance(value, dict):
162 8751c041 Iustin Pop
    if value:
163 09dc9a02 Iustin Pop
      lines.append("-- Following lines come from dictionary %s" % fqn)
164 8751c041 Iustin Pop
      for k in sorted(value.keys()):
165 79a04823 Iustin Pop
        lines.extend(ConvertVariable(prefix, DictKeyName(name, k),
166 79a04823 Iustin Pop
                                     value[k], all_items))
167 2325bfcf Iustin Pop
  elif isinstance(value, tuple):
168 2325bfcf Iustin Pop
    tvs = [HaskellTypeVal(elem) for elem in value]
169 2325bfcf Iustin Pop
    if compat.all(e is not None for e in tvs):
170 2325bfcf Iustin Pop
      ttypes = ", ".join(e[0] for e in tvs)
171 79a04823 Iustin Pop
      tvals = FormatListElems(all_items, pfx_name, value, [e[1] for e in tvs])
172 09dc9a02 Iustin Pop
      lines.append("-- | Converted from Python tuple %s" % fqn)
173 2325bfcf Iustin Pop
      lines.append("%s :: (%s)" % (hs_name, ttypes))
174 2325bfcf Iustin Pop
      lines.append("%s = (%s)" % (hs_name, tvals))
175 2325bfcf Iustin Pop
    else:
176 09dc9a02 Iustin Pop
      lines.append("-- Skipped tuple %s, cannot convert all elements" % fqn)
177 cf57f778 Iustin Pop
  elif isinstance(value, (list, set, frozenset)):
178 cf57f778 Iustin Pop
    # Lists and frozensets are handled the same in Haskell: as lists,
179 cf57f778 Iustin Pop
    # since lists are immutable and we don't need for constants the
180 cf57f778 Iustin Pop
    # high-speed of an actual Set type. However, we can only convert
181 cf57f778 Iustin Pop
    # them if they have the same type for all elements (which is a
182 cf57f778 Iustin Pop
    # normal expectation for constants, our code should be well
183 cf57f778 Iustin Pop
    # behaved); note that this is different from the tuples case,
184 cf57f778 Iustin Pop
    # where we always (for some values of always) can convert
185 cf57f778 Iustin Pop
    tvs = [HaskellTypeVal(elem) for elem in value]
186 cf57f778 Iustin Pop
    if compat.all(e is not None for e in tvs):
187 cf57f778 Iustin Pop
      ttypes, tvals = zip(*tvs)
188 cf57f778 Iustin Pop
      uniq_types = set(ttypes)
189 cf57f778 Iustin Pop
      if len(uniq_types) == 1:
190 79a04823 Iustin Pop
        values = FormatListElems(all_items, pfx_name, value, tvals)
191 09dc9a02 Iustin Pop
        lines.append("-- | Converted from Python list or set %s" % fqn)
192 cf57f778 Iustin Pop
        lines.append("%s :: [%s]" % (hs_name, uniq_types.pop()))
193 79a04823 Iustin Pop
        lines.append("%s = [%s]" % (hs_name, values))
194 cf57f778 Iustin Pop
      else:
195 09dc9a02 Iustin Pop
        lines.append("-- | Skipped list/set %s, is not homogeneous" % fqn)
196 cf57f778 Iustin Pop
    else:
197 09dc9a02 Iustin Pop
      lines.append("-- | Skipped list/set %s, cannot convert all elems" % fqn)
198 26fce8df Iustin Pop
  elif isinstance(value, RE_TYPE):
199 26fce8df Iustin Pop
    tvs = HaskellTypeVal(value.pattern)
200 26fce8df Iustin Pop
    assert tvs is not None
201 09dc9a02 Iustin Pop
    lines.append("-- | Converted from Python RE object %s" % fqn)
202 26fce8df Iustin Pop
    lines.append("%s :: %s" % (hs_name, tvs[0]))
203 26fce8df Iustin Pop
    lines.append("%s = %s" % (hs_name, tvs[1]))
204 8751c041 Iustin Pop
  else:
205 09dc9a02 Iustin Pop
    lines.append("-- Skipped %s, %s not handled" % (fqn, type(value)))
206 8751c041 Iustin Pop
  return lines
207 8751c041 Iustin Pop
208 8751c041 Iustin Pop
209 09dc9a02 Iustin Pop
def Convert(module, prefix):
210 d99d1e36 Iustin Pop
  """Converts the constants to Haskell.
211 d99d1e36 Iustin Pop
212 d99d1e36 Iustin Pop
  """
213 d99d1e36 Iustin Pop
  lines = [""]
214 d99d1e36 Iustin Pop
215 79a04823 Iustin Pop
  all_items = dict((name, getattr(module, name)) for name in dir(module))
216 d99d1e36 Iustin Pop
217 79a04823 Iustin Pop
  for name in sorted(all_items.keys()):
218 79a04823 Iustin Pop
    value = all_items[name]
219 79a04823 Iustin Pop
    new_lines = ConvertVariable(prefix, name, value, all_items)
220 09dc9a02 Iustin Pop
    if new_lines:
221 09dc9a02 Iustin Pop
      lines.extend(new_lines)
222 09dc9a02 Iustin Pop
      lines.append("")
223 d99d1e36 Iustin Pop
224 d99d1e36 Iustin Pop
  return "\n".join(lines)
225 d99d1e36 Iustin Pop
226 d99d1e36 Iustin Pop
227 d99d1e36 Iustin Pop
def main():
228 09dc9a02 Iustin Pop
  print Convert(constants, "")
229 09dc9a02 Iustin Pop
  print Convert(luxi, "luxi")
230 d99d1e36 Iustin Pop
231 d99d1e36 Iustin Pop
232 d99d1e36 Iustin Pop
if __name__ == "__main__":
233 d99d1e36 Iustin Pop
  main()