1 #!/usr/bin/env python 2 # Copyright 2013 The Chromium Authors. All rights reserved. 3 # Use of this source code is governed by a BSD-style license that can be 4 # found in the LICENSE file. 5 6 """Generates a syntax tree from a Mojo IDL file.""" 7 8 9 import sys 10 import os.path 11 12 13 # Try to load the ply module, if not, then assume it is in the third_party 14 # directory. 15 try: 16 # Disable lint check which fails to find the ply module. 17 # pylint: disable=F0401 18 from ply import lex 19 from ply import yacc 20 except ImportError: 21 module_path, module_name = os.path.split(__file__) 22 third_party = os.path.join( 23 module_path, os.pardir, os.pardir, os.pardir, os.pardir, 'third_party') 24 sys.path.append(third_party) 25 # pylint: disable=F0401 26 from ply import lex 27 from ply import yacc 28 29 30 def ListFromConcat(*items): 31 """Generate list by concatenating inputs""" 32 itemsout = [] 33 for item in items: 34 if item is None: 35 continue 36 if type(item) is not type([]): 37 itemsout.append(item) 38 else: 39 itemsout.extend(item) 40 41 return itemsout 42 43 44 class Lexer(object): 45 46 # This field is required by lex to specify the complete list of valid tokens. 47 tokens = ( 48 'NAME', 49 'NUMBER', 50 51 'ARRAY', 52 'ORDINAL', 53 54 'MSGPIPE', 55 'MSGPIPEARRAY', 56 57 'MODULE', 58 'STRUCT', 59 'INTERFACE', 60 'ENUM', 61 'VOID', 62 63 'LCURLY', 64 'RCURLY', 65 'LPAREN', 66 'RPAREN', 67 'LBRACKET', 68 'RBRACKET', 69 'COMMA', 70 'SEMICOLON', 71 'EQUALS', 72 ) 73 74 t_LCURLY = r'{' 75 t_RCURLY = r'}' 76 t_LPAREN = r'\(' 77 t_RPAREN = r'\)' 78 t_LBRACKET = r'\[' 79 t_RBRACKET = r'\]' 80 t_COMMA = r',' 81 t_SEMICOLON = r';' 82 t_EQUALS = r'=' 83 t_NAME = r'[a-zA-Z_][a-zA-Z0-9_]*' 84 t_ARRAY = r'[a-zA-Z_][a-zA-Z0-9_]*\[\]' 85 t_NUMBER = r'\d+' 86 t_ORDINAL = r'@[0-9]*' 87 88 def t_MSGPIPE(self, t): 89 r'handle<message_pipe>' 90 return t 91 92 def t_MSGPIPEARRAY(self, t): 93 r'handle<message_pipe>\[\]' 94 return t 95 96 def t_MODULE(self, t): 97 r'module' 98 return t 99 100 def t_STRUCT(self, t): 101 r'struct' 102 return t 103 104 def t_INTERFACE(self, t): 105 r'interface' 106 return t 107 108 def t_ENUM(self, t): 109 r'enum' 110 return t 111 112 def t_VOID(self, t): 113 r'void' 114 return t 115 116 # Ignore C and C++ style comments 117 def t_COMMENT(self, t): 118 r'(/\*(.|\n)*?\*/)|(//.*(\n[ \t]*//.*)*)' 119 pass 120 121 # Ignored characters 122 t_ignore = " \t" 123 124 def t_newline(self, t): 125 r'\n+' 126 t.lexer.lineno += t.value.count("\n") 127 128 def t_error(self, t): 129 print("Illegal character '%s'" % t.value[0]) 130 t.lexer.skip(1) 131 132 133 class Parser(object): 134 135 def __init__(self, lexer): 136 self.tokens = lexer.tokens 137 138 def p_module(self, p): 139 """module : MODULE NAME LCURLY definitions RCURLY""" 140 p[0] = ('MODULE', p[2], p[4]) 141 142 def p_definitions(self, p): 143 """definitions : definition definitions 144 |""" 145 if len(p) > 1: 146 p[0] = ListFromConcat(p[1], p[2]) 147 148 def p_definition(self, p): 149 """definition : struct 150 | interface 151 | enum""" 152 p[0] = p[1] 153 154 def p_attribute_section(self, p): 155 """attribute_section : LBRACKET attributes RBRACKET 156 | """ 157 if len(p) > 3: 158 p[0] = p[2] 159 160 def p_attributes(self, p): 161 """attributes : attribute 162 | attribute COMMA attributes 163 | """ 164 if len(p) == 2: 165 p[0] = ListFromConcat(p[1]) 166 elif len(p) > 3: 167 p[0] = ListFromConcat(p[1], p[3]) 168 169 def p_attribute(self, p): 170 """attribute : NAME EQUALS NUMBER 171 | NAME EQUALS NAME""" 172 p[0] = ('ATTRIBUTE', p[1], p[3]) 173 174 def p_struct(self, p): 175 """struct : attribute_section STRUCT NAME LCURLY fields RCURLY SEMICOLON""" 176 p[0] = ('STRUCT', p[3], p[1], p[5]) 177 178 def p_fields(self, p): 179 """fields : field fields 180 |""" 181 if len(p) > 1: 182 p[0] = ListFromConcat(p[1], p[2]) 183 184 def p_field(self, p): 185 """field : typename NAME ordinal SEMICOLON""" 186 p[0] = ('FIELD', p[1], p[2], p[3]) 187 188 def p_interface(self, p): 189 """interface : attribute_section INTERFACE NAME LCURLY methods RCURLY SEMICOLON""" 190 p[0] = ('INTERFACE', p[3], p[1], p[5]) 191 192 def p_methods(self, p): 193 """methods : method methods 194 | """ 195 if len(p) > 1: 196 p[0] = ListFromConcat(p[1], p[2]) 197 198 def p_method(self, p): 199 """method : VOID NAME LPAREN parameters RPAREN ordinal SEMICOLON""" 200 p[0] = ('METHOD', p[2], p[4], p[6]) 201 202 def p_parameters(self, p): 203 """parameters : parameter 204 | parameter COMMA parameters 205 | """ 206 if len(p) == 1: 207 p[0] = [] 208 elif len(p) == 2: 209 p[0] = ListFromConcat(p[1]) 210 elif len(p) > 3: 211 p[0] = ListFromConcat(p[1], p[3]) 212 213 def p_parameter(self, p): 214 """parameter : typename NAME ordinal""" 215 p[0] = ('PARAM', p[1], p[2], p[3]) 216 217 def p_typename(self, p): 218 """typename : NAME 219 | ARRAY 220 | MSGPIPE 221 | MSGPIPEARRAY""" 222 p[0] = p[1] 223 224 def p_ordinal(self, p): 225 """ordinal : ORDINAL 226 | """ 227 if len(p) > 1: 228 p[0] = p[1] 229 230 def p_enum(self, p): 231 """enum : ENUM NAME LCURLY enum_fields RCURLY SEMICOLON""" 232 p[0] = ('ENUM', p[2], p[4]) 233 234 def p_enum_fields(self, p): 235 """enum_fields : enum_field 236 | enum_field COMMA enum_fields 237 |""" 238 if len(p) == 2: 239 p[0] = ListFromConcat(p[1]) 240 elif len(p) > 3: 241 p[0] = ListFromConcat(p[1], p[3]) 242 243 def p_enum_field(self, p): 244 """enum_field : NAME 245 | NAME EQUALS NUMBER""" 246 if len(p) == 2: 247 p[0] = ('ENUM_FIELD', p[1], None) 248 else: 249 p[0] = ('ENUM_FIELD', p[1], p[3]) 250 251 def p_error(self, e): 252 print('error: %s'%e) 253 254 255 def Parse(filename): 256 lexer = Lexer() 257 parser = Parser(lexer) 258 259 lex.lex(object=lexer) 260 yacc.yacc(module=parser, debug=0, write_tables=0) 261 262 tree = yacc.parse(open(filename).read()) 263 return tree 264 265 266 def Main(): 267 if len(sys.argv) < 2: 268 print("usage: %s filename" % (sys.argv[0])) 269 sys.exit(1) 270 tree = Parse(filename=sys.argv[1]) 271 print(tree) 272 273 274 if __name__ == '__main__': 275 Main() 276