• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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"""Translates parse tree to Mojom IR."""
6
7import re
8
9from . import ast
10
11
12def _DuplicateName(values):
13  """Returns the 'name' of the first entry in |values| whose 'name' has already
14     been encountered. If there are no duplicates, returns None."""
15  names = set()
16  for value in values:
17    if value['name'] in names:
18      return value['name']
19    names.add(value['name'])
20  return None
21
22def _MapTreeForType(func, tree, type_to_map, scope):
23  assert isinstance(type_to_map, type)
24  if not tree:
25    return []
26  result = [func(subtree)
27            for subtree in tree if isinstance(subtree, type_to_map)]
28  duplicate_name = _DuplicateName(result)
29  if duplicate_name:
30    raise Exception('Names in mojom must be unique within a scope. The name '
31                    '"%s" is used more than once within the scope "%s".' %
32                    (duplicate_name, scope))
33  return result
34
35def _MapKind(kind):
36  map_to_kind = {'bool': 'b',
37                 'int8': 'i8',
38                 'int16': 'i16',
39                 'int32': 'i32',
40                 'int64': 'i64',
41                 'uint8': 'u8',
42                 'uint16': 'u16',
43                 'uint32': 'u32',
44                 'uint64': 'u64',
45                 'float': 'f',
46                 'double': 'd',
47                 'string': 's',
48                 'handle': 'h',
49                 'handle<data_pipe_consumer>': 'h:d:c',
50                 'handle<data_pipe_producer>': 'h:d:p',
51                 'handle<message_pipe>': 'h:m',
52                 'handle<shared_buffer>': 'h:s'}
53  if kind.endswith('?'):
54    base_kind = _MapKind(kind[0:-1])
55    # NOTE: This doesn't rule out enum types. Those will be detected later, when
56    # cross-reference is established.
57    reference_kinds = ('m', 's', 'h', 'a', 'r', 'x', 'asso')
58    if re.split('[^a-z]', base_kind, 1)[0] not in reference_kinds:
59      raise Exception(
60          'A type (spec "%s") cannot be made nullable' % base_kind)
61    return '?' + base_kind
62  if kind.endswith('}'):
63    lbracket = kind.rfind('{')
64    value = kind[0:lbracket]
65    return 'm[' + _MapKind(kind[lbracket+1:-1]) + '][' + _MapKind(value) + ']'
66  if kind.endswith(']'):
67    lbracket = kind.rfind('[')
68    typename = kind[0:lbracket]
69    return 'a' + kind[lbracket+1:-1] + ':' + _MapKind(typename)
70  if kind.endswith('&'):
71    return 'r:' + _MapKind(kind[0:-1])
72  if kind.startswith('asso<'):
73    assert kind.endswith('>')
74    return 'asso:' + _MapKind(kind[5:-1])
75  if kind in map_to_kind:
76    return map_to_kind[kind]
77  return 'x:' + kind
78
79def _AddOptional(dictionary, key, value):
80  if value is not None:
81    dictionary[key] = value;
82
83def _AttributeListToDict(attribute_list):
84  if attribute_list is None:
85    return None
86  assert isinstance(attribute_list, ast.AttributeList)
87  # TODO(vtl): Check for duplicate keys here.
88  return dict([(attribute.key, attribute.value)
89                   for attribute in attribute_list])
90
91def _EnumToDict(enum):
92  def EnumValueToDict(enum_value):
93    assert isinstance(enum_value, ast.EnumValue)
94    data = {'name': enum_value.name}
95    _AddOptional(data, 'value', enum_value.value)
96    _AddOptional(data, 'attributes',
97                 _AttributeListToDict(enum_value.attribute_list))
98    return data
99
100  assert isinstance(enum, ast.Enum)
101  data = {'name': enum.name,
102          'native_only': enum.enum_value_list is None }
103  if not data['native_only']:
104    data.update({'fields': map(EnumValueToDict, enum.enum_value_list)})
105  _AddOptional(data, 'attributes', _AttributeListToDict(enum.attribute_list))
106  return data
107
108def _ConstToDict(const):
109  assert isinstance(const, ast.Const)
110  return {'name': const.name,
111          'kind': _MapKind(const.typename),
112          'value': const.value}
113
114
115class _MojomBuilder(object):
116  def __init__(self):
117    self.mojom = {}
118
119  def Build(self, tree, name):
120    def StructToDict(struct):
121      def StructFieldToDict(struct_field):
122        assert isinstance(struct_field, ast.StructField)
123        data = {'name': struct_field.name,
124                'kind': _MapKind(struct_field.typename)}
125        _AddOptional(data, 'ordinal',
126                     struct_field.ordinal.value
127                         if struct_field.ordinal else None)
128        _AddOptional(data, 'default', struct_field.default_value)
129        _AddOptional(data, 'attributes',
130                     _AttributeListToDict(struct_field.attribute_list))
131        return data
132
133      assert isinstance(struct, ast.Struct)
134      data = {'name': struct.name,
135              'native_only': struct.body is None}
136      if not data['native_only']:
137        data.update({
138            'fields': _MapTreeForType(StructFieldToDict, struct.body,
139                                      ast.StructField, struct.name),
140            'enums': _MapTreeForType(_EnumToDict, struct.body, ast.Enum,
141                                     struct.name),
142            'constants': _MapTreeForType(_ConstToDict, struct.body,
143                                         ast.Const, struct.name)})
144      _AddOptional(data, 'attributes',
145                   _AttributeListToDict(struct.attribute_list))
146      return data
147
148    def UnionToDict(union):
149      def UnionFieldToDict(union_field):
150        assert isinstance(union_field, ast.UnionField)
151        data = {'name': union_field.name,
152                'kind': _MapKind(union_field.typename)}
153        _AddOptional(data, 'ordinal',
154                     union_field.ordinal.value
155                         if union_field.ordinal else None)
156        _AddOptional(data, 'attributes',
157                     _AttributeListToDict(union_field.attribute_list))
158        return data
159
160      assert isinstance(union, ast.Union)
161      data = {'name': union.name,
162              'fields': _MapTreeForType(UnionFieldToDict, union.body,
163                                        ast.UnionField, union.name)}
164      _AddOptional(data, 'attributes',
165                   _AttributeListToDict(union.attribute_list))
166      return data
167
168    def InterfaceToDict(interface):
169      def MethodToDict(method):
170        def ParameterToDict(param):
171          assert isinstance(param, ast.Parameter)
172          data = {'name': param.name,
173                  'kind': _MapKind(param.typename)}
174          _AddOptional(data, 'ordinal',
175                       param.ordinal.value if param.ordinal else None)
176          _AddOptional(data, 'attributes',
177                       _AttributeListToDict(param.attribute_list))
178          return data
179
180        assert isinstance(method, ast.Method)
181        data = {'name': method.name,
182                'parameters': map(ParameterToDict, method.parameter_list)}
183        if method.response_parameter_list is not None:
184          data['response_parameters'] = map(ParameterToDict,
185                                            method.response_parameter_list)
186        _AddOptional(data, 'ordinal',
187                     method.ordinal.value if method.ordinal else None)
188        _AddOptional(data, 'attributes',
189                     _AttributeListToDict(method.attribute_list))
190        return data
191
192      assert isinstance(interface, ast.Interface)
193      data = {'name': interface.name,
194              'methods': _MapTreeForType(MethodToDict, interface.body,
195                                         ast.Method, interface.name),
196              'enums': _MapTreeForType(_EnumToDict, interface.body, ast.Enum,
197                                       interface.name),
198              'constants': _MapTreeForType(_ConstToDict, interface.body,
199                                           ast.Const, interface.name)}
200      _AddOptional(data, 'attributes',
201                   _AttributeListToDict(interface.attribute_list))
202      return data
203
204    assert isinstance(tree, ast.Mojom)
205    self.mojom['name'] = name
206    self.mojom['namespace'] = tree.module.name[1] if tree.module else ''
207    self.mojom['imports'] = \
208        [{'filename': imp.import_filename} for imp in tree.import_list]
209    self.mojom['structs'] = \
210        _MapTreeForType(StructToDict, tree.definition_list, ast.Struct, name)
211    self.mojom['unions'] = \
212        _MapTreeForType(UnionToDict, tree.definition_list, ast.Union, name)
213    self.mojom['interfaces'] = \
214        _MapTreeForType(InterfaceToDict, tree.definition_list, ast.Interface,
215                        name)
216    self.mojom['enums'] = \
217        _MapTreeForType(_EnumToDict, tree.definition_list, ast.Enum, name)
218    self.mojom['constants'] = \
219        _MapTreeForType(_ConstToDict, tree.definition_list, ast.Const, name)
220    _AddOptional(self.mojom, 'attributes',
221                 _AttributeListToDict(tree.module.attribute_list)
222                     if tree.module else None)
223    return self.mojom
224
225
226def Translate(tree, name):
227  """Translate AST to Mojom IR.
228
229  Args:
230    tree: The AST as a mojom.parse.ast.Mojom object.
231    name: The filename as a str.
232
233  Returns:
234    The Mojom IR as a dict.
235  """
236  return _MojomBuilder().Build(tree, name)
237