blob: cd5c358ce50f40c72a64282c15dec2a50871b752 [file] [log] [blame]
# 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."""
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<data_pipe_consumer>': 'h:d:c',
'handle<data_pipe_producer>': 'h:d:p',
'handle<message_pipe>': 'h:m',
'handle<shared_buffer>': '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')
if base_kind[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 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,
'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,
'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):
return _MojomBuilder().Build(tree, name)