Statistics
| Branch: | Tag: | Revision:

root / autotools / convert-constants @ 09dc9a02

History | View | Annotate | Download (5.8 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 ConvertVariable(prefix, name, value):
88
  """Converts a given variable to Haskell code.
89

    
90
  @param prefix: a prefix for the Haskell name (useful for module
91
      identification)
92
  @param name: the Python name
93
  @param value: the value
94
  @return: a list of Haskell code lines
95

    
96
  """
97
  lines = []
98
  if prefix:
99
    pfx_name = prefix + "_"
100
    fqn = prefix + "." + name
101
  else:
102
    pfx_name = ""
103
    fqn = name
104
  hs_name = NameRules(pfx_name + name)
105
  hs_typeval = HaskellTypeVal(value)
106
  if (isinstance(value, types.ModuleType) or callable(value) or
107
      PRIVATE_RE.match(name)):
108
    # no sense in marking these, as we don't _want_ to convert them; the
109
    # message in the next if block is for datatypes we don't _know_
110
    # (yet) how to convert
111
    pass
112
  elif not CONSTANT_RE.match(name):
113
    lines.append("-- Skipped %s %s, not constant" % (fqn, type(value)))
114
  elif hs_typeval is not None:
115
    # this is a simple value
116
    (hs_type, hs_val) = hs_typeval
117
    lines.append("-- | Converted from Python constant %s" % fqn)
118
    lines.append("%s :: %s" % (hs_name, hs_type))
119
    lines.append("%s = %s" % (hs_name, hs_val))
120
  elif isinstance(value, dict):
121
    if value:
122
      lines.append("-- Following lines come from dictionary %s" % fqn)
123
      for k in sorted(value.keys()):
124
        lines.extend(ConvertVariable(prefix, DictKeyName(name, k), value[k]))
125
  elif isinstance(value, tuple):
126
    tvs = [HaskellTypeVal(elem) for elem in value]
127
    if compat.all(e is not None for e in tvs):
128
      ttypes = ", ".join(e[0] for e in tvs)
129
      tvals = ", ".join(e[1] for e in tvs)
130
      lines.append("-- | Converted from Python tuple %s" % fqn)
131
      lines.append("%s :: (%s)" % (hs_name, ttypes))
132
      lines.append("%s = (%s)" % (hs_name, tvals))
133
    else:
134
      lines.append("-- Skipped tuple %s, cannot convert all elements" % fqn)
135
  elif isinstance(value, (list, set, frozenset)):
136
    # Lists and frozensets are handled the same in Haskell: as lists,
137
    # since lists are immutable and we don't need for constants the
138
    # high-speed of an actual Set type. However, we can only convert
139
    # them if they have the same type for all elements (which is a
140
    # normal expectation for constants, our code should be well
141
    # behaved); note that this is different from the tuples case,
142
    # where we always (for some values of always) can convert
143
    tvs = [HaskellTypeVal(elem) for elem in value]
144
    if compat.all(e is not None for e in tvs):
145
      ttypes, tvals = zip(*tvs)
146
      uniq_types = set(ttypes)
147
      if len(uniq_types) == 1:
148
        lines.append("-- | Converted from Python list or set %s" % fqn)
149
        lines.append("%s :: [%s]" % (hs_name, uniq_types.pop()))
150
        lines.append("%s = [%s]" % (hs_name, ", ".join(tvals)))
151
      else:
152
        lines.append("-- | Skipped list/set %s, is not homogeneous" % fqn)
153
    else:
154
      lines.append("-- | Skipped list/set %s, cannot convert all elems" % fqn)
155
  elif isinstance(value, RE_TYPE):
156
    tvs = HaskellTypeVal(value.pattern)
157
    assert tvs is not None
158
    lines.append("-- | Converted from Python RE object %s" % fqn)
159
    lines.append("%s :: %s" % (hs_name, tvs[0]))
160
    lines.append("%s = %s" % (hs_name, tvs[1]))
161
  else:
162
    lines.append("-- Skipped %s, %s not handled" % (fqn, type(value)))
163
  return lines
164

    
165

    
166
def Convert(module, prefix):
167
  """Converts the constants to Haskell.
168

    
169
  """
170
  lines = [""]
171

    
172
  all_names = dir(module)
173

    
174
  for name in all_names:
175
    value = getattr(module, name)
176
    new_lines = ConvertVariable(prefix, name, value)
177
    if new_lines:
178
      lines.extend(new_lines)
179
      lines.append("")
180

    
181
  return "\n".join(lines)
182

    
183

    
184
def main():
185
  print Convert(constants, "")
186
  print Convert(luxi, "luxi")
187

    
188

    
189
if __name__ == "__main__":
190
  main()