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:
mojo/public/bindings
0
mojo/public/bindings/generators/__init__.py
Normal file
0
mojo/public/bindings/generators/__init__.py
Normal file
38
mojo/public/bindings/mojo_idl.py
Executable file
38
mojo/public/bindings/mojo_idl.py
Executable file
@ -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
mojo/public/bindings/parser/__init__.py
Normal file
0
mojo/public/bindings/parser/__init__.py
Normal file
232
mojo/public/bindings/parser/mojo_parser.py
Executable file
232
mojo/public/bindings/parser/mojo_parser.py
Executable file
@ -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()
|
115
mojo/public/bindings/parser/mojo_translate.py
Executable file
115
mojo/public/bindings/parser/mojo_translate.py
Executable file
@ -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()
|
Reference in New Issue
Block a user