Blink roll 25b6bd3a7a131ffe68d809546ad1a20707915cdc:3a503f41ae42e5b79cfcd2ff10e65afde...
[chromium-blink-merge.git] / tools / idl_parser / idl_parser.py
blob9b63f94cf83cab79e32426f78f85218ab0a0ba3c
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 # [10.1] Removed unsupported: Serializer
269 def p_AttributeOrOperationOrIterator(self, p):
270 """AttributeOrOperationOrIterator : Stringifier
271 | StaticMember
272 | ReadWriteAttribute
273 | OperationOrIterator"""
274 p[0] = p[1]
276 # [11]
277 def p_Dictionary(self, p):
278 """Dictionary : DICTIONARY identifier Inheritance '{' DictionaryMembers '}' ';'"""
279 p[0] = self.BuildNamed('Dictionary', p, 2, ListFromConcat(p[3], p[5]))
281 # [11.1] Error recovery for regular Dictionary
282 def p_DictionaryError(self, p):
283 """Dictionary : DICTIONARY error ';'"""
284 p[0] = self.BuildError(p, 'Dictionary')
286 # [12]
287 def p_DictionaryMembers(self, p):
288 """DictionaryMembers : ExtendedAttributeList DictionaryMember DictionaryMembers
289 |"""
290 if len(p) > 1:
291 p[2].AddChildren(p[1])
292 p[0] = ListFromConcat(p[2], p[3])
294 # [13]
295 def p_DictionaryMember(self, p):
296 """DictionaryMember : Type identifier Default ';'"""
297 p[0] = self.BuildNamed('Key', p, 2, ListFromConcat(p[1], p[3]))
299 # [14] NOT IMPLEMENTED (Required)
301 # [15]
302 def p_PartialDictionary(self, p):
303 """PartialDictionary : DICTIONARY identifier '{' DictionaryMembers '}' ';'"""
304 partial = self.BuildTrue('Partial')
305 p[0] = self.BuildNamed('Dictionary', p, 2, ListFromConcat(p[4], partial))
307 # [15.1] Error recovery for Partial Dictionary
308 def p_PartialDictionaryError(self, p):
309 """PartialDictionary : DICTIONARY error ';'"""
310 p[0] = self.BuildError(p, 'PartialDictionary')
312 # [16]
313 def p_Default(self, p):
314 """Default : '=' DefaultValue
315 |"""
316 if len(p) > 1:
317 p[0] = self.BuildProduction('Default', p, 2, p[2])
319 # [17]
320 def p_DefaultValue(self, p):
321 """DefaultValue : ConstValue
322 | string"""
323 if type(p[1]) == str:
324 p[0] = ListFromConcat(self.BuildAttribute('TYPE', 'DOMString'),
325 self.BuildAttribute('NAME', p[1]))
326 else:
327 p[0] = p[1]
329 # [] - Not specified
330 def p_Exception(self, p):
331 """Exception : EXCEPTION identifier Inheritance '{' ExceptionMembers '}' ';'"""
332 p[0] = self.BuildNamed('Exception', p, 2, ListFromConcat(p[3], p[5]))
334 # [] - Not specified
335 def p_ExceptionMembers(self, p):
336 """ExceptionMembers : ExtendedAttributeList ExceptionMember ExceptionMembers
337 |"""
338 if len(p) > 1:
339 p[2].AddChildren(p[1])
340 p[0] = ListFromConcat(p[2], p[3])
342 # [.1] Error recovery for ExceptionMembers - Not specified
343 def p_ExceptionMembersError(self, p):
344 """ExceptionMembers : error"""
345 p[0] = self.BuildError(p, 'ExceptionMembers')
347 # [18]
348 def p_Inheritance(self, p):
349 """Inheritance : ':' identifier
350 |"""
351 if len(p) > 1:
352 p[0] = self.BuildNamed('Inherit', p, 2)
354 # [19]
355 def p_Enum(self, p):
356 """Enum : ENUM identifier '{' EnumValueList '}' ';'"""
357 p[0] = self.BuildNamed('Enum', p, 2, p[4])
359 # [19.1] Error recovery for Enums
360 def p_EnumError(self, p):
361 """Enum : ENUM error ';'"""
362 p[0] = self.BuildError(p, 'Enum')
364 # [20]
365 def p_EnumValueList(self, p):
366 """EnumValueList : ExtendedAttributeList string EnumValueListComma"""
367 enum = self.BuildNamed('EnumItem', p, 2, p[1])
368 p[0] = ListFromConcat(enum, p[3])
370 # [21]
371 def p_EnumValueListComma(self, p):
372 """EnumValueListComma : ',' EnumValueListString
373 |"""
374 if len(p) > 1:
375 p[0] = p[2]
377 # [22]
378 def p_EnumValueListString(self, p):
379 """EnumValueListString : ExtendedAttributeList string EnumValueListComma
380 |"""
381 if len(p) > 1:
382 enum = self.BuildNamed('EnumItem', p, 2, p[1])
383 p[0] = ListFromConcat(enum, p[3])
385 # [23]
386 def p_CallbackRest(self, p):
387 """CallbackRest : identifier '=' ReturnType '(' ArgumentList ')' ';'"""
388 arguments = self.BuildProduction('Arguments', p, 4, p[5])
389 p[0] = self.BuildNamed('Callback', p, 1, ListFromConcat(p[3], arguments))
391 # [24]
392 def p_Typedef(self, p):
393 """Typedef : TYPEDEF ExtendedAttributeListNoComments Type identifier ';'"""
394 p[0] = self.BuildNamed('Typedef', p, 4, ListFromConcat(p[2], p[3]))
396 # [24.1] Error recovery for Typedefs
397 def p_TypedefError(self, p):
398 """Typedef : TYPEDEF error ';'"""
399 p[0] = self.BuildError(p, 'Typedef')
401 # [25]
402 def p_ImplementsStatement(self, p):
403 """ImplementsStatement : identifier IMPLEMENTS identifier ';'"""
404 name = self.BuildAttribute('REFERENCE', p[3])
405 p[0] = self.BuildNamed('Implements', p, 1, name)
407 # [26]
408 def p_Const(self, p):
409 """Const : CONST ConstType identifier '=' ConstValue ';'"""
410 value = self.BuildProduction('Value', p, 5, p[5])
411 p[0] = self.BuildNamed('Const', p, 3, ListFromConcat(p[2], value))
413 # [27]
414 def p_ConstValue(self, p):
415 """ConstValue : BooleanLiteral
416 | FloatLiteral
417 | integer
418 | null"""
419 if type(p[1]) == str:
420 p[0] = ListFromConcat(self.BuildAttribute('TYPE', 'integer'),
421 self.BuildAttribute('NAME', p[1]))
422 else:
423 p[0] = p[1]
425 # [27.1] Add definition for NULL
426 def p_null(self, p):
427 """null : NULL"""
428 p[0] = ListFromConcat(self.BuildAttribute('TYPE', 'NULL'),
429 self.BuildAttribute('NAME', 'NULL'))
431 # [28]
432 def p_BooleanLiteral(self, p):
433 """BooleanLiteral : TRUE
434 | FALSE"""
435 value = self.BuildAttribute('VALUE', Boolean(p[1] == 'true'))
436 p[0] = ListFromConcat(self.BuildAttribute('TYPE', 'boolean'), value)
438 # [29]
439 def p_FloatLiteral(self, p):
440 """FloatLiteral : float
441 | '-' INFINITY
442 | INFINITY
443 | NAN """
444 if len(p) > 2:
445 val = '-Infinity'
446 else:
447 val = p[1]
448 p[0] = ListFromConcat(self.BuildAttribute('TYPE', 'float'),
449 self.BuildAttribute('VALUE', val))
451 # [30-34] NOT IMPLEMENTED (Serializer)
453 # [35]
454 def p_Stringifier(self, p):
455 """Stringifier : STRINGIFIER StringifierRest"""
456 p[0] = self.BuildProduction('Stringifier', p, 1, p[2])
458 # [36]
459 def p_StringifierRest(self, p):
460 """StringifierRest : AttributeRest
461 | ReturnType OperationRest
462 | ';'"""
463 if len(p) == 3:
464 p[2].AddChildren(p[1])
465 p[0] = p[2]
466 elif p[1] != ';':
467 p[0] = p[1]
469 # [37]
470 def p_StaticMember(self, p):
471 """StaticMember : STATIC StaticMemberRest"""
472 p[2].AddChildren(self.BuildTrue('STATIC'))
473 p[0] = p[2]
475 # [38]
476 def p_StaticMemberRest(self, p):
477 """StaticMemberRest : AttributeRest
478 | ReturnType OperationRest"""
479 if len(p) == 2:
480 p[0] = p[1]
481 else:
482 p[2].AddChildren(p[1])
483 p[0] = p[2]
485 # [39] NOT IMPLEMENTED (ReadOnlyMember)
486 # [40] NOT IMPLEMENTED (ReadOnlyMemberReset)
488 # [41]
489 def p_ReadWriteAttribute(self, p):
490 """ReadWriteAttribute : Inherit AttributeRest"""
491 p[2].AddChildren(ListFromConcat(p[1]))
492 p[0] = p[2]
494 # [41] Deprecated - Remove this entry after blink stops using it.
495 def p_Attribute(self, p):
496 """Attribute : ReadWriteAttribute"""
497 p[0] = p[1]
499 # [42]
500 def p_AttributeRest(self, p):
501 """AttributeRest : ReadOnly ATTRIBUTE Type identifier ';'"""
502 p[0] = self.BuildNamed('Attribute', p, 4,
503 ListFromConcat(p[1], p[3]))
505 # [43] NOT IMPLEMENTED (AttributeName)
506 # [44] NOT IMPLEMENTED (AttributeNameKeyword)
508 # [45]
509 def p_Inherit(self, p):
510 """Inherit : INHERIT
511 |"""
512 if len(p) > 1:
513 p[0] = self.BuildTrue('INHERIT')
515 # [46]
516 def p_ReadOnly(self, p):
517 """ReadOnly : READONLY
518 |"""
519 if len(p) > 1:
520 p[0] = self.BuildTrue('READONLY')
522 # [47]
523 def p_OperationOrIterator(self, p):
524 """OperationOrIterator : ReturnType OperationOrIteratorRest
525 | SpecialOperation"""
526 if len(p) == 3:
527 p[2].AddChildren(p[1])
528 p[0] = p[2]
529 else:
530 p[0] = p[1]
532 # [48]
533 def p_SpecialOperation(self, p):
534 """SpecialOperation : Special Specials ReturnType OperationRest"""
535 p[4].AddChildren(ListFromConcat(p[1], p[2], p[3]))
536 p[0] = p[4]
538 # [49]
539 def p_Specials(self, p):
540 """Specials : Special Specials
541 | """
542 if len(p) > 1:
543 p[0] = ListFromConcat(p[1], p[2])
545 # [50]
546 def p_Special(self, p):
547 """Special : GETTER
548 | SETTER
549 | CREATOR
550 | DELETER
551 | LEGACYCALLER"""
552 p[0] = self.BuildTrue(p[1].upper())
554 # [51]
555 def p_OperationOrIteratorRest(self, p):
556 """OperationOrIteratorRest : OperationRest"""
557 p[0] = p[1]
559 # [51]
560 def p_OperationRest(self, p):
561 """OperationRest : OptionalIdentifier '(' ArgumentList ')' ';'"""
562 arguments = self.BuildProduction('Arguments', p, 2, p[3])
563 p[0] = self.BuildNamed('Operation', p, 1, arguments)
565 # [52]
566 def p_OptionalIdentifier(self, p):
567 """OptionalIdentifier : identifier
568 |"""
569 if len(p) > 1:
570 p[0] = p[1]
571 else:
572 p[0] = '_unnamed_'
574 # [53]
575 def p_ArgumentList(self, p):
576 """ArgumentList : Argument Arguments
577 |"""
578 if len(p) > 1:
579 p[0] = ListFromConcat(p[1], p[2])
581 # [53.1] ArgumentList error recovery
582 def p_ArgumentListError(self, p):
583 """ArgumentList : error """
584 p[0] = self.BuildError(p, 'ArgumentList')
586 # [54]
587 def p_Arguments(self, p):
588 """Arguments : ',' Argument Arguments
589 |"""
590 if len(p) > 1:
591 p[0] = ListFromConcat(p[2], p[3])
593 # [55]
594 def p_Argument(self, p):
595 """Argument : ExtendedAttributeList OptionalOrRequiredArgument"""
596 p[2].AddChildren(p[1])
597 p[0] = p[2]
599 # [56]
600 def p_OptionalOrRequiredArgument(self, p):
601 """OptionalOrRequiredArgument : OPTIONAL Type ArgumentName Default
602 | Type Ellipsis ArgumentName"""
603 if len(p) > 4:
604 arg = self.BuildNamed('Argument', p, 3, ListFromConcat(p[2], p[4]))
605 arg.AddChildren(self.BuildTrue('OPTIONAL'))
606 else:
607 arg = self.BuildNamed('Argument', p, 3, ListFromConcat(p[1], p[2]))
608 p[0] = arg
610 # [57]
611 def p_ArgumentName(self, p):
612 """ArgumentName : ArgumentNameKeyword
613 | identifier"""
614 p[0] = p[1]
616 # [58]
617 def p_Ellipsis(self, p):
618 """Ellipsis : ELLIPSIS
619 |"""
620 if len(p) > 1:
621 p[0] = self.BuildNamed('Argument', p, 1)
622 p[0].AddChildren(self.BuildTrue('ELLIPSIS'))
624 # [] Unspecified
625 def p_ExceptionMember(self, p):
626 """ExceptionMember : Const
627 | ExceptionField"""
628 p[0] = p[1]
630 # [] Unspecified
631 def p_ExceptionField(self, p):
632 """ExceptionField : Type identifier ';'"""
633 p[0] = self.BuildNamed('ExceptionField', p, 2, p[1])
635 # [] Error recovery for ExceptionMembers - Unspecified
636 def p_ExceptionFieldError(self, p):
637 """ExceptionField : error"""
638 p[0] = self.BuildError(p, 'ExceptionField')
640 # [59] NOT IMPLEMENTED (Iterable)
641 # [60] NOT IMPLEMENTED (OptionalType)
642 # [61] NOT IMPLEMENTED (ReadWriteMaplike)
643 # [62] NOT IMPLEMENTED (ReadWriteSetlike)
644 # [63] NOT IMPLEMENTED (MaplikeRest)
645 # [64] NOT IMPLEMENTED (SetlikeRest)
647 # [65] No comment version for mid statement attributes.
648 def p_ExtendedAttributeListNoComments(self, p):
649 """ExtendedAttributeListNoComments : '[' ExtendedAttribute ExtendedAttributes ']'
650 | """
651 if len(p) > 2:
652 items = ListFromConcat(p[2], p[3])
653 p[0] = self.BuildProduction('ExtAttributes', p, 1, items)
655 # [65.1] Add optional comment field for start of statements.
656 def p_ExtendedAttributeList(self, p):
657 """ExtendedAttributeList : Comments '[' ExtendedAttribute ExtendedAttributes ']'
658 | Comments """
659 if len(p) > 2:
660 items = ListFromConcat(p[3], p[4])
661 attribs = self.BuildProduction('ExtAttributes', p, 2, items)
662 p[0] = ListFromConcat(p[1], attribs)
663 else:
664 p[0] = p[1]
666 # [66]
667 def p_ExtendedAttributes(self, p):
668 """ExtendedAttributes : ',' ExtendedAttribute ExtendedAttributes
669 |"""
670 if len(p) > 1:
671 p[0] = ListFromConcat(p[2], p[3])
673 # We only support:
674 # [ identifier ]
675 # [ identifier ( ArgumentList ) ]
676 # [ identifier = identifier ]
677 # [ identifier = ( IdentifierList ) ]
678 # [ identifier = identifier ( ArgumentList ) ]
679 # [66] map directly to [91-93, 95]
680 # [67-69, 71] are unsupported
681 def p_ExtendedAttribute(self, p):
682 """ExtendedAttribute : ExtendedAttributeNoArgs
683 | ExtendedAttributeArgList
684 | ExtendedAttributeIdent
685 | ExtendedAttributeIdentList
686 | ExtendedAttributeNamedArgList"""
687 p[0] = p[1]
689 # [71]
690 def p_ArgumentNameKeyword(self, p):
691 """ArgumentNameKeyword : ATTRIBUTE
692 | CALLBACK
693 | CONST
694 | CREATOR
695 | DELETER
696 | DICTIONARY
697 | ENUM
698 | EXCEPTION
699 | GETTER
700 | IMPLEMENTS
701 | INHERIT
702 | LEGACYCALLER
703 | PARTIAL
704 | SERIALIZER
705 | SETTER
706 | STATIC
707 | STRINGIFIER
708 | TYPEDEF
709 | UNRESTRICTED"""
710 p[0] = p[1]
712 # [72] NOT IMPLEMENTED (OtherOrComma)
714 # [73]
715 def p_Type(self, p):
716 """Type : SingleType
717 | UnionType TypeSuffix"""
718 if len(p) == 2:
719 p[0] = self.BuildProduction('Type', p, 1, p[1])
720 else:
721 p[0] = self.BuildProduction('Type', p, 1, ListFromConcat(p[1], p[2]))
723 # [74]
724 def p_SingleType(self, p):
725 """SingleType : NonAnyType
726 | ANY TypeSuffixStartingWithArray"""
727 if len(p) == 2:
728 p[0] = p[1]
729 else:
730 p[0] = ListFromConcat(self.BuildProduction('Any', p, 1), p[2])
732 # [75]
733 def p_UnionType(self, p):
734 """UnionType : '(' UnionMemberType OR UnionMemberType UnionMemberTypes ')'"""
736 # [76]
737 def p_UnionMemberType(self, p):
738 """UnionMemberType : NonAnyType
739 | UnionType TypeSuffix
740 | ANY '[' ']' TypeSuffix"""
741 # [77]
742 def p_UnionMemberTypes(self, p):
743 """UnionMemberTypes : OR UnionMemberType UnionMemberTypes
744 |"""
746 # [78] Moved BYTESTRING, DOMSTRING, OBJECT, DATE, REGEXP to PrimitiveType
747 # Moving all built-in types into PrimitiveType makes it easier to
748 # differentiate between them and 'identifier', since p[1] would be a string in
749 # both cases.
750 def p_NonAnyType(self, p):
751 """NonAnyType : PrimitiveType TypeSuffix
752 | PromiseType Null
753 | identifier TypeSuffix
754 | SEQUENCE '<' Type '>' Null"""
755 if len(p) == 3:
756 if type(p[1]) == str:
757 typeref = self.BuildNamed('Typeref', p, 1)
758 else:
759 typeref = p[1]
760 p[0] = ListFromConcat(typeref, p[2])
762 if len(p) == 6:
763 p[0] = self.BuildProduction('Sequence', p, 1, ListFromConcat(p[3], p[5]))
765 # [79] NOT IMPLEMENTED (BufferRelatedType)
767 # [80]
768 def p_ConstType(self, p):
769 """ConstType : PrimitiveType Null
770 | identifier Null"""
771 if type(p[1]) == str:
772 p[0] = self.BuildNamed('Typeref', p, 1, p[2])
773 else:
774 p[1].AddChildren(p[2])
775 p[0] = p[1]
778 # [81] Added BYTESTRING, DOMSTRING, OBJECT, DATE, REGEXP
779 def p_PrimitiveType(self, p):
780 """PrimitiveType : UnsignedIntegerType
781 | UnrestrictedFloatType
782 | BOOLEAN
783 | BYTE
784 | OCTET
785 | BYTESTRING
786 | DOMSTRING
787 | OBJECT
788 | DATE
789 | REGEXP"""
790 if type(p[1]) == str:
791 p[0] = self.BuildNamed('PrimitiveType', p, 1)
792 else:
793 p[0] = p[1]
796 # [82]
797 def p_UnrestrictedFloatType(self, p):
798 """UnrestrictedFloatType : UNRESTRICTED FloatType
799 | FloatType"""
800 if len(p) == 2:
801 typeref = self.BuildNamed('PrimitiveType', p, 1)
802 else:
803 typeref = self.BuildNamed('PrimitiveType', p, 2)
804 typeref.AddChildren(self.BuildTrue('UNRESTRICTED'))
805 p[0] = typeref
808 # [83]
809 def p_FloatType(self, p):
810 """FloatType : FLOAT
811 | DOUBLE"""
812 p[0] = p[1]
814 # [84]
815 def p_UnsignedIntegerType(self, p):
816 """UnsignedIntegerType : UNSIGNED IntegerType
817 | IntegerType"""
818 if len(p) == 2:
819 p[0] = p[1]
820 else:
821 p[0] = 'unsigned ' + p[2]
823 # [85]
824 def p_IntegerType(self, p):
825 """IntegerType : SHORT
826 | LONG OptionalLong"""
827 if len(p) == 2:
828 p[0] = p[1]
829 else:
830 p[0] = p[1] + p[2]
832 # [86]
833 def p_OptionalLong(self, p):
834 """OptionalLong : LONG
835 | """
836 if len(p) > 1:
837 p[0] = ' ' + p[1]
838 else:
839 p[0] = ''
841 # [87] Add unqualified Promise
842 def p_PromiseType(self, p):
843 """PromiseType : PROMISE '<' ReturnType '>'
844 | PROMISE"""
845 if len(p) == 2:
846 # Promise without resolution type is not specified in the Web IDL spec.
847 # As it is used in some specs and in the blink implementation,
848 # we allow that here.
849 resolution_type = self.BuildProduction('Type', p, 1,
850 self.BuildProduction('Any', p, 1))
851 p[0] = self.BuildNamed('Promise', p, 1, resolution_type)
852 else:
853 p[0] = self.BuildNamed('Promise', p, 1, p[3])
855 # [88] Add support for sized array
856 def p_TypeSuffix(self, p):
857 """TypeSuffix : '[' integer ']' TypeSuffix
858 | '[' ']' TypeSuffix
859 | '?' TypeSuffixStartingWithArray
860 | """
861 if len(p) == 5:
862 p[0] = self.BuildNamed('Array', p, 2, p[4])
864 if len(p) == 4:
865 p[0] = self.BuildProduction('Array', p, 1, p[3])
867 if len(p) == 3:
868 p[0] = ListFromConcat(self.BuildTrue('NULLABLE'), p[2])
871 # [89]
872 def p_TypeSuffixStartingWithArray(self, p):
873 """TypeSuffixStartingWithArray : '[' ']' TypeSuffix
874 | """
875 if len(p) > 1:
876 p[0] = self.BuildProduction('Array', p, 0, p[3])
878 # [90]
879 def p_Null(self, p):
880 """Null : '?'
881 |"""
882 if len(p) > 1:
883 p[0] = self.BuildTrue('NULLABLE')
885 # [91]
886 def p_ReturnType(self, p):
887 """ReturnType : Type
888 | VOID"""
889 if p[1] == 'void':
890 p[0] = self.BuildProduction('Type', p, 1)
891 p[0].AddChildren(self.BuildNamed('PrimitiveType', p, 1))
892 else:
893 p[0] = p[1]
895 # [92]
896 def p_IdentifierList(self, p):
897 """IdentifierList : identifier Identifiers"""
898 p[0] = ListFromConcat(p[1], p[2])
900 # [93]
901 def p_Identifiers(self, p):
902 """Identifiers : ',' identifier Identifiers
903 |"""
904 if len(p) > 1:
905 p[0] = ListFromConcat(p[2], p[3])
907 # [94]
908 def p_ExtendedAttributeNoArgs(self, p):
909 """ExtendedAttributeNoArgs : identifier"""
910 p[0] = self.BuildNamed('ExtAttribute', p, 1)
912 # [95]
913 def p_ExtendedAttributeArgList(self, p):
914 """ExtendedAttributeArgList : identifier '(' ArgumentList ')'"""
915 arguments = self.BuildProduction('Arguments', p, 2, p[3])
916 p[0] = self.BuildNamed('ExtAttribute', p, 1, arguments)
918 # [96]
919 def p_ExtendedAttributeIdent(self, p):
920 """ExtendedAttributeIdent : identifier '=' identifier"""
921 value = self.BuildAttribute('VALUE', p[3])
922 p[0] = self.BuildNamed('ExtAttribute', p, 1, value)
924 # [97]
925 def p_ExtendedAttributeIdentList(self, p):
926 """ExtendedAttributeIdentList : identifier '=' '(' IdentifierList ')'"""
927 value = self.BuildAttribute('VALUE', p[4])
928 p[0] = self.BuildNamed('ExtAttribute', p, 1, value)
930 # [98]
931 def p_ExtendedAttributeNamedArgList(self, p):
932 """ExtendedAttributeNamedArgList : identifier '=' identifier '(' ArgumentList ')'"""
933 args = self.BuildProduction('Arguments', p, 4, p[5])
934 value = self.BuildNamed('Call', p, 3, args)
935 p[0] = self.BuildNamed('ExtAttribute', p, 1, value)
938 # Parser Errors
940 # p_error is called whenever the parser can not find a pattern match for
941 # a set of items from the current state. The p_error function defined here
942 # is triggered logging an error, and parsing recovery happens as the
943 # p_<type>_error functions defined above are called. This allows the parser
944 # to continue so as to capture more than one error per file.
946 def p_error(self, t):
947 if t:
948 lineno = t.lineno
949 pos = t.lexpos
950 prev = self.yaccobj.symstack[-1]
951 if type(prev) == lex.LexToken:
952 msg = "Unexpected %s after %s." % (
953 TokenTypeName(t), TokenTypeName(prev))
954 else:
955 msg = "Unexpected %s." % (t.value)
956 else:
957 last = self.LastToken()
958 lineno = last.lineno
959 pos = last.lexpos
960 msg = "Unexpected end of file after %s." % TokenTypeName(last)
961 self.yaccobj.restart()
963 # Attempt to remap the error to a friendlier form
964 if msg in ERROR_REMAP:
965 msg = ERROR_REMAP[msg]
967 self._last_error_msg = msg
968 self._last_error_lineno = lineno
969 self._last_error_pos = pos
971 def Warn(self, node, msg):
972 sys.stdout.write(node.GetLogLine(msg))
973 self.parse_warnings += 1
975 def LastToken(self):
976 return self.lexer.last
978 def __init__(self, lexer, verbose=False, debug=False, mute_error=False):
979 self.lexer = lexer
980 self.tokens = lexer.KnownTokens()
981 self.yaccobj = yacc.yacc(module=self, tabmodule=None, debug=debug,
982 optimize=0, write_tables=0)
983 self.parse_debug = debug
984 self.verbose = verbose
985 self.mute_error = mute_error
986 self._parse_errors = 0
987 self._parse_warnings = 0
988 self._last_error_msg = None
989 self._last_error_lineno = 0
990 self._last_error_pos = 0
994 # BuildProduction
996 # Production is the set of items sent to a grammar rule resulting in a new
997 # item being returned.
999 # p - Is the Yacc production object containing the stack of items
1000 # index - Index into the production of the name for the item being produced.
1001 # cls - The type of item being producted
1002 # childlist - The children of the new item
1003 def BuildProduction(self, cls, p, index, childlist=None):
1004 try:
1005 if not childlist:
1006 childlist = []
1008 filename = self.lexer.Lexer().filename
1009 lineno = p.lineno(index)
1010 pos = p.lexpos(index)
1011 out = IDLNode(cls, filename, lineno, pos, childlist)
1012 return out
1013 except:
1014 print 'Exception while parsing:'
1015 for num, item in enumerate(p):
1016 print ' [%d] %s' % (num, ExpandProduction(item))
1017 if self.LastToken():
1018 print 'Last token: %s' % str(self.LastToken())
1019 raise
1021 def BuildNamed(self, cls, p, index, childlist=None):
1022 childlist = ListFromConcat(childlist)
1023 childlist.append(self.BuildAttribute('NAME', p[index]))
1024 return self.BuildProduction(cls, p, index, childlist)
1026 def BuildComment(self, cls, p, index):
1027 name = p[index]
1029 # Remove comment markers
1030 lines = []
1031 if name[:2] == '//':
1032 # For C++ style, remove any leading whitespace and the '//' marker from
1033 # each line.
1034 form = 'cc'
1035 for line in name.split('\n'):
1036 start = line.find('//')
1037 lines.append(line[start+2:])
1038 else:
1039 # For C style, remove ending '*/''
1040 form = 'c'
1041 for line in name[:-2].split('\n'):
1042 # Remove characters until start marker for this line '*' if found
1043 # otherwise it should be blank.
1044 offs = line.find('*')
1045 if offs >= 0:
1046 line = line[offs + 1:].rstrip()
1047 else:
1048 line = ''
1049 lines.append(line)
1050 name = '\n'.join(lines)
1051 childlist = [self.BuildAttribute('NAME', name),
1052 self.BuildAttribute('FORM', form)]
1053 return self.BuildProduction(cls, p, index, childlist)
1056 # BuildError
1058 # Build and Errror node as part of the recovery process.
1061 def BuildError(self, p, prod):
1062 self._parse_errors += 1
1063 name = self.BuildAttribute('NAME', self._last_error_msg)
1064 line = self.BuildAttribute('LINE', self._last_error_lineno)
1065 pos = self.BuildAttribute('POS', self._last_error_pos)
1066 prod = self.BuildAttribute('PROD', prod)
1068 node = self.BuildProduction('Error', p, 1,
1069 ListFromConcat(name, line, pos, prod))
1070 if not self.mute_error:
1071 node.Error(self._last_error_msg)
1073 return node
1076 # BuildAttribute
1078 # An ExtendedAttribute is a special production that results in a property
1079 # which is applied to the adjacent item. Attributes have no children and
1080 # instead represent key/value pairs.
1082 def BuildAttribute(self, key, val):
1083 return IDLAttribute(key, val)
1085 def BuildFalse(self, key):
1086 return IDLAttribute(key, Boolean(False))
1088 def BuildTrue(self, key):
1089 return IDLAttribute(key, Boolean(True))
1091 def GetErrors(self):
1092 # Access lexer errors, despite being private
1093 # pylint: disable=W0212
1094 return self._parse_errors + self.lexer._lex_errors
1097 # ParseData
1099 # Attempts to parse the current data loaded in the lexer.
1101 def ParseText(self, filename, data):
1102 self._parse_errors = 0
1103 self._parse_warnings = 0
1104 self._last_error_msg = None
1105 self._last_error_lineno = 0
1106 self._last_error_pos = 0
1108 try:
1109 self.lexer.Tokenize(data, filename)
1110 nodes = self.yaccobj.parse(lexer=self.lexer) or []
1111 name = self.BuildAttribute('NAME', filename)
1112 return IDLNode('File', filename, 0, 0, nodes + [name])
1114 except lex.LexError as lexError:
1115 sys.stderr.write('Error in token: %s\n' % str(lexError))
1116 return None
1120 def ParseFile(parser, filename):
1121 """Parse a file and return a File type of node."""
1122 with open(filename) as fileobject:
1123 try:
1124 out = parser.ParseText(filename, fileobject.read())
1125 out.SetProperty('DATETIME', time.ctime(os.path.getmtime(filename)))
1126 out.SetProperty('ERRORS', parser.GetErrors())
1127 return out
1129 except Exception as e:
1130 last = parser.LastToken()
1131 sys.stderr.write('%s(%d) : Internal parsing error\n\t%s.\n' % (
1132 filename, last.lineno, str(e)))
1135 def main(argv):
1136 nodes = []
1137 parser = IDLParser(IDLLexer())
1138 errors = 0
1139 for filename in argv:
1140 filenode = ParseFile(parser, filename)
1141 if (filenode):
1142 errors += filenode.GetProperty('ERRORS')
1143 nodes.append(filenode)
1145 ast = IDLNode('AST', '__AST__', 0, 0, nodes)
1147 print '\n'.join(ast.Tree(accept_props=['PROD']))
1148 if errors:
1149 print '\nFound %d errors.\n' % errors
1151 return errors
1154 if __name__ == '__main__':
1155 sys.exit(main(sys.argv[1:]))