diff options
author | darin@chromium.org <darin@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-10-30 17:10:42 +0000 |
---|---|---|
committer | darin@chromium.org <darin@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-10-30 17:10:42 +0000 |
commit | cbaaa4ee4a979053f68a1f01e8c1517a803ee315 (patch) | |
tree | 5830fd0c29d378de672119a989fef3d6cd6f0245 /mojo | |
parent | 8bb4798147cbd8b582cb7e2a0aa7c029a62cdecc (diff) | |
download | chromium_src-cbaaa4ee4a979053f68a1f01e8c1517a803ee315.zip chromium_src-cbaaa4ee4a979053f68a1f01e8c1517a803ee315.tar.gz chromium_src-cbaaa4ee4a979053f68a1f01e8c1517a803ee315.tar.bz2 |
Mojo: Add basic IDL parser and frontend
mojo_parser - parses IDL, produces syntax tree
mojo_translate - translates syntax tree to Mojom IR
mojo_idl - frontend that hooks it all together
The result "mojo_idl foo.idl" ingests the IDL file and spits out C++ code!
R=davemoore@chromium.org
Review URL: https://codereview.chromium.org/47543003
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@231860 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'mojo')
-rw-r--r-- | mojo/public/bindings/generators/__init__.py | 0 | ||||
-rwxr-xr-x | mojo/public/bindings/mojo_idl.py | 38 | ||||
-rw-r--r-- | mojo/public/bindings/parser/__init__.py | 0 | ||||
-rwxr-xr-x | mojo/public/bindings/parser/mojo_parser.py | 232 | ||||
-rwxr-xr-x | mojo/public/bindings/parser/mojo_translate.py | 115 |
5 files changed, 385 insertions, 0 deletions
diff --git a/mojo/public/bindings/generators/__init__.py b/mojo/public/bindings/generators/__init__.py new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/mojo/public/bindings/generators/__init__.py diff --git a/mojo/public/bindings/mojo_idl.py b/mojo/public/bindings/mojo_idl.py new file mode 100755 index 0000000..48a01ab --- /dev/null +++ b/mojo/public/bindings/mojo_idl.py @@ -0,0 +1,38 @@ +#!/usr/bin/env python +# Copyright 2013 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. + +"""The frontend for the Mojo bindings system.""" + + +import sys +from optparse import OptionParser +from parser import mojo_parser +from parser import mojo_translate +from generators import mojom_data +from generators import mojom_cpp_generator + + +def Main(): + parser = OptionParser(usage="usage: %prog [options] filename1 [filename2...]") + parser.add_option("-o", "--outdir", dest="outdir", default=".", + help="specify output directory") + (options, args) = parser.parse_args() + + if len(args) < 1: + parser.print_help() + sys.exit(1) + + for filename in args: + # TODO(darin): There's clearly too many layers of translation here! We can + # at least avoid generating the serialized Mojom IR. + tree = mojo_parser.Parse(filename) + mojom = mojo_translate.Translate(tree) + module = mojom_data.ModuleFromData(mojom) + cpp = mojom_cpp_generator.CPPGenerator(module) + cpp.GenerateFiles(options.outdir) + + +if __name__ == '__main__': + Main() diff --git a/mojo/public/bindings/parser/__init__.py b/mojo/public/bindings/parser/__init__.py new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/mojo/public/bindings/parser/__init__.py diff --git a/mojo/public/bindings/parser/mojo_parser.py b/mojo/public/bindings/parser/mojo_parser.py new file mode 100755 index 0000000..a967fbc --- /dev/null +++ b/mojo/public/bindings/parser/mojo_parser.py @@ -0,0 +1,232 @@ +#!/usr/bin/env python +# Copyright 2013 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. + +"""Generates a syntax tree from a Mojo IDL file.""" + + +import sys +import os.path + + +# Try to load the ply module, if not, then assume it is in the third_party +# directory. +try: + # Disable lint check which fails to find the ply module. + # pylint: disable=F0401 + from ply import lex + from ply import yacc +except ImportError: + module_path, module_name = os.path.split(__file__) + third_party = os.path.join( + module_path, os.pardir, os.pardir, os.pardir, os.pardir, 'third_party') + sys.path.append(third_party) + # pylint: disable=F0401 + from ply import lex + from ply import yacc + + +def ListFromConcat(*items): + """Generate list by concatenating inputs""" + itemsout = [] + for item in items: + if item is None: + continue + if type(item) is not type([]): + itemsout.append(item) + else: + itemsout.extend(item) + + return itemsout + + +class Lexer(object): + + # This field is required by lex to specify the complete list of valid tokens. + tokens = ( + 'NAME', + 'NUMBER', + + 'ARRAY', + 'ORDINAL', + + 'MODULE', + 'STRUCT', + 'INTERFACE', + 'VOID', + + 'LCURLY', + 'RCURLY', + 'LPAREN', + 'RPAREN', + 'LBRACKET', + 'RBRACKET', + 'COMMA', + 'SEMICOLON', + 'EQUALS', + ) + + t_LCURLY = r'{' + t_RCURLY = r'}' + t_LPAREN = r'\(' + t_RPAREN = r'\)' + t_LBRACKET = r'\[' + t_RBRACKET = r'\]' + t_COMMA = r',' + t_SEMICOLON = r';' + t_EQUALS = r'=' + t_NAME = r'[a-zA-Z_][a-zA-Z0-9_]*' + t_ARRAY = r'[a-zA-Z_][a-zA-Z0-9_]*\[\]' + t_NUMBER = r'\d+' + t_ORDINAL = r'@[0-9]*' + + def t_MODULE(self, t): + r'module' + return t + + def t_STRUCT(self, t): + r'struct' + return t + + def t_INTERFACE(self, t): + r'interface' + return t + + def t_VOID(self, t): + r'void' + return t + + # Ignore C and C++ style comments + def t_COMMENT(self, t): + r'(/\*(.|\n)*?\*/)|(//.*(\n[ \t]*//.*)*)' + pass + + # Ignored characters + t_ignore = " \t" + + def t_newline(self, t): + r'\n+' + t.lexer.lineno += t.value.count("\n") + + def t_error(self, t): + print("Illegal character '%s'" % t.value[0]) + t.lexer.skip(1) + + +class Parser(object): + + def __init__(self, lexer): + self.tokens = lexer.tokens + + def p_module(self, p): + """module : MODULE NAME LCURLY definitions RCURLY""" + p[0] = ('MODULE', p[2], p[4]) + + def p_definitions(self, p): + """definitions : definition definitions + |""" + if len(p) > 1: + p[0] = ListFromConcat(p[1], p[2]) + + def p_definition(self, p): + """definition : struct + | interface""" + p[0] = p[1] + + def p_attribute_section(self, p): + """attribute_section : LBRACKET attributes RBRACKET + | """ + if len(p) > 3: + p[0] = p[2] + + def p_attributes(self, p): + """attributes : attribute + | attribute COMMA attributes + | """ + if len(p) == 2: + p[0] = ListFromConcat(p[1]) + elif len(p) > 3: + p[0] = ListFromConcat(p[1], p[3]) + + def p_attribute(self, p): + """attribute : NAME EQUALS NUMBER""" + p[0] = ('ATTRIBUTE', p[1], p[3]) + + def p_struct(self, p): + """struct : attribute_section STRUCT NAME LCURLY fields RCURLY SEMICOLON""" + p[0] = ('STRUCT', p[3], p[1], p[5]) + + def p_fields(self, p): + """fields : field fields + |""" + if len(p) > 1: + p[0] = ListFromConcat(p[1], p[2]) + + def p_field(self, p): + """field : typename NAME ordinal SEMICOLON""" + p[0] = ('FIELD', p[1], p[2], p[3]) + + def p_interface(self, p): + """interface : INTERFACE NAME LCURLY methods RCURLY SEMICOLON""" + p[0] = ('INTERFACE', p[2], p[4]) + + def p_methods(self, p): + """methods : method methods + | """ + if len(p) > 1: + p[0] = ListFromConcat(p[1], p[2]) + + def p_method(self, p): + """method : VOID NAME LPAREN parameters RPAREN ordinal SEMICOLON""" + p[0] = ('METHOD', p[2], p[4], p[6]) + + def p_parameters(self, p): + """parameters : parameter + | parameter COMMA parameters + | """ + if len(p) == 2: + p[0] = p[1] + elif len(p) > 3: + p[0] = ListFromConcat(p[1], p[3]) + + def p_parameter(self, p): + """parameter : typename NAME ordinal""" + p[0] = ('PARAM', p[1], p[2], p[3]) + + def p_typename(self, p): + """typename : NAME + | ARRAY""" + p[0] = p[1] + + def p_ordinal(self, p): + """ordinal : ORDINAL + | """ + if len(p) > 1: + p[0] = p[1] + + def p_error(self, e): + print('error: %s'%e) + + +def Parse(filename): + lexer = Lexer() + parser = Parser(lexer) + + lex.lex(object=lexer) + yacc.yacc(module=parser, debug=0, write_tables=0) + + tree = yacc.parse(open(filename).read()) + return tree + + +def Main(): + if len(sys.argv) < 2: + print("usage: %s filename" % (sys.argv[0])) + sys.exit(1) + tree = Parse(filename=sys.argv[1]) + print(tree) + + +if __name__ == '__main__': + Main() diff --git a/mojo/public/bindings/parser/mojo_translate.py b/mojo/public/bindings/parser/mojo_translate.py new file mode 100755 index 0000000..c562b52 --- /dev/null +++ b/mojo/public/bindings/parser/mojo_translate.py @@ -0,0 +1,115 @@ +#!/usr/bin/env python +# Copyright 2013 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. + +"""Translate parse tree to Mojom IR""" + + +import sys + + +def MapKind(kind): + # todo: add more types + map_to_kind = { 'bool': 'b', + 'int8': 'i8', + 'int16': 'i16', + 'int32': 'i32', + 'int64': 'i64', + 'uint8': 'u8', + 'uint16': 'u16', + 'uint32': 'u32', + 'uint64': 'u64', + 'string': 's', + 'handle': 'h' } + if kind.endswith('[]'): + return 'a:' + MapKind(kind[0:len(kind)-2]) + if kind in map_to_kind: + return map_to_kind[kind] + return 'x:' + kind + + +def MapOrdinal(ordinal): + return int(ordinal[1:]) # Strip leading '@' + + +def MapFields(fields): + out = [] + for field in fields: + if field[0] == 'FIELD': + out.append({'name': field[2], + 'kind': MapKind(field[1]), + 'ordinal': MapOrdinal(field[3])}) + return out + + +def MapParameters(parameters): + out = [] + for parameter in parameters: + if parameter[0] == 'PARAM': + out.append({'name': parameter[2], + 'kind': MapKind(parameter[1]), + 'ordinal': MapOrdinal(parameter[3])}) + return out + + +def MapMethods(methods): + out = [] + for method in methods: + if method[0] == 'METHOD': + out.append({'name': method[1], + 'parameters': MapParameters(method[2]), + 'ordinal': MapOrdinal(method[3])}) + return out + + +class MojomBuilder(): + + def __init__(self): + self.mojom = {} + + def AddStruct(self, name, attributes, fields): + struct = {} + struct['name'] = name + struct['fields'] = MapFields(fields) + self.mojom['structs'].append(struct) + # TODO(darin): Add support for |attributes| + + def AddInterface(self, name, methods): + interface = {} + interface['name'] = name + interface['methods'] = MapMethods(methods) + self.mojom['interfaces'].append(interface) + + def AddModule(self, name, contents): + self.mojom['name'] = name + self.mojom['namespace'] = name + self.mojom['structs'] = [] + self.mojom['interfaces'] = [] + for item in contents: + if item[0] == 'STRUCT': + self.AddStruct(name=item[1], attributes=item[2], fields=item[3]) + elif item[0] == 'INTERFACE': + self.AddInterface(name=item[1], methods=item[2]) + + def Build(self, tree): + if tree[0] == 'MODULE': + self.AddModule(name=tree[1], contents=tree[2]) + return self.mojom + + +def Translate(tree): + return MojomBuilder().Build(tree) + + +def Main(): + if len(sys.argv) < 2: + print("usage: %s filename" % (sys.argv[0])) + sys.exit(1) + tree = eval(open(sys.argv[1]).read()) + result = Translate(tree) + print(result) + + +if __name__ == '__main__': + Main() |