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