Statistics
| Branch: | Tag: | Revision:

root / autotools / convert-constants @ 79a04823

History | View | Annotate | Download (7.4 kB)

1
#!/usr/bin/python
2
#
3

    
4
# Copyright (C) 2011, 2012 Google Inc.
5
#
6
# This program is free software; you can redistribute it and/or modify
7
# it under the terms of the GNU General Public License as published by
8
# the Free Software Foundation; either version 2 of the License, or
9
# (at your option) any later version.
10
#
11
# This program is distributed in the hope that it will be useful, but
12
# WITHOUT ANY WARRANTY; without even the implied warranty of
13
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14
# General Public License for more details.
15
#
16
# You should have received a copy of the GNU General Public License
17
# along with this program; if not, write to the Free Software
18
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19
# 02110-1301, USA.
20

    
21
"""Script for converting Python constants to Haskell code fragments.
22

    
23
"""
24

    
25
import re
26
import types
27

    
28
from ganeti import compat
29
from ganeti import constants
30
from ganeti import luxi
31

    
32
#: Constant name regex
33
CONSTANT_RE = re.compile("^[A-Z][A-Z0-9_-]+$")
34

    
35
#: Private name regex
36
PRIVATE_RE = re.compile("^__.+__$")
37

    
38
#: The type of regex objects
39
RE_TYPE = type(CONSTANT_RE)
40

    
41

    
42
def NameRules(name):
43
  """Converts the upper-cased Python name to Haskell camelCase.
44

    
45
  """
46
  name = name.replace("-", "_")
47
  elems = name.split("_")
48
  return elems[0].lower() + "".join(e.capitalize() for e in elems[1:])
49

    
50

    
51
def StringValueRules(value):
52
  """Converts a string value from Python to Haskell.
53

    
54
  """
55
  value = value.encode("string_escape") # escapes backslashes
56
  value = value.replace("\"", "\\\"")
57
  return value
58

    
59

    
60
def DictKeyName(dict_name, key_name):
61
  """Converts a dict plus key name to a full name.
62

    
63
  """
64
  return"%s_%s" % (dict_name, str(key_name).upper())
65

    
66

    
67
def HaskellTypeVal(value):
68
  """Returns the Haskell type and value for a Python value.
69

    
70
  Note that this only work for 'plain' Python types.
71

    
72
  @returns: (string, string) or None, if we can't determine the type.
73

    
74
  """
75
  if isinstance(value, basestring):
76
    return ("String", "\"%s\"" % StringValueRules(value))
77
  elif isinstance(value, int):
78
    return ("Int", "%d" % value)
79
  elif isinstance(value, long):
80
    return ("Integer", "%d" % value)
81
  elif isinstance(value, float):
82
    return ("Double", "%f" % value)
83
  else:
84
    return None
85

    
86

    
87
def IdentifyOrigin(all_items, value):
88
  """Tries to identify a constant name from a constant's value.
89

    
90
  This uses a simple algorithm: is there a constant (and only one)
91
  with the same value? If so, then it returns that constants' name.
92

    
93
  @note: it is recommended to use this only for tuples/lists/sets, and
94
      not for individual (top-level) values
95
  @param all_items: a dictionary of name/values for the current module
96
  @param value: the value for which we try to find an origin
97

    
98
  """
99
  found = [name for (name, v) in all_items.items() if v is value]
100
  if len(found) == 1:
101
    return found[0]
102
  else:
103
    return None
104

    
105

    
106
def FormatListElems(all_items, pfx_name, ovals, tvals):
107
  """Formats a list's elements.
108

    
109
  This formats the elements as either values or, if we find all
110
  origins, as names.
111

    
112
  @param all_items: a dictionary of name/values for the current module
113
  @param pfx_name: the prefix name currently used
114
  @param ovals: the list of actual (Python) values
115
  @param tvals: the list of values we want to format in the Haskell form
116

    
117
  """
118
  origins = [IdentifyOrigin(all_items, v) for v in ovals]
119
  if compat.all(x is not None for x in origins):
120
    values = [NameRules(pfx_name + origin) for origin in origins]
121
  else:
122
    values = tvals
123
  return ", ".join(values)
124

    
125

    
126
def ConvertVariable(prefix, name, value, all_items):
127
  """Converts a given variable to Haskell code.
128

    
129
  @param prefix: a prefix for the Haskell name (useful for module
130
      identification)
131
  @param name: the Python name
132
  @param value: the value
133
  @param all_items: a dictionary of name/value for the module being
134
      processed
135
  @return: a list of Haskell code lines
136

    
137
  """
138
  lines = []
139
  if prefix:
140
    pfx_name = prefix + "_"
141
    fqn = prefix + "." + name
142
  else:
143
    pfx_name = ""
144
    fqn = name
145
  hs_name = NameRules(pfx_name + name)
146
  hs_typeval = HaskellTypeVal(value)
147
  if (isinstance(value, types.ModuleType) or callable(value) or
148
      PRIVATE_RE.match(name)):
149
    # no sense in marking these, as we don't _want_ to convert them; the
150
    # message in the next if block is for datatypes we don't _know_
151
    # (yet) how to convert
152
    pass
153
  elif not CONSTANT_RE.match(name):
154
    lines.append("-- Skipped %s %s, not constant" % (fqn, type(value)))
155
  elif hs_typeval is not None:
156
    # this is a simple value
157
    (hs_type, hs_val) = hs_typeval
158
    lines.append("-- | Converted from Python constant %s" % fqn)
159
    lines.append("%s :: %s" % (hs_name, hs_type))
160
    lines.append("%s = %s" % (hs_name, hs_val))
161
  elif isinstance(value, dict):
162
    if value:
163
      lines.append("-- Following lines come from dictionary %s" % fqn)
164
      for k in sorted(value.keys()):
165
        lines.extend(ConvertVariable(prefix, DictKeyName(name, k),
166
                                     value[k], all_items))
167
  elif isinstance(value, tuple):
168
    tvs = [HaskellTypeVal(elem) for elem in value]
169
    if compat.all(e is not None for e in tvs):
170
      ttypes = ", ".join(e[0] for e in tvs)
171
      tvals = FormatListElems(all_items, pfx_name, value, [e[1] for e in tvs])
172
      lines.append("-- | Converted from Python tuple %s" % fqn)
173
      lines.append("%s :: (%s)" % (hs_name, ttypes))
174
      lines.append("%s = (%s)" % (hs_name, tvals))
175
    else:
176
      lines.append("-- Skipped tuple %s, cannot convert all elements" % fqn)
177
  elif isinstance(value, (list, set, frozenset)):
178
    # Lists and frozensets are handled the same in Haskell: as lists,
179
    # since lists are immutable and we don't need for constants the
180
    # high-speed of an actual Set type. However, we can only convert
181
    # them if they have the same type for all elements (which is a
182
    # normal expectation for constants, our code should be well
183
    # behaved); note that this is different from the tuples case,
184
    # where we always (for some values of always) can convert
185
    tvs = [HaskellTypeVal(elem) for elem in value]
186
    if compat.all(e is not None for e in tvs):
187
      ttypes, tvals = zip(*tvs)
188
      uniq_types = set(ttypes)
189
      if len(uniq_types) == 1:
190
        values = FormatListElems(all_items, pfx_name, value, tvals)
191
        lines.append("-- | Converted from Python list or set %s" % fqn)
192
        lines.append("%s :: [%s]" % (hs_name, uniq_types.pop()))
193
        lines.append("%s = [%s]" % (hs_name, values))
194
      else:
195
        lines.append("-- | Skipped list/set %s, is not homogeneous" % fqn)
196
    else:
197
      lines.append("-- | Skipped list/set %s, cannot convert all elems" % fqn)
198
  elif isinstance(value, RE_TYPE):
199
    tvs = HaskellTypeVal(value.pattern)
200
    assert tvs is not None
201
    lines.append("-- | Converted from Python RE object %s" % fqn)
202
    lines.append("%s :: %s" % (hs_name, tvs[0]))
203
    lines.append("%s = %s" % (hs_name, tvs[1]))
204
  else:
205
    lines.append("-- Skipped %s, %s not handled" % (fqn, type(value)))
206
  return lines
207

    
208

    
209
def Convert(module, prefix):
210
  """Converts the constants to Haskell.
211

    
212
  """
213
  lines = [""]
214

    
215
  all_items = dict((name, getattr(module, name)) for name in dir(module))
216

    
217
  for name in sorted(all_items.keys()):
218
    value = all_items[name]
219
    new_lines = ConvertVariable(prefix, name, value, all_items)
220
    if new_lines:
221
      lines.extend(new_lines)
222
      lines.append("")
223

    
224
  return "\n".join(lines)
225

    
226

    
227
def main():
228
  print Convert(constants, "")
229
  print Convert(luxi, "luxi")
230

    
231

    
232
if __name__ == "__main__":
233
  main()