# Copyright 2014 The Chromium Authors. All rights reserved. # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. """Translates parse tree to Mojom IR.""" import re from . import ast def _DuplicateName(values): """Returns the 'name' of the first entry in |values| whose 'name' has already been encountered. If there are no duplicates, returns None.""" names = set() for value in values: if value['name'] in names: return value['name'] names.add(value['name']) return None def _MapTreeForType(func, tree, type_to_map, scope): assert isinstance(type_to_map, type) if not tree: return [] result = [func(subtree) for subtree in tree if isinstance(subtree, type_to_map)] duplicate_name = _DuplicateName(result) if duplicate_name: raise Exception('Names in mojom must be unique within a scope. The name ' '"%s" is used more than once within the scope "%s".' % (duplicate_name, scope)) return result def _MapKind(kind): map_to_kind = {'bool': 'b', 'int8': 'i8', 'int16': 'i16', 'int32': 'i32', 'int64': 'i64', 'uint8': 'u8', 'uint16': 'u16', 'uint32': 'u32', 'uint64': 'u64', 'float': 'f', 'double': 'd', 'string': 's', 'handle': 'h', 'handle': 'h:d:c', 'handle': 'h:d:p', 'handle': 'h:m', 'handle': 'h:s'} if kind.endswith('?'): base_kind = _MapKind(kind[0:-1]) # NOTE: This doesn't rule out enum types. Those will be detected later, when # cross-reference is established. reference_kinds = ('m', 's', 'h', 'a', 'r', 'x', 'asso') if re.split('[^a-z]', base_kind, 1)[0] not in reference_kinds: raise Exception( 'A type (spec "%s") cannot be made nullable' % base_kind) return '?' + base_kind if kind.endswith('}'): lbracket = kind.rfind('{') value = kind[0:lbracket] return 'm[' + _MapKind(kind[lbracket+1:-1]) + '][' + _MapKind(value) + ']' if kind.endswith(']'): lbracket = kind.rfind('[') typename = kind[0:lbracket] return 'a' + kind[lbracket+1:-1] + ':' + _MapKind(typename) if kind.endswith('&'): return 'r:' + _MapKind(kind[0:-1]) if kind.startswith('asso<'): assert kind.endswith('>') return 'asso:' + _MapKind(kind[5:-1]) if kind in map_to_kind: return map_to_kind[kind] return 'x:' + kind def _AddOptional(dictionary, key, value): if value is not None: dictionary[key] = value; def _AttributeListToDict(attribute_list): if attribute_list is None: return None assert isinstance(attribute_list, ast.AttributeList) # TODO(vtl): Check for duplicate keys here. return dict([(attribute.key, attribute.value) for attribute in attribute_list]) def _EnumToDict(enum): def EnumValueToDict(enum_value): assert isinstance(enum_value, ast.EnumValue) data = {'name': enum_value.name} _AddOptional(data, 'value', enum_value.value) _AddOptional(data, 'attributes', _AttributeListToDict(enum_value.attribute_list)) return data assert isinstance(enum, ast.Enum) data = {'name': enum.name, 'native_only': enum.enum_value_list is None } if not data['native_only']: data.update({'fields': map(EnumValueToDict, enum.enum_value_list)}) _AddOptional(data, 'attributes', _AttributeListToDict(enum.attribute_list)) return data def _ConstToDict(const): assert isinstance(const, ast.Const) return {'name': const.name, 'kind': _MapKind(const.typename), 'value': const.value} class _MojomBuilder(object): def __init__(self): self.mojom = {} def Build(self, tree, name): def StructToDict(struct): def StructFieldToDict(struct_field): assert isinstance(struct_field, ast.StructField) data = {'name': struct_field.name, 'kind': _MapKind(struct_field.typename)} _AddOptional(data, 'ordinal', struct_field.ordinal.value if struct_field.ordinal else None) _AddOptional(data, 'default', struct_field.default_value) _AddOptional(data, 'attributes', _AttributeListToDict(struct_field.attribute_list)) return data assert isinstance(struct, ast.Struct) data = {'name': struct.name, 'native_only': struct.body is None} if not data['native_only']: data.update({ 'fields': _MapTreeForType(StructFieldToDict, struct.body, ast.StructField, struct.name), 'enums': _MapTreeForType(_EnumToDict, struct.body, ast.Enum, struct.name), 'constants': _MapTreeForType(_ConstToDict, struct.body, ast.Const, struct.name)}) _AddOptional(data, 'attributes', _AttributeListToDict(struct.attribute_list)) return data def UnionToDict(union): def UnionFieldToDict(union_field): assert isinstance(union_field, ast.UnionField) data = {'name': union_field.name, 'kind': _MapKind(union_field.typename)} _AddOptional(data, 'ordinal', union_field.ordinal.value if union_field.ordinal else None) _AddOptional(data, 'attributes', _AttributeListToDict(union_field.attribute_list)) return data assert isinstance(union, ast.Union) data = {'name': union.name, 'fields': _MapTreeForType(UnionFieldToDict, union.body, ast.UnionField, union.name)} _AddOptional(data, 'attributes', _AttributeListToDict(union.attribute_list)) return data def InterfaceToDict(interface): def MethodToDict(method): def ParameterToDict(param): assert isinstance(param, ast.Parameter) data = {'name': param.name, 'kind': _MapKind(param.typename)} _AddOptional(data, 'ordinal', param.ordinal.value if param.ordinal else None) _AddOptional(data, 'attributes', _AttributeListToDict(param.attribute_list)) return data assert isinstance(method, ast.Method) data = {'name': method.name, 'parameters': map(ParameterToDict, method.parameter_list)} if method.response_parameter_list is not None: data['response_parameters'] = map(ParameterToDict, method.response_parameter_list) _AddOptional(data, 'ordinal', method.ordinal.value if method.ordinal else None) _AddOptional(data, 'attributes', _AttributeListToDict(method.attribute_list)) return data assert isinstance(interface, ast.Interface) data = {'name': interface.name, 'methods': _MapTreeForType(MethodToDict, interface.body, ast.Method, interface.name), 'enums': _MapTreeForType(_EnumToDict, interface.body, ast.Enum, interface.name), 'constants': _MapTreeForType(_ConstToDict, interface.body, ast.Const, interface.name)} _AddOptional(data, 'attributes', _AttributeListToDict(interface.attribute_list)) return data assert isinstance(tree, ast.Mojom) self.mojom['name'] = name self.mojom['namespace'] = tree.module.name[1] if tree.module else '' self.mojom['imports'] = \ [{'filename': imp.import_filename} for imp in tree.import_list] self.mojom['structs'] = \ _MapTreeForType(StructToDict, tree.definition_list, ast.Struct, name) self.mojom['unions'] = \ _MapTreeForType(UnionToDict, tree.definition_list, ast.Union, name) self.mojom['interfaces'] = \ _MapTreeForType(InterfaceToDict, tree.definition_list, ast.Interface, name) self.mojom['enums'] = \ _MapTreeForType(_EnumToDict, tree.definition_list, ast.Enum, name) self.mojom['constants'] = \ _MapTreeForType(_ConstToDict, tree.definition_list, ast.Const, name) _AddOptional(self.mojom, 'attributes', _AttributeListToDict(tree.module.attribute_list) if tree.module else None) return self.mojom def Translate(tree, name): """Translate AST to Mojom IR. Args: tree: The AST as a mojom.parse.ast.Mojom object. name: The filename as a str. Returns: The Mojom IR as a dict. """ return _MojomBuilder().Build(tree, name)