0

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
This commit is contained in:
darin@chromium.org
2013-10-30 17:10:42 +00:00
parent 8bb4798147
commit cbaaa4ee4a
5 changed files with 385 additions and 0 deletions

@ -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()

@ -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()

@ -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()