summaryrefslogtreecommitdiffstats
path: root/mojo
diff options
context:
space:
mode:
authordarin@chromium.org <darin@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-10-30 17:10:42 +0000
committerdarin@chromium.org <darin@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-10-30 17:10:42 +0000
commitcbaaa4ee4a979053f68a1f01e8c1517a803ee315 (patch)
tree5830fd0c29d378de672119a989fef3d6cd6f0245 /mojo
parent8bb4798147cbd8b582cb7e2a0aa7c029a62cdecc (diff)
downloadchromium_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__.py0
-rwxr-xr-xmojo/public/bindings/mojo_idl.py38
-rw-r--r--mojo/public/bindings/parser/__init__.py0
-rwxr-xr-xmojo/public/bindings/parser/mojo_parser.py232
-rwxr-xr-xmojo/public/bindings/parser/mojo_translate.py115
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()