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
7
8import re
9
10from . import ast
11
12
13def _MapTreeForType(func, tree, type_to_map):
14  assert isinstance(type_to_map, type)
15  if not tree:
16    return []
17  return [func(subtree) for subtree in tree if isinstance(subtree, type_to_map)]
18
19_FIXED_ARRAY_REGEXP = re.compile(r'\[[0-9]+\]')
20
21def _MapKind(kind):
22  map_to_kind = {'bool': 'b',
23                 'int8': 'i8',
24                 'int16': 'i16',
25                 'int32': 'i32',
26                 'int64': 'i64',
27                 'uint8': 'u8',
28                 'uint16': 'u16',
29                 'uint32': 'u32',
30                 'uint64': 'u64',
31                 'float': 'f',
32                 'double': 'd',
33                 'string': 's',
34                 'handle': 'h',
35                 'handle<data_pipe_consumer>': 'h:d:c',
36                 'handle<data_pipe_producer>': 'h:d:p',
37                 'handle<message_pipe>': 'h:m',
38                 'handle<shared_buffer>': 'h:s'}
39  if kind.endswith('?'):
40    base_kind = _MapKind(kind[0:-1])
41    # NOTE: This doesn't rule out enum types. Those will be detected later, when
42    # cross-reference is established.
43    reference_kinds = ('s', 'h', 'a', 'r', 'x')
44    if base_kind[0] not in reference_kinds:
45      raise Exception(
46          'A type (spec "%s") cannot be made nullable' % base_kind)
47    return '?' + base_kind
48  if kind.endswith('[]'):
49    typename = kind[0:-2]
50    if _FIXED_ARRAY_REGEXP.search(typename):
51      raise Exception('Arrays of fixed sized arrays not supported')
52    return 'a:' + _MapKind(typename)
53  if kind.endswith(']'):
54    lbracket = kind.rfind('[')
55    typename = kind[0:lbracket]
56    if typename.find('[') != -1:
57      raise Exception('Fixed sized arrays of arrays not supported')
58    return 'a' + kind[lbracket+1:-1] + ':' + _MapKind(typename)
59  if kind.endswith('&'):
60    return 'r:' + _MapKind(kind[0:-1])
61  if kind in map_to_kind:
62    return map_to_kind[kind]
63  return 'x:' + kind
64
65def _AttributeListToDict(attribute_list):
66  if attribute_list is None:
67    return {}
68  assert isinstance(attribute_list, ast.AttributeList)
69  # TODO(vtl): Check for duplicate keys here.
70  return dict([(attribute.key, attribute.value)
71                   for attribute in attribute_list])
72
73def _EnumToDict(enum):
74  def EnumValueToDict(enum_value):
75    assert isinstance(enum_value, ast.EnumValue)
76    return {'name': enum_value.name,
77            'value': enum_value.value}
78
79  assert isinstance(enum, ast.Enum)
80  return {'name': enum.name,
81          'fields': map(EnumValueToDict, enum.enum_value_list)}
82
83def _ConstToDict(const):
84  assert isinstance(const, ast.Const)
85  return {'name': const.name,
86          'kind': _MapKind(const.typename),
87          'value': const.value}
88
89
90class _MojomBuilder(object):
91  def __init__(self):
92    self.mojom = {}
93
94  def Build(self, tree, name):
95    def StructToDict(struct):
96      def StructFieldToDict(struct_field):
97        assert isinstance(struct_field, ast.StructField)
98        return {'name': struct_field.name,
99                'kind': _MapKind(struct_field.typename),
100                'ordinal': struct_field.ordinal.value \
101                    if struct_field.ordinal else None,
102                'default': struct_field.default_value}
103
104      assert isinstance(struct, ast.Struct)
105      return {'name': struct.name,
106              'attributes': _AttributeListToDict(struct.attribute_list),
107              'fields': _MapTreeForType(StructFieldToDict, struct.body,
108                                        ast.StructField),
109              'enums': _MapTreeForType(_EnumToDict, struct.body, ast.Enum),
110              'constants': _MapTreeForType(_ConstToDict, struct.body,
111                                           ast.Const)}
112
113    def InterfaceToDict(interface):
114      def MethodToDict(method):
115        def ParameterToDict(param):
116          assert isinstance(param, ast.Parameter)
117          return {'name': param.name,
118                  'kind': _MapKind(param.typename),
119                  'ordinal': param.ordinal.value if param.ordinal else None}
120
121        assert isinstance(method, ast.Method)
122        rv = {'name': method.name,
123              'parameters': map(ParameterToDict, method.parameter_list),
124              'ordinal': method.ordinal.value if method.ordinal else None}
125        if method.response_parameter_list is not None:
126          rv['response_parameters'] = map(ParameterToDict,
127                                          method.response_parameter_list)
128        return rv
129
130      assert isinstance(interface, ast.Interface)
131      attributes = _AttributeListToDict(interface.attribute_list)
132      return {'name': interface.name,
133              'attributes': attributes,
134              'client': attributes.get('Client'),
135              'methods': _MapTreeForType(MethodToDict, interface.body,
136                                         ast.Method),
137              'enums': _MapTreeForType(_EnumToDict, interface.body, ast.Enum),
138              'constants': _MapTreeForType(_ConstToDict, interface.body,
139                                           ast.Const)}
140
141    assert isinstance(tree, ast.Mojom)
142    self.mojom['name'] = name
143    self.mojom['namespace'] = tree.module.name[1] if tree.module else ''
144    self.mojom['imports'] = \
145        [{'filename': imp.import_filename} for imp in tree.import_list]
146    self.mojom['attributes'] = \
147        _AttributeListToDict(tree.module.attribute_list) if tree.module else {}
148    self.mojom['structs'] = \
149        _MapTreeForType(StructToDict, tree.definition_list, ast.Struct)
150    self.mojom['interfaces'] = \
151        _MapTreeForType(InterfaceToDict, tree.definition_list, ast.Interface)
152    self.mojom['enums'] = \
153        _MapTreeForType(_EnumToDict, tree.definition_list, ast.Enum)
154    self.mojom['constants'] = \
155        _MapTreeForType(_ConstToDict, tree.definition_list, ast.Const)
156    return self.mojom
157
158
159def Translate(tree, name):
160  return _MojomBuilder().Build(tree, name)
161