Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(91)

Side by Side Diff: third_party/chrome/ppapi/generators/idl_parser.py

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

Powered by Google App Engine
This is Rietveld 408576698