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