[email protected] | 2ec654a | 2012-01-10 17:47:00 | [diff] [blame] | 1 | #!/usr/bin/env python |
| 2 | # Copyright (c) 2012 The Chromium Authors. All rights reserved. |
[email protected] | d3864f5c1 | 2011-04-08 15:19:04 | [diff] [blame] | 3 | # Use of this source code is governed by a BSD-style license that can be |
| 4 | # found in the LICENSE file. |
| 5 | |
[email protected] | e4bda4e | 2011-04-20 17:41:58 | [diff] [blame] | 6 | """ Parser for PPAPI IDL """ |
[email protected] | d3864f5c1 | 2011-04-08 15:19:04 | [diff] [blame] | 7 | |
| 8 | # |
| 9 | # IDL Parser |
| 10 | # |
| 11 | # The parser is uses the PLY yacc library to build a set of parsing rules based |
| 12 | # on WebIDL. |
| 13 | # |
| 14 | # WebIDL, and WebIDL regular expressions can be found at: |
| 15 | # http://dev.w3.org/2006/webapi/WebIDL/ |
| 16 | # PLY can be found at: |
| 17 | # http://www.dabeaz.com/ply/ |
| 18 | # |
| 19 | # The parser generates a tree by recursively matching sets of items against |
| 20 | # defined patterns. When a match is made, that set of items is reduced |
| 21 | # to a new item. The new item can provide a match for parent patterns. |
| 22 | # In this way an AST is built (reduced) depth first. |
| 23 | |
| 24 | |
| 25 | import getopt |
[email protected] | 7a9f43d | 2011-05-04 18:01:45 | [diff] [blame] | 26 | import glob |
[email protected] | d3864f5c1 | 2011-04-08 15:19:04 | [diff] [blame] | 27 | import os.path |
| 28 | import re |
| 29 | import sys |
[email protected] | 5eef288 | 2011-07-19 00:08:54 | [diff] [blame] | 30 | import time |
[email protected] | d3864f5c1 | 2011-04-08 15:19:04 | [diff] [blame] | 31 | |
[email protected] | 1679636 | 2011-07-02 19:43:19 | [diff] [blame] | 32 | from idl_ast import IDLAst |
[email protected] | 4ad3696 | 2011-04-26 18:27:57 | [diff] [blame] | 33 | from idl_log import ErrOut, InfoOut, WarnOut |
[email protected] | d3864f5c1 | 2011-04-08 15:19:04 | [diff] [blame] | 34 | from idl_lexer import IDLLexer |
[email protected] | 1679636 | 2011-07-02 19:43:19 | [diff] [blame] | 35 | from idl_node import IDLAttribute, IDLFile, IDLNode |
[email protected] | 5b497ed | 2011-05-15 22:08:56 | [diff] [blame] | 36 | from idl_option import GetOption, Option, ParseOptions |
[email protected] | caff0c3 | 2011-07-22 16:34:13 | [diff] [blame] | 37 | from idl_lint import Lint |
[email protected] | 4ad3696 | 2011-04-26 18:27:57 | [diff] [blame] | 38 | |
[email protected] | d3864f5c1 | 2011-04-08 15:19:04 | [diff] [blame] | 39 | from ply import lex |
| 40 | from ply import yacc |
| 41 | |
[email protected] | 5b497ed | 2011-05-15 22:08:56 | [diff] [blame] | 42 | Option('build_debug', 'Debug tree building.') |
| 43 | Option('parse_debug', 'Debug parse reduction steps.') |
| 44 | Option('token_debug', 'Debug token generation.') |
| 45 | Option('dump_tree', 'Dump the tree.') |
[email protected] | f93616a | 2011-10-04 22:54:22 | [diff] [blame] | 46 | Option('srcroot', 'Working directory.', default=os.path.join('..', 'api')) |
[email protected] | 2069a3c | 2011-12-01 01:58:17 | [diff] [blame] | 47 | Option('include_private', 'Include private IDL directory in default API paths.') |
[email protected] | d3864f5c1 | 2011-04-08 15:19:04 | [diff] [blame] | 48 | |
| 49 | # |
| 50 | # ERROR_REMAP |
| 51 | # |
| 52 | # Maps the standard error formula into a more friendly error message. |
| 53 | # |
| 54 | ERROR_REMAP = { |
| 55 | 'Unexpected ")" after "(".' : 'Empty argument list.', |
| 56 | 'Unexpected ")" after ",".' : 'Missing argument.', |
| 57 | 'Unexpected "}" after ",".' : 'Trailing comma in block.', |
| 58 | 'Unexpected "}" after "{".' : 'Unexpected empty block.', |
[email protected] | 1679636 | 2011-07-02 19:43:19 | [diff] [blame] | 59 | 'Unexpected comment after "}".' : 'Unexpected trailing comment.', |
[email protected] | d3864f5c1 | 2011-04-08 15:19:04 | [diff] [blame] | 60 | 'Unexpected "{" after keyword "enum".' : 'Enum missing name.', |
| 61 | 'Unexpected "{" after keyword "struct".' : 'Struct missing name.', |
| 62 | 'Unexpected "{" after keyword "interface".' : 'Interface missing name.', |
| 63 | } |
| 64 | |
| 65 | # DumpReduction |
| 66 | # |
| 67 | # Prints out the set of items which matched a particular pattern and the |
| 68 | # new item or set it was reduced to. |
| 69 | def DumpReduction(cls, p): |
| 70 | if p[0] is None: |
[email protected] | 4ad3696 | 2011-04-26 18:27:57 | [diff] [blame] | 71 | InfoOut.Log("OBJ: %s(%d) - None\n" % (cls, len(p))) |
[email protected] | 1679636 | 2011-07-02 19:43:19 | [diff] [blame] | 72 | InfoOut.Log(" [%s]\n" % [str(x) for x in p[1:]]) |
[email protected] | d3864f5c1 | 2011-04-08 15:19:04 | [diff] [blame] | 73 | else: |
| 74 | out = "" |
| 75 | for index in range(len(p) - 1): |
| 76 | out += " >%s< " % str(p[index + 1]) |
[email protected] | 4ad3696 | 2011-04-26 18:27:57 | [diff] [blame] | 77 | InfoOut.Log("OBJ: %s(%d) - %s : %s\n" % (cls, len(p), str(p[0]), out)) |
[email protected] | d3864f5c1 | 2011-04-08 15:19:04 | [diff] [blame] | 78 | |
| 79 | |
| 80 | # CopyToList |
| 81 | # |
| 82 | # Takes an input item, list, or None, and returns a new list of that set. |
| 83 | def CopyToList(item): |
| 84 | # If the item is 'Empty' make it an empty list |
| 85 | if not item: item = [] |
| 86 | |
| 87 | # If the item is not a list |
| 88 | if type(item) is not type([]): item = [item] |
| 89 | |
| 90 | # Make a copy we can modify |
| 91 | return list(item) |
| 92 | |
| 93 | |
| 94 | |
| 95 | # ListFromConcat |
| 96 | # |
| 97 | # Generate a new List by joining of two sets of inputs which can be an |
| 98 | # individual item, a list of items, or None. |
| 99 | def ListFromConcat(*items): |
| 100 | itemsout = [] |
| 101 | for item in items: |
| 102 | itemlist = CopyToList(item) |
| 103 | itemsout.extend(itemlist) |
| 104 | |
| 105 | return itemsout |
| 106 | |
| 107 | |
| 108 | # TokenTypeName |
| 109 | # |
| 110 | # Generate a string which has the type and value of the token. |
| 111 | def TokenTypeName(t): |
| 112 | if t.type == 'SYMBOL': return 'symbol %s' % t.value |
| 113 | if t.type in ['HEX', 'INT', 'OCT', 'FLOAT']: |
| 114 | return 'value %s' % t.value |
| 115 | if t.type == 'STRING' : return 'string "%s"' % t.value |
[email protected] | 1679636 | 2011-07-02 19:43:19 | [diff] [blame] | 116 | if t.type == 'COMMENT' : return 'comment' |
[email protected] | d3864f5c1 | 2011-04-08 15:19:04 | [diff] [blame] | 117 | if t.type == t.value: return '"%s"' % t.value |
| 118 | return 'keyword "%s"' % t.value |
| 119 | |
| 120 | |
[email protected] | e4bda4e | 2011-04-20 17:41:58 | [diff] [blame] | 121 | # |
[email protected] | d3864f5c1 | 2011-04-08 15:19:04 | [diff] [blame] | 122 | # IDL Parser |
| 123 | # |
| 124 | # The Parser inherits the from the Lexer to provide PLY with the tokenizing |
| 125 | # definitions. Parsing patterns are encoded as function where p_<name> is |
| 126 | # is called any time a patern matching the function documentation is found. |
| 127 | # Paterns are expressed in the form of: |
| 128 | # """ <new item> : <item> .... |
| 129 | # | <item> ....""" |
| 130 | # |
| 131 | # Where new item is the result of a match against one or more sets of items |
| 132 | # separated by the "|". |
| 133 | # |
| 134 | # The function is called with an object 'p' where p[0] is the output object |
| 135 | # and p[n] is the set of inputs for positive values of 'n'. Len(p) can be |
| 136 | # used to distinguish between multiple item sets in the pattern. |
| 137 | # |
| 138 | # For more details on parsing refer to the PLY documentation at |
| 139 | # http://www.dabeaz.com/ply/ |
| 140 | # |
| 141 | # |
| 142 | # The parser uses the following conventions: |
| 143 | # a <type>_block defines a block of <type> definitions in the form of: |
| 144 | # [comment] [ext_attr_block] <type> <name> '{' <type>_list '}' ';' |
| 145 | # A block is reduced by returning an object of <type> with a name of <name> |
| 146 | # which in turn has <type>_list as children. |
| 147 | # |
| 148 | # A [comment] is a optional C style comment block enclosed in /* ... */ which |
| 149 | # is appended to the adjacent node as a child. |
| 150 | # |
| 151 | # A [ext_attr_block] is an optional list of Extended Attributes which is |
| 152 | # appended to the adjacent node as a child. |
| 153 | # |
| 154 | # a <type>_list defines a list of <type> items which will be passed as a |
| 155 | # list of children to the parent pattern. A list is in the form of: |
| 156 | # [comment] [ext_attr_block] <...DEF...> ';' <type>_list | (empty) |
| 157 | # or |
| 158 | # [comment] [ext_attr_block] <...DEF...> <type>_cont |
| 159 | # |
| 160 | # In the first form, the list is reduced recursively, where the right side |
| 161 | # <type>_list is first reduced then joined with pattern currently being |
| 162 | # matched. The list is terminated with the (empty) pattern is matched. |
| 163 | # |
| 164 | # In the second form the list is reduced recursively, where the right side |
| 165 | # <type>_cont is first reduced then joined with the pattern currently being |
| 166 | # matched. The type_<cont> is in the form of: |
| 167 | # ',' <type>_list | (empty) |
| 168 | # The <type>_cont form is used to consume the ',' which only occurs when |
| 169 | # there is more than one object in the list. The <type>_cont also provides |
| 170 | # the terminating (empty) definition. |
| 171 | # |
| 172 | |
| 173 | |
| 174 | class IDLParser(IDLLexer): |
| 175 | # TOP |
| 176 | # |
| 177 | # This pattern defines the top of the parse tree. The parse tree is in the |
| 178 | # the form of: |
| 179 | # |
| 180 | # top |
| 181 | # *modifiers |
| 182 | # *comments |
| 183 | # *ext_attr_block |
| 184 | # ext_attr_list |
| 185 | # attr_arg_list |
| 186 | # *integer, value |
| 187 | # *param_list |
| 188 | # *typeref |
| 189 | # |
| 190 | # top_list |
| 191 | # describe_block |
| 192 | # describe_list |
| 193 | # enum_block |
[email protected] | 1679636 | 2011-07-02 19:43:19 | [diff] [blame] | 194 | # enum_item |
[email protected] | d3864f5c1 | 2011-04-08 15:19:04 | [diff] [blame] | 195 | # interface_block |
[email protected] | 1679636 | 2011-07-02 19:43:19 | [diff] [blame] | 196 | # member |
| 197 | # label_block |
| 198 | # label_item |
[email protected] | d3864f5c1 | 2011-04-08 15:19:04 | [diff] [blame] | 199 | # struct_block |
| 200 | # member |
| 201 | # typedef_decl |
| 202 | # typedef_data |
| 203 | # typedef_func |
| 204 | # |
| 205 | # (* sub matches found at multiple levels and are not truly children of top) |
| 206 | # |
| 207 | # We force all input files to start with two comments. The first comment is a |
| 208 | # Copyright notice followed by a set of file wide Extended Attributes, followed |
| 209 | # by the file comment and finally by file level patterns. |
| 210 | # |
| 211 | # Find the Copyright, File comment, and optional file wide attributes. We |
| 212 | # use a match with COMMENT instead of comments to force the token to be |
| 213 | # present. The extended attributes and the top_list become siblings which |
| 214 | # in turn are children of the file object created from the results of top. |
| 215 | def p_top(self, p): |
| 216 | """top : COMMENT COMMENT ext_attr_block top_list""" |
| 217 | |
[email protected] | 1679636 | 2011-07-02 19:43:19 | [diff] [blame] | 218 | Copyright = self.BuildComment('Copyright', p, 1) |
| 219 | Filedoc = self.BuildComment('Comment', p, 2) |
[email protected] | d3864f5c1 | 2011-04-08 15:19:04 | [diff] [blame] | 220 | |
[email protected] | f0dd202f | 2011-06-07 01:36:01 | [diff] [blame] | 221 | p[0] = ListFromConcat(Copyright, Filedoc, p[3], p[4]) |
[email protected] | d3864f5c1 | 2011-04-08 15:19:04 | [diff] [blame] | 222 | if self.parse_debug: DumpReduction('top', p) |
| 223 | |
| 224 | # Build a list of top level items. |
| 225 | def p_top_list(self, p): |
| 226 | """top_list : describe_block top_list |
| 227 | | enum_block top_list |
[email protected] | 1679636 | 2011-07-02 19:43:19 | [diff] [blame] | 228 | | inline top_list |
[email protected] | d3864f5c1 | 2011-04-08 15:19:04 | [diff] [blame] | 229 | | interface_block top_list |
[email protected] | 1679636 | 2011-07-02 19:43:19 | [diff] [blame] | 230 | | label_block top_list |
[email protected] | d3864f5c1 | 2011-04-08 15:19:04 | [diff] [blame] | 231 | | struct_block top_list |
[email protected] | 1679636 | 2011-07-02 19:43:19 | [diff] [blame] | 232 | | typedef_decl top_list |
[email protected] | d3864f5c1 | 2011-04-08 15:19:04 | [diff] [blame] | 233 | | """ |
| 234 | if len(p) > 2: |
| 235 | p[0] = ListFromConcat(p[1], p[2]) |
| 236 | if self.parse_debug: DumpReduction('top_list', p) |
| 237 | |
| 238 | # Recover from error and continue parsing at the next top match. |
| 239 | def p_top_error(self, p): |
| 240 | """top_list : error top_list""" |
| 241 | p[0] = p[2] |
| 242 | |
| 243 | # |
| 244 | # Modifier List |
| 245 | # |
| 246 | # |
| 247 | def p_modifiers(self, p): |
| 248 | """modifiers : comments ext_attr_block""" |
| 249 | p[0] = ListFromConcat(p[1], p[2]) |
| 250 | if self.parse_debug: DumpReduction('modifiers', p) |
| 251 | |
| 252 | # |
| 253 | # Comments |
| 254 | # |
| 255 | # Comments are optional list of C style comment objects. Comments are returned |
| 256 | # as a list or None. |
| 257 | # |
| 258 | def p_comments(self, p): |
| 259 | """comments : COMMENT comments |
| 260 | | """ |
| 261 | if len(p) > 1: |
[email protected] | 1679636 | 2011-07-02 19:43:19 | [diff] [blame] | 262 | child = self.BuildComment('Comment', p, 1) |
[email protected] | d3864f5c1 | 2011-04-08 15:19:04 | [diff] [blame] | 263 | p[0] = ListFromConcat(child, p[2]) |
| 264 | if self.parse_debug: DumpReduction('comments', p) |
| 265 | else: |
| 266 | if self.parse_debug: DumpReduction('no comments', p) |
| 267 | |
[email protected] | 1679636 | 2011-07-02 19:43:19 | [diff] [blame] | 268 | |
[email protected] | d3864f5c1 | 2011-04-08 15:19:04 | [diff] [blame] | 269 | # |
[email protected] | 1679636 | 2011-07-02 19:43:19 | [diff] [blame] | 270 | # Inline |
| 271 | # |
| 272 | # Inline blocks define option code to be emitted based on language tag, |
| 273 | # in the form of: |
| 274 | # #inline <LANGUAGE> |
| 275 | # <CODE> |
| 276 | # #endinl |
| 277 | # |
| 278 | def p_inline(self, p): |
| 279 | """inline : modifiers INLINE""" |
| 280 | words = p[2].split() |
| 281 | name = self.BuildAttribute('NAME', words[1]) |
| 282 | lines = p[2].split('\n') |
| 283 | value = self.BuildAttribute('VALUE', '\n'.join(lines[1:-1]) + '\n') |
| 284 | children = ListFromConcat(name, value, p[1]) |
| 285 | p[0] = self.BuildProduction('Inline', p, 2, children) |
| 286 | if self.parse_debug: DumpReduction('inline', p) |
| 287 | |
[email protected] | d3864f5c1 | 2011-04-08 15:19:04 | [diff] [blame] | 288 | # Extended Attributes |
| 289 | # |
| 290 | # Extended Attributes denote properties which will be applied to a node in the |
| 291 | # AST. A list of extended attributes are denoted by a brackets '[' ... ']' |
| 292 | # enclosing a comma separated list of extended attributes in the form of: |
| 293 | # |
| 294 | # Name |
| 295 | # Name=HEX | INT | OCT | FLOAT |
| 296 | # Name="STRING" |
| 297 | # Name=Function(arg ...) |
| 298 | # TODO(noelallen) -Not currently supported: |
| 299 | # ** Name(arg ...) ... |
| 300 | # ** Name=Scope::Value |
| 301 | # |
| 302 | # Extended Attributes are returned as a list or None. |
| 303 | |
| 304 | def p_ext_attr_block(self, p): |
| 305 | """ext_attr_block : '[' ext_attr_list ']' |
| 306 | | """ |
| 307 | if len(p) > 1: |
| 308 | p[0] = p[2] |
| 309 | if self.parse_debug: DumpReduction('ext_attr_block', p) |
| 310 | else: |
| 311 | if self.parse_debug: DumpReduction('no ext_attr_block', p) |
| 312 | |
| 313 | def p_ext_attr_list(self, p): |
[email protected] | 1679636 | 2011-07-02 19:43:19 | [diff] [blame] | 314 | """ext_attr_list : SYMBOL '=' SYMBOL ext_attr_cont |
| 315 | | SYMBOL '=' value ext_attr_cont |
| 316 | | SYMBOL '=' SYMBOL param_list ext_attr_cont |
| 317 | | SYMBOL ext_attr_cont""" |
| 318 | # If there are 4 tokens plus a return slot, this must be in the form |
| 319 | # SYMBOL = SYMBOL|value ext_attr_cont |
[email protected] | d3864f5c1 | 2011-04-08 15:19:04 | [diff] [blame] | 320 | if len(p) == 5: |
[email protected] | 1679636 | 2011-07-02 19:43:19 | [diff] [blame] | 321 | p[0] = ListFromConcat(self.BuildAttribute(p[1], p[3]), p[4]) |
| 322 | # If there are 5 tokens plus a return slot, this must be in the form |
| 323 | # SYMBOL = SYMBOL (param_list) ext_attr_cont |
| 324 | elif len(p) == 6: |
| 325 | member = self.BuildNamed('Member', p, 3, [p[4]]) |
| 326 | p[0] = ListFromConcat(self.BuildAttribute(p[1], member), p[5]) |
| 327 | # Otherwise, this must be: SYMBOL ext_attr_cont |
| 328 | else: |
| 329 | p[0] = ListFromConcat(self.BuildAttribute(p[1], 'True'), p[2]) |
[email protected] | d3864f5c1 | 2011-04-08 15:19:04 | [diff] [blame] | 330 | if self.parse_debug: DumpReduction('ext_attribute_list', p) |
| 331 | |
| 332 | def p_ext_attr_cont(self, p): |
| 333 | """ext_attr_cont : ',' ext_attr_list |
| 334 | |""" |
[email protected] | e4bda4e | 2011-04-20 17:41:58 | [diff] [blame] | 335 | if len(p) > 1: p[0] = p[2] |
[email protected] | d3864f5c1 | 2011-04-08 15:19:04 | [diff] [blame] | 336 | if self.parse_debug: DumpReduction('ext_attribute_cont', p) |
| 337 | |
[email protected] | 1679636 | 2011-07-02 19:43:19 | [diff] [blame] | 338 | def p_ext_attr_func(self, p): |
| 339 | """ext_attr_list : SYMBOL '(' attr_arg_list ')' ext_attr_cont""" |
| 340 | p[0] = ListFromConcat(self.BuildAttribute(p[1] + '()', p[3]), p[5]) |
| 341 | if self.parse_debug: DumpReduction('attr_arg_func', p) |
| 342 | |
| 343 | def p_ext_attr_arg_list(self, p): |
[email protected] | d3864f5c1 | 2011-04-08 15:19:04 | [diff] [blame] | 344 | """attr_arg_list : SYMBOL attr_arg_cont |
[email protected] | 1679636 | 2011-07-02 19:43:19 | [diff] [blame] | 345 | | value attr_arg_cont""" |
| 346 | p[0] = ListFromConcat(p[1], p[2]) |
[email protected] | d3864f5c1 | 2011-04-08 15:19:04 | [diff] [blame] | 347 | |
| 348 | def p_attr_arg_cont(self, p): |
| 349 | """attr_arg_cont : ',' attr_arg_list |
| 350 | | """ |
[email protected] | d3864f5c1 | 2011-04-08 15:19:04 | [diff] [blame] | 351 | if self.parse_debug: DumpReduction('attr_arg_cont', p) |
[email protected] | 1679636 | 2011-07-02 19:43:19 | [diff] [blame] | 352 | if len(p) > 1: p[0] = p[2] |
[email protected] | d3864f5c1 | 2011-04-08 15:19:04 | [diff] [blame] | 353 | |
| 354 | def p_attr_arg_error(self, p): |
| 355 | """attr_arg_cont : error attr_arg_cont""" |
| 356 | p[0] = p[2] |
| 357 | if self.parse_debug: DumpReduction('attr_arg_error', p) |
| 358 | |
| 359 | |
| 360 | # |
| 361 | # Describe |
| 362 | # |
| 363 | # A describe block is defined at the top level. It provides a mechanism for |
| 364 | # attributing a group of ext_attr to a describe_list. Members of the |
| 365 | # describe list are language specific 'Type' declarations |
| 366 | # |
| 367 | def p_describe_block(self, p): |
| 368 | """describe_block : modifiers DESCRIBE '{' describe_list '}' ';'""" |
[email protected] | ec5af27 | 2011-07-19 01:21:53 | [diff] [blame] | 369 | children = ListFromConcat(p[1], p[4]) |
[email protected] | d3864f5c1 | 2011-04-08 15:19:04 | [diff] [blame] | 370 | p[0] = self.BuildProduction('Describe', p, 2, children) |
| 371 | if self.parse_debug: DumpReduction('describe_block', p) |
| 372 | |
| 373 | def p_describe_list(self, p): |
| 374 | """describe_list : modifiers SYMBOL ';' describe_list |
| 375 | | modifiers ENUM ';' describe_list |
| 376 | | modifiers STRUCT ';' describe_list |
| 377 | | modifiers TYPEDEF ';' describe_list |
| 378 | | """ |
| 379 | if len(p) > 1: |
[email protected] | ec5af27 | 2011-07-19 01:21:53 | [diff] [blame] | 380 | Type = self.BuildNamed('Type', p, 2, p[1]) |
[email protected] | d3864f5c1 | 2011-04-08 15:19:04 | [diff] [blame] | 381 | p[0] = ListFromConcat(Type, p[4]) |
| 382 | |
| 383 | def p_describe_error(self, p): |
| 384 | """describe_list : error describe_list""" |
| 385 | p[0] = p[2] |
| 386 | |
| 387 | # |
| 388 | # Constant Values (integer, value) |
| 389 | # |
| 390 | # Constant values can be found at various levels. A Constant value is returns |
| 391 | # as the string value after validated against a FLOAT, HEX, INT, OCT or |
| 392 | # STRING pattern as appropriate. |
| 393 | # |
| 394 | def p_value(self, p): |
| 395 | """value : FLOAT |
| 396 | | HEX |
| 397 | | INT |
| 398 | | OCT |
| 399 | | STRING""" |
| 400 | p[0] = p[1] |
| 401 | if self.parse_debug: DumpReduction('value', p) |
| 402 | |
| 403 | def p_value_lshift(self, p): |
| 404 | """value : integer LSHIFT INT""" |
[email protected] | 1679636 | 2011-07-02 19:43:19 | [diff] [blame] | 405 | p[0] = "%s << %s" % (p[1], p[3]) |
[email protected] | d3864f5c1 | 2011-04-08 15:19:04 | [diff] [blame] | 406 | if self.parse_debug: DumpReduction('value', p) |
| 407 | |
| 408 | # Integers are numbers which may not be floats used in cases like array sizes. |
| 409 | def p_integer(self, p): |
| 410 | """integer : HEX |
| 411 | | INT |
| 412 | | OCT""" |
| 413 | p[0] = p[1] |
[email protected] | 1679636 | 2011-07-02 19:43:19 | [diff] [blame] | 414 | if self.parse_debug: DumpReduction('integer', p) |
| 415 | |
[email protected] | a2365a2 | 2011-10-07 22:43:04 | [diff] [blame] | 416 | # |
| 417 | # Expression |
| 418 | # |
| 419 | # A simple arithmetic expression. |
| 420 | # |
| 421 | precedence = ( |
| 422 | ('left','|','&','^'), |
| 423 | ('left','LSHIFT','RSHIFT'), |
| 424 | ('left','+','-'), |
| 425 | ('left','*','/'), |
| 426 | ('right','UMINUS','~'), |
| 427 | ) |
[email protected] | 1679636 | 2011-07-02 19:43:19 | [diff] [blame] | 428 | |
[email protected] | a2365a2 | 2011-10-07 22:43:04 | [diff] [blame] | 429 | def p_expression_binop(self, p): |
| 430 | """expression : expression LSHIFT expression |
| 431 | | expression RSHIFT expression |
| 432 | | expression '|' expression |
| 433 | | expression '&' expression |
| 434 | | expression '^' expression |
| 435 | | expression '+' expression |
| 436 | | expression '-' expression |
| 437 | | expression '*' expression |
| 438 | | expression '/' expression""" |
| 439 | p[0] = "%s %s %s" % (str(p[1]), str(p[2]), str(p[3])) |
| 440 | if self.parse_debug: DumpReduction('expression_binop', p) |
| 441 | |
| 442 | def p_expression_unop(self, p): |
| 443 | """expression : '-' expression %prec UMINUS |
| 444 | | '~' expression %prec '~'""" |
| 445 | p[0] = "%s%s" % (str(p[1]), str(p[2])) |
| 446 | if self.parse_debug: DumpReduction('expression_unop', p) |
| 447 | |
| 448 | def p_expression_term(self, p): |
| 449 | "expression : '(' expression ')'" |
| 450 | p[0] = "%s%s%s" % (str(p[1]), str(p[2]), str(p[3])) |
| 451 | if self.parse_debug: DumpReduction('expression_term', p) |
| 452 | |
| 453 | def p_expression_symbol(self, p): |
| 454 | "expression : SYMBOL" |
| 455 | p[0] = p[1] |
| 456 | if self.parse_debug: DumpReduction('expression_symbol', p) |
| 457 | |
| 458 | def p_expression_integer(self, p): |
| 459 | "expression : integer" |
| 460 | p[0] = p[1] |
| 461 | if self.parse_debug: DumpReduction('expression_integer', p) |
[email protected] | 1679636 | 2011-07-02 19:43:19 | [diff] [blame] | 462 | |
| 463 | # |
| 464 | # Array List |
| 465 | # |
| 466 | # Defined a list of array sizes (if any). |
| 467 | # |
| 468 | def p_arrays(self, p): |
| 469 | """arrays : '[' ']' arrays |
| 470 | | '[' integer ']' arrays |
| 471 | | """ |
| 472 | # If there are 3 tokens plus a return slot it is an unsized array |
| 473 | if len(p) == 4: |
| 474 | array = self.BuildProduction('Array', p, 1) |
| 475 | p[0] = ListFromConcat(array, p[3]) |
| 476 | # If there are 4 tokens plus a return slot it is a fixed array |
| 477 | elif len(p) == 5: |
| 478 | count = self.BuildAttribute('FIXED', p[2]) |
| 479 | array = self.BuildProduction('Array', p, 2, [count]) |
| 480 | p[0] = ListFromConcat(array, p[4]) |
| 481 | # If there is only a return slot, do not fill it for this terminator. |
| 482 | elif len(p) == 1: return |
| 483 | if self.parse_debug: DumpReduction('arrays', p) |
[email protected] | d3864f5c1 | 2011-04-08 15:19:04 | [diff] [blame] | 484 | |
| 485 | # |
| 486 | # Parameter List |
| 487 | # |
| 488 | # A parameter list is a collection of arguments which are passed to a |
[email protected] | 7a9f43d | 2011-05-04 18:01:45 | [diff] [blame] | 489 | # function. |
[email protected] | d3864f5c1 | 2011-04-08 15:19:04 | [diff] [blame] | 490 | # |
[email protected] | d3864f5c1 | 2011-04-08 15:19:04 | [diff] [blame] | 491 | def p_param_list(self, p): |
[email protected] | 1679636 | 2011-07-02 19:43:19 | [diff] [blame] | 492 | """param_list : '(' param_item param_cont ')' |
| 493 | | '(' ')' """ |
| 494 | if len(p) > 3: |
| 495 | args = ListFromConcat(p[2], p[3]) |
[email protected] | 7a9f43d | 2011-05-04 18:01:45 | [diff] [blame] | 496 | else: |
[email protected] | 38c0f7e | 2011-06-02 01:16:30 | [diff] [blame] | 497 | args = [] |
[email protected] | 1679636 | 2011-07-02 19:43:19 | [diff] [blame] | 498 | p[0] = self.BuildProduction('Callspec', p, 1, args) |
[email protected] | 7a9f43d | 2011-05-04 18:01:45 | [diff] [blame] | 499 | if self.parse_debug: DumpReduction('param_list', p) |
| 500 | |
| 501 | def p_param_item(self, p): |
[email protected] | 1679636 | 2011-07-02 19:43:19 | [diff] [blame] | 502 | """param_item : modifiers SYMBOL arrays SYMBOL""" |
| 503 | typeref = self.BuildAttribute('TYPEREF', p[2]) |
| 504 | children = ListFromConcat(p[1],typeref, p[3]) |
| 505 | p[0] = self.BuildNamed('Param', p, 4, children) |
[email protected] | 7a9f43d | 2011-05-04 18:01:45 | [diff] [blame] | 506 | if self.parse_debug: DumpReduction('param_item', p) |
[email protected] | d3864f5c1 | 2011-04-08 15:19:04 | [diff] [blame] | 507 | |
| 508 | def p_param_cont(self, p): |
[email protected] | 7a9f43d | 2011-05-04 18:01:45 | [diff] [blame] | 509 | """param_cont : ',' param_item param_cont |
[email protected] | d3864f5c1 | 2011-04-08 15:19:04 | [diff] [blame] | 510 | | """ |
| 511 | if len(p) > 1: |
[email protected] | 1679636 | 2011-07-02 19:43:19 | [diff] [blame] | 512 | p[0] = ListFromConcat(p[2], p[3]) |
[email protected] | d3864f5c1 | 2011-04-08 15:19:04 | [diff] [blame] | 513 | if self.parse_debug: DumpReduction('param_cont', p) |
| 514 | |
| 515 | def p_param_error(self, p): |
| 516 | """param_cont : error param_cont""" |
| 517 | p[0] = p[2] |
| 518 | |
[email protected] | d3864f5c1 | 2011-04-08 15:19:04 | [diff] [blame] | 519 | |
| 520 | # |
[email protected] | 38c0f7e | 2011-06-02 01:16:30 | [diff] [blame] | 521 | # Typedef |
| 522 | # |
| 523 | # A typedef creates a new referencable type. The tyepdef can specify an array |
| 524 | # definition as well as a function declaration. |
| 525 | # |
| 526 | def p_typedef_data(self, p): |
[email protected] | 1679636 | 2011-07-02 19:43:19 | [diff] [blame] | 527 | """typedef_decl : modifiers TYPEDEF SYMBOL SYMBOL ';' """ |
| 528 | typeref = self.BuildAttribute('TYPEREF', p[3]) |
| 529 | children = ListFromConcat(p[1], typeref) |
| 530 | p[0] = self.BuildNamed('Typedef', p, 4, children) |
[email protected] | 38c0f7e | 2011-06-02 01:16:30 | [diff] [blame] | 531 | if self.parse_debug: DumpReduction('typedef_data', p) |
| 532 | |
[email protected] | 1679636 | 2011-07-02 19:43:19 | [diff] [blame] | 533 | def p_typedef_array(self, p): |
| 534 | """typedef_decl : modifiers TYPEDEF SYMBOL arrays SYMBOL ';' """ |
| 535 | typeref = self.BuildAttribute('TYPEREF', p[3]) |
| 536 | children = ListFromConcat(p[1], typeref, p[4]) |
| 537 | p[0] = self.BuildNamed('Typedef', p, 5, children) |
| 538 | if self.parse_debug: DumpReduction('typedef_array', p) |
| 539 | |
[email protected] | 38c0f7e | 2011-06-02 01:16:30 | [diff] [blame] | 540 | def p_typedef_func(self, p): |
[email protected] | 1679636 | 2011-07-02 19:43:19 | [diff] [blame] | 541 | """typedef_decl : modifiers TYPEDEF SYMBOL SYMBOL param_list ';' """ |
| 542 | typeref = self.BuildAttribute('TYPEREF', p[3]) |
| 543 | children = ListFromConcat(p[1], typeref, p[5]) |
| 544 | p[0] = self.BuildNamed('Typedef', p, 4, children) |
[email protected] | 38c0f7e | 2011-06-02 01:16:30 | [diff] [blame] | 545 | if self.parse_debug: DumpReduction('typedef_func', p) |
| 546 | |
| 547 | # |
[email protected] | d3864f5c1 | 2011-04-08 15:19:04 | [diff] [blame] | 548 | # Enumeration |
| 549 | # |
| 550 | # An enumeration is a set of named integer constants. An enumeration |
| 551 | # is valid type which can be referenced in other definitions. |
| 552 | # |
| 553 | def p_enum_block(self, p): |
| 554 | """enum_block : modifiers ENUM SYMBOL '{' enum_list '}' ';'""" |
[email protected] | 1679636 | 2011-07-02 19:43:19 | [diff] [blame] | 555 | p[0] = self.BuildNamed('Enum', p, 3, ListFromConcat(p[1], p[5])) |
[email protected] | d3864f5c1 | 2011-04-08 15:19:04 | [diff] [blame] | 556 | if self.parse_debug: DumpReduction('enum_block', p) |
| 557 | |
| 558 | def p_enum_list(self, p): |
[email protected] | a2365a2 | 2011-10-07 22:43:04 | [diff] [blame] | 559 | """enum_list : modifiers SYMBOL '=' expression enum_cont |
[email protected] | 1679636 | 2011-07-02 19:43:19 | [diff] [blame] | 560 | | modifiers SYMBOL enum_cont""" |
| 561 | if len(p) > 4: |
| 562 | val = self.BuildAttribute('VALUE', p[4]) |
| 563 | enum = self.BuildNamed('EnumItem', p, 2, ListFromConcat(val, p[1])) |
| 564 | p[0] = ListFromConcat(enum, p[5]) |
| 565 | else: |
| 566 | enum = self.BuildNamed('EnumItem', p, 2, p[1]) |
| 567 | p[0] = ListFromConcat(enum, p[3]) |
[email protected] | d3864f5c1 | 2011-04-08 15:19:04 | [diff] [blame] | 568 | if self.parse_debug: DumpReduction('enum_list', p) |
| 569 | |
| 570 | def p_enum_cont(self, p): |
| 571 | """enum_cont : ',' enum_list |
| 572 | |""" |
| 573 | if len(p) > 1: p[0] = p[2] |
| 574 | if self.parse_debug: DumpReduction('enum_cont', p) |
| 575 | |
| 576 | def p_enum_cont_error(self, p): |
| 577 | """enum_cont : error enum_cont""" |
| 578 | p[0] = p[2] |
| 579 | if self.parse_debug: DumpReduction('enum_error', p) |
| 580 | |
| 581 | |
| 582 | # |
[email protected] | 1679636 | 2011-07-02 19:43:19 | [diff] [blame] | 583 | # Label |
| 584 | # |
| 585 | # A label is a special kind of enumeration which allows us to go from a |
| 586 | # set of labels |
| 587 | # |
| 588 | def p_label_block(self, p): |
| 589 | """label_block : modifiers LABEL SYMBOL '{' label_list '}' ';'""" |
| 590 | p[0] = self.BuildNamed('Label', p, 3, ListFromConcat(p[1], p[5])) |
| 591 | if self.parse_debug: DumpReduction('label_block', p) |
| 592 | |
| 593 | def p_label_list(self, p): |
| 594 | """label_list : modifiers SYMBOL '=' FLOAT label_cont""" |
| 595 | val = self.BuildAttribute('VALUE', p[4]) |
| 596 | label = self.BuildNamed('LabelItem', p, 2, ListFromConcat(val, p[1])) |
| 597 | p[0] = ListFromConcat(label, p[5]) |
| 598 | if self.parse_debug: DumpReduction('label_list', p) |
| 599 | |
| 600 | def p_label_cont(self, p): |
| 601 | """label_cont : ',' label_list |
| 602 | |""" |
| 603 | if len(p) > 1: p[0] = p[2] |
| 604 | if self.parse_debug: DumpReduction('label_cont', p) |
| 605 | |
| 606 | def p_label_cont_error(self, p): |
| 607 | """label_cont : error label_cont""" |
| 608 | p[0] = p[2] |
| 609 | if self.parse_debug: DumpReduction('label_error', p) |
| 610 | |
| 611 | |
| 612 | # |
| 613 | # Members |
| 614 | # |
| 615 | # A member attribute or function of a struct or interface. |
| 616 | # |
| 617 | def p_member_attribute(self, p): |
| 618 | """member_attribute : modifiers SYMBOL SYMBOL """ |
| 619 | typeref = self.BuildAttribute('TYPEREF', p[2]) |
| 620 | children = ListFromConcat(p[1], typeref) |
| 621 | p[0] = self.BuildNamed('Member', p, 3, children) |
| 622 | if self.parse_debug: DumpReduction('attribute', p) |
| 623 | |
| 624 | def p_member_attribute_array(self, p): |
| 625 | """member_attribute : modifiers SYMBOL arrays SYMBOL """ |
| 626 | typeref = self.BuildAttribute('TYPEREF', p[2]) |
| 627 | children = ListFromConcat(p[1], typeref, p[3]) |
| 628 | p[0] = self.BuildNamed('Member', p, 4, children) |
| 629 | if self.parse_debug: DumpReduction('attribute', p) |
| 630 | |
| 631 | def p_member_function(self, p): |
| 632 | """member_function : modifiers SYMBOL SYMBOL param_list""" |
| 633 | typeref = self.BuildAttribute('TYPEREF', p[2]) |
| 634 | children = ListFromConcat(p[1], typeref, p[4]) |
| 635 | p[0] = self.BuildNamed('Member', p, 3, children) |
| 636 | if self.parse_debug: DumpReduction('function', p) |
| 637 | |
| 638 | # |
[email protected] | d3864f5c1 | 2011-04-08 15:19:04 | [diff] [blame] | 639 | # Interface |
| 640 | # |
| 641 | # An interface is a named collection of functions. |
| 642 | # |
| 643 | def p_interface_block(self, p): |
[email protected] | 1679636 | 2011-07-02 19:43:19 | [diff] [blame] | 644 | """interface_block : modifiers INTERFACE SYMBOL '{' interface_list '}' ';'""" |
| 645 | p[0] = self.BuildNamed('Interface', p, 3, ListFromConcat(p[1], p[5])) |
[email protected] | d3864f5c1 | 2011-04-08 15:19:04 | [diff] [blame] | 646 | if self.parse_debug: DumpReduction('interface_block', p) |
| 647 | |
[email protected] | 1679636 | 2011-07-02 19:43:19 | [diff] [blame] | 648 | def p_interface_list(self, p): |
| 649 | """interface_list : member_function ';' interface_list |
| 650 | | """ |
[email protected] | d3864f5c1 | 2011-04-08 15:19:04 | [diff] [blame] | 651 | if len(p) > 1 : |
[email protected] | 1679636 | 2011-07-02 19:43:19 | [diff] [blame] | 652 | p[0] = ListFromConcat(p[1], p[3]) |
| 653 | if self.parse_debug: DumpReduction('interface_list', p) |
[email protected] | d3864f5c1 | 2011-04-08 15:19:04 | [diff] [blame] | 654 | |
[email protected] | 1679636 | 2011-07-02 19:43:19 | [diff] [blame] | 655 | def p_interface_error(self, p): |
| 656 | """interface_list : error interface_list""" |
[email protected] | d3864f5c1 | 2011-04-08 15:19:04 | [diff] [blame] | 657 | p[0] = p[2] |
| 658 | |
| 659 | # |
| 660 | # Struct |
| 661 | # |
| 662 | # A struct is a named collection of members which in turn reference other |
| 663 | # types. The struct is a referencable type. |
| 664 | # |
| 665 | def p_struct_block(self, p): |
| 666 | """struct_block : modifiers STRUCT SYMBOL '{' struct_list '}' ';'""" |
[email protected] | 1679636 | 2011-07-02 19:43:19 | [diff] [blame] | 667 | children = ListFromConcat(p[1], p[5]) |
| 668 | p[0] = self.BuildNamed('Struct', p, 3, children) |
[email protected] | d3864f5c1 | 2011-04-08 15:19:04 | [diff] [blame] | 669 | if self.parse_debug: DumpReduction('struct_block', p) |
| 670 | |
| 671 | def p_struct_list(self, p): |
[email protected] | 1679636 | 2011-07-02 19:43:19 | [diff] [blame] | 672 | """struct_list : member_attribute ';' struct_list |
| 673 | | member_function ';' struct_list |
| 674 | |""" |
| 675 | if len(p) > 1: p[0] = ListFromConcat(p[1], p[3]) |
[email protected] | d3864f5c1 | 2011-04-08 15:19:04 | [diff] [blame] | 676 | |
[email protected] | 1679636 | 2011-07-02 19:43:19 | [diff] [blame] | 677 | def p_struct_error(self, p): |
| 678 | """struct_list : error struct_list""" |
| 679 | p[0] = p[2] |
[email protected] | d3864f5c1 | 2011-04-08 15:19:04 | [diff] [blame] | 680 | |
| 681 | # |
| 682 | # Parser Errors |
| 683 | # |
| 684 | # p_error is called whenever the parser can not find a pattern match for |
| 685 | # a set of items from the current state. The p_error function defined here |
| 686 | # is triggered logging an error, and parsing recover happens as the |
| 687 | # p_<type>_error functions defined above are called. This allows the parser |
| 688 | # to continue so as to capture more than one error per file. |
| 689 | # |
| 690 | def p_error(self, t): |
| 691 | filename = self.lexobj.filename |
| 692 | self.parse_errors += 1 |
| 693 | if t: |
| 694 | lineno = t.lineno |
| 695 | pos = t.lexpos |
| 696 | prev = self.yaccobj.symstack[-1] |
| 697 | if type(prev) == lex.LexToken: |
| 698 | msg = "Unexpected %s after %s." % ( |
| 699 | TokenTypeName(t), TokenTypeName(prev)) |
| 700 | else: |
| 701 | msg = "Unexpected %s." % (t.value) |
| 702 | else: |
| 703 | lineno = self.last.lineno |
| 704 | pos = self.last.lexpos |
| 705 | msg = "Unexpected end of file after %s." % TokenTypeName(self.last) |
| 706 | self.yaccobj.restart() |
| 707 | |
| 708 | # Attempt to remap the error to a friendlier form |
| 709 | if msg in ERROR_REMAP: |
| 710 | msg = ERROR_REMAP[msg] |
| 711 | |
| 712 | # Log the error |
[email protected] | 4ad3696 | 2011-04-26 18:27:57 | [diff] [blame] | 713 | ErrOut.LogLine(filename, lineno, pos, msg) |
[email protected] | d3864f5c1 | 2011-04-08 15:19:04 | [diff] [blame] | 714 | |
[email protected] | 38c0f7e | 2011-06-02 01:16:30 | [diff] [blame] | 715 | def Warn(self, node, msg): |
| 716 | WarnOut.LogLine(node.filename, node.lineno, node.pos, msg) |
| 717 | self.parse_warnings += 1 |
[email protected] | d3864f5c1 | 2011-04-08 15:19:04 | [diff] [blame] | 718 | |
[email protected] | 5b497ed | 2011-05-15 22:08:56 | [diff] [blame] | 719 | def __init__(self): |
| 720 | IDLLexer.__init__(self) |
[email protected] | d3864f5c1 | 2011-04-08 15:19:04 | [diff] [blame] | 721 | self.yaccobj = yacc.yacc(module=self, tabmodule=None, debug=False, |
| 722 | optimize=0, write_tables=0) |
| 723 | |
[email protected] | 5b497ed | 2011-05-15 22:08:56 | [diff] [blame] | 724 | self.build_debug = GetOption('build_debug') |
| 725 | self.parse_debug = GetOption('parse_debug') |
| 726 | self.token_debug = GetOption('token_debug') |
| 727 | self.verbose = GetOption('verbose') |
[email protected] | d3864f5c1 | 2011-04-08 15:19:04 | [diff] [blame] | 728 | self.parse_errors = 0 |
| 729 | |
| 730 | # |
| 731 | # Tokenizer |
| 732 | # |
| 733 | # The token function returns the next token provided by IDLLexer for matching |
| 734 | # against the leaf paterns. |
| 735 | # |
| 736 | def token(self): |
| 737 | tok = self.lexobj.token() |
| 738 | if tok: |
| 739 | self.last = tok |
| 740 | if self.token_debug: |
[email protected] | 4ad3696 | 2011-04-26 18:27:57 | [diff] [blame] | 741 | InfoOut.Log("TOKEN %s(%s)" % (tok.type, tok.value)) |
[email protected] | d3864f5c1 | 2011-04-08 15:19:04 | [diff] [blame] | 742 | return tok |
| 743 | |
| 744 | # |
| 745 | # BuildProduction |
| 746 | # |
| 747 | # Production is the set of items sent to a grammar rule resulting in a new |
| 748 | # item being returned. |
| 749 | # |
| 750 | # p - Is the Yacc production object containing the stack of items |
| 751 | # index - Index into the production of the name for the item being produced. |
| 752 | # cls - The type of item being producted |
| 753 | # childlist - The children of the new item |
[email protected] | 1679636 | 2011-07-02 19:43:19 | [diff] [blame] | 754 | def BuildProduction(self, cls, p, index, childlist=None): |
| 755 | if not childlist: childlist = [] |
[email protected] | d3864f5c1 | 2011-04-08 15:19:04 | [diff] [blame] | 756 | filename = self.lexobj.filename |
| 757 | lineno = p.lineno(index) |
| 758 | pos = p.lexpos(index) |
[email protected] | 1679636 | 2011-07-02 19:43:19 | [diff] [blame] | 759 | out = IDLNode(cls, filename, lineno, pos, childlist) |
[email protected] | d3864f5c1 | 2011-04-08 15:19:04 | [diff] [blame] | 760 | if self.build_debug: |
[email protected] | 1679636 | 2011-07-02 19:43:19 | [diff] [blame] | 761 | InfoOut.Log("Building %s" % out) |
[email protected] | e4bda4e | 2011-04-20 17:41:58 | [diff] [blame] | 762 | return out |
[email protected] | d3864f5c1 | 2011-04-08 15:19:04 | [diff] [blame] | 763 | |
[email protected] | 1679636 | 2011-07-02 19:43:19 | [diff] [blame] | 764 | def BuildNamed(self, cls, p, index, childlist=None): |
| 765 | if not childlist: childlist = [] |
| 766 | childlist.append(self.BuildAttribute('NAME', p[index])) |
| 767 | return self.BuildProduction(cls, p, index, childlist) |
| 768 | |
| 769 | def BuildComment(self, cls, p, index): |
| 770 | name = p[index] |
| 771 | |
| 772 | # Remove comment markers |
| 773 | if name[:2] == '//': |
| 774 | # For C++ style, remove the preceding '//' |
| 775 | form = 'cc' |
| 776 | name = name[2:].rstrip() |
| 777 | else: |
| 778 | # For C style, remove ending '*/'' |
| 779 | form = 'c' |
| 780 | lines = [] |
| 781 | for line in name[:-2].split('\n'): |
| 782 | # Remove characters until start marker for this line '*' if found |
| 783 | # otherwise it should be blank. |
| 784 | offs = line.find('*') |
| 785 | if offs >= 0: |
| 786 | line = line[offs + 1:].rstrip() |
| 787 | else: |
| 788 | line = '' |
| 789 | lines.append(line) |
| 790 | name = '\n'.join(lines) |
| 791 | |
| 792 | childlist = [self.BuildAttribute('NAME', name), |
| 793 | self.BuildAttribute('FORM', form)] |
| 794 | return self.BuildProduction(cls, p, index, childlist) |
| 795 | |
[email protected] | d3864f5c1 | 2011-04-08 15:19:04 | [diff] [blame] | 796 | # |
[email protected] | 1679636 | 2011-07-02 19:43:19 | [diff] [blame] | 797 | # BuildAttribute |
[email protected] | d3864f5c1 | 2011-04-08 15:19:04 | [diff] [blame] | 798 | # |
| 799 | # An ExtendedAttribute is a special production that results in a property |
| 800 | # which is applied to the adjacent item. Attributes have no children and |
| 801 | # instead represent key/value pairs. |
| 802 | # |
[email protected] | 1679636 | 2011-07-02 19:43:19 | [diff] [blame] | 803 | def BuildAttribute(self, key, val): |
| 804 | return IDLAttribute(key, val) |
| 805 | |
[email protected] | d3864f5c1 | 2011-04-08 15:19:04 | [diff] [blame] | 806 | |
| 807 | # |
| 808 | # ParseData |
| 809 | # |
| 810 | # Attempts to parse the current data loaded in the lexer. |
| 811 | # |
[email protected] | 38c0f7e | 2011-06-02 01:16:30 | [diff] [blame] | 812 | def ParseData(self, data, filename='<Internal>'): |
| 813 | self.SetData(filename, data) |
[email protected] | d3864f5c1 | 2011-04-08 15:19:04 | [diff] [blame] | 814 | try: |
[email protected] | 7a9f43d | 2011-05-04 18:01:45 | [diff] [blame] | 815 | self.parse_errors = 0 |
[email protected] | 38c0f7e | 2011-06-02 01:16:30 | [diff] [blame] | 816 | self.parse_warnings = 0 |
[email protected] | d3864f5c1 | 2011-04-08 15:19:04 | [diff] [blame] | 817 | return self.yaccobj.parse(lexer=self) |
| 818 | |
| 819 | except lex.LexError as le: |
[email protected] | 4ad3696 | 2011-04-26 18:27:57 | [diff] [blame] | 820 | ErrOut.Log(str(le)) |
[email protected] | d3864f5c1 | 2011-04-08 15:19:04 | [diff] [blame] | 821 | return [] |
| 822 | |
| 823 | # |
| 824 | # ParseFile |
| 825 | # |
| 826 | # Loads a new file into the lexer and attemps to parse it. |
| 827 | # |
| 828 | def ParseFile(self, filename): |
[email protected] | 5eef288 | 2011-07-19 00:08:54 | [diff] [blame] | 829 | date = time.ctime(os.path.getmtime(filename)) |
[email protected] | 1679636 | 2011-07-02 19:43:19 | [diff] [blame] | 830 | data = open(filename).read() |
[email protected] | d3864f5c1 | 2011-04-08 15:19:04 | [diff] [blame] | 831 | if self.verbose: |
[email protected] | 4ad3696 | 2011-04-26 18:27:57 | [diff] [blame] | 832 | InfoOut.Log("Parsing %s" % filename) |
[email protected] | d3864f5c1 | 2011-04-08 15:19:04 | [diff] [blame] | 833 | try: |
[email protected] | 38c0f7e | 2011-06-02 01:16:30 | [diff] [blame] | 834 | out = self.ParseData(data, filename) |
[email protected] | 1679636 | 2011-07-02 19:43:19 | [diff] [blame] | 835 | |
| 836 | # If we have a src root specified, remove it from the path |
| 837 | srcroot = GetOption('srcroot') |
| 838 | if srcroot and filename.find(srcroot) == 0: |
| 839 | filename = filename[len(srcroot) + 1:] |
[email protected] | 5eef288 | 2011-07-19 00:08:54 | [diff] [blame] | 840 | filenode = IDLFile(filename, out, self.parse_errors + self.lex_errors) |
| 841 | filenode.SetProperty('DATETIME', date) |
| 842 | return filenode |
[email protected] | d3864f5c1 | 2011-04-08 15:19:04 | [diff] [blame] | 843 | |
| 844 | except Exception as e: |
[email protected] | 4ad3696 | 2011-04-26 18:27:57 | [diff] [blame] | 845 | ErrOut.LogLine(filename, self.last.lineno, self.last.lexpos, |
| 846 | 'Internal parsing error - %s.' % str(e)) |
[email protected] | d3864f5c1 | 2011-04-08 15:19:04 | [diff] [blame] | 847 | raise |
[email protected] | d3864f5c1 | 2011-04-08 15:19:04 | [diff] [blame] | 848 | |
| 849 | |
| 850 | |
[email protected] | e4bda4e | 2011-04-20 17:41:58 | [diff] [blame] | 851 | # |
| 852 | # Flatten Tree |
| 853 | # |
| 854 | # Flattens the tree of IDLNodes for use in testing. |
| 855 | # |
[email protected] | d3864f5c1 | 2011-04-08 15:19:04 | [diff] [blame] | 856 | def FlattenTree(node): |
| 857 | add_self = False |
| 858 | out = [] |
[email protected] | 1679636 | 2011-07-02 19:43:19 | [diff] [blame] | 859 | for child in node.children: |
| 860 | if child.IsA('Comment'): |
[email protected] | d3864f5c1 | 2011-04-08 15:19:04 | [diff] [blame] | 861 | add_self = True |
| 862 | else: |
[email protected] | 1679636 | 2011-07-02 19:43:19 | [diff] [blame] | 863 | out.extend(FlattenTree(child)) |
[email protected] | d3864f5c1 | 2011-04-08 15:19:04 | [diff] [blame] | 864 | |
| 865 | if add_self: |
| 866 | out = [str(node)] + out |
| 867 | return out |
| 868 | |
| 869 | |
[email protected] | 1679636 | 2011-07-02 19:43:19 | [diff] [blame] | 870 | def TestErrors(filename, filenode): |
| 871 | nodelist = filenode.GetChildren() |
| 872 | |
[email protected] | d3864f5c1 | 2011-04-08 15:19:04 | [diff] [blame] | 873 | lexer = IDLLexer() |
| 874 | data = open(filename).read() |
| 875 | lexer.SetData(filename, data) |
| 876 | |
| 877 | pass_comments = [] |
| 878 | fail_comments = [] |
| 879 | while True: |
| 880 | tok = lexer.lexobj.token() |
| 881 | if tok == None: break |
| 882 | if tok.type == 'COMMENT': |
[email protected] | 1679636 | 2011-07-02 19:43:19 | [diff] [blame] | 883 | args = tok.value[3:-3].split() |
| 884 | if args[0] == 'OK': |
| 885 | pass_comments.append((tok.lineno, ' '.join(args[1:]))) |
[email protected] | d3864f5c1 | 2011-04-08 15:19:04 | [diff] [blame] | 886 | else: |
[email protected] | 1679636 | 2011-07-02 19:43:19 | [diff] [blame] | 887 | if args[0] == 'FAIL': |
| 888 | fail_comments.append((tok.lineno, ' '.join(args[1:]))) |
[email protected] | d3864f5c1 | 2011-04-08 15:19:04 | [diff] [blame] | 889 | obj_list = [] |
[email protected] | 7a9f43d | 2011-05-04 18:01:45 | [diff] [blame] | 890 | for node in nodelist: |
[email protected] | d3864f5c1 | 2011-04-08 15:19:04 | [diff] [blame] | 891 | obj_list.extend(FlattenTree(node)) |
| 892 | |
| 893 | errors = 0 |
| 894 | |
| 895 | # |
| 896 | # Check for expected successes |
| 897 | # |
| 898 | obj_cnt = len(obj_list) |
| 899 | pass_cnt = len(pass_comments) |
| 900 | if obj_cnt != pass_cnt: |
[email protected] | 4ad3696 | 2011-04-26 18:27:57 | [diff] [blame] | 901 | InfoOut.Log("Mismatched pass (%d) vs. nodes built (%d)." |
[email protected] | d3864f5c1 | 2011-04-08 15:19:04 | [diff] [blame] | 902 | % (pass_cnt, obj_cnt)) |
[email protected] | 4ad3696 | 2011-04-26 18:27:57 | [diff] [blame] | 903 | InfoOut.Log("PASS: %s" % [x[1] for x in pass_comments]) |
| 904 | InfoOut.Log("OBJS: %s" % obj_list) |
[email protected] | d3864f5c1 | 2011-04-08 15:19:04 | [diff] [blame] | 905 | errors += 1 |
| 906 | if pass_cnt > obj_cnt: pass_cnt = obj_cnt |
| 907 | |
| 908 | for i in range(pass_cnt): |
| 909 | line, comment = pass_comments[i] |
| 910 | if obj_list[i] != comment: |
[email protected] | 4ad3696 | 2011-04-26 18:27:57 | [diff] [blame] | 911 | ErrOut.LogLine(filename, line, None, "OBJ %s : EXPECTED %s\n" % |
| 912 | (obj_list[i], comment)) |
[email protected] | d3864f5c1 | 2011-04-08 15:19:04 | [diff] [blame] | 913 | errors += 1 |
| 914 | |
| 915 | # |
| 916 | # Check for expected errors |
| 917 | # |
[email protected] | 4ad3696 | 2011-04-26 18:27:57 | [diff] [blame] | 918 | err_list = ErrOut.DrainLog() |
[email protected] | d3864f5c1 | 2011-04-08 15:19:04 | [diff] [blame] | 919 | err_cnt = len(err_list) |
| 920 | fail_cnt = len(fail_comments) |
| 921 | if err_cnt != fail_cnt: |
[email protected] | 4ad3696 | 2011-04-26 18:27:57 | [diff] [blame] | 922 | InfoOut.Log("Mismatched fail (%d) vs. errors seen (%d)." |
[email protected] | d3864f5c1 | 2011-04-08 15:19:04 | [diff] [blame] | 923 | % (fail_cnt, err_cnt)) |
[email protected] | 4ad3696 | 2011-04-26 18:27:57 | [diff] [blame] | 924 | InfoOut.Log("FAIL: %s" % [x[1] for x in fail_comments]) |
| 925 | InfoOut.Log("ERRS: %s" % err_list) |
[email protected] | d3864f5c1 | 2011-04-08 15:19:04 | [diff] [blame] | 926 | errors += 1 |
| 927 | if fail_cnt > err_cnt: fail_cnt = err_cnt |
| 928 | |
| 929 | for i in range(fail_cnt): |
| 930 | line, comment = fail_comments[i] |
[email protected] | 4ad3696 | 2011-04-26 18:27:57 | [diff] [blame] | 931 | err = err_list[i].strip() |
| 932 | |
[email protected] | d3864f5c1 | 2011-04-08 15:19:04 | [diff] [blame] | 933 | if err_list[i] != comment: |
[email protected] | 4ad3696 | 2011-04-26 18:27:57 | [diff] [blame] | 934 | ErrOut.Log("%s(%d) Error\n\tERROR : %s\n\tEXPECT: %s" % ( |
[email protected] | d3864f5c1 | 2011-04-08 15:19:04 | [diff] [blame] | 935 | filename, line, err_list[i], comment)) |
| 936 | errors += 1 |
| 937 | |
| 938 | # Clear the error list for the next run |
| 939 | err_list = [] |
| 940 | return errors |
| 941 | |
| 942 | |
[email protected] | 7a9f43d | 2011-05-04 18:01:45 | [diff] [blame] | 943 | def TestFile(parser, filename): |
| 944 | # Capture errors instead of reporting them so we can compare them |
| 945 | # with the expected errors. |
| 946 | ErrOut.SetConsole(False) |
| 947 | ErrOut.SetCapture(True) |
[email protected] | 4ad3696 | 2011-04-26 18:27:57 | [diff] [blame] | 948 | |
[email protected] | 1679636 | 2011-07-02 19:43:19 | [diff] [blame] | 949 | filenode = parser.ParseFile(filename) |
[email protected] | 7a9f43d | 2011-05-04 18:01:45 | [diff] [blame] | 950 | |
| 951 | # Renable output |
| 952 | ErrOut.SetConsole(True) |
| 953 | ErrOut.SetCapture(False) |
| 954 | |
| 955 | # Compare captured errors |
[email protected] | 1679636 | 2011-07-02 19:43:19 | [diff] [blame] | 956 | return TestErrors(filename, filenode) |
[email protected] | 7a9f43d | 2011-05-04 18:01:45 | [diff] [blame] | 957 | |
| 958 | |
[email protected] | 1679636 | 2011-07-02 19:43:19 | [diff] [blame] | 959 | def TestErrorFiles(filter): |
[email protected] | 7a9f43d | 2011-05-04 18:01:45 | [diff] [blame] | 960 | idldir = os.path.split(sys.argv[0])[0] |
| 961 | idldir = os.path.join(idldir, 'test_parser', '*.idl') |
| 962 | filenames = glob.glob(idldir) |
[email protected] | 5b497ed | 2011-05-15 22:08:56 | [diff] [blame] | 963 | parser = IDLParser() |
[email protected] | 7a9f43d | 2011-05-04 18:01:45 | [diff] [blame] | 964 | total_errs = 0 |
| 965 | for filename in filenames: |
[email protected] | 1679636 | 2011-07-02 19:43:19 | [diff] [blame] | 966 | if filter and filename not in filter: continue |
[email protected] | 7a9f43d | 2011-05-04 18:01:45 | [diff] [blame] | 967 | errs = TestFile(parser, filename) |
| 968 | if errs: |
| 969 | ErrOut.Log("%s test failed with %d error(s)." % (filename, errs)) |
| 970 | total_errs += errs |
| 971 | |
| 972 | if total_errs: |
| 973 | ErrOut.Log("Failed parsing test.") |
| 974 | else: |
| 975 | InfoOut.Log("Passed parsing test.") |
| 976 | return total_errs |
| 977 | |
[email protected] | 2ec654a | 2012-01-10 17:47:00 | [diff] [blame] | 978 | |
[email protected] | 1679636 | 2011-07-02 19:43:19 | [diff] [blame] | 979 | def TestNamespaceFiles(filter): |
[email protected] | 7a9f43d | 2011-05-04 18:01:45 | [diff] [blame] | 980 | idldir = os.path.split(sys.argv[0])[0] |
| 981 | idldir = os.path.join(idldir, 'test_namespace', '*.idl') |
| 982 | filenames = glob.glob(idldir) |
[email protected] | 1679636 | 2011-07-02 19:43:19 | [diff] [blame] | 983 | testnames = [] |
| 984 | |
| 985 | for filename in filenames: |
| 986 | if filter and filename not in filter: continue |
| 987 | testnames.append(filename) |
| 988 | |
| 989 | # If we have no files to test, then skip this test |
| 990 | if not testnames: |
| 991 | InfoOut.Log('No files to test for namespace.') |
| 992 | return 0 |
[email protected] | 7a9f43d | 2011-05-04 18:01:45 | [diff] [blame] | 993 | |
| 994 | InfoOut.SetConsole(False) |
[email protected] | 1679636 | 2011-07-02 19:43:19 | [diff] [blame] | 995 | ast = ParseFiles(testnames) |
[email protected] | 7a9f43d | 2011-05-04 18:01:45 | [diff] [blame] | 996 | InfoOut.SetConsole(True) |
| 997 | |
[email protected] | 1679636 | 2011-07-02 19:43:19 | [diff] [blame] | 998 | errs = ast.GetProperty('ERRORS') |
| 999 | if errs: |
[email protected] | 7a9f43d | 2011-05-04 18:01:45 | [diff] [blame] | 1000 | ErrOut.Log("Failed namespace test.") |
| 1001 | else: |
| 1002 | InfoOut.Log("Passed namespace test.") |
[email protected] | 1679636 | 2011-07-02 19:43:19 | [diff] [blame] | 1003 | return errs |
[email protected] | 7a9f43d | 2011-05-04 18:01:45 | [diff] [blame] | 1004 | |
[email protected] | 2e4361ae | 2011-12-15 00:03:35 | [diff] [blame] | 1005 | default_dirs = ['.', 'trusted', 'dev', 'private'] |
[email protected] | 5b497ed | 2011-05-15 22:08:56 | [diff] [blame] | 1006 | def ParseFiles(filenames): |
| 1007 | parser = IDLParser() |
[email protected] | 7a9f43d | 2011-05-04 18:01:45 | [diff] [blame] | 1008 | filenodes = [] |
[email protected] | 7a9f43d | 2011-05-04 18:01:45 | [diff] [blame] | 1009 | |
[email protected] | 5eef288 | 2011-07-19 00:08:54 | [diff] [blame] | 1010 | if not filenames: |
| 1011 | filenames = [] |
| 1012 | srcroot = GetOption('srcroot') |
[email protected] | 2069a3c | 2011-12-01 01:58:17 | [diff] [blame] | 1013 | dirs = default_dirs |
| 1014 | if GetOption('include_private'): |
| 1015 | dirs += ['private'] |
| 1016 | for dirname in dirs: |
[email protected] | 406793d | 2011-11-14 22:26:54 | [diff] [blame] | 1017 | srcdir = os.path.join(srcroot, dirname, '*.idl') |
[email protected] | ec5af27 | 2011-07-19 01:21:53 | [diff] [blame] | 1018 | srcdir = os.path.normpath(srcdir) |
| 1019 | filenames += sorted(glob.glob(srcdir)) |
[email protected] | 5eef288 | 2011-07-19 00:08:54 | [diff] [blame] | 1020 | |
[email protected] | 4a41db5 | 2012-01-13 00:02:56 | [diff] [blame] | 1021 | if not filenames: |
| 1022 | ErrOut.Log('No sources provided.') |
| 1023 | |
[email protected] | 7a9f43d | 2011-05-04 18:01:45 | [diff] [blame] | 1024 | for filename in filenames: |
[email protected] | 1679636 | 2011-07-02 19:43:19 | [diff] [blame] | 1025 | filenode = parser.ParseFile(filename) |
[email protected] | 7a9f43d | 2011-05-04 18:01:45 | [diff] [blame] | 1026 | filenodes.append(filenode) |
| 1027 | |
| 1028 | ast = IDLAst(filenodes) |
[email protected] | 1679636 | 2011-07-02 19:43:19 | [diff] [blame] | 1029 | if GetOption('dump_tree'): ast.Dump(0) |
[email protected] | caff0c3 | 2011-07-22 16:34:13 | [diff] [blame] | 1030 | |
| 1031 | Lint(ast) |
[email protected] | 1679636 | 2011-07-02 19:43:19 | [diff] [blame] | 1032 | return ast |
[email protected] | 7a9f43d | 2011-05-04 18:01:45 | [diff] [blame] | 1033 | |
[email protected] | 4ad3696 | 2011-04-26 18:27:57 | [diff] [blame] | 1034 | |
[email protected] | d3864f5c1 | 2011-04-08 15:19:04 | [diff] [blame] | 1035 | def Main(args): |
[email protected] | 5b497ed | 2011-05-15 22:08:56 | [diff] [blame] | 1036 | filenames = ParseOptions(args) |
[email protected] | d3864f5c1 | 2011-04-08 15:19:04 | [diff] [blame] | 1037 | |
[email protected] | 5b497ed | 2011-05-15 22:08:56 | [diff] [blame] | 1038 | # If testing... |
| 1039 | if GetOption('test'): |
[email protected] | 1679636 | 2011-07-02 19:43:19 | [diff] [blame] | 1040 | errs = TestErrorFiles(filenames) |
| 1041 | errs = TestNamespaceFiles(filenames) |
[email protected] | 7a9f43d | 2011-05-04 18:01:45 | [diff] [blame] | 1042 | if errs: |
| 1043 | ErrOut.Log("Parser failed with %d errors." % errs) |
| 1044 | return -1 |
[email protected] | 5b497ed | 2011-05-15 22:08:56 | [diff] [blame] | 1045 | return 0 |
[email protected] | 4ad3696 | 2011-04-26 18:27:57 | [diff] [blame] | 1046 | |
[email protected] | 5b497ed | 2011-05-15 22:08:56 | [diff] [blame] | 1047 | # Otherwise, build the AST |
[email protected] | 1679636 | 2011-07-02 19:43:19 | [diff] [blame] | 1048 | ast = ParseFiles(filenames) |
| 1049 | errs = ast.GetProperty('ERRORS') |
| 1050 | if errs: |
| 1051 | ErrOut.Log('Found %d error(s).' % errs); |
[email protected] | 7a9f43d | 2011-05-04 18:01:45 | [diff] [blame] | 1052 | InfoOut.Log("%d files processed." % len(filenames)) |
[email protected] | 1679636 | 2011-07-02 19:43:19 | [diff] [blame] | 1053 | return errs |
[email protected] | d3864f5c1 | 2011-04-08 15:19:04 | [diff] [blame] | 1054 | |
[email protected] | 2ec654a | 2012-01-10 17:47:00 | [diff] [blame] | 1055 | |
[email protected] | d3864f5c1 | 2011-04-08 15:19:04 | [diff] [blame] | 1056 | if __name__ == '__main__': |
| 1057 | sys.exit(Main(sys.argv[1:])) |