Removed data compression UMA from ProxyService
[chromium-blink-merge.git] / tools / idl_parser / idl_parser.py
bloba19785bfb1b54792ab99cd2fdfad7d25aaabb726
1 #!/usr/bin/env python
2 # Copyright (c) 2013 The Chromium Authors. All rights reserved.
3 # Use of this source code is governed by a BSD-style license that can be
4 # found in the LICENSE file.
6 """ Parser for PPAPI IDL """
9 # IDL Parser
11 # The parser is uses the PLY yacc library to build a set of parsing rules based
12 # on WebIDL.
14 # WebIDL, and WebIDL grammar can be found at:
15 # http://heycam.github.io/webidl/
16 # PLY can be found at:
17 # http://www.dabeaz.com/ply/
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.
26 # Disable check for line length and Member as Function due to how grammar rules
27 # are defined with PLY
29 # pylint: disable=R0201
30 # pylint: disable=C0301
32 import os.path
33 import sys
34 import time
36 from idl_lexer import IDLLexer
37 from idl_node import IDLAttribute, IDLNode
40 # Try to load the ply module, if not, then assume it is in the third_party
41 # directory.
43 try:
44 # Disable lint check which fails to find the ply module.
45 # pylint: disable=F0401
46 from ply import lex
47 from ply import yacc
48 except ImportError:
49 module_path, module_name = os.path.split(__file__)
50 third_party = os.path.join(module_path, os.par, os.par, 'third_party')
51 sys.path.append(third_party)
52 # pylint: disable=F0401
53 from ply import lex
54 from ply import yacc
57 # ERROR_REMAP
59 # Maps the standard error formula into a more friendly error message.
61 ERROR_REMAP = {
62 'Unexpected ")" after "(".' : 'Empty argument list.',
63 'Unexpected ")" after ",".' : 'Missing argument.',
64 'Unexpected "}" after ",".' : 'Trailing comma in block.',
65 'Unexpected "}" after "{".' : 'Unexpected empty block.',
66 'Unexpected comment after "}".' : 'Unexpected trailing comment.',
67 'Unexpected "{" after keyword "enum".' : 'Enum missing name.',
68 'Unexpected "{" after keyword "struct".' : 'Struct missing name.',
69 'Unexpected "{" after keyword "interface".' : 'Interface missing name.',
73 def Boolean(val):
74 """Convert to strict boolean type."""
75 if val:
76 return True
77 return False
80 def ListFromConcat(*items):
81 """Generate list by concatenating inputs"""
82 itemsout = []
83 for item in items:
84 if item is None:
85 continue
86 if type(item) is not type([]):
87 itemsout.append(item)
88 else:
89 itemsout.extend(item)
91 return itemsout
93 def ExpandProduction(p):
94 if type(p) == list:
95 return '[' + ', '.join([ExpandProduction(x) for x in p]) + ']'
96 if type(p) == IDLNode:
97 return 'Node:' + str(p)
98 if type(p) == IDLAttribute:
99 return 'Attr:' + str(p)
100 if type(p) == str:
101 return 'str:' + p
102 return '%s:%s' % (p.__class__.__name__, str(p))
104 # TokenTypeName
106 # Generate a string which has the type and value of the token.
108 def TokenTypeName(t):
109 if t.type == 'SYMBOL':
110 return 'symbol %s' % t.value
111 if t.type in ['HEX', 'INT', 'OCT', 'FLOAT']:
112 return 'value %s' % t.value
113 if t.type == 'string' :
114 return 'string "%s"' % t.value
115 if t.type == 'COMMENT' :
116 return 'comment'
117 if t.type == t.value:
118 return '"%s"' % t.value
119 if t.type == ',':
120 return 'Comma'
121 if t.type == 'identifier':
122 return 'identifier "%s"' % t.value
123 return 'keyword "%s"' % t.value
127 # IDL Parser
129 # The Parser inherits the from the Lexer to provide PLY with the tokenizing
130 # definitions. Parsing patterns are encoded as functions where p_<name> is
131 # is called any time a patern matching the function documentation is found.
132 # Paterns are expressed in the form of:
133 # """ <new item> : <item> ....
134 # | <item> ...."""
136 # Where new item is the result of a match against one or more sets of items
137 # separated by the "|".
139 # The function is called with an object 'p' where p[0] is the output object
140 # and p[n] is the set of inputs for positive values of 'n'. Len(p) can be
141 # used to distinguish between multiple item sets in the pattern.
143 # For more details on parsing refer to the PLY documentation at
144 # http://www.dabeaz.com/ply/
146 # The parser is based on the WebIDL standard. See:
147 # http://heycam.github.io/webidl/#idl-grammar
149 # The various productions are annotated so that the WHOLE number greater than
150 # zero in the comment denotes the matching WebIDL grammar definition.
152 # Productions with a fractional component in the comment denote additions to
153 # the WebIDL spec, such as comments.
157 class IDLParser(object):
159 # We force all input files to start with two comments. The first comment is a
160 # Copyright notice followed by a file comment and finally by file level
161 # productions.
163 # [0] Insert a TOP definition for Copyright and Comments
164 def p_Top(self, p):
165 """Top : COMMENT COMMENT Definitions"""
166 Copyright = self.BuildComment('Copyright', p, 1)
167 Filedoc = self.BuildComment('Comment', p, 2)
168 p[0] = ListFromConcat(Copyright, Filedoc, p[3])
170 # [0.1] Add support for Multiple COMMENTS
171 def p_Comments(self, p):
172 """Comments : CommentsRest"""
173 if len(p) > 1:
174 p[0] = p[1]
176 # [0.2] Produce a COMMENT and aggregate sibling comments
177 def p_CommentsRest(self, p):
178 """CommentsRest : COMMENT CommentsRest
179 | """
180 if len(p) > 1:
181 p[0] = ListFromConcat(self.BuildComment('Comment', p, 1), p[2])
185 #The parser is based on the WebIDL standard. See:
186 # http://heycam.github.io/webidl/#idl-grammar
188 # [1]
189 def p_Definitions(self, p):
190 """Definitions : ExtendedAttributeList Definition Definitions
191 | """
192 if len(p) > 1:
193 p[2].AddChildren(p[1])
194 p[0] = ListFromConcat(p[2], p[3])
196 # [2]
197 def p_Definition(self, p):
198 """Definition : CallbackOrInterface
199 | Partial
200 | Dictionary
201 | Exception
202 | Enum
203 | Typedef
204 | ImplementsStatement"""
205 p[0] = p[1]
207 # [2.1] Error recovery for definition
208 def p_DefinitionError(self, p):
209 """Definition : error ';'"""
210 p[0] = self.BuildError(p, 'Definition')
212 # [3]
213 def p_CallbackOrInterface(self, p):
214 """CallbackOrInterface : CALLBACK CallbackRestOrInterface
215 | Interface"""
216 if len(p) > 2:
217 p[0] = p[2]
218 else:
219 p[0] = p[1]
221 # [4]
222 def p_CallbackRestOrInterface(self, p):
223 """CallbackRestOrInterface : CallbackRest
224 | Interface"""
225 p[0] = p[1]
227 # [5]
228 def p_Interface(self, p):
229 """Interface : INTERFACE identifier Inheritance '{' InterfaceMembers '}' ';'"""
230 p[0] = self.BuildNamed('Interface', p, 2, ListFromConcat(p[3], p[5]))
232 # [6]
233 def p_Partial(self, p):
234 """Partial : PARTIAL PartialDefinition"""
235 p[2].AddChildren(self.BuildTrue('Partial'))
236 p[0] = p[2]
238 # [6.1] Error recovery for Partial
239 def p_PartialError(self, p):
240 """Partial : PARTIAL error"""
241 p[0] = self.BuildError(p, 'Partial')
243 # [7]
244 def p_PartialDefinition(self, p):
245 """PartialDefinition : PartialDictionary
246 | PartialInterface"""
247 p[0] = p[1]
249 # [8]
250 def p_PartialInterface(self, p):
251 """PartialInterface : INTERFACE identifier '{' InterfaceMembers '}' ';'"""
252 p[0] = self.BuildNamed('Interface', p, 2, p[4])
254 # [9]
255 def p_InterfaceMembers(self, p):
256 """InterfaceMembers : ExtendedAttributeList InterfaceMember InterfaceMembers
257 |"""
258 if len(p) > 1:
259 p[2].AddChildren(p[1])
260 p[0] = ListFromConcat(p[2], p[3])
262 # [10]
263 def p_InterfaceMember(self, p):
264 """InterfaceMember : Const
265 | AttributeOrOperationOrIterator"""
266 p[0] = p[1]
268 # [11]
269 def p_Dictionary(self, p):
270 """Dictionary : DICTIONARY identifier Inheritance '{' DictionaryMembers '}' ';'"""
271 p[0] = self.BuildNamed('Dictionary', p, 2, ListFromConcat(p[3], p[5]))
273 # [11.1] Error recovery for regular Dictionary
274 def p_DictionaryError(self, p):
275 """Dictionary : DICTIONARY error ';'"""
276 p[0] = self.BuildError(p, 'Dictionary')
278 # [12]
279 def p_DictionaryMembers(self, p):
280 """DictionaryMembers : ExtendedAttributeList DictionaryMember DictionaryMembers
281 |"""
282 if len(p) > 1:
283 p[2].AddChildren(p[1])
284 p[0] = ListFromConcat(p[2], p[3])
286 # [13]
287 def p_DictionaryMember(self, p):
288 """DictionaryMember : Type identifier Default ';'"""
289 p[0] = self.BuildNamed('Key', p, 2, ListFromConcat(p[1], p[3]))
291 # [14]
292 def p_PartialDictionary(self, p):
293 """PartialDictionary : DICTIONARY identifier '{' DictionaryMembers '}' ';'"""
294 partial = self.BuildTrue('Partial')
295 p[0] = self.BuildNamed('Dictionary', p, 2, ListFromConcat(p[4], partial))
297 # [14.1] Error recovery for Partial Dictionary
298 def p_PartialDictionaryError(self, p):
299 """PartialDictionary : DICTIONARY error ';'"""
300 p[0] = self.BuildError(p, 'PartialDictionary')
302 # [15]
303 def p_Default(self, p):
304 """Default : '=' DefaultValue
305 |"""
306 if len(p) > 1:
307 p[0] = self.BuildProduction('Default', p, 2, p[2])
309 # [16]
310 def p_DefaultValue(self, p):
311 """DefaultValue : ConstValue
312 | string"""
313 if type(p[1]) == str:
314 p[0] = ListFromConcat(self.BuildAttribute('TYPE', 'DOMString'),
315 self.BuildAttribute('NAME', p[1]))
316 else:
317 p[0] = p[1]
319 # [17]
320 def p_Exception(self, p):
321 """Exception : EXCEPTION identifier Inheritance '{' ExceptionMembers '}' ';'"""
322 p[0] = self.BuildNamed('Exception', p, 2, ListFromConcat(p[3], p[5]))
324 # [18]
325 def p_ExceptionMembers(self, p):
326 """ExceptionMembers : ExtendedAttributeList ExceptionMember ExceptionMembers
327 |"""
328 if len(p) > 1:
329 p[2].AddChildren(p[1])
330 p[0] = ListFromConcat(p[2], p[3])
332 # [18.1] Error recovery for ExceptionMembers
333 def p_ExceptionMembersError(self, p):
334 """ExceptionMembers : error"""
335 p[0] = self.BuildError(p, 'ExceptionMembers')
337 # [19]
338 def p_Inheritance(self, p):
339 """Inheritance : ':' identifier
340 |"""
341 if len(p) > 1:
342 p[0] = self.BuildNamed('Inherit', p, 2)
344 # [20]
345 def p_Enum(self, p):
346 """Enum : ENUM identifier '{' EnumValueList '}' ';'"""
347 p[0] = self.BuildNamed('Enum', p, 2, p[4])
349 # [20.1] Error recovery for Enums
350 def p_EnumError(self, p):
351 """Enum : ENUM error ';'"""
352 p[0] = self.BuildError(p, 'Enum')
354 # [21]
355 def p_EnumValueList(self, p):
356 """EnumValueList : ExtendedAttributeList string EnumValueListComma"""
357 enum = self.BuildNamed('EnumItem', p, 2, p[1])
358 p[0] = ListFromConcat(enum, p[3])
360 # [22]
361 def p_EnumValueListComma(self, p):
362 """EnumValueListComma : ',' EnumValueListString
363 |"""
364 if len(p) > 1:
365 p[0] = p[2]
367 # [23]
368 def p_EnumValueListString(self, p):
369 """EnumValueListString : ExtendedAttributeList string EnumValueListComma
370 |"""
371 if len(p) > 1:
372 enum = self.BuildNamed('EnumItem', p, 2, p[1])
373 p[0] = ListFromConcat(enum, p[3])
375 # [24]
376 def p_CallbackRest(self, p):
377 """CallbackRest : identifier '=' ReturnType '(' ArgumentList ')' ';'"""
378 arguments = self.BuildProduction('Arguments', p, 4, p[5])
379 p[0] = self.BuildNamed('Callback', p, 1, ListFromConcat(p[3], arguments))
381 # [25]
382 def p_Typedef(self, p):
383 """Typedef : TYPEDEF ExtendedAttributeListNoComments Type identifier ';'"""
384 p[0] = self.BuildNamed('Typedef', p, 4, ListFromConcat(p[2], p[3]))
386 # [25.1] Error recovery for Typedefs
387 def p_TypedefError(self, p):
388 """Typedef : TYPEDEF error ';'"""
389 p[0] = self.BuildError(p, 'Typedef')
391 # [26]
392 def p_ImplementsStatement(self, p):
393 """ImplementsStatement : identifier IMPLEMENTS identifier ';'"""
394 name = self.BuildAttribute('REFERENCE', p[3])
395 p[0] = self.BuildNamed('Implements', p, 1, name)
397 # [27]
398 def p_Const(self, p):
399 """Const : CONST ConstType identifier '=' ConstValue ';'"""
400 value = self.BuildProduction('Value', p, 5, p[5])
401 p[0] = self.BuildNamed('Const', p, 3, ListFromConcat(p[2], value))
403 # [28]
404 def p_ConstValue(self, p):
405 """ConstValue : BooleanLiteral
406 | FloatLiteral
407 | integer
408 | null"""
409 if type(p[1]) == str:
410 p[0] = ListFromConcat(self.BuildAttribute('TYPE', 'integer'),
411 self.BuildAttribute('NAME', p[1]))
412 else:
413 p[0] = p[1]
415 # [28.1] Add definition for NULL
416 def p_null(self, p):
417 """null : NULL"""
418 p[0] = ListFromConcat(self.BuildAttribute('TYPE', 'NULL'),
419 self.BuildAttribute('NAME', 'NULL'))
421 # [29]
422 def p_BooleanLiteral(self, p):
423 """BooleanLiteral : TRUE
424 | FALSE"""
425 value = self.BuildAttribute('VALUE', Boolean(p[1] == 'true'))
426 p[0] = ListFromConcat(self.BuildAttribute('TYPE', 'boolean'), value)
428 # [30]
429 def p_FloatLiteral(self, p):
430 """FloatLiteral : float
431 | '-' INFINITY
432 | INFINITY
433 | NAN """
434 if len(p) > 2:
435 val = '-Infinity'
436 else:
437 val = p[1]
438 p[0] = ListFromConcat(self.BuildAttribute('TYPE', 'float'),
439 self.BuildAttribute('VALUE', val))
441 # [31] Removed unsupported: Serializer
442 def p_AttributeOrOperationOrIterator(self, p):
443 """AttributeOrOperationOrIterator : Stringifier
444 | StaticMember
445 | Attribute
446 | OperationOrIterator"""
447 p[0] = p[1]
449 # [32-37] NOT IMPLEMENTED (Serializer)
451 # [38]
452 def p_Stringifier(self, p):
453 """Stringifier : STRINGIFIER StringifierRest"""
454 p[0] = self.BuildProduction('Stringifier', p, 1, p[2])
456 # [39]
457 def p_StringifierRest(self, p):
458 """StringifierRest : AttributeRest
459 | ReturnType OperationRest
460 | ';'"""
461 if len(p) == 3:
462 p[2].AddChildren(p[1])
463 p[0] = p[2]
464 elif p[1] != ';':
465 p[0] = p[1]
467 # [40]
468 def p_StaticMember(self, p):
469 """StaticMember : STATIC StaticMemberRest"""
470 p[2].AddChildren(self.BuildTrue('STATIC'))
471 p[0] = p[2]
473 # [41]
474 def p_StaticMemberRest(self, p):
475 """StaticMemberRest : AttributeRest
476 | ReturnType OperationRest"""
477 if len(p) == 2:
478 p[0] = p[1]
479 else:
480 p[2].AddChildren(p[1])
481 p[0] = p[2]
483 # [42]
484 def p_Attribute(self, p):
485 """Attribute : Inherit AttributeRest"""
486 p[2].AddChildren(ListFromConcat(p[1]))
487 p[0] = p[2]
489 # [43]
490 def p_AttributeRest(self, p):
491 """AttributeRest : ReadOnly ATTRIBUTE Type identifier ';'"""
492 p[0] = self.BuildNamed('Attribute', p, 4,
493 ListFromConcat(p[1], p[3]))
495 # [44]
496 def p_Inherit(self, p):
497 """Inherit : INHERIT
498 |"""
499 if len(p) > 1:
500 p[0] = self.BuildTrue('INHERIT')
502 # [45]
503 def p_ReadOnly(self, p):
504 """ReadOnly : READONLY
505 |"""
506 if len(p) > 1:
507 p[0] = self.BuildTrue('READONLY')
509 # [46]
510 def p_OperationOrIterator(self, p):
511 """OperationOrIterator : ReturnType OperationOrIteratorRest
512 | SpecialOperation"""
513 if len(p) == 3:
514 p[2].AddChildren(p[1])
515 p[0] = p[2]
516 else:
517 p[0] = p[1]
519 # [47]
520 def p_SpecialOperation(self, p):
521 """SpecialOperation : Special Specials ReturnType OperationRest"""
522 p[4].AddChildren(ListFromConcat(p[1], p[2], p[3]))
523 p[0] = p[4]
525 # [48]
526 def p_Specials(self, p):
527 """Specials : Special Specials
528 | """
529 if len(p) > 1:
530 p[0] = ListFromConcat(p[1], p[2])
532 # [49]
533 def p_Special(self, p):
534 """Special : GETTER
535 | SETTER
536 | CREATOR
537 | DELETER
538 | LEGACYCALLER"""
539 p[0] = self.BuildTrue(p[1].upper())
541 # [50] Removed unsupported: IteratorRest
542 def p_OperationOrIteratorRest(self, p):
543 """OperationOrIteratorRest : OperationRest"""
544 p[0] = p[1]
546 # [51-53] NOT IMPLEMENTED (IteratorRest)
548 # [54]
549 def p_OperationRest(self, p):
550 """OperationRest : OptionalIdentifier '(' ArgumentList ')' ';'"""
551 arguments = self.BuildProduction('Arguments', p, 2, p[3])
552 p[0] = self.BuildNamed('Operation', p, 1, arguments)
554 # [55]
555 def p_OptionalIdentifier(self, p):
556 """OptionalIdentifier : identifier
557 |"""
558 if len(p) > 1:
559 p[0] = p[1]
560 else:
561 p[0] = '_unnamed_'
563 # [56]
564 def p_ArgumentList(self, p):
565 """ArgumentList : Argument Arguments
566 |"""
567 if len(p) > 1:
568 p[0] = ListFromConcat(p[1], p[2])
570 # [56.1] ArgumentList error recovery
571 def p_ArgumentListError(self, p):
572 """ArgumentList : error """
573 p[0] = self.BuildError(p, 'ArgumentList')
575 # [57]
576 def p_Arguments(self, p):
577 """Arguments : ',' Argument Arguments
578 |"""
579 if len(p) > 1:
580 p[0] = ListFromConcat(p[2], p[3])
582 # [58]
583 def p_Argument(self, p):
584 """Argument : ExtendedAttributeList OptionalOrRequiredArgument"""
585 p[2].AddChildren(p[1])
586 p[0] = p[2]
588 # [59]
589 def p_OptionalOrRequiredArgument(self, p):
590 """OptionalOrRequiredArgument : OPTIONAL Type ArgumentName Default
591 | Type Ellipsis ArgumentName"""
592 if len(p) > 4:
593 arg = self.BuildNamed('Argument', p, 3, ListFromConcat(p[2], p[4]))
594 arg.AddChildren(self.BuildTrue('OPTIONAL'))
595 else:
596 arg = self.BuildNamed('Argument', p, 3, ListFromConcat(p[1], p[2]))
597 p[0] = arg
599 # [60]
600 def p_ArgumentName(self, p):
601 """ArgumentName : ArgumentNameKeyword
602 | identifier"""
603 p[0] = p[1]
605 # [61]
606 def p_Ellipsis(self, p):
607 """Ellipsis : ELLIPSIS
608 |"""
609 if len(p) > 1:
610 p[0] = self.BuildNamed('Argument', p, 1)
611 p[0].AddChildren(self.BuildTrue('ELLIPSIS'))
613 # [62]
614 def p_ExceptionMember(self, p):
615 """ExceptionMember : Const
616 | ExceptionField"""
617 p[0] = p[1]
619 # [63]
620 def p_ExceptionField(self, p):
621 """ExceptionField : Type identifier ';'"""
622 p[0] = self.BuildNamed('ExceptionField', p, 2, p[1])
624 # [63.1] Error recovery for ExceptionMembers
625 def p_ExceptionFieldError(self, p):
626 """ExceptionField : error"""
627 p[0] = self.BuildError(p, 'ExceptionField')
629 # [64] No comment version for mid statement attributes.
630 def p_ExtendedAttributeListNoComments(self, p):
631 """ExtendedAttributeListNoComments : '[' ExtendedAttribute ExtendedAttributes ']'
632 | """
633 if len(p) > 2:
634 items = ListFromConcat(p[2], p[3])
635 p[0] = self.BuildProduction('ExtAttributes', p, 1, items)
637 # [64.1] Add optional comment field for start of statements.
638 def p_ExtendedAttributeList(self, p):
639 """ExtendedAttributeList : Comments '[' ExtendedAttribute ExtendedAttributes ']'
640 | Comments """
641 if len(p) > 2:
642 items = ListFromConcat(p[3], p[4])
643 attribs = self.BuildProduction('ExtAttributes', p, 2, items)
644 p[0] = ListFromConcat(p[1], attribs)
645 else:
646 p[0] = p[1]
648 # [65]
649 def p_ExtendedAttributes(self, p):
650 """ExtendedAttributes : ',' ExtendedAttribute ExtendedAttributes
651 |"""
652 if len(p) > 1:
653 p[0] = ListFromConcat(p[2], p[3])
655 # We only support:
656 # [ identifier ]
657 # [ identifier ( ArgumentList ) ]
658 # [ identifier = identifier ]
659 # [ identifier = ( IdentifierList ) ]
660 # [ identifier = identifier ( ArgumentList ) ]
661 # [66] map directly to [91-93, 95]
662 # [67-69, 71] are unsupported
663 def p_ExtendedAttribute(self, p):
664 """ExtendedAttribute : ExtendedAttributeNoArgs
665 | ExtendedAttributeArgList
666 | ExtendedAttributeIdent
667 | ExtendedAttributeIdentList
668 | ExtendedAttributeNamedArgList"""
669 p[0] = p[1]
671 # [70]
672 def p_ArgumentNameKeyword(self, p):
673 """ArgumentNameKeyword : ATTRIBUTE
674 | CALLBACK
675 | CONST
676 | CREATOR
677 | DELETER
678 | DICTIONARY
679 | ENUM
680 | EXCEPTION
681 | GETTER
682 | IMPLEMENTS
683 | INHERIT
684 | LEGACYCALLER
685 | PARTIAL
686 | SERIALIZER
687 | SETTER
688 | STATIC
689 | STRINGIFIER
690 | TYPEDEF
691 | UNRESTRICTED"""
692 p[0] = p[1]
694 # [72]
695 def p_Type(self, p):
696 """Type : SingleType
697 | UnionType TypeSuffix"""
698 if len(p) == 2:
699 p[0] = self.BuildProduction('Type', p, 1, p[1])
700 else:
701 p[0] = self.BuildProduction('Type', p, 1, ListFromConcat(p[1], p[2]))
703 # [73]
704 def p_SingleType(self, p):
705 """SingleType : NonAnyType
706 | ANY TypeSuffixStartingWithArray"""
707 if len(p) == 2:
708 p[0] = p[1]
709 else:
710 p[0] = ListFromConcat(self.BuildProduction('Any', p, 1), p[2])
712 # [74]
713 def p_UnionType(self, p):
714 """UnionType : '(' UnionMemberType OR UnionMemberType UnionMemberTypes ')'"""
716 # [75]
717 def p_UnionMemberType(self, p):
718 """UnionMemberType : NonAnyType
719 | UnionType TypeSuffix
720 | ANY '[' ']' TypeSuffix"""
721 # [76]
722 def p_UnionMemberTypes(self, p):
723 """UnionMemberTypes : OR UnionMemberType UnionMemberTypes
724 |"""
726 # [77] Moved BYTESTRING, DOMSTRING, OBJECT, DATE, REGEXP to PrimitiveType
727 # Moving all built-in types into PrimitiveType makes it easier to
728 # differentiate between them and 'identifier', since p[1] would be a string in
729 # both cases.
730 def p_NonAnyType(self, p):
731 """NonAnyType : PrimitiveType TypeSuffix
732 | identifier TypeSuffix
733 | SEQUENCE '<' Type '>' Null"""
734 if len(p) == 3:
735 if type(p[1]) == str:
736 typeref = self.BuildNamed('Typeref', p, 1)
737 else:
738 typeref = p[1]
739 p[0] = ListFromConcat(typeref, p[2])
741 if len(p) == 6:
742 p[0] = self.BuildProduction('Sequence', p, 1, ListFromConcat(p[3], p[5]))
745 # [78]
746 def p_ConstType(self, p):
747 """ConstType : PrimitiveType Null
748 | identifier Null"""
749 if type(p[1]) == str:
750 p[0] = self.BuildNamed('Typeref', p, 1, p[2])
751 else:
752 p[1].AddChildren(p[2])
753 p[0] = p[1]
756 # [79] Added BYTESTRING, DOMSTRING, OBJECT, DATE, REGEXP
757 def p_PrimitiveType(self, p):
758 """PrimitiveType : UnsignedIntegerType
759 | UnrestrictedFloatType
760 | BOOLEAN
761 | BYTE
762 | OCTET
763 | BYTESTRING
764 | DOMSTRING
765 | OBJECT
766 | DATE
767 | REGEXP"""
768 if type(p[1]) == str:
769 p[0] = self.BuildNamed('PrimitiveType', p, 1)
770 else:
771 p[0] = p[1]
774 # [80]
775 def p_UnrestrictedFloatType(self, p):
776 """UnrestrictedFloatType : UNRESTRICTED FloatType
777 | FloatType"""
778 if len(p) == 2:
779 typeref = self.BuildNamed('PrimitiveType', p, 1)
780 else:
781 typeref = self.BuildNamed('PrimitiveType', p, 2)
782 typeref.AddChildren(self.BuildTrue('UNRESTRICTED'))
783 p[0] = typeref
786 # [81]
787 def p_FloatType(self, p):
788 """FloatType : FLOAT
789 | DOUBLE"""
790 p[0] = p[1]
792 # [82]
793 def p_UnsignedIntegerType(self, p):
794 """UnsignedIntegerType : UNSIGNED IntegerType
795 | IntegerType"""
796 if len(p) == 2:
797 p[0] = p[1]
798 else:
799 p[0] = 'unsigned ' + p[2]
801 # [83]
802 def p_IntegerType(self, p):
803 """IntegerType : SHORT
804 | LONG OptionalLong"""
805 if len(p) == 2:
806 p[0] = p[1]
807 else:
808 p[0] = p[1] + p[2]
810 # [84]
811 def p_OptionalLong(self, p):
812 """OptionalLong : LONG
813 | """
814 if len(p) > 1:
815 p[0] = ' ' + p[1]
816 else:
817 p[0] = ''
820 # [85] Add support for sized array
821 def p_TypeSuffix(self, p):
822 """TypeSuffix : '[' integer ']' TypeSuffix
823 | '[' ']' TypeSuffix
824 | '?' TypeSuffixStartingWithArray
825 | """
826 if len(p) == 5:
827 p[0] = self.BuildNamed('Array', p, 2, p[4])
829 if len(p) == 4:
830 p[0] = self.BuildProduction('Array', p, 1, p[3])
832 if len(p) == 3:
833 p[0] = ListFromConcat(self.BuildTrue('NULLABLE'), p[2])
836 # [86]
837 def p_TypeSuffixStartingWithArray(self, p):
838 """TypeSuffixStartingWithArray : '[' ']' TypeSuffix
839 | """
840 if len(p) > 1:
841 p[0] = self.BuildProduction('Array', p, 0, p[3])
843 # [87]
844 def p_Null(self, p):
845 """Null : '?'
846 |"""
847 if len(p) > 1:
848 p[0] = self.BuildTrue('NULLABLE')
850 # [88]
851 def p_ReturnType(self, p):
852 """ReturnType : Type
853 | VOID"""
854 if p[1] == 'void':
855 p[0] = self.BuildProduction('Type', p, 1)
856 p[0].AddChildren(self.BuildNamed('PrimitiveType', p, 1))
857 else:
858 p[0] = p[1]
860 # [89]
861 def p_IdentifierList(self, p):
862 """IdentifierList : identifier Identifiers"""
863 p[0] = ListFromConcat(p[1], p[2])
865 # [90]
866 def p_Identifiers(self, p):
867 """Identifiers : ',' identifier Identifiers
868 |"""
869 if len(p) > 1:
870 p[0] = ListFromConcat(p[2], p[3])
872 # [91]
873 def p_ExtendedAttributeNoArgs(self, p):
874 """ExtendedAttributeNoArgs : identifier"""
875 p[0] = self.BuildNamed('ExtAttribute', p, 1)
877 # [92]
878 def p_ExtendedAttributeArgList(self, p):
879 """ExtendedAttributeArgList : identifier '(' ArgumentList ')'"""
880 arguments = self.BuildProduction('Arguments', p, 2, p[3])
881 p[0] = self.BuildNamed('ExtAttribute', p, 1, arguments)
883 # [93]
884 def p_ExtendedAttributeIdent(self, p):
885 """ExtendedAttributeIdent : identifier '=' identifier"""
886 value = self.BuildAttribute('VALUE', p[3])
887 p[0] = self.BuildNamed('ExtAttribute', p, 1, value)
889 # [94]
890 def p_ExtendedAttributeIdentList(self, p):
891 """ExtendedAttributeIdentList : identifier '=' '(' IdentifierList ')'"""
892 value = self.BuildAttribute('VALUE', p[4])
893 p[0] = self.BuildNamed('ExtAttribute', p, 1, value)
895 # [95]
896 def p_ExtendedAttributeNamedArgList(self, p):
897 """ExtendedAttributeNamedArgList : identifier '=' identifier '(' ArgumentList ')'"""
898 args = self.BuildProduction('Arguments', p, 4, p[5])
899 value = self.BuildNamed('Call', p, 3, args)
900 p[0] = self.BuildNamed('ExtAttribute', p, 1, value)
902 # [96] NOT IMPLEMENTED (ExtendedAttributeTypePair)
905 # Parser Errors
907 # p_error is called whenever the parser can not find a pattern match for
908 # a set of items from the current state. The p_error function defined here
909 # is triggered logging an error, and parsing recovery happens as the
910 # p_<type>_error functions defined above are called. This allows the parser
911 # to continue so as to capture more than one error per file.
913 def p_error(self, t):
914 if t:
915 lineno = t.lineno
916 pos = t.lexpos
917 prev = self.yaccobj.symstack[-1]
918 if type(prev) == lex.LexToken:
919 msg = "Unexpected %s after %s." % (
920 TokenTypeName(t), TokenTypeName(prev))
921 else:
922 msg = "Unexpected %s." % (t.value)
923 else:
924 last = self.LastToken()
925 lineno = last.lineno
926 pos = last.lexpos
927 msg = "Unexpected end of file after %s." % TokenTypeName(last)
928 self.yaccobj.restart()
930 # Attempt to remap the error to a friendlier form
931 if msg in ERROR_REMAP:
932 msg = ERROR_REMAP[msg]
934 self._last_error_msg = msg
935 self._last_error_lineno = lineno
936 self._last_error_pos = pos
938 def Warn(self, node, msg):
939 sys.stdout.write(node.GetLogLine(msg))
940 self.parse_warnings += 1
942 def LastToken(self):
943 return self.lexer.last
945 def __init__(self, lexer, verbose=False, debug=False, mute_error=False):
946 self.lexer = lexer
947 self.tokens = lexer.KnownTokens()
948 self.yaccobj = yacc.yacc(module=self, tabmodule=None, debug=debug,
949 optimize=0, write_tables=0)
950 self.parse_debug = debug
951 self.verbose = verbose
952 self.mute_error = mute_error
953 self._parse_errors = 0
954 self._parse_warnings = 0
955 self._last_error_msg = None
956 self._last_error_lineno = 0
957 self._last_error_pos = 0
961 # BuildProduction
963 # Production is the set of items sent to a grammar rule resulting in a new
964 # item being returned.
966 # p - Is the Yacc production object containing the stack of items
967 # index - Index into the production of the name for the item being produced.
968 # cls - The type of item being producted
969 # childlist - The children of the new item
970 def BuildProduction(self, cls, p, index, childlist=None):
971 try:
972 if not childlist:
973 childlist = []
975 filename = self.lexer.Lexer().filename
976 lineno = p.lineno(index)
977 pos = p.lexpos(index)
978 out = IDLNode(cls, filename, lineno, pos, childlist)
979 return out
980 except:
981 print 'Exception while parsing:'
982 for num, item in enumerate(p):
983 print ' [%d] %s' % (num, ExpandProduction(item))
984 if self.LastToken():
985 print 'Last token: %s' % str(self.LastToken())
986 raise
988 def BuildNamed(self, cls, p, index, childlist=None):
989 childlist = ListFromConcat(childlist)
990 childlist.append(self.BuildAttribute('NAME', p[index]))
991 return self.BuildProduction(cls, p, index, childlist)
993 def BuildComment(self, cls, p, index):
994 name = p[index]
996 # Remove comment markers
997 lines = []
998 if name[:2] == '//':
999 # For C++ style, remove any leading whitespace and the '//' marker from
1000 # each line.
1001 form = 'cc'
1002 for line in name.split('\n'):
1003 start = line.find('//')
1004 lines.append(line[start+2:])
1005 else:
1006 # For C style, remove ending '*/''
1007 form = 'c'
1008 for line in name[:-2].split('\n'):
1009 # Remove characters until start marker for this line '*' if found
1010 # otherwise it should be blank.
1011 offs = line.find('*')
1012 if offs >= 0:
1013 line = line[offs + 1:].rstrip()
1014 else:
1015 line = ''
1016 lines.append(line)
1017 name = '\n'.join(lines)
1018 childlist = [self.BuildAttribute('NAME', name),
1019 self.BuildAttribute('FORM', form)]
1020 return self.BuildProduction(cls, p, index, childlist)
1023 # BuildError
1025 # Build and Errror node as part of the recovery process.
1028 def BuildError(self, p, prod):
1029 self._parse_errors += 1
1030 name = self.BuildAttribute('NAME', self._last_error_msg)
1031 line = self.BuildAttribute('LINE', self._last_error_lineno)
1032 pos = self.BuildAttribute('POS', self._last_error_pos)
1033 prod = self.BuildAttribute('PROD', prod)
1035 node = self.BuildProduction('Error', p, 1,
1036 ListFromConcat(name, line, pos, prod))
1037 if not self.mute_error:
1038 node.Error(self._last_error_msg)
1040 return node
1043 # BuildAttribute
1045 # An ExtendedAttribute is a special production that results in a property
1046 # which is applied to the adjacent item. Attributes have no children and
1047 # instead represent key/value pairs.
1049 def BuildAttribute(self, key, val):
1050 return IDLAttribute(key, val)
1052 def BuildFalse(self, key):
1053 return IDLAttribute(key, Boolean(False))
1055 def BuildTrue(self, key):
1056 return IDLAttribute(key, Boolean(True))
1058 def GetErrors(self):
1059 # Access lexer errors, despite being private
1060 # pylint: disable=W0212
1061 return self._parse_errors + self.lexer._lex_errors
1064 # ParseData
1066 # Attempts to parse the current data loaded in the lexer.
1068 def ParseText(self, filename, data):
1069 self._parse_errors = 0
1070 self._parse_warnings = 0
1071 self._last_error_msg = None
1072 self._last_error_lineno = 0
1073 self._last_error_pos = 0
1075 try:
1076 self.lexer.Tokenize(data, filename)
1077 nodes = self.yaccobj.parse(lexer=self.lexer) or []
1078 name = self.BuildAttribute('NAME', filename)
1079 return IDLNode('File', filename, 0, 0, nodes + [name])
1081 except lex.LexError as lexError:
1082 sys.stderr.write('Error in token: %s\n' % str(lexError))
1083 return None
1087 def ParseFile(parser, filename):
1088 """Parse a file and return a File type of node."""
1089 with open(filename) as fileobject:
1090 try:
1091 out = parser.ParseText(filename, fileobject.read())
1092 out.SetProperty('DATETIME', time.ctime(os.path.getmtime(filename)))
1093 out.SetProperty('ERRORS', parser.GetErrors())
1094 return out
1096 except Exception as e:
1097 last = parser.LastToken()
1098 sys.stderr.write('%s(%d) : Internal parsing error\n\t%s.\n' % (
1099 filename, last.lineno, str(e)))
1102 def main(argv):
1103 nodes = []
1104 parser = IDLParser(IDLLexer())
1105 errors = 0
1106 for filename in argv:
1107 filenode = ParseFile(parser, filename)
1108 if (filenode):
1109 errors += filenode.GetProperty('ERRORS')
1110 nodes.append(filenode)
1112 ast = IDLNode('AST', '__AST__', 0, 0, nodes)
1114 print '\n'.join(ast.Tree(accept_props=['PROD']))
1115 if errors:
1116 print '\nFound %d errors.\n' % errors
1118 return errors
1121 if __name__ == '__main__':
1122 sys.exit(main(sys.argv[1:]))