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, attribute_list, enum_value_list, **kwargs):
140    assert attribute_list is None or isinstance(attribute_list, AttributeList)
141    assert enum_value_list is None or isinstance(enum_value_list, EnumValueList)
142    super(Enum, self).__init__(name, **kwargs)
143    self.attribute_list = attribute_list
144    self.enum_value_list = enum_value_list
145
146  def __eq__(self, other):
147    return super(Enum, self).__eq__(other) and \
148           self.attribute_list == other.attribute_list and \
149           self.enum_value_list == other.enum_value_list
150
151
152class EnumValue(Definition):
153  """Represents a definition of an enum value."""
154
155  def __init__(self, name, attribute_list, value, **kwargs):
156    # The optional value is either an int (which is current a string) or a
157    # "wrapped identifier".
158    assert attribute_list is None or isinstance(attribute_list, AttributeList)
159    assert value is None or isinstance(value, (str, tuple))
160    super(EnumValue, self).__init__(name, **kwargs)
161    self.attribute_list = attribute_list
162    self.value = value
163
164  def __eq__(self, other):
165    return super(EnumValue, self).__eq__(other) and \
166           self.attribute_list == other.attribute_list and \
167           self.value == other.value
168
169
170class EnumValueList(NodeListBase):
171  """Represents a list of enum value definitions (i.e., the "body" of an enum
172  definition)."""
173
174  _list_item_type = EnumValue
175
176
177class Import(NodeBase):
178  """Represents an import statement."""
179
180  def __init__(self, import_filename, **kwargs):
181    assert isinstance(import_filename, str)
182    super(Import, self).__init__(**kwargs)
183    self.import_filename = import_filename
184
185  def __eq__(self, other):
186    return super(Import, self).__eq__(other) and \
187           self.import_filename == other.import_filename
188
189
190class ImportList(NodeListBase):
191  """Represents a list (i.e., sequence) of import statements."""
192
193  _list_item_type = Import
194
195
196class Interface(Definition):
197  """Represents an interface definition."""
198
199  def __init__(self, name, attribute_list, body, **kwargs):
200    assert attribute_list is None or isinstance(attribute_list, AttributeList)
201    assert isinstance(body, InterfaceBody)
202    super(Interface, self).__init__(name, **kwargs)
203    self.attribute_list = attribute_list
204    self.body = body
205
206  def __eq__(self, other):
207    return super(Interface, self).__eq__(other) and \
208           self.attribute_list == other.attribute_list and \
209           self.body == other.body
210
211
212class Method(Definition):
213  """Represents a method definition."""
214
215  def __init__(self, name, attribute_list, ordinal, parameter_list,
216               response_parameter_list, **kwargs):
217    assert attribute_list is None or isinstance(attribute_list, AttributeList)
218    assert ordinal is None or isinstance(ordinal, Ordinal)
219    assert isinstance(parameter_list, ParameterList)
220    assert response_parameter_list is None or \
221           isinstance(response_parameter_list, ParameterList)
222    super(Method, self).__init__(name, **kwargs)
223    self.attribute_list = attribute_list
224    self.ordinal = ordinal
225    self.parameter_list = parameter_list
226    self.response_parameter_list = response_parameter_list
227
228  def __eq__(self, other):
229    return super(Method, self).__eq__(other) and \
230           self.attribute_list == other.attribute_list and \
231           self.ordinal == other.ordinal and \
232           self.parameter_list == other.parameter_list and \
233           self.response_parameter_list == other.response_parameter_list
234
235
236# This needs to be declared after |Method|.
237class InterfaceBody(NodeListBase):
238  """Represents the body of (i.e., list of definitions inside) an interface."""
239
240  _list_item_type = (Const, Enum, Method)
241
242
243class Module(NodeBase):
244  """Represents a module statement."""
245
246  def __init__(self, name, attribute_list, **kwargs):
247    # |name| is either none or a "wrapped identifier".
248    assert name is None or isinstance(name, tuple)
249    assert attribute_list is None or isinstance(attribute_list, AttributeList)
250    super(Module, self).__init__(**kwargs)
251    self.name = name
252    self.attribute_list = attribute_list
253
254  def __eq__(self, other):
255    return super(Module, self).__eq__(other) and \
256           self.name == other.name and \
257           self.attribute_list == other.attribute_list
258
259
260class Mojom(NodeBase):
261  """Represents an entire .mojom file. (This is the root node.)"""
262
263  def __init__(self, module, import_list, definition_list, **kwargs):
264    assert module is None or isinstance(module, Module)
265    assert isinstance(import_list, ImportList)
266    assert isinstance(definition_list, list)
267    super(Mojom, self).__init__(**kwargs)
268    self.module = module
269    self.import_list = import_list
270    self.definition_list = definition_list
271
272  def __eq__(self, other):
273    return super(Mojom, self).__eq__(other) and \
274           self.module == other.module and \
275           self.import_list == other.import_list and \
276           self.definition_list == other.definition_list
277
278  def __repr__(self):
279    return "%s(%r, %r, %r)" % (self.__class__.__name__, self.module,
280                               self.import_list, self.definition_list)
281
282
283class Ordinal(NodeBase):
284  """Represents an ordinal value labeling, e.g., a struct field."""
285
286  def __init__(self, value, **kwargs):
287    assert isinstance(value, int)
288    super(Ordinal, self).__init__(**kwargs)
289    self.value = value
290
291  def __eq__(self, other):
292    return super(Ordinal, self).__eq__(other) and \
293           self.value == other.value
294
295
296class Parameter(NodeBase):
297  """Represents a method request or response parameter."""
298
299  def __init__(self, name, attribute_list, ordinal, typename, **kwargs):
300    assert isinstance(name, str)
301    assert attribute_list is None or isinstance(attribute_list, AttributeList)
302    assert ordinal is None or isinstance(ordinal, Ordinal)
303    assert isinstance(typename, str)
304    super(Parameter, self).__init__(**kwargs)
305    self.name = name
306    self.attribute_list = attribute_list
307    self.ordinal = ordinal
308    self.typename = typename
309
310  def __eq__(self, other):
311    return super(Parameter, self).__eq__(other) and \
312           self.name == other.name and \
313           self.attribute_list == other.attribute_list and \
314           self.ordinal == other.ordinal and \
315           self.typename == other.typename
316
317
318class ParameterList(NodeListBase):
319  """Represents a list of (method request or response) parameters."""
320
321  _list_item_type = Parameter
322
323
324class Struct(Definition):
325  """Represents a struct definition."""
326
327  def __init__(self, name, attribute_list, body, **kwargs):
328    assert attribute_list is None or isinstance(attribute_list, AttributeList)
329    assert isinstance(body, StructBody) or body is None
330    super(Struct, self).__init__(name, **kwargs)
331    self.attribute_list = attribute_list
332    self.body = body
333
334  def __eq__(self, other):
335    return super(Struct, self).__eq__(other) and \
336           self.attribute_list == other.attribute_list and \
337           self.body == other.body
338
339
340class StructField(Definition):
341  """Represents a struct field definition."""
342
343  def __init__(self, name, attribute_list, ordinal, typename, default_value,
344               **kwargs):
345    assert isinstance(name, str)
346    assert attribute_list is None or isinstance(attribute_list, AttributeList)
347    assert ordinal is None or isinstance(ordinal, Ordinal)
348    assert isinstance(typename, str)
349    # The optional default value is currently either a value as a string or a
350    # "wrapped identifier".
351    assert default_value is None or isinstance(default_value, (str, tuple))
352    super(StructField, self).__init__(name, **kwargs)
353    self.attribute_list = attribute_list
354    self.ordinal = ordinal
355    self.typename = typename
356    self.default_value = default_value
357
358  def __eq__(self, other):
359    return super(StructField, self).__eq__(other) and \
360           self.attribute_list == other.attribute_list and \
361           self.ordinal == other.ordinal and \
362           self.typename == other.typename and \
363           self.default_value == other.default_value
364
365
366# This needs to be declared after |StructField|.
367class StructBody(NodeListBase):
368  """Represents the body of (i.e., list of definitions inside) a struct."""
369
370  _list_item_type = (Const, Enum, StructField)
371
372
373class Union(Definition):
374  """Represents a union definition."""
375
376  def __init__(self, name, attribute_list, body, **kwargs):
377    assert attribute_list is None or isinstance(attribute_list, AttributeList)
378    assert isinstance(body, UnionBody)
379    super(Union, self).__init__(name, **kwargs)
380    self.attribute_list = attribute_list
381    self.body = body
382
383  def __eq__(self, other):
384    return super(Union, self).__eq__(other) and \
385           self.attribute_list == other.attribute_list and \
386           self.body == other.body
387
388
389class UnionField(Definition):
390
391  def __init__(self, name, attribute_list, ordinal, typename, **kwargs):
392    assert isinstance(name, str)
393    assert attribute_list is None or isinstance(attribute_list, AttributeList)
394    assert ordinal is None or isinstance(ordinal, Ordinal)
395    assert isinstance(typename, str)
396    super(UnionField, self).__init__(name, **kwargs)
397    self.attribute_list = attribute_list
398    self.ordinal = ordinal
399    self.typename = typename
400
401  def __eq__(self, other):
402    return super(UnionField, self).__eq__(other) and \
403           self.attribute_list == other.attribute_list and \
404           self.ordinal == other.ordinal and \
405           self.typename == other.typename
406
407
408class UnionBody(NodeListBase):
409
410  _list_item_type = UnionField
411