# Copyright 2014 The Chromium Authors # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. """Node classes for the AST for a Mojo IDL file.""" # Note: For convenience of testing, you probably want to define __eq__() methods # for all node types; it's okay to be slightly lax (e.g., not compare filename # and lineno). You may also define __repr__() to help with analyzing test # failures, especially for more complex types. import os.path # Instance of 'NodeListBase' has no '_list_item_type' member (no-member) # pylint: disable=no-member class NodeBase: """Base class for nodes in the AST.""" def __init__(self, filename=None, lineno=None): self.filename = filename self.lineno = lineno def __eq__(self, other): # We want strict comparison of the two object's types. Disable pylint's # insistence upon recommending isinstance(). # pylint: disable=unidiomatic-typecheck return type(self) == type(other) # Make != the inverse of ==. (Subclasses shouldn't have to override this.) def __ne__(self, other): return not self == other # TODO(vtl): Some of this is complicated enough that it should be tested. class NodeListBase(NodeBase): """Represents a list of other nodes, all having the same type. (This is meant to be subclassed, with subclasses defining _list_item_type to be the class (or classes, in a tuple) of the members of the list.)""" def __init__(self, item_or_items=None, **kwargs): super().__init__(**kwargs) self.items = [] if item_or_items is None: pass elif isinstance(item_or_items, list): for item in item_or_items: assert isinstance(item, self._list_item_type) self.Append(item) else: assert isinstance(item_or_items, self._list_item_type) self.Append(item_or_items) # Support iteration. For everything else, users should just access |items| # directly. (We intentionally do NOT supply |__len__()| or |__nonzero__()|, so # |bool(NodeListBase())| is true.) def __iter__(self): return self.items.__iter__() def __eq__(self, other): return super().__eq__(other) and \ self.items == other.items # Implement this so that on failure, we get slightly more sensible output. def __repr__(self): return self.__class__.__name__ + "([" + \ ", ".join([repr(elem) for elem in self.items]) + "])" def Insert(self, item): """Inserts item at the front of the list.""" assert isinstance(item, self._list_item_type) self.items.insert(0, item) self._UpdateFilenameAndLineno() def Append(self, item): """Appends item to the end of the list.""" assert isinstance(item, self._list_item_type) self.items.append(item) self._UpdateFilenameAndLineno() def _UpdateFilenameAndLineno(self): if self.items: self.filename = self.items[0].filename self.lineno = self.items[0].lineno class Definition(NodeBase): """Represents a definition of anything that has a global name (e.g., enums, enum values, consts, structs, struct fields, interfaces). (This does not include parameter definitions.) This class is meant to be subclassed.""" def __init__(self, mojom_name, **kwargs): assert isinstance(mojom_name, str) NodeBase.__init__(self, **kwargs) self.mojom_name = mojom_name ################################################################################ class Attribute(NodeBase): """Represents an attribute.""" def __init__(self, key, value, **kwargs): assert isinstance(key, str) super().__init__(**kwargs) self.key = key self.value = value def __eq__(self, other): return super().__eq__(other) and \ self.key == other.key and \ self.value == other.value class AttributeList(NodeListBase): """Represents a list attributes.""" _list_item_type = Attribute class Const(Definition): """Represents a const definition.""" def __init__(self, mojom_name, attribute_list, typename, value, **kwargs): assert attribute_list is None or isinstance(attribute_list, AttributeList) # The typename is currently passed through as a string. assert isinstance(typename, str) # The value is either a literal (currently passed through as a string) or a # "wrapped identifier". assert isinstance(value, (tuple, str)) super().__init__(mojom_name, **kwargs) self.attribute_list = attribute_list self.typename = typename self.value = value def __eq__(self, other): return super().__eq__(other) and \ self.attribute_list == other.attribute_list and \ self.typename == other.typename and \ self.value == other.value class Enum(Definition): """Represents an enum definition.""" def __init__(self, mojom_name, attribute_list, enum_value_list, **kwargs): assert attribute_list is None or isinstance(attribute_list, AttributeList) assert enum_value_list is None or isinstance(enum_value_list, EnumValueList) super().__init__(mojom_name, **kwargs) self.attribute_list = attribute_list self.enum_value_list = enum_value_list def __eq__(self, other): return super().__eq__(other) and \ self.attribute_list == other.attribute_list and \ self.enum_value_list == other.enum_value_list class EnumValue(Definition): """Represents a definition of an enum value.""" def __init__(self, mojom_name, attribute_list, value, **kwargs): # The optional value is either an int (which is current a string) or a # "wrapped identifier". assert attribute_list is None or isinstance(attribute_list, AttributeList) assert value is None or isinstance(value, (tuple, str)) super().__init__(mojom_name, **kwargs) self.attribute_list = attribute_list self.value = value def __eq__(self, other): return super().__eq__(other) and \ self.attribute_list == other.attribute_list and \ self.value == other.value class EnumValueList(NodeListBase): """Represents a list of enum value definitions (i.e., the "body" of an enum definition).""" _list_item_type = EnumValue class Feature(Definition): """Represents a runtime feature definition.""" def __init__(self, mojom_name, attribute_list, body, **kwargs): assert attribute_list is None or isinstance(attribute_list, AttributeList) assert isinstance(body, FeatureBody) or body is None super().__init__(mojom_name, **kwargs) self.attribute_list = attribute_list self.body = body def __eq__(self, other): return super().__eq__(other) and \ self.attribute_list == other.attribute_list and \ self.body == other.body def __repr__(self): return "Feature(mojom_name = %s, attribute_list = %s, body = %s)" % ( self.mojom_name, self.attribute_list, self.body) # This needs to be declared after `FeatureConst` and `FeatureField`. class FeatureBody(NodeListBase): """Represents the body of (i.e., list of definitions inside) a feature.""" # Features are compile time helpers so all fields are initializers/consts # for the underlying platform feature type. _list_item_type = (Const) class Import(NodeBase): """Represents an import statement.""" def __init__(self, attribute_list, import_filename, **kwargs): assert attribute_list is None or isinstance(attribute_list, AttributeList) assert isinstance(import_filename, str) super().__init__(**kwargs) self.attribute_list = attribute_list # TODO(crbug.com/953884): Use pathlib once we're migrated fully to Python 3. self.import_filename = os.path.normpath(import_filename).replace('\\', '/') def __eq__(self, other): return super().__eq__(other) and \ self.attribute_list == other.attribute_list and \ self.import_filename == other.import_filename class ImportList(NodeListBase): """Represents a list (i.e., sequence) of import statements.""" _list_item_type = Import class Interface(Definition): """Represents an interface definition.""" def __init__(self, mojom_name, attribute_list, body, **kwargs): assert attribute_list is None or isinstance(attribute_list, AttributeList) assert isinstance(body, InterfaceBody) super().__init__(mojom_name, **kwargs) self.attribute_list = attribute_list self.body = body def __eq__(self, other): return super().__eq__(other) and \ self.attribute_list == other.attribute_list and \ self.body == other.body class Method(Definition): """Represents a method definition.""" def __init__(self, mojom_name, attribute_list, ordinal, parameter_list, response_parameter_list, **kwargs): assert attribute_list is None or isinstance(attribute_list, AttributeList) assert ordinal is None or isinstance(ordinal, Ordinal) assert isinstance(parameter_list, ParameterList) assert response_parameter_list is None or \ isinstance(response_parameter_list, ParameterList) super().__init__(mojom_name, **kwargs) self.attribute_list = attribute_list self.ordinal = ordinal self.parameter_list = parameter_list self.response_parameter_list = response_parameter_list def __eq__(self, other): return super().__eq__(other) and \ self.attribute_list == other.attribute_list and \ self.ordinal == other.ordinal and \ self.parameter_list == other.parameter_list and \ self.response_parameter_list == other.response_parameter_list # This needs to be declared after |Method|. class InterfaceBody(NodeListBase): """Represents the body of (i.e., list of definitions inside) an interface.""" _list_item_type = (Const, Enum, Method) class Module(NodeBase): """Represents a module statement.""" def __init__(self, mojom_namespace, attribute_list, **kwargs): # |mojom_namespace| is either none or a "wrapped identifier". assert mojom_namespace is None or isinstance(mojom_namespace, tuple) assert attribute_list is None or isinstance(attribute_list, AttributeList) super().__init__(**kwargs) self.mojom_namespace = mojom_namespace self.attribute_list = attribute_list def __eq__(self, other): return super().__eq__(other) and \ self.mojom_namespace == other.mojom_namespace and \ self.attribute_list == other.attribute_list class Mojom(NodeBase): """Represents an entire .mojom file. (This is the root node.)""" def __init__(self, module, import_list, definition_list, **kwargs): assert module is None or isinstance(module, Module) assert isinstance(import_list, ImportList) assert isinstance(definition_list, list) super().__init__(**kwargs) self.module = module self.import_list = import_list self.definition_list = definition_list def __eq__(self, other): return super().__eq__(other) and \ self.module == other.module and \ self.import_list == other.import_list and \ self.definition_list == other.definition_list def __repr__(self): return "%s(%r, %r, %r)" % (self.__class__.__name__, self.module, self.import_list, self.definition_list) class Ordinal(NodeBase): """Represents an ordinal value labeling, e.g., a struct field.""" def __init__(self, value, **kwargs): assert isinstance(value, int) super().__init__(**kwargs) self.value = value def __eq__(self, other): return super().__eq__(other) and \ self.value == other.value class Parameter(NodeBase): """Represents a method request or response parameter.""" def __init__(self, mojom_name, attribute_list, ordinal, typename, **kwargs): assert isinstance(mojom_name, str) assert attribute_list is None or isinstance(attribute_list, AttributeList) assert ordinal is None or isinstance(ordinal, Ordinal) assert isinstance(typename, str) super().__init__(**kwargs) self.mojom_name = mojom_name self.attribute_list = attribute_list self.ordinal = ordinal self.typename = typename def __eq__(self, other): return super().__eq__(other) and \ self.mojom_name == other.mojom_name and \ self.attribute_list == other.attribute_list and \ self.ordinal == other.ordinal and \ self.typename == other.typename class ParameterList(NodeListBase): """Represents a list of (method request or response) parameters.""" _list_item_type = Parameter class Struct(Definition): """Represents a struct definition.""" def __init__(self, mojom_name, attribute_list, body, **kwargs): assert attribute_list is None or isinstance(attribute_list, AttributeList) assert isinstance(body, StructBody) or body is None super().__init__(mojom_name, **kwargs) self.attribute_list = attribute_list self.body = body def __eq__(self, other): return super().__eq__(other) and \ self.attribute_list == other.attribute_list and \ self.body == other.body def __repr__(self): return "Struct(mojom_name = %s, attribute_list = %s, body = %s)" % ( self.mojom_name, self.attribute_list, self.body) class StructField(Definition): """Represents a struct field definition.""" def __init__(self, mojom_name, attribute_list, ordinal, typename, default_value, **kwargs): assert isinstance(mojom_name, str) assert attribute_list is None or isinstance(attribute_list, AttributeList) assert ordinal is None or isinstance(ordinal, Ordinal) assert isinstance(typename, str) # The optional default value is currently either a value as a string or a # "wrapped identifier". assert default_value is None or isinstance(default_value, (str, tuple)) super().__init__(mojom_name, **kwargs) self.attribute_list = attribute_list self.ordinal = ordinal self.typename = typename self.default_value = default_value def __eq__(self, other): return super().__eq__(other) and \ self.attribute_list == other.attribute_list and \ self.ordinal == other.ordinal and \ self.typename == other.typename and \ self.default_value == other.default_value def __repr__(self): return ("StructField(mojom_name = %s, attribute_list = %s, ordinal = %s, " "typename = %s, default_value = %s") % ( self.mojom_name, self.attribute_list, self.ordinal, self.typename, self.default_value) # This needs to be declared after |StructField|. class StructBody(NodeListBase): """Represents the body of (i.e., list of definitions inside) a struct.""" _list_item_type = (Const, Enum, StructField) class Union(Definition): """Represents a union definition.""" def __init__(self, mojom_name, attribute_list, body, **kwargs): assert attribute_list is None or isinstance(attribute_list, AttributeList) assert isinstance(body, UnionBody) super().__init__(mojom_name, **kwargs) self.attribute_list = attribute_list self.body = body def __eq__(self, other): return super().__eq__(other) and \ self.attribute_list == other.attribute_list and \ self.body == other.body class UnionField(Definition): def __init__(self, mojom_name, attribute_list, ordinal, typename, **kwargs): assert isinstance(mojom_name, str) assert attribute_list is None or isinstance(attribute_list, AttributeList) assert ordinal is None or isinstance(ordinal, Ordinal) assert isinstance(typename, str) super().__init__(mojom_name, **kwargs) self.attribute_list = attribute_list self.ordinal = ordinal self.typename = typename def __eq__(self, other): return super().__eq__(other) and \ self.attribute_list == other.attribute_list and \ self.ordinal == other.ordinal and \ self.typename == other.typename class UnionBody(NodeListBase): _list_item_type = UnionField