#!/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', 'MSGPIPE', 'MSGPIPEARRAY', 'MODULE', 'STRUCT', 'INTERFACE', 'ENUM', '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_MSGPIPE(self, t): r'handle<message_pipe>' return t def t_MSGPIPEARRAY(self, t): r'handle<message_pipe>\[\]' return t 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_ENUM(self, t): r'enum' 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 | enum""" 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 | NAME EQUALS NAME""" 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 : attribute_section INTERFACE NAME LCURLY methods RCURLY SEMICOLON""" p[0] = ('INTERFACE', p[3], p[1], p[5]) 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) == 1: p[0] = [] elif len(p) == 2: p[0] = ListFromConcat(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 | MSGPIPE | MSGPIPEARRAY""" p[0] = p[1] def p_ordinal(self, p): """ordinal : ORDINAL | """ if len(p) > 1: p[0] = p[1] def p_enum(self, p): """enum : ENUM NAME LCURLY enum_fields RCURLY SEMICOLON""" p[0] = ('ENUM', p[2], p[4]) def p_enum_fields(self, p): """enum_fields : enum_field | enum_field COMMA enum_fields |""" if len(p) == 2: p[0] = ListFromConcat(p[1]) elif len(p) > 3: p[0] = ListFromConcat(p[1], p[3]) def p_enum_field(self, p): """enum_field : NAME | NAME EQUALS NUMBER""" if len(p) == 2: p[0] = ('ENUM_FIELD', p[1], None) else: p[0] = ('ENUM_FIELD', p[1], p[3]) 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()