aboutsummaryrefslogtreecommitdiffstats
path: root/3rdparty/pybind11/tests/test_enum.py
blob: 7fe9b618d6e80d4b52d9ae5280c9896a76ad2c88 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
import pytest
from pybind11_tests import enums as m


def test_unscoped_enum():
    assert str(m.UnscopedEnum.EOne) == "UnscopedEnum.EOne"
    assert str(m.UnscopedEnum.ETwo) == "UnscopedEnum.ETwo"
    assert str(m.EOne) == "UnscopedEnum.EOne"

    # name property
    assert m.UnscopedEnum.EOne.name == "EOne"
    assert m.UnscopedEnum.ETwo.name == "ETwo"
    assert m.EOne.name == "EOne"
    # name readonly
    with pytest.raises(AttributeError):
        m.UnscopedEnum.EOne.name = ""
    # name returns a copy
    foo = m.UnscopedEnum.EOne.name
    foo = "bar"
    assert m.UnscopedEnum.EOne.name == "EOne"

    # __members__ property
    assert m.UnscopedEnum.__members__ == \
        {"EOne": m.UnscopedEnum.EOne, "ETwo": m.UnscopedEnum.ETwo, "EThree": m.UnscopedEnum.EThree}
    # __members__ readonly
    with pytest.raises(AttributeError):
        m.UnscopedEnum.__members__ = {}
    # __members__ returns a copy
    foo = m.UnscopedEnum.__members__
    foo["bar"] = "baz"
    assert m.UnscopedEnum.__members__ == \
        {"EOne": m.UnscopedEnum.EOne, "ETwo": m.UnscopedEnum.ETwo, "EThree": m.UnscopedEnum.EThree}

    for docstring_line in '''An unscoped enumeration

Members:

  EOne : Docstring for EOne

  ETwo : Docstring for ETwo

  EThree : Docstring for EThree'''.split('\n'):
        assert docstring_line in m.UnscopedEnum.__doc__

    # Unscoped enums will accept ==/!= int comparisons
    y = m.UnscopedEnum.ETwo
    assert y == 2
    assert 2 == y
    assert y != 3
    assert 3 != y
    # Compare with None
    assert (y != None)  # noqa: E711
    assert not (y == None)  # noqa: E711
    # Compare with an object
    assert (y != object())
    assert not (y == object())
    # Compare with string
    assert y != "2"
    assert "2" != y
    assert not ("2" == y)
    assert not (y == "2")

    with pytest.raises(TypeError):
        y < object()

    with pytest.raises(TypeError):
        y <= object()

    with pytest.raises(TypeError):
        y > object()

    with pytest.raises(TypeError):
        y >= object()

    with pytest.raises(TypeError):
        y | object()

    with pytest.raises(TypeError):
        y & object()

    with pytest.raises(TypeError):
        y ^ object()

    assert int(m.UnscopedEnum.ETwo) == 2
    assert str(m.UnscopedEnum(2)) == "UnscopedEnum.ETwo"

    # order
    assert m.UnscopedEnum.EOne < m.UnscopedEnum.ETwo
    assert m.UnscopedEnum.EOne < 2
    assert m.UnscopedEnum.ETwo > m.UnscopedEnum.EOne
    assert m.UnscopedEnum.ETwo > 1
    assert m.UnscopedEnum.ETwo <= 2
    assert m.UnscopedEnum.ETwo >= 2
    assert m.UnscopedEnum.EOne <= m.UnscopedEnum.ETwo
    assert m.UnscopedEnum.EOne <= 2
    assert m.UnscopedEnum.ETwo >= m.UnscopedEnum.EOne
    assert m.UnscopedEnum.ETwo >= 1
    assert not (m.UnscopedEnum.ETwo < m.UnscopedEnum.EOne)
    assert not (2 < m.UnscopedEnum.EOne)

    # arithmetic
    assert m.UnscopedEnum.EOne & m.UnscopedEnum.EThree == m.UnscopedEnum.EOne
    assert m.UnscopedEnum.EOne | m.UnscopedEnum.ETwo == m.UnscopedEnum.EThree
    assert m.UnscopedEnum.EOne ^ m.UnscopedEnum.EThree == m.UnscopedEnum.ETwo


def test_scoped_enum():
    assert m.test_scoped_enum(m.ScopedEnum.Three) == "ScopedEnum::Three"
    z = m.ScopedEnum.Two
    assert m.test_scoped_enum(z) == "ScopedEnum::Two"

    # Scoped enums will *NOT* accept ==/!= int comparisons (Will always return False)
    assert not z == 3
    assert not 3 == z
    assert z != 3
    assert 3 != z
    # Compare with None
    assert (z != None)  # noqa: E711
    assert not (z == None)  # noqa: E711
    # Compare with an object
    assert (z != object())
    assert not (z == object())
    # Scoped enums will *NOT* accept >, <, >= and <= int comparisons (Will throw exceptions)
    with pytest.raises(TypeError):
        z > 3
    with pytest.raises(TypeError):
        z < 3
    with pytest.raises(TypeError):
        z >= 3
    with pytest.raises(TypeError):
        z <= 3

    # order
    assert m.ScopedEnum.Two < m.ScopedEnum.Three
    assert m.ScopedEnum.Three > m.ScopedEnum.Two
    assert m.ScopedEnum.Two <= m.ScopedEnum.Three
    assert m.ScopedEnum.Two <= m.ScopedEnum.Two
    assert m.ScopedEnum.Two >= m.ScopedEnum.Two
    assert m.ScopedEnum.Three >= m.ScopedEnum.Two


def test_implicit_conversion():
    assert str(m.ClassWithUnscopedEnum.EMode.EFirstMode) == "EMode.EFirstMode"
    assert str(m.ClassWithUnscopedEnum.EFirstMode) == "EMode.EFirstMode"

    f = m.ClassWithUnscopedEnum.test_function
    first = m.ClassWithUnscopedEnum.EFirstMode
    second = m.ClassWithUnscopedEnum.ESecondMode

    assert f(first) == 1

    assert f(first) == f(first)
    assert not f(first) != f(first)

    assert f(first) != f(second)
    assert not f(first) == f(second)

    assert f(first) == int(f(first))
    assert not f(first) != int(f(first))

    assert f(first) != int(f(second))
    assert not f(first) == int(f(second))

    # noinspection PyDictCreation
    x = {f(first): 1, f(second): 2}
    x[f(first)] = 3
    x[f(second)] = 4
    # Hashing test
    assert str(x) == "{EMode.EFirstMode: 3, EMode.ESecondMode: 4}"


def test_binary_operators():
    assert int(m.Flags.Read) == 4
    assert int(m.Flags.Write) == 2
    assert int(m.Flags.Execute) == 1
    assert int(m.Flags.Read | m.Flags.Write | m.Flags.Execute) == 7
    assert int(m.Flags.Read | m.Flags.Write) == 6
    assert int(m.Flags.Read | m.Flags.Execute) == 5
    assert int(m.Flags.Write | m.Flags.Execute) == 3
    assert int(m.Flags.Write | 1) == 3
    assert ~m.Flags.Write == -3

    state = m.Flags.Read | m.Flags.Write
    assert (state & m.Flags.Read) != 0
    assert (state & m.Flags.Write) != 0
    assert (state & m.Flags.Execute) == 0
    assert (state & 1) == 0

    state2 = ~state
    assert state2 == -7
    assert int(state ^ state2) == -1


def test_enum_to_int():
    m.test_enum_to_int(m.Flags.Read)
    m.test_enum_to_int(m.ClassWithUnscopedEnum.EMode.EFirstMode)
    m.test_enum_to_uint(m.Flags.Read)
    m.test_enum_to_uint(m.ClassWithUnscopedEnum.EMode.EFirstMode)
    m.test_enum_to_long_long(m.Flags.Read)
    m.test_enum_to_long_long(m.ClassWithUnscopedEnum.EMode.EFirstMode)


def test_duplicate_enum_name():
    with pytest.raises(ValueError) as excinfo:
        m.register_bad_enum()
    assert str(excinfo.value) == 'SimpleEnum: element "ONE" already exists!'
pan>() != ' (\n': raise 'no open parenthesis after Format_Type' # Read formats l = lr.get() pat_field_name = re.compile(' Format_(\w+),?\n') while l != ' );\n': m = pat_field_name.match(l) if m is None: print l raise 'bad literal within Format_Type' name = m.group(1) formats.append(name) fields[name] = {} l = lr.get() # Read fields l = lr.get() pat_fields = re.compile(' -- Fields of Format_(\w+):\n') pat_field_desc = re.compile(' -- (\w+) : (\w+).*\n') format_name = '' common_desc = {} # Read until common fields. while l != ' -- Common fields are:\n': l = lr.get() format_name = 'Common' nbr_formats = 0 while True: # 1) Read field description l = lr.get() desc = common_desc.copy() while True: m = pat_field_desc.match(l) if m is None: break desc[m.group(1)] = m.group(2) l = lr.get() # print 'For: ' + format_name + ': ' + m.group(1) # 2) Disp if format_name == 'Common': common_desc = desc else: fields[format_name] = desc # 3) Read next format if l == '\n': if nbr_formats == len(fields): break else: l = lr.get() # One for a format m = pat_fields.match(l) if m is not None: format_name = m.group(1) if format_name not in fields: raise ParseError( lr, 'Format ' + format_name + ' is unknown') nbr_formats = nbr_formats + 1 else: raise ParseError(lr, 'unhandled format line') return (formats, fields) # Read kinds and kinds ranges. def read_kinds(filename): lr = linereader(filename) kinds = [] # Search for 'type Iir_Kind is' while lr.get() != ' type ' + type_name + ' is\n': pass # Skip '(' if lr.get() != ' (\n': raise ParseError( lr, 'no open parenthesis after "type ' + type_name + '"') # Read literals pat_node = re.compile(' ' + prefix_name + '(\w+),?( +-- .*)?\n') pat_comment = re.compile('( +-- .*)?\n') while True: l = lr.get() if l == ' );\n': break m = pat_node.match(l) if m: kinds.append(m.group(1)) continue m = pat_comment.match(l) if not m: raise ParseError(lr, 'Unknown line within kind declaration') # Check subtypes pat_subtype = re.compile(' subtype ' + r'(\w+) is ' + type_name + ' range\n') pat_first = re.compile(' ' + prefix_name + r'(\w+) ..\n') pat_last = re.compile(' ' + prefix_name + r'(\w+);\n') pat_middle = re.compile(' --' + prefix_name + r'(\w+)\n') kinds_ranges = {} while True: l = lr.get() # Start of methods is also end of subtypes. if l == ' -- General methods.\n': break # Found a subtype. m = pat_subtype.match(l) if m: # Check first bound name = m.group(1) if not name.startswith(prefix_range_name): raise ParseError(lr, 'incorrect prefix for subtype') name = name[len(prefix_range_name):] l = lr.get() mf = pat_first.match(l) if not mf: raise ParseError(lr, 'badly formated first bound of subtype') first = kinds.index(mf.group(1)) idx = first has_middle = None # Read until last bound while True: l = lr.get() ml = pat_middle.match(l) if ml: # Check element in the middle n = ml.group(1) if n not in kinds: raise ParseError( lr, "unknown kind " + n + " in subtype") if kinds.index(n) != idx + 1: raise ParseError( lr, "missing " + kinds[idx + 1] + " in subtype") has_middle = True idx = idx + 1 else: # Check last bound ml = pat_last.match(l) if ml: last = kinds.index(ml.group(1)) if last != idx + 1 and has_middle: raise ParseError( lr, "missing " + kinds[idx] + " in subtype") break raise ParseError(lr, "unhandled line in subtype") kinds_ranges[name] = kinds[first:last+1] return (kinds, kinds_ranges) # Read functions def read_methods(filename): lr = linereader(filename) funcs = [] pat_field = re.compile(r' -- Field: ([\w,]+)( \w+)?( \(\w+\))?\n') pat_conv = re.compile(r'^ \((\w+)\)$') pat_func = re.compile( r' function Get_(\w+) \((\w+) : (\w+)\) return (\w+);\n') pat_proc = re.compile( r' procedure Set_(\w+) \((\w+) : (\w+); (\w+) : (\w+)\);\n') pat_end = re.compile('end [A-Za-z.]+;\n') while True: l = lr.get() # Start of methods if l == ' -- General methods.\n': break while True: l = lr.get() if pat_end.match(l): break m = pat_field.match(l) if m: fields = m.group(1).split(',') # Extract access modifier acc = m.group(2) if acc: acc = acc.strip() # Extract conversion conv = m.group(3) if conv: mc = pat_conv.match(conv) if not mc: raise ParseError(lr, 'conversion ill formed') conv = mc.group(1) if conv not in conversions: raise ParseError(lr, 'unknown conversion ' + conv) else: conv = None if len(fields) > 1 and conv != 'grp': raise ParseError(lr, 'bad conversion for multiple fields') # Read function l = lr.get() mf = pat_func.match(l) if not mf: raise ParseError( lr, 'function declaration expected after Field') # Read procedure l = lr.get() mp = pat_proc.match(l) if not mp: raise ParseError( lr, 'procedure declaration expected after function') # Consistency check between function and procedure if mf.group(1) != mp.group(1): raise ParseError(lr, 'function and procedure name mismatch') if mf.group(2) != mp.group(2): raise ParseError(lr, 'parameter name mismatch with function') if mf.group(3) != mp.group(3): raise ParseError(lr, 'parameter type mismatch with function') if mf.group(4) != mp.group(5): raise ParseError(lr, 'result type mismatch with function') funcs.append(FuncDesc(mf.group(1), fields, conv, acc, mp.group(2), mp.group(3), mp.group(4), mp.group(5))) return funcs # Read description for one node # LR is the line reader. NAMES is the list of (node name, format) # (one description may describe several nodes). # A comment start at column 2 or 4 or later. def read_nodes_fields(lr, names, fields, nodes, funcs_dict): pat_only = re.compile(' -- Only for ' + prefix_name + '(\w+):\n') pat_only_bad = re.compile (' -- *Only for.*\n') pat_field = re.compile(' -- Get/Set_(\w+) \((Alias )?([\w,]+)\)\n') pat_comment = re.compile(' --(| [^ ].*| .*)\n') # Create nodes cur_nodes = [] for (nm, fmt) in names: if fmt not in fields: raise ParseError(lr, 'unknown format "{}"'.format(fmt)) n = NodeDesc(nm, fmt, {x: None for x in fields[fmt]}, {}) nodes[nm] = n cur_nodes.append(n) # Skip comments l = lr.l while pat_comment.match(l): l = lr.get() # Look for fields while l != '\n': # Skip comments while pat_comment.match(l): l = lr.get() # Handle 'Only ...' m = pat_only.match(l) if m: only_nodes = [] while True: name = m.group(1) n = nodes.get(name, None) if n is None: raise ParseError(lr, 'node is unknown') if n not in cur_nodes: raise ParseError(lr, 'node not currently described') only_nodes.append(n) l = lr.get() m = pat_only.match(l) if not m: break else: # By default a field applies to all nodes. only_nodes = cur_nodes # Skip comments while pat_comment.match(l): l = lr.get() # Handle field: '-- Get/Set_FUNC (Alias? FIELD)' m = pat_field.match(l) if not m: if pat_only_bad.match(l): raise ParseError(lr, "misleading 'Only for' comment") else: raise ParseError(lr, 'bad line in node description') func = m.group(1) alias = m.group(2) fields = m.group(3).split(',') # Check the function exists and if the field is correct. if func not in funcs_dict: raise ParseError(lr, 'unknown function') func = funcs_dict[func] if func.fields != fields: raise ParseError(lr, 'fields mismatch') for c in only_nodes: for f in fields: if f not in c.fields: raise ParseError( lr, 'field ' + f + ' does not exist in node') if not alias: for f in fields: if c.fields[f]: raise ParseError( lr, 'field ' + f + ' already used') c.fields[f] = func c.order.append(f) c.attrs[func.name] = func l = lr.get() def read_nodes(filename, kinds, kinds_ranges, fields, funcs): """Read description for all nodes.""" lr = linereader(filename) funcs_dict = {x.name: x for x in funcs} nodes = {} # Skip until start while lr.get() != ' -- Start of ' + type_name + '.\n': pass pat_decl = re.compile(' -- ' + prefix_name + '(\w+) \((\w+)\)\n') pat_decls = re.compile(' -- ' + prefix_range_name + '(\w+) \((\w+)\)\n') pat_comment_line = re.compile(' --+\n') pat_comment_box = re.compile(' --( .*)?\n') while True: l = lr.get() if l == ' -- End of ' + type_name + '.\n': break if l == '\n': continue m = pat_decl.match(l) if m: # List of nodes being described by the current description. names = [] # Declaration of the first node while True: name = m.group(1) if name not in kinds: raise ParseError(lr, 'unknown node') fmt = m.group(2) names.append((name, fmt)) if name in nodes: raise ParseError( lr, 'node {} already described'.format(name)); # There might be several nodes described at once. l = lr.get() m = pat_decl.match(l) if not m: break read_nodes_fields(lr, names, fields, nodes, funcs_dict) continue m = pat_decls.match(l) if m: # List of nodes being described by the current description. name = m.group(1) fmt = m.group(2) names = [(k, fmt) for k in kinds_ranges[name]] l = lr.get() read_nodes_fields(lr, names, fields, nodes, funcs_dict) continue if pat_comment_line.match(l) or pat_comment_box.match(l): continue raise ParseError(lr, 'bad line in node description') for k in kinds: if k not in nodes: raise ParseError(lr, 'no desription for "{}"'.format(k)) return nodes def gen_choices(choices): """Generate a choice 'when A | B ... Z =>' using elements of CHOICES.""" is_first = True for c in choices: if is_first: print ' ', print 'when', else: print print ' ', print ' |', print prefix_name + c, is_first = False print '=>' def gen_get_format(formats, nodes, kinds): """Generate the Get_Format function.""" print ' function Get_Format (Kind : ' + type_name + ') ' + \ 'return Format_Type is' print ' begin' print ' case Kind is' for f in formats: choices = [k for k in kinds if nodes[k].format == f] gen_choices(choices) print ' return Format_' + f + ';' print ' end case;' print ' end Get_Format;' def gen_subprg_header(decl): if len(decl) < 76: print decl + ' is' else: print decl print ' is' print ' begin' def gen_assert(func): print ' pragma Assert (' + func.pname + ' /= Null_' + node_type + ');' cond = '(Has_' + func.name + ' (Get_Kind (' + func.pname + ')),' msg = '"no field ' + func.name + '");' if len(cond) < 60: print ' pragma Assert ' + cond print ' ' + msg else: print ' pragma Assert' print ' ' + cond print ' ' + msg def get_field_type(fields, f): for fld in fields.values(): if f in fld: return fld[f] return None def gen_get_set(func, nodes, fields): """Generate Get_XXX/Set_XXX subprograms for FUNC.""" rtype = func.rtype # If the function needs several fields, it must be user defined if func.conv == 'grp': print ' type %s_Conv is record' % rtype for f in func.fields: print ' %s: %s;' % (f, get_field_type(fields, f)) print ' end record;' print ' pragma Pack (%s_Conv);' % rtype print " pragma Assert (%s_Conv'Size = %s'Size);" % (rtype, rtype) print else: f = func.fields[0] g = 'Get_' + f + ' (' + func.pname + ')' s = func.rname if func.conv: if func.conv == 'uc': field_type = get_field_type(fields, f) g = field_type + '_To_' + rtype + ' (' + g + ')' s = rtype + '_To_' + field_type + ' (' + s + ')' elif func.conv == 'pos': g = rtype + "'Val (" + g + ')' s = rtype + "'Pos (" + s + ')' subprg = ' function Get_' + func.name + ' (' + func.pname \ + ' : ' + func.ptype + ') return ' + rtype if func.conv == 'grp': print subprg print ' is' print ' function To_%s is new Ada.Unchecked_Conversion' % \ func.rtype print ' (%s_Conv, %s);' % (rtype, rtype) print ' Conv : %s_Conv;' % rtype print ' begin' else: gen_subprg_header(subprg) gen_assert(func) if func.conv == 'grp': for f in func.fields: print ' Conv.%s := Get_%s (%s);' % (f, f, func.pname) g = 'To_%s (Conv)' % rtype print ' return ' + g + ';' print ' end Get_' + func.name + ';' print subprg = ' procedure Set_' + func.name + ' (' \ + func.pname + ' : ' + func.ptype + '; ' \ + func.rname + ' : ' + func.rtype + ')' if func.conv == 'grp': print subprg print ' is' print ' function To_%s_Conv is new Ada.Unchecked_Conversion' % \ func.rtype print ' (%s, %s_Conv);' % (rtype, rtype) print ' Conv : %s_Conv;' % rtype print ' begin' else: gen_subprg_header(subprg) gen_assert(func) if func.conv == 'grp': print ' Conv := To_%s_Conv (%s);' % (rtype, func.rname) for f in func.fields: print ' Set_%s (%s, Conv.%s);' % (f, func.pname, f) else: print ' Set_' + f + ' (' + func.pname + ', ' + s + ');' print ' end Set_' + func.name + ';' print def funcs_of_node(n): return sorted([fv.name for fv in n.fields.values() if fv]) def gen_has_func_spec(name, suff): spec = ' function Has_' + name + ' (K : ' + type_name + ')' ret = ' return Boolean' + suff if len(spec) < 60: print spec + ret else: print spec print ' ' + ret def do_disp_formats(): for fmt in fields: print "Fields of Format_"+fmt fld = fields[fmt] for k in fld: print ' ' + k + ' (' + fld[k] + ')' def do_disp_kinds(): print "Kinds are:" for k in kinds: print ' ' + prefix_name + k def do_disp_funcs(): print "Functions are:" for f in funcs: s = '{0} ({1}: {2}'.format(f.name, f.field, f.rtype) if f.acc: s += ' acc:' + f.acc if f.conv: s += ' conv:' + f.conv s += ')' print s def do_disp_types(): print "Types are:" s = set([]) for f in funcs: s |= set([f.rtype]) for t in sorted(s): print ' ' + t def do_disp_nodes(): for k in kinds: v = nodes[k] print prefix_name + k + ' (' + v.format + ')' flds = [fk for fk, fv in v.fields.items() if fv] for fk in sorted(flds): print ' ' + fk + ': ' + v.fields[fk].name def do_get_format(): gen_get_format(formats, nodes) def do_body(): lr = linereader(template_file) while True: l = lr.get().rstrip() print l if l == ' -- Subprograms': gen_get_format(formats, nodes, kinds) print for f in funcs: gen_get_set(f, nodes, fields) if l[0:3] == 'end': break def get_types(): s = set([]) for f in funcs: s |= set([f.rtype]) return [t for t in sorted(s)] def get_attributes(): s = set([]) for f in funcs: if f.acc: s |= set([f.acc]) res = [t for t in sorted(s)] res.insert(0, 'None') return res def gen_enum(prefix, vals): last = None for v in vals: if last: print last + ',' last = prefix + v print last def do_meta_specs(): lr = linereader(meta_base_file + '.ads.in') types = get_types() while True: l = lr.get().rstrip() if l == ' -- TYPES': gen_enum(' Type_', types) elif l == ' -- FIELDS': gen_enum(' Field_', [f.name for f in funcs]) elif l == ' -- ATTRS': gen_enum(' Attr_', get_attributes()) elif l == ' -- FUNCS': for t in types: print ' function Get_' + t print ' (N : ' + node_type + '; F : Fields_Enum) return '\ + t + ';' print ' procedure Set_' + t print ' (N : ' + node_type + '; F : Fields_Enum; V: ' \ + t + ');' print for f in funcs: gen_has_func_spec(f.name, ';') elif l[0:3] == 'end': print l break else: print l def do_meta_body(): lr = linereader(meta_base_file + '.adb.in') while True: l = lr.get().rstrip() if l == ' -- FIELDS_TYPE': last = None for f in funcs: if last: print last + ',' last = ' Field_' + f.name + ' => Type_' + f.rtype print last elif l == ' -- FIELD_IMAGE': for f in funcs: print ' when Field_' + f.name + ' =>' print ' return "' + f.name.lower() + '";' elif l == ' -- IIR_IMAGE': for k in kinds: print ' when ' + prefix_name + k + ' =>' print ' return "' + k.lower() + '";' elif l == ' -- FIELD_ATTRIBUTE': for f in funcs: print ' when Field_' + f.name + ' =>' if f.acc: attr = f.acc else: attr = 'None' print ' return Attr_' + attr + ';' elif l == ' -- FIELDS_ARRAY': last = None nodes_types = [node_type, node_type + '_List', node_type + '_Flist'] for k in kinds: v = nodes[k] if last: print last + ',' last = None print ' -- ' + prefix_name + k # Get list of physical fields for V, in some order. if flag_keep_order: flds = v.order else: # First non Iir and no Iir_List. flds = sorted([fk for fk, fv in v.fields.items() if fv and fv.rtype not in nodes_types]) # Then Iir and Iir_List in order of appearance flds += (fv for fv in v.order if v.fields[fv].rtype in nodes_types) # Print the corresponding node field, but remove duplicate due # to 'grp'. fldsn = [] for fk in flds: if last: print last + ',' # Remove duplicate fn = v.fields[fk].name if fn not in fldsn: last = ' Field_' + fn fldsn.append(fn) else: last = None if last: print last elif l == ' -- FIELDS_ARRAY_POS': pos = -1 last = None for k in kinds: v = nodes[k] # Create a set to remove duplicate for 'grp'. flds = set([fv.name for fk, fv in v.fields.items() if fv]) pos += len(flds) if last: print last + ',' last = ' ' + prefix_name + k + ' => {}'.format(pos) print last elif l == ' -- FUNCS_BODY': # Build list of types s = set([]) for f in funcs: s |= set([f.rtype]) types = [t for t in sorted(s)] for t in types: print ' function Get_' + t print ' (N : ' + node_type + '; F : Fields_Enum) return '\ + t + ' is' print ' begin' print ' pragma Assert (Fields_Type (F) = Type_' + t + ');' print ' case F is' for f in funcs: if f.rtype == t: print ' when Field_' + f.name + ' =>' print ' return Get_' + f.name + ' (N);' print ' when others =>' print ' raise Internal_Error;' print ' end case;' print ' end Get_' + t + ';' print print ' procedure Set_' + t print ' (N : ' + node_type + '; F : Fields_Enum; V: ' \ + t + ') is' print ' begin' print ' pragma Assert (Fields_Type (F) = Type_' + t + ');' print ' case F is' for f in funcs: if f.rtype == t: print ' when Field_' + f.name + ' =>' print ' Set_' + f.name + ' (N, V);' print ' when others =>' print ' raise Internal_Error;' print ' end case;' print ' end Set_' + t + ';' print for f in funcs: gen_has_func_spec(f.name, ' is') choices = [k for k in kinds if f.name in nodes[k].attrs] if len(choices) == 0: print ' pragma Unreferenced (K);' print ' begin' if len(choices) == 0: print ' return False;' elif len(choices) == 1: print ' return K = ' + prefix_name + choices[0] + ';' else: print ' case K is' gen_choices(choices) print ' return True;' print ' when others =>' print ' return False;' print ' end case;' print ' end Has_' + f.name + ';' print elif l[0:3] == 'end': print l break else: print l actions = {'disp-nodes': do_disp_nodes, 'disp-kinds': do_disp_kinds, 'disp-formats': do_disp_formats, 'disp-funcs': do_disp_funcs, 'disp-types': do_disp_types, 'get_format': do_get_format, 'body': do_body, 'meta_specs': do_meta_specs, 'meta_body': do_meta_body} def main(): parser = argparse.ArgumentParser(description='Meta-grammar processor') parser.add_argument('action', choices=actions.keys(), default='disp-nodes') parser.add_argument('--field-file', dest='field_file', default='nodes.ads', help='specify file which defines fields') parser.add_argument('--kind-file', dest='kind_file', default='iirs.ads', help='specify file which defines nodes kind') parser.add_argument('--node-file', dest='node_file', default='iirs.ads', help='specify file which defines nodes and methods') parser.add_argument('--template-file', dest='template_file', default='iirs.adb.in', help='specify template body file') parser.add_argument('--meta-basename', dest='meta_basename', default='nodes_meta', help='specify base name of meta files') parser.add_argument('--kind-type', dest='kind_type', default='Iir_Kind', help='name of kind type') parser.add_argument('--kind-prefix', dest='kind_prefix', default='Iir_Kind_', help='prefix for kind literals') parser.add_argument('--kind-range-prefix', dest='kind_range_prefix', default='Iir_Kinds_', help='prefix for kind subtype (range)') parser.add_argument('--node-type', dest='node_type', default='Iir', help='name of the node type') parser.add_argument('--keep-order', dest='flag_keep_order', action='store_true', help='keep field order of nodes') parser.set_defaults(flag_keep_order=False) args = parser.parse_args() # At some point, it would be simpler to create a class... global formats, fields, nodes, kinds, kinds_ranges, funcs global type_name, prefix_name, template_file, node_type, meta_base_file global prefix_range_name, flag_keep_order, kind_file type_name = args.kind_type prefix_name = args.kind_prefix prefix_range_name = args.kind_range_prefix template_file = args.template_file node_type = args.node_type meta_base_file = args.meta_basename flag_keep_order = args.flag_keep_order field_file = args.field_file kind_file = args.kind_file node_file = args.node_file try: (formats, fields) = read_fields(field_file) (kinds, kinds_ranges) = read_kinds(kind_file) funcs = read_methods(node_file) nodes = read_nodes(node_file, kinds, kinds_ranges, fields, funcs) except ParseError as e: print >> sys.stderr, e print >> sys.stderr, \ "in {0}:{1}:{2}".format(e.lr.filename, e.lr.lineno, e.lr.l) sys.exit(1) f = actions.get(args.action, None) if not f: print >> sys.stderr, "Action {0} is unknown".format(args.action) sys.exit(1) f() if __name__ == '__main__': main()