1# Copyright 2014 The Chromium Authors. All rights reserved.
2# Use of this source code is governed by a BSD-style license that can be
3# found in the LICENSE file.
4
5"""Node classes for the AST for a Mojo IDL file."""
6
7# Note: For convenience of testing, you probably want to define __eq__() methods
8# for all node types; it's okay to be slightly lax (e.g., not compare filename
9# and lineno). You may also define __repr__() to help with analyzing test
10# failures, especially for more complex types.
11
12
13class NodeBase(object):
14  """Base class for nodes in the AST."""
15
16  def __init__(self, filename=None, lineno=None):
17    self.filename = filename
18    self.lineno = lineno
19
20  def __eq__(self, other):
21    return type(self) == type(other)
22
23  # Make != the inverse of ==. (Subclasses shouldn't have to override this.)
24  def __ne__(self, other):
25    return not self == other
26
27
28# TODO(vtl): Some of this is complicated enough that it should be tested.
29class NodeListBase(NodeBase):
30  """Represents a list of other nodes, all having the same type. (This is meant
31  to be subclassed, with subclasses defining _list_item_type to be the class (or
32  classes, in a tuple) of the members of the list.)"""
33
34  def __init__(self, item_or_items=None, **kwargs):
35    super(NodeListBase, self).__init__(**kwargs)
36    self.items = []
37    if item_or_items is None:
38      pass
39    elif isinstance(item_or_items, list):
40      for item in item_or_items:
41        assert isinstance(item, self._list_item_type)
42        self.Append(item)
43    else:
44      assert isinstance(item_or_items, self._list_item_type)
45      self.Append(item_or_items)
46
47  # Support iteration. For everything else, users should just access |items|
48  # directly. (We intentionally do NOT supply |__len__()| or |__nonzero__()|, so
49  # |bool(NodeListBase())| is true.)
50  def __iter__(self):
51    return self.items.__iter__()
52
53  def __eq__(self, other):
54    return super(NodeListBase, self).__eq__(other) and \
55           self.items == other.items
56
57  # Implement this so that on failure, we get slightly more sensible output.
58  def __repr__(self):
59    return self.__class__.__name__ + "([" + \
60           ", ".join([repr(elem) for elem in self.items]) + "])"
61
62  def Insert(self, item):
63    """Inserts item at the front of the list."""
64
65    assert isinstance(item, self._list_item_type)
66    self.items.insert(0, item)
67    self._UpdateFilenameAndLineno()
68
69  def Append(self, item):
70    """Appends item to the end of the list."""
71
72    assert isinstance(item, self._list_item_type)
73    self.items.append(item)
74    self._UpdateFilenameAndLineno()
75
76  def _UpdateFilenameAndLineno(self):
77    if self.items:
78      self.filename = self.items[0].filename
79      self.lineno = self.items[0].lineno
80
81
82class Definition(NodeBase):
83  """Represents a definition of anything that has a global name (e.g., enums,
84  enum values, consts, structs, struct fields, interfaces). (This does not
85  include parameter definitions.) This class is meant to be subclassed."""
86
87  def __init__(self, name, **kwargs):
88    assert isinstance(name, str)
89    NodeBase.__init__(self, **kwargs)
90    self.name = name
91
92
93################################################################################
94
95
96class Attribute(NodeBase):
97  """Represents an attribute."""
98
99  def __init__(self, key, value, **kwargs):
100    assert isinstance(key, str)
101    super(Attribute, self).__init__(**kwargs)
102    self.key = key
103    self.value = value
104
105  def __eq__(self, other):
106    return super(Attribute, self).__eq__(other) and \
107           self.key == other.key and \
108           self.value == other.value
109
110
111class AttributeList(NodeListBase):
112  """Represents a list attributes."""
113
114  _list_item_type = Attribute
115
116
117class Const(Definition):
118  """Represents a const definition."""
119
120  def __init__(self, name, typename, value, **kwargs):
121    # The typename is currently passed through as a string.
122    assert isinstance(typename, str)
123    # The value is either a literal (currently passed through as a string) or a
124    # "wrapped identifier".
125    assert isinstance(value, str) or isinstance(value, tuple)
126    super(Const, self).__init__(name, **kwargs)
127    self.typename = typename
128    self.value = value
129
130  def __eq__(self, other):
131    return super(Const, self).__eq__(other) and \
132           self.typename == other.typename and \
133           self.value == other.value
134
135
136class Enum(Definition):
137  """Represents an enum definition."""
138
139  def __init__(self, name, enum_value_list, **kwargs):
140    assert isinstance(enum_value_list, EnumValueList)
141    super(Enum, self).__init__(name, **kwargs)
142    self.enum_value_list = enum_value_list
143
144  def __eq__(self, other):
145    return super(Enum, self).__eq__(other) and \
146           self.enum_value_list == other.enum_value_list
147
148
149class EnumValue(Definition):
150  """Represents a definition of an enum value."""
151
152  def __init__(self, name, value, **kwargs):
153    # The optional value is either an int (which is current a string) or a
154    # "wrapped identifier".
155    assert value is None or isinstance(value, (str, tuple))
156    super(EnumValue, self).__init__(name, **kwargs)
157    self.value = value
158
159  def __eq__(self, other):
160    return super(EnumValue, self).__eq__(other) and \
161           self.value == other.value
162
163
164class EnumValueList(NodeListBase):
165  """Represents a list of enum value definitions (i.e., the "body" of an enum
166  definition)."""
167
168  _list_item_type = EnumValue
169
170
171class Import(NodeBase):
172  """Represents an import statement."""
173
174  def __init__(self, import_filename, **kwargs):
175    assert isinstance(import_filename, str)
176    super(Import, self).__init__(**kwargs)
177    self.import_filename = import_filename
178
179  def __eq__(self, other):
180    return super(Import, self).__eq__(other) and \
181           self.import_filename == other.import_filename
182
183
184class ImportList(NodeListBase):
185  """Represents a list (i.e., sequence) of import statements."""
186
187  _list_item_type = Import
188
189
190class Interface(Definition):
191  """Represents an interface definition."""
192
193  def __init__(self, name, attribute_list, body, **kwargs):
194    assert attribute_list is None or isinstance(attribute_list, AttributeList)
195    assert isinstance(body, InterfaceBody)
196    super(Interface, self).__init__(name, **kwargs)
197    self.attribute_list = attribute_list
198    self.body = body
199
200  def __eq__(self, other):
201    return super(Interface, self).__eq__(other) and \
202           self.attribute_list == other.attribute_list and \
203           self.body == other.body
204
205
206class Method(Definition):
207  """Represents a method definition."""
208
209  def __init__(self, name, ordinal, parameter_list, response_parameter_list,
210               **kwargs):
211    assert ordinal is None or isinstance(ordinal, Ordinal)
212    assert isinstance(parameter_list, ParameterList)
213    assert response_parameter_list is None or \
214           isinstance(response_parameter_list, ParameterList)
215    super(Method, self).__init__(name, **kwargs)
216    self.ordinal = ordinal
217    self.parameter_list = parameter_list
218    self.response_parameter_list = response_parameter_list
219
220  def __eq__(self, other):
221    return super(Method, self).__eq__(other) and \
222           self.ordinal == other.ordinal and \
223           self.parameter_list == other.parameter_list and \
224           self.response_parameter_list == other.response_parameter_list
225
226
227# This needs to be declared after |Method|.
228class InterfaceBody(NodeListBase):
229  """Represents the body of (i.e., list of definitions inside) an interface."""
230
231  _list_item_type = (Const, Enum, Method)
232
233
234class Module(NodeBase):
235  """Represents a module statement."""
236
237  def __init__(self, name, attribute_list, **kwargs):
238    # |name| is either none or a "wrapped identifier".
239    assert name is None or isinstance(name, tuple)
240    assert attribute_list is None or isinstance(attribute_list, AttributeList)
241    super(Module, self).__init__(**kwargs)
242    self.name = name
243    self.attribute_list = attribute_list
244
245  def __eq__(self, other):
246    return super(Module, self).__eq__(other) and \
247           self.name == other.name and \
248           self.attribute_list == other.attribute_list
249
250
251class Mojom(NodeBase):
252  """Represents an entire .mojom file. (This is the root node."""
253
254  def __init__(self, module, import_list, definition_list, **kwargs):
255    assert module is None or isinstance(module, Module)
256    assert isinstance(import_list, ImportList)
257    assert isinstance(definition_list, list)
258    super(Mojom, self).__init__(**kwargs)
259    self.module = module
260    self.import_list = import_list
261    self.definition_list = definition_list
262
263  def __eq__(self, other):
264    return super(Mojom, self).__eq__(other) and \
265           self.module == other.module and \
266           self.import_list == other.import_list and \
267           self.definition_list == other.definition_list
268
269  def __repr__(self):
270    return "%s(%r, %r, %r)" % (self.__class__.__name__, self.module,
271                               self.import_list, self.definition_list)
272
273
274class Ordinal(NodeBase):
275  """Represents an ordinal value labeling, e.g., a struct field."""
276
277  def __init__(self, value, **kwargs):
278    assert isinstance(value, int)
279    super(Ordinal, self).__init__(**kwargs)
280    self.value = value
281
282  def __eq__(self, other):
283    return super(Ordinal, self).__eq__(other) and \
284           self.value == other.value
285
286
287class Parameter(NodeBase):
288  """Represents a method request or response parameter."""
289
290  def __init__(self, name, ordinal, typename, **kwargs):
291    assert isinstance(name, str)
292    assert ordinal is None or isinstance(ordinal, Ordinal)
293    assert isinstance(typename, str)
294    super(Parameter, self).__init__(**kwargs)
295    self.name = name
296    self.ordinal = ordinal
297    self.typename = typename
298
299  def __eq__(self, other):
300    return super(Parameter, self).__eq__(other) and \
301           self.name == other.name and \
302           self.ordinal == other.ordinal and \
303           self.typename == other.typename
304
305
306class ParameterList(NodeListBase):
307  """Represents a list of (method request or response) parameters."""
308
309  _list_item_type = Parameter
310
311
312class Struct(Definition):
313  """Represents a struct definition."""
314
315  def __init__(self, name, attribute_list, body, **kwargs):
316    assert attribute_list is None or isinstance(attribute_list, AttributeList)
317    assert isinstance(body, StructBody)
318    super(Struct, self).__init__(name, **kwargs)
319    self.attribute_list = attribute_list
320    self.body = body
321
322  def __eq__(self, other):
323    return super(Struct, self).__eq__(other) and \
324           self.attribute_list == other.attribute_list and \
325           self.body == other.body
326
327
328class StructField(Definition):
329  """Represents a struct field definition."""
330
331  def __init__(self, name, ordinal, typename, default_value, **kwargs):
332    assert isinstance(name, str)
333    assert ordinal is None or isinstance(ordinal, Ordinal)
334    assert isinstance(typename, str)
335    # The optional default value is currently either a value as a string or a
336    # "wrapped identifier".
337    assert default_value is None or isinstance(default_value, (str, tuple))
338    super(StructField, self).__init__(name, **kwargs)
339    self.ordinal = ordinal
340    self.typename = typename
341    self.default_value = default_value
342
343  def __eq__(self, other):
344    return super(StructField, self).__eq__(other) and \
345           self.ordinal == other.ordinal and \
346           self.typename == other.typename and \
347           self.default_value == other.default_value
348
349
350# This needs to be declared after |StructField|.
351class StructBody(NodeListBase):
352  """Represents the body of (i.e., list of definitions inside) a struct."""
353
354  _list_item_type = (Const, Enum, StructField)
355