Remove APIPermission::kFileSystemWriteDirectory
[chromium-blink-merge.git] / tools / idl_parser / idl_parser.py
blob45f8d7c58276a9d922b21ba6b4cbf2448cd907d4
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 # [5.1] Error recovery for interface.
233 def p_InterfaceError(self, p):
234 """Interface : INTERFACE identifier Inheritance '{' error"""
235 p[0] = self.BuildError(p, 'Interface')
237 # [6]
238 def p_Partial(self, p):
239 """Partial : PARTIAL PartialDefinition"""
240 p[2].AddChildren(self.BuildTrue('Partial'))
241 p[0] = p[2]
243 # [6.1] Error recovery for Partial
244 def p_PartialError(self, p):
245 """Partial : PARTIAL error"""
246 p[0] = self.BuildError(p, 'Partial')
248 # [7]
249 def p_PartialDefinition(self, p):
250 """PartialDefinition : PartialDictionary
251 | PartialInterface"""
252 p[0] = p[1]
254 # [8]
255 def p_PartialInterface(self, p):
256 """PartialInterface : INTERFACE identifier '{' InterfaceMembers '}' ';'"""
257 p[0] = self.BuildNamed('Interface', p, 2, p[4])
259 # [9]
260 def p_InterfaceMembers(self, p):
261 """InterfaceMembers : ExtendedAttributeList InterfaceMember InterfaceMembers
262 |"""
263 if len(p) > 1:
264 p[2].AddChildren(p[1])
265 p[0] = ListFromConcat(p[2], p[3])
267 # [9.1] Error recovery for InterfaceMembers
268 def p_InterfaceMembersError(self, p):
269 """InterfaceMembers : error"""
270 p[0] = self.BuildError(p, 'InterfaceMembers')
272 # [10] Removed unsupported: Serializer
273 def p_InterfaceMember(self, p):
274 """InterfaceMember : Const
275 | Operation
276 | Serializer
277 | Stringifier
278 | StaticMember
279 | Iterable
280 | ReadonlyMember
281 | ReadWriteAttribute
282 | ReadWriteMaplike
283 | ReadWriteSetlike"""
284 p[0] = p[1]
286 # [11]
287 def p_Dictionary(self, p):
288 """Dictionary : DICTIONARY identifier Inheritance '{' DictionaryMembers '}' ';'"""
289 p[0] = self.BuildNamed('Dictionary', p, 2, ListFromConcat(p[3], p[5]))
291 # [11.1] Error recovery for regular Dictionary
292 def p_DictionaryError(self, p):
293 """Dictionary : DICTIONARY error ';'"""
294 p[0] = self.BuildError(p, 'Dictionary')
296 # [11.2] Error recovery for regular Dictionary
297 # (for errors inside dictionary definition)
298 def p_DictionaryError2(self, p):
299 """Dictionary : DICTIONARY identifier Inheritance '{' error"""
300 p[0] = self.BuildError(p, 'Dictionary')
302 # [12]
303 def p_DictionaryMembers(self, p):
304 """DictionaryMembers : ExtendedAttributeList DictionaryMember DictionaryMembers
305 |"""
306 if len(p) > 1:
307 p[2].AddChildren(p[1])
308 p[0] = ListFromConcat(p[2], p[3])
310 # [12.1] Error recovery for DictionaryMembers
311 def p_DictionaryMembersError(self, p):
312 """DictionaryMembers : ExtendedAttributeList error"""
313 p[0] = self.BuildError(p, 'DictionaryMembers')
315 # [13]
316 def p_DictionaryMember(self, p):
317 """DictionaryMember : Required Type identifier Default ';'"""
318 p[0] = self.BuildNamed('Key', p, 3, ListFromConcat(p[1], p[2], p[4]))
320 # [14]
321 def p_Required(self, p):
322 """Required : REQUIRED
323 |"""
324 if len(p) > 1:
325 p[0] = self.BuildTrue('REQUIRED')
327 # [15]
328 def p_PartialDictionary(self, p):
329 """PartialDictionary : DICTIONARY identifier '{' DictionaryMembers '}' ';'"""
330 partial = self.BuildTrue('Partial')
331 p[0] = self.BuildNamed('Dictionary', p, 2, ListFromConcat(p[4], partial))
333 # [15.1] Error recovery for Partial Dictionary
334 def p_PartialDictionaryError(self, p):
335 """PartialDictionary : DICTIONARY error ';'"""
336 p[0] = self.BuildError(p, 'PartialDictionary')
338 # [16]
339 def p_Default(self, p):
340 """Default : '=' DefaultValue
341 |"""
342 if len(p) > 1:
343 p[0] = self.BuildProduction('Default', p, 2, p[2])
345 # [17]
346 def p_DefaultValue(self, p):
347 """DefaultValue : ConstValue
348 | string
349 | '[' ']'"""
350 if len(p) == 3:
351 p[0] = ListFromConcat(self.BuildAttribute('TYPE', 'sequence'),
352 self.BuildAttribute('VALUE', '[]'))
353 elif type(p[1]) == str:
354 p[0] = ListFromConcat(self.BuildAttribute('TYPE', 'DOMString'),
355 self.BuildAttribute('NAME', p[1]))
356 else:
357 p[0] = p[1]
359 # [] - Not specified
360 def p_Exception(self, p):
361 """Exception : EXCEPTION identifier Inheritance '{' ExceptionMembers '}' ';'"""
362 p[0] = self.BuildNamed('Exception', p, 2, ListFromConcat(p[3], p[5]))
364 # [] - Not specified
365 def p_ExceptionMembers(self, p):
366 """ExceptionMembers : ExtendedAttributeList ExceptionMember ExceptionMembers
367 |"""
368 if len(p) > 1:
369 p[2].AddChildren(p[1])
370 p[0] = ListFromConcat(p[2], p[3])
372 # [.1] Error recovery for ExceptionMembers - Not specified
373 def p_ExceptionMembersError(self, p):
374 """ExceptionMembers : error"""
375 p[0] = self.BuildError(p, 'ExceptionMembers')
377 # [18]
378 def p_Inheritance(self, p):
379 """Inheritance : ':' identifier
380 |"""
381 if len(p) > 1:
382 p[0] = self.BuildNamed('Inherit', p, 2)
384 # [19]
385 def p_Enum(self, p):
386 """Enum : ENUM identifier '{' EnumValueList '}' ';'"""
387 p[0] = self.BuildNamed('Enum', p, 2, p[4])
389 # [19.1] Error recovery for Enums
390 def p_EnumError(self, p):
391 """Enum : ENUM error ';'"""
392 p[0] = self.BuildError(p, 'Enum')
394 # [20]
395 def p_EnumValueList(self, p):
396 """EnumValueList : ExtendedAttributeList string EnumValueListComma"""
397 enum = self.BuildNamed('EnumItem', p, 2, p[1])
398 p[0] = ListFromConcat(enum, p[3])
400 # [21]
401 def p_EnumValueListComma(self, p):
402 """EnumValueListComma : ',' EnumValueListString
403 |"""
404 if len(p) > 1:
405 p[0] = p[2]
407 # [22]
408 def p_EnumValueListString(self, p):
409 """EnumValueListString : ExtendedAttributeList string EnumValueListComma
410 |"""
411 if len(p) > 1:
412 enum = self.BuildNamed('EnumItem', p, 2, p[1])
413 p[0] = ListFromConcat(enum, p[3])
415 # [23]
416 def p_CallbackRest(self, p):
417 """CallbackRest : identifier '=' ReturnType '(' ArgumentList ')' ';'"""
418 arguments = self.BuildProduction('Arguments', p, 4, p[5])
419 p[0] = self.BuildNamed('Callback', p, 1, ListFromConcat(p[3], arguments))
421 # [24]
422 def p_Typedef(self, p):
423 """Typedef : TYPEDEF ExtendedAttributeListNoComments Type identifier ';'"""
424 p[0] = self.BuildNamed('Typedef', p, 4, ListFromConcat(p[2], p[3]))
426 # [24.1] Error recovery for Typedefs
427 def p_TypedefError(self, p):
428 """Typedef : TYPEDEF error ';'"""
429 p[0] = self.BuildError(p, 'Typedef')
431 # [25]
432 def p_ImplementsStatement(self, p):
433 """ImplementsStatement : identifier IMPLEMENTS identifier ';'"""
434 name = self.BuildAttribute('REFERENCE', p[3])
435 p[0] = self.BuildNamed('Implements', p, 1, name)
437 # [26]
438 def p_Const(self, p):
439 """Const : CONST ConstType identifier '=' ConstValue ';'"""
440 value = self.BuildProduction('Value', p, 5, p[5])
441 p[0] = self.BuildNamed('Const', p, 3, ListFromConcat(p[2], value))
443 # [27]
444 def p_ConstValue(self, p):
445 """ConstValue : BooleanLiteral
446 | FloatLiteral
447 | integer
448 | null"""
449 if type(p[1]) == str:
450 p[0] = ListFromConcat(self.BuildAttribute('TYPE', 'integer'),
451 self.BuildAttribute('NAME', p[1]))
452 else:
453 p[0] = p[1]
455 # [27.1] Add definition for NULL
456 def p_null(self, p):
457 """null : NULL"""
458 p[0] = ListFromConcat(self.BuildAttribute('TYPE', 'NULL'),
459 self.BuildAttribute('NAME', 'NULL'))
461 # [28]
462 def p_BooleanLiteral(self, p):
463 """BooleanLiteral : TRUE
464 | FALSE"""
465 value = self.BuildAttribute('VALUE', Boolean(p[1] == 'true'))
466 p[0] = ListFromConcat(self.BuildAttribute('TYPE', 'boolean'), value)
468 # [29]
469 def p_FloatLiteral(self, p):
470 """FloatLiteral : float
471 | '-' INFINITY
472 | INFINITY
473 | NAN """
474 if len(p) > 2:
475 val = '-Infinity'
476 else:
477 val = p[1]
478 p[0] = ListFromConcat(self.BuildAttribute('TYPE', 'float'),
479 self.BuildAttribute('VALUE', val))
481 # [30]
482 def p_Serializer(self, p):
483 """Serializer : SERIALIZER SerializerRest"""
484 p[0] = self.BuildProduction('Serializer', p, 1, p[2])
486 # [31]
487 # TODO(jl): This adds ReturnType and ';', missing from the spec's grammar.
488 # https://www.w3.org/Bugs/Public/show_bug.cgi?id=20361
489 def p_SerializerRest(self, p):
490 """SerializerRest : ReturnType OperationRest
491 | '=' SerializationPattern ';'
492 | ';'"""
493 if len(p) == 3:
494 p[2].AddChildren(p[1])
495 p[0] = p[2]
496 elif len(p) == 4:
497 p[0] = p[2]
499 # [32]
500 def p_SerializationPattern(self, p):
501 """SerializationPattern : '{' SerializationPatternMap '}'
502 | '[' SerializationPatternList ']'
503 | identifier"""
504 if len(p) > 2:
505 p[0] = p[2]
506 else:
507 p[0] = self.BuildAttribute('ATTRIBUTE', p[1])
509 # [33]
510 # TODO(jl): This adds the "ATTRIBUTE" and "INHERIT ',' ATTRIBUTE" variants,
511 # missing from the spec's grammar.
512 # https://www.w3.org/Bugs/Public/show_bug.cgi?id=20361
513 def p_SerializationPatternMap(self, p):
514 """SerializationPatternMap : GETTER
515 | ATTRIBUTE
516 | INHERIT ',' ATTRIBUTE
517 | INHERIT Identifiers
518 | identifier Identifiers
519 |"""
520 p[0] = self.BuildProduction('Map', p, 0)
521 if len(p) == 4:
522 p[0].AddChildren(self.BuildTrue('INHERIT'))
523 p[0].AddChildren(self.BuildTrue('ATTRIBUTE'))
524 elif len(p) > 1:
525 if p[1] == 'getter':
526 p[0].AddChildren(self.BuildTrue('GETTER'))
527 elif p[1] == 'attribute':
528 p[0].AddChildren(self.BuildTrue('ATTRIBUTE'))
529 else:
530 if p[1] == 'inherit':
531 p[0].AddChildren(self.BuildTrue('INHERIT'))
532 attributes = p[2]
533 else:
534 attributes = ListFromConcat(p[1], p[2])
535 p[0].AddChildren(self.BuildAttribute('ATTRIBUTES', attributes))
537 # [34]
538 def p_SerializationPatternList(self, p):
539 """SerializationPatternList : GETTER
540 | identifier Identifiers
541 |"""
542 p[0] = self.BuildProduction('List', p, 0)
543 if len(p) > 1:
544 if p[1] == 'getter':
545 p[0].AddChildren(self.BuildTrue('GETTER'))
546 else:
547 attributes = ListFromConcat(p[1], p[2])
548 p[0].AddChildren(self.BuildAttribute('ATTRIBUTES', attributes))
550 # [35]
551 def p_Stringifier(self, p):
552 """Stringifier : STRINGIFIER StringifierRest"""
553 p[0] = self.BuildProduction('Stringifier', p, 1, p[2])
555 # [36]
556 def p_StringifierRest(self, p):
557 """StringifierRest : AttributeRest
558 | ReturnType OperationRest
559 | ';'"""
560 if len(p) == 3:
561 p[2].AddChildren(p[1])
562 p[0] = p[2]
563 elif p[1] != ';':
564 p[0] = p[1]
566 # [37]
567 def p_StaticMember(self, p):
568 """StaticMember : STATIC StaticMemberRest"""
569 p[2].AddChildren(self.BuildTrue('STATIC'))
570 p[0] = p[2]
572 # [38]
573 def p_StaticMemberRest(self, p):
574 """StaticMemberRest : ReadOnly AttributeRest
575 | ReturnType OperationRest"""
576 if len(p) == 2:
577 p[0] = p[1]
578 else:
579 p[2].AddChildren(p[1])
580 p[0] = p[2]
582 # [39]
583 def p_ReadonlyMember(self, p):
584 """ReadonlyMember : READONLY ReadonlyMemberRest"""
585 p[2].AddChildren(self.BuildTrue('READONLY'))
586 p[0] = p[2]
588 # [40]
589 def p_ReadonlyMemberRest(self, p):
590 """ReadonlyMemberRest : AttributeRest
591 | MaplikeRest
592 | SetlikeRest"""
593 p[0] = p[1]
595 # [41]
596 def p_ReadWriteAttribute(self, p):
597 """ReadWriteAttribute : INHERIT ReadOnly AttributeRest
598 | AttributeRest"""
599 if len(p) > 2:
600 inherit = self.BuildTrue('INHERIT')
601 p[3].AddChildren(ListFromConcat(inherit, p[2]))
602 p[0] = p[3]
603 else:
604 p[0] = p[1]
606 # [42]
607 def p_AttributeRest(self, p):
608 """AttributeRest : ATTRIBUTE Type AttributeName ';'"""
609 p[0] = self.BuildNamed('Attribute', p, 3, p[2])
611 # [43]
612 def p_AttributeName(self, p):
613 """AttributeName : AttributeNameKeyword
614 | identifier"""
615 p[0] = p[1]
617 # [44]
618 def p_AttributeNameKeyword(self, p):
619 """AttributeNameKeyword : REQUIRED"""
620 p[0] = p[1]
622 # [45] Unreferenced in the specification
624 # [46]
625 def p_ReadOnly(self, p):
626 """ReadOnly : READONLY
627 |"""
628 if len(p) > 1:
629 p[0] = self.BuildTrue('READONLY')
631 # [47]
632 def p_Operation(self, p):
633 """Operation : ReturnType OperationRest
634 | SpecialOperation"""
635 if len(p) == 3:
636 p[2].AddChildren(p[1])
637 p[0] = p[2]
638 else:
639 p[0] = p[1]
641 # [48]
642 def p_SpecialOperation(self, p):
643 """SpecialOperation : Special Specials ReturnType OperationRest"""
644 p[4].AddChildren(ListFromConcat(p[1], p[2], p[3]))
645 p[0] = p[4]
647 # [49]
648 def p_Specials(self, p):
649 """Specials : Special Specials
650 | """
651 if len(p) > 1:
652 p[0] = ListFromConcat(p[1], p[2])
654 # [50]
655 def p_Special(self, p):
656 """Special : GETTER
657 | SETTER
658 | CREATOR
659 | DELETER
660 | LEGACYCALLER"""
661 p[0] = self.BuildTrue(p[1].upper())
663 # [51]
664 def p_OperationRest(self, p):
665 """OperationRest : OptionalIdentifier '(' ArgumentList ')' ';'"""
666 arguments = self.BuildProduction('Arguments', p, 2, p[3])
667 p[0] = self.BuildNamed('Operation', p, 1, arguments)
669 # [52]
670 def p_OptionalIdentifier(self, p):
671 """OptionalIdentifier : identifier
672 |"""
673 if len(p) > 1:
674 p[0] = p[1]
675 else:
676 p[0] = '_unnamed_'
678 # [53]
679 def p_ArgumentList(self, p):
680 """ArgumentList : Argument Arguments
681 |"""
682 if len(p) > 1:
683 p[0] = ListFromConcat(p[1], p[2])
685 # [53.1] ArgumentList error recovery
686 def p_ArgumentListError(self, p):
687 """ArgumentList : error """
688 p[0] = self.BuildError(p, 'ArgumentList')
690 # [54]
691 def p_Arguments(self, p):
692 """Arguments : ',' Argument Arguments
693 |"""
694 if len(p) > 1:
695 p[0] = ListFromConcat(p[2], p[3])
697 # [54.1] Arguments error recovery
698 def p_ArgumentsError(self, p):
699 """Arguments : ',' error"""
700 p[0] = self.BuildError(p, 'Arguments')
702 # [55]
703 def p_Argument(self, p):
704 """Argument : ExtendedAttributeList OptionalOrRequiredArgument"""
705 p[2].AddChildren(p[1])
706 p[0] = p[2]
708 # [56]
709 def p_OptionalOrRequiredArgument(self, p):
710 """OptionalOrRequiredArgument : OPTIONAL Type ArgumentName Default
711 | Type Ellipsis ArgumentName"""
712 if len(p) > 4:
713 arg = self.BuildNamed('Argument', p, 3, ListFromConcat(p[2], p[4]))
714 arg.AddChildren(self.BuildTrue('OPTIONAL'))
715 else:
716 arg = self.BuildNamed('Argument', p, 3, ListFromConcat(p[1], p[2]))
717 p[0] = arg
719 # [57]
720 def p_ArgumentName(self, p):
721 """ArgumentName : ArgumentNameKeyword
722 | identifier"""
723 p[0] = p[1]
725 # [58]
726 def p_Ellipsis(self, p):
727 """Ellipsis : ELLIPSIS
728 |"""
729 if len(p) > 1:
730 p[0] = self.BuildNamed('Argument', p, 1)
731 p[0].AddChildren(self.BuildTrue('ELLIPSIS'))
733 # [] Unspecified
734 def p_ExceptionMember(self, p):
735 """ExceptionMember : Const
736 | ExceptionField"""
737 p[0] = p[1]
739 # [] Unspecified
740 def p_ExceptionField(self, p):
741 """ExceptionField : Type identifier ';'"""
742 p[0] = self.BuildNamed('ExceptionField', p, 2, p[1])
744 # [] Error recovery for ExceptionMembers - Unspecified
745 def p_ExceptionFieldError(self, p):
746 """ExceptionField : error"""
747 p[0] = self.BuildError(p, 'ExceptionField')
749 # [59]
750 def p_Iterable(self, p):
751 """Iterable : ITERABLE '<' Type OptionalType '>' ';'
752 | LEGACYITERABLE '<' Type '>' ';'"""
753 if len(p) > 6:
754 childlist = ListFromConcat(p[3], p[4])
755 p[0] = self.BuildProduction('Iterable', p, 2, childlist)
756 else:
757 p[0] = self.BuildProduction('LegacyIterable', p, 2, p[3])
759 # [60]
760 def p_OptionalType(self, p):
761 """OptionalType : ',' Type
762 |"""
763 if len(p) > 1:
764 p[0] = p[2]
766 # [61]
767 def p_ReadWriteMaplike(self, p):
768 """ReadWriteMaplike : MaplikeRest"""
769 p[0] = p[1]
771 # [62]
772 def p_ReadWriteSetlike(self, p):
773 """ReadWriteSetlike : SetlikeRest"""
774 p[0] = p[1]
776 # [63]
777 def p_MaplikeRest(self, p):
778 """MaplikeRest : MAPLIKE '<' Type ',' Type '>' ';'"""
779 childlist = ListFromConcat(p[3], p[5])
780 p[0] = self.BuildProduction('Maplike', p, 2, childlist)
782 # [64]
783 def p_SetlikeRest(self, p):
784 """SetlikeRest : SETLIKE '<' Type '>' ';'"""
785 p[0] = self.BuildProduction('Setlike', p, 2, p[3])
787 # [65] No comment version for mid statement attributes.
788 def p_ExtendedAttributeListNoComments(self, p):
789 """ExtendedAttributeListNoComments : '[' ExtendedAttribute ExtendedAttributes ']'
790 | """
791 if len(p) > 2:
792 items = ListFromConcat(p[2], p[3])
793 p[0] = self.BuildProduction('ExtAttributes', p, 1, items)
795 # [65.1] Add optional comment field for start of statements.
796 def p_ExtendedAttributeList(self, p):
797 """ExtendedAttributeList : Comments '[' ExtendedAttribute ExtendedAttributes ']'
798 | Comments """
799 if len(p) > 2:
800 items = ListFromConcat(p[3], p[4])
801 attribs = self.BuildProduction('ExtAttributes', p, 2, items)
802 p[0] = ListFromConcat(p[1], attribs)
803 else:
804 p[0] = p[1]
806 # [66]
807 def p_ExtendedAttributes(self, p):
808 """ExtendedAttributes : ',' ExtendedAttribute ExtendedAttributes
809 |"""
810 if len(p) > 1:
811 p[0] = ListFromConcat(p[2], p[3])
813 # We only support:
814 # [ identifier ]
815 # [ identifier ( ArgumentList ) ]
816 # [ identifier = identifier ]
817 # [ identifier = ( IdentifierList ) ]
818 # [ identifier = identifier ( ArgumentList ) ]
819 # [66] map directly to [91-93, 95]
820 # [67-69, 71] are unsupported
821 def p_ExtendedAttribute(self, p):
822 """ExtendedAttribute : ExtendedAttributeNoArgs
823 | ExtendedAttributeArgList
824 | ExtendedAttributeIdent
825 | ExtendedAttributeIdentList
826 | ExtendedAttributeNamedArgList"""
827 p[0] = p[1]
829 # [71]
830 def p_ArgumentNameKeyword(self, p):
831 """ArgumentNameKeyword : ATTRIBUTE
832 | CALLBACK
833 | CONST
834 | CREATOR
835 | DELETER
836 | DICTIONARY
837 | ENUM
838 | EXCEPTION
839 | GETTER
840 | IMPLEMENTS
841 | INHERIT
842 | LEGACYCALLER
843 | PARTIAL
844 | SERIALIZER
845 | SETTER
846 | STATIC
847 | STRINGIFIER
848 | TYPEDEF
849 | UNRESTRICTED"""
850 p[0] = p[1]
852 # [72] NOT IMPLEMENTED (OtherOrComma)
854 # [73]
855 def p_Type(self, p):
856 """Type : SingleType
857 | UnionType TypeSuffix"""
858 if len(p) == 2:
859 p[0] = self.BuildProduction('Type', p, 1, p[1])
860 else:
861 p[0] = self.BuildProduction('Type', p, 1, ListFromConcat(p[1], p[2]))
863 # [74]
864 def p_SingleType(self, p):
865 """SingleType : NonAnyType
866 | ANY TypeSuffixStartingWithArray"""
867 if len(p) == 2:
868 p[0] = p[1]
869 else:
870 p[0] = ListFromConcat(self.BuildProduction('Any', p, 1), p[2])
872 # [75]
873 def p_UnionType(self, p):
874 """UnionType : '(' UnionMemberType OR UnionMemberType UnionMemberTypes ')'"""
876 # [76]
877 def p_UnionMemberType(self, p):
878 """UnionMemberType : NonAnyType
879 | UnionType TypeSuffix
880 | ANY '[' ']' TypeSuffix"""
881 # [77]
882 def p_UnionMemberTypes(self, p):
883 """UnionMemberTypes : OR UnionMemberType UnionMemberTypes
884 |"""
886 # [78] Moved BYTESTRING, DOMSTRING, OBJECT, DATE, REGEXP to PrimitiveType
887 # Moving all built-in types into PrimitiveType makes it easier to
888 # differentiate between them and 'identifier', since p[1] would be a string in
889 # both cases.
890 def p_NonAnyType(self, p):
891 """NonAnyType : PrimitiveType TypeSuffix
892 | PromiseType Null
893 | identifier TypeSuffix
894 | SEQUENCE '<' Type '>' Null
895 | FROZENARRAY '<' Type '>' Null"""
896 if len(p) == 3:
897 if type(p[1]) == str:
898 typeref = self.BuildNamed('Typeref', p, 1)
899 else:
900 typeref = p[1]
901 p[0] = ListFromConcat(typeref, p[2])
903 if len(p) == 6:
904 cls = 'Sequence' if p[1] == 'sequence' else 'FrozenArray'
905 p[0] = self.BuildProduction(cls, p, 1, ListFromConcat(p[3], p[5]))
907 # [79] NOT IMPLEMENTED (BufferRelatedType)
909 # [80]
910 def p_ConstType(self, p):
911 """ConstType : PrimitiveType Null
912 | identifier Null"""
913 if type(p[1]) == str:
914 p[0] = self.BuildNamed('Typeref', p, 1, p[2])
915 else:
916 p[1].AddChildren(p[2])
917 p[0] = p[1]
920 # [81] Added BYTESTRING, DOMSTRING, OBJECT, DATE, REGEXP
921 def p_PrimitiveType(self, p):
922 """PrimitiveType : UnsignedIntegerType
923 | UnrestrictedFloatType
924 | BOOLEAN
925 | BYTE
926 | OCTET
927 | BYTESTRING
928 | DOMSTRING
929 | OBJECT
930 | DATE
931 | REGEXP"""
932 if type(p[1]) == str:
933 p[0] = self.BuildNamed('PrimitiveType', p, 1)
934 else:
935 p[0] = p[1]
938 # [82]
939 def p_UnrestrictedFloatType(self, p):
940 """UnrestrictedFloatType : UNRESTRICTED FloatType
941 | FloatType"""
942 if len(p) == 2:
943 typeref = self.BuildNamed('PrimitiveType', p, 1)
944 else:
945 typeref = self.BuildNamed('PrimitiveType', p, 2)
946 typeref.AddChildren(self.BuildTrue('UNRESTRICTED'))
947 p[0] = typeref
950 # [83]
951 def p_FloatType(self, p):
952 """FloatType : FLOAT
953 | DOUBLE"""
954 p[0] = p[1]
956 # [84]
957 def p_UnsignedIntegerType(self, p):
958 """UnsignedIntegerType : UNSIGNED IntegerType
959 | IntegerType"""
960 if len(p) == 2:
961 p[0] = p[1]
962 else:
963 p[0] = 'unsigned ' + p[2]
965 # [85]
966 def p_IntegerType(self, p):
967 """IntegerType : SHORT
968 | LONG OptionalLong"""
969 if len(p) == 2:
970 p[0] = p[1]
971 else:
972 p[0] = p[1] + p[2]
974 # [86]
975 def p_OptionalLong(self, p):
976 """OptionalLong : LONG
977 | """
978 if len(p) > 1:
979 p[0] = ' ' + p[1]
980 else:
981 p[0] = ''
983 # [87] Add unqualified Promise
984 def p_PromiseType(self, p):
985 """PromiseType : PROMISE '<' ReturnType '>'
986 | PROMISE"""
987 if len(p) == 2:
988 # Promise without resolution type is not specified in the Web IDL spec.
989 # As it is used in some specs and in the blink implementation,
990 # we allow that here.
991 resolution_type = self.BuildProduction('Type', p, 1,
992 self.BuildProduction('Any', p, 1))
993 p[0] = self.BuildNamed('Promise', p, 1, resolution_type)
994 else:
995 p[0] = self.BuildNamed('Promise', p, 1, p[3])
997 # [88] Add support for sized array
998 def p_TypeSuffix(self, p):
999 """TypeSuffix : '[' integer ']' TypeSuffix
1000 | '[' ']' TypeSuffix
1001 | '?' TypeSuffixStartingWithArray
1002 | """
1003 if len(p) == 5:
1004 p[0] = self.BuildNamed('Array', p, 2, p[4])
1006 if len(p) == 4:
1007 p[0] = self.BuildProduction('Array', p, 1, p[3])
1009 if len(p) == 3:
1010 p[0] = ListFromConcat(self.BuildTrue('NULLABLE'), p[2])
1013 # [89]
1014 def p_TypeSuffixStartingWithArray(self, p):
1015 """TypeSuffixStartingWithArray : '[' ']' TypeSuffix
1016 | """
1017 if len(p) > 1:
1018 p[0] = self.BuildProduction('Array', p, 0, p[3])
1020 # [90]
1021 def p_Null(self, p):
1022 """Null : '?'
1023 |"""
1024 if len(p) > 1:
1025 p[0] = self.BuildTrue('NULLABLE')
1027 # [91]
1028 def p_ReturnType(self, p):
1029 """ReturnType : Type
1030 | VOID"""
1031 if p[1] == 'void':
1032 p[0] = self.BuildProduction('Type', p, 1)
1033 p[0].AddChildren(self.BuildNamed('PrimitiveType', p, 1))
1034 else:
1035 p[0] = p[1]
1037 # [92]
1038 def p_IdentifierList(self, p):
1039 """IdentifierList : identifier Identifiers"""
1040 p[0] = ListFromConcat(p[1], p[2])
1042 # [93]
1043 def p_Identifiers(self, p):
1044 """Identifiers : ',' identifier Identifiers
1045 |"""
1046 if len(p) > 1:
1047 p[0] = ListFromConcat(p[2], p[3])
1049 # [94]
1050 def p_ExtendedAttributeNoArgs(self, p):
1051 """ExtendedAttributeNoArgs : identifier"""
1052 p[0] = self.BuildNamed('ExtAttribute', p, 1)
1054 # [95]
1055 def p_ExtendedAttributeArgList(self, p):
1056 """ExtendedAttributeArgList : identifier '(' ArgumentList ')'"""
1057 arguments = self.BuildProduction('Arguments', p, 2, p[3])
1058 p[0] = self.BuildNamed('ExtAttribute', p, 1, arguments)
1060 # [96]
1061 def p_ExtendedAttributeIdent(self, p):
1062 """ExtendedAttributeIdent : identifier '=' identifier"""
1063 value = self.BuildAttribute('VALUE', p[3])
1064 p[0] = self.BuildNamed('ExtAttribute', p, 1, value)
1066 # [97]
1067 def p_ExtendedAttributeIdentList(self, p):
1068 """ExtendedAttributeIdentList : identifier '=' '(' IdentifierList ')'"""
1069 value = self.BuildAttribute('VALUE', p[4])
1070 p[0] = self.BuildNamed('ExtAttribute', p, 1, value)
1072 # [98]
1073 def p_ExtendedAttributeNamedArgList(self, p):
1074 """ExtendedAttributeNamedArgList : identifier '=' identifier '(' ArgumentList ')'"""
1075 args = self.BuildProduction('Arguments', p, 4, p[5])
1076 value = self.BuildNamed('Call', p, 3, args)
1077 p[0] = self.BuildNamed('ExtAttribute', p, 1, value)
1080 # Parser Errors
1082 # p_error is called whenever the parser can not find a pattern match for
1083 # a set of items from the current state. The p_error function defined here
1084 # is triggered logging an error, and parsing recovery happens as the
1085 # p_<type>_error functions defined above are called. This allows the parser
1086 # to continue so as to capture more than one error per file.
1088 def p_error(self, t):
1089 if t:
1090 lineno = t.lineno
1091 pos = t.lexpos
1092 prev = self.yaccobj.symstack[-1]
1093 if type(prev) == lex.LexToken:
1094 msg = "Unexpected %s after %s." % (
1095 TokenTypeName(t), TokenTypeName(prev))
1096 else:
1097 msg = "Unexpected %s." % (t.value)
1098 else:
1099 last = self.LastToken()
1100 lineno = last.lineno
1101 pos = last.lexpos
1102 msg = "Unexpected end of file after %s." % TokenTypeName(last)
1103 self.yaccobj.restart()
1105 # Attempt to remap the error to a friendlier form
1106 if msg in ERROR_REMAP:
1107 msg = ERROR_REMAP[msg]
1109 self._last_error_msg = msg
1110 self._last_error_lineno = lineno
1111 self._last_error_pos = pos
1113 def Warn(self, node, msg):
1114 sys.stdout.write(node.GetLogLine(msg))
1115 self.parse_warnings += 1
1117 def LastToken(self):
1118 return self.lexer.last
1120 def __init__(self, lexer, verbose=False, debug=False, mute_error=False):
1121 self.lexer = lexer
1122 self.tokens = lexer.KnownTokens()
1123 self.yaccobj = yacc.yacc(module=self, tabmodule=None, debug=debug,
1124 optimize=0, write_tables=0)
1125 self.parse_debug = debug
1126 self.verbose = verbose
1127 self.mute_error = mute_error
1128 self._parse_errors = 0
1129 self._parse_warnings = 0
1130 self._last_error_msg = None
1131 self._last_error_lineno = 0
1132 self._last_error_pos = 0
1136 # BuildProduction
1138 # Production is the set of items sent to a grammar rule resulting in a new
1139 # item being returned.
1141 # p - Is the Yacc production object containing the stack of items
1142 # index - Index into the production of the name for the item being produced.
1143 # cls - The type of item being producted
1144 # childlist - The children of the new item
1145 def BuildProduction(self, cls, p, index, childlist=None):
1146 try:
1147 if not childlist:
1148 childlist = []
1150 filename = self.lexer.Lexer().filename
1151 lineno = p.lineno(index)
1152 pos = p.lexpos(index)
1153 out = IDLNode(cls, filename, lineno, pos, childlist)
1154 return out
1155 except:
1156 print 'Exception while parsing:'
1157 for num, item in enumerate(p):
1158 print ' [%d] %s' % (num, ExpandProduction(item))
1159 if self.LastToken():
1160 print 'Last token: %s' % str(self.LastToken())
1161 raise
1163 def BuildNamed(self, cls, p, index, childlist=None):
1164 childlist = ListFromConcat(childlist)
1165 childlist.append(self.BuildAttribute('NAME', p[index]))
1166 return self.BuildProduction(cls, p, index, childlist)
1168 def BuildComment(self, cls, p, index):
1169 name = p[index]
1171 # Remove comment markers
1172 lines = []
1173 if name[:2] == '//':
1174 # For C++ style, remove any leading whitespace and the '//' marker from
1175 # each line.
1176 form = 'cc'
1177 for line in name.split('\n'):
1178 start = line.find('//')
1179 lines.append(line[start+2:])
1180 else:
1181 # For C style, remove ending '*/''
1182 form = 'c'
1183 for line in name[:-2].split('\n'):
1184 # Remove characters until start marker for this line '*' if found
1185 # otherwise it should be blank.
1186 offs = line.find('*')
1187 if offs >= 0:
1188 line = line[offs + 1:].rstrip()
1189 else:
1190 line = ''
1191 lines.append(line)
1192 name = '\n'.join(lines)
1193 childlist = [self.BuildAttribute('NAME', name),
1194 self.BuildAttribute('FORM', form)]
1195 return self.BuildProduction(cls, p, index, childlist)
1198 # BuildError
1200 # Build and Errror node as part of the recovery process.
1203 def BuildError(self, p, prod):
1204 self._parse_errors += 1
1205 name = self.BuildAttribute('NAME', self._last_error_msg)
1206 line = self.BuildAttribute('LINE', self._last_error_lineno)
1207 pos = self.BuildAttribute('POS', self._last_error_pos)
1208 prod = self.BuildAttribute('PROD', prod)
1210 node = self.BuildProduction('Error', p, 1,
1211 ListFromConcat(name, line, pos, prod))
1212 if not self.mute_error:
1213 node.Error(self._last_error_msg)
1215 return node
1218 # BuildAttribute
1220 # An ExtendedAttribute is a special production that results in a property
1221 # which is applied to the adjacent item. Attributes have no children and
1222 # instead represent key/value pairs.
1224 def BuildAttribute(self, key, val):
1225 return IDLAttribute(key, val)
1227 def BuildFalse(self, key):
1228 return IDLAttribute(key, Boolean(False))
1230 def BuildTrue(self, key):
1231 return IDLAttribute(key, Boolean(True))
1233 def GetErrors(self):
1234 # Access lexer errors, despite being private
1235 # pylint: disable=W0212
1236 return self._parse_errors + self.lexer._lex_errors
1239 # ParseData
1241 # Attempts to parse the current data loaded in the lexer.
1243 def ParseText(self, filename, data):
1244 self._parse_errors = 0
1245 self._parse_warnings = 0
1246 self._last_error_msg = None
1247 self._last_error_lineno = 0
1248 self._last_error_pos = 0
1250 try:
1251 self.lexer.Tokenize(data, filename)
1252 nodes = self.yaccobj.parse(lexer=self.lexer) or []
1253 name = self.BuildAttribute('NAME', filename)
1254 return IDLNode('File', filename, 0, 0, nodes + [name])
1256 except lex.LexError as lexError:
1257 sys.stderr.write('Error in token: %s\n' % str(lexError))
1258 return None
1262 def ParseFile(parser, filename):
1263 """Parse a file and return a File type of node."""
1264 with open(filename) as fileobject:
1265 try:
1266 out = parser.ParseText(filename, fileobject.read())
1267 out.SetProperty('DATETIME', time.ctime(os.path.getmtime(filename)))
1268 out.SetProperty('ERRORS', parser.GetErrors())
1269 return out
1271 except Exception as e:
1272 last = parser.LastToken()
1273 sys.stderr.write('%s(%d) : Internal parsing error\n\t%s.\n' % (
1274 filename, last.lineno, str(e)))
1277 def main(argv):
1278 nodes = []
1279 parser = IDLParser(IDLLexer())
1280 errors = 0
1281 for filename in argv:
1282 filenode = ParseFile(parser, filename)
1283 if (filenode):
1284 errors += filenode.GetProperty('ERRORS')
1285 nodes.append(filenode)
1287 ast = IDLNode('AST', '__AST__', 0, 0, nodes)
1289 print '\n'.join(ast.Tree(accept_props=['PROD']))
1290 if errors:
1291 print '\nFound %d errors.\n' % errors
1293 return errors
1296 if __name__ == '__main__':
1297 sys.exit(main(sys.argv[1:]))