Update To 11.40.268.0
[platform/framework/web/crosswalk.git] / src / tools / idl_parser / idl_parser.py
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.
5
6 """ Parser for PPAPI IDL """
7
8 #
9 # IDL Parser
10 #
11 # The parser is uses the PLY yacc library to build a set of parsing rules based
12 # on WebIDL.
13 #
14 # WebIDL, and WebIDL grammar can be found at:
15 #   http://heycam.github.io/webidl/
16 # PLY can be found at:
17 #   http://www.dabeaz.com/ply/
18 #
19 # The parser generates a tree by recursively matching sets of items against
20 # defined patterns.  When a match is made, that set of items is reduced
21 # to a new item.   The new item can provide a match for parent patterns.
22 # In this way an AST is built (reduced) depth first.
23 #
24
25 #
26 # Disable check for line length and Member as Function due to how grammar rules
27 # are defined with PLY
28 #
29 # pylint: disable=R0201
30 # pylint: disable=C0301
31
32 import os.path
33 import sys
34 import time
35
36 from idl_lexer import IDLLexer
37 from idl_node import IDLAttribute, IDLNode
38
39 #
40 # Try to load the ply module, if not, then assume it is in the third_party
41 # directory.
42 #
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
55
56 #
57 # ERROR_REMAP
58 #
59 # Maps the standard error formula into a more friendly error message.
60 #
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.',
70 }
71
72
73 def Boolean(val):
74   """Convert to strict boolean type."""
75   if val:
76     return True
77   return False
78
79
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)
90
91   return itemsout
92
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))
103
104 # TokenTypeName
105 #
106 # Generate a string which has the type and value of the token.
107 #
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
124
125
126 #
127 # IDL Parser
128 #
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> ...."""
135 #
136 # Where new item is the result of a match against one or more sets of items
137 # separated by the "|".
138 #
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.
142 #
143 # For more details on parsing refer to the PLY documentation at
144 #    http://www.dabeaz.com/ply/
145 #
146 # The parser is based on the WebIDL standard.  See:
147 #    http://heycam.github.io/webidl/#idl-grammar
148 #
149 # The various productions are annotated so that the WHOLE number greater than
150 # zero in the comment denotes the matching WebIDL grammar definition.
151 #
152 # Productions with a fractional component in the comment denote additions to
153 # the WebIDL spec, such as comments.
154 #
155
156
157 class IDLParser(object):
158 #
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.
162 #
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])
169
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]
175
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])
182
183
184 #
185 #The parser is based on the WebIDL standard.  See:
186 # http://heycam.github.io/webidl/#idl-grammar
187 #
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])
195
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]
206
207   # [2.1] Error recovery for definition
208   def p_DefinitionError(self, p):
209     """Definition : error ';'"""
210     p[0] = self.BuildError(p, 'Definition')
211
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]
220
221   # [4]
222   def p_CallbackRestOrInterface(self, p):
223     """CallbackRestOrInterface : CallbackRest
224                                | Interface"""
225     p[0] = p[1]
226
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]))
231
232   # [6]
233   def p_Partial(self, p):
234     """Partial : PARTIAL PartialDefinition"""
235     p[2].AddChildren(self.BuildTrue('Partial'))
236     p[0] = p[2]
237
238   # [6.1] Error recovery for Partial
239   def p_PartialError(self, p):
240     """Partial : PARTIAL error"""
241     p[0] = self.BuildError(p, 'Partial')
242
243   # [7]
244   def p_PartialDefinition(self, p):
245     """PartialDefinition : PartialDictionary
246                          | PartialInterface"""
247     p[0] = p[1]
248
249   # [8]
250   def p_PartialInterface(self, p):
251     """PartialInterface : INTERFACE identifier '{' InterfaceMembers '}' ';'"""
252     p[0] = self.BuildNamed('Interface', p, 2, p[4])
253
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])
261
262   # [10]
263   def p_InterfaceMember(self, p):
264     """InterfaceMember : Const
265                        | AttributeOrOperationOrIterator"""
266     p[0] = p[1]
267
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]
275
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]))
280
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')
285
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])
293
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]))
298
299   # [14] NOT IMPLEMENTED (Required)
300
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))
306
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')
311
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])
318
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]
328
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]))
333
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])
341
342   # [.1] Error recovery for ExceptionMembers - Not specified
343   def p_ExceptionMembersError(self, p):
344     """ExceptionMembers : error"""
345     p[0] = self.BuildError(p, 'ExceptionMembers')
346
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)
353
354   # [19]
355   def p_Enum(self, p):
356     """Enum : ENUM identifier '{' EnumValueList '}' ';'"""
357     p[0] = self.BuildNamed('Enum', p, 2, p[4])
358
359   # [19.1] Error recovery for Enums
360   def p_EnumError(self, p):
361     """Enum : ENUM error ';'"""
362     p[0] = self.BuildError(p, 'Enum')
363
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])
369
370   # [21]
371   def p_EnumValueListComma(self, p):
372     """EnumValueListComma : ',' EnumValueListString
373                           |"""
374     if len(p) > 1:
375       p[0] = p[2]
376
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])
384
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))
390
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]))
395
396   # [24.1] Error recovery for Typedefs
397   def p_TypedefError(self, p):
398     """Typedef : TYPEDEF error ';'"""
399     p[0] = self.BuildError(p, 'Typedef')
400
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)
406
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))
412
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]
424
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'))
430
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)
437
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))
450
451   # [30-34] NOT IMPLEMENTED (Serializer)
452
453   # [35]
454   def p_Stringifier(self, p):
455     """Stringifier : STRINGIFIER StringifierRest"""
456     p[0] = self.BuildProduction('Stringifier', p, 1, p[2])
457
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]
468
469   # [37]
470   def p_StaticMember(self, p):
471     """StaticMember : STATIC StaticMemberRest"""
472     p[2].AddChildren(self.BuildTrue('STATIC'))
473     p[0] = p[2]
474
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]
484
485   # [39] NOT IMPLEMENTED (ReadOnlyMember)
486   # [40] NOT IMPLEMENTED (ReadOnlyMemberReset)
487
488   # [41]
489   def p_ReadWriteAttribute(self, p):
490     """ReadWriteAttribute : Inherit AttributeRest"""
491     p[2].AddChildren(ListFromConcat(p[1]))
492     p[0] = p[2]
493
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]
498
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]))
504
505   # [43] NOT IMPLEMENTED (AttributeName)
506   # [44] NOT IMPLEMENTED (AttributeNameKeyword)
507
508   # [45]
509   def p_Inherit(self, p):
510     """Inherit : INHERIT
511                |"""
512     if len(p) > 1:
513       p[0] = self.BuildTrue('INHERIT')
514
515   # [46]
516   def p_ReadOnly(self, p):
517     """ReadOnly : READONLY
518                 |"""
519     if len(p) > 1:
520       p[0] = self.BuildTrue('READONLY')
521
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]
531
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]
537
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])
544
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())
553
554   # [51]
555   def p_OperationOrIteratorRest(self, p):
556     """OperationOrIteratorRest : OperationRest"""
557     p[0] = p[1]
558
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)
564
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_'
573
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])
580
581   # [53.1] ArgumentList error recovery
582   def p_ArgumentListError(self, p):
583     """ArgumentList : error """
584     p[0] = self.BuildError(p, 'ArgumentList')
585
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])
592
593   # [55]
594   def p_Argument(self, p):
595     """Argument : ExtendedAttributeList OptionalOrRequiredArgument"""
596     p[2].AddChildren(p[1])
597     p[0] = p[2]
598
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
609
610   # [57]
611   def p_ArgumentName(self, p):
612     """ArgumentName : ArgumentNameKeyword
613                     | identifier"""
614     p[0] = p[1]
615
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'))
623
624   # [] Unspecified
625   def p_ExceptionMember(self, p):
626     """ExceptionMember : Const
627                        | ExceptionField"""
628     p[0] = p[1]
629
630   # [] Unspecified
631   def p_ExceptionField(self, p):
632     """ExceptionField : Type identifier ';'"""
633     p[0] = self.BuildNamed('ExceptionField', p, 2, p[1])
634
635   # [] Error recovery for ExceptionMembers - Unspecified
636   def p_ExceptionFieldError(self, p):
637     """ExceptionField : error"""
638     p[0] = self.BuildError(p, 'ExceptionField')
639
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)
646
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)
654
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]
665
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])
672
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]
688
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]
711
712   # [72] NOT IMPLEMENTED (OtherOrComma)
713
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]))
722
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])
731
732   # [75]
733   def p_UnionType(self, p):
734     """UnionType : '(' UnionMemberType OR UnionMemberType UnionMemberTypes ')'"""
735
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                         |"""
745
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])
761
762     if len(p) == 6:
763       p[0] = self.BuildProduction('Sequence', p, 1, ListFromConcat(p[3], p[5]))
764
765   # [79] NOT IMPLEMENTED (BufferRelatedType)
766
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]
776
777
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]
794
795
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
806
807
808   # [83]
809   def p_FloatType(self, p):
810     """FloatType : FLOAT
811                  | DOUBLE"""
812     p[0] = p[1]
813
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]
822
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]
831
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] = ''
840
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])
854
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])
863
864     if len(p) == 4:
865       p[0] = self.BuildProduction('Array', p, 1, p[3])
866
867     if len(p) == 3:
868       p[0] = ListFromConcat(self.BuildTrue('NULLABLE'), p[2])
869
870
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])
877
878   # [90]
879   def p_Null(self, p):
880     """Null : '?'
881             |"""
882     if len(p) > 1:
883       p[0] = self.BuildTrue('NULLABLE')
884
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]
894
895   # [92]
896   def p_IdentifierList(self, p):
897     """IdentifierList : identifier Identifiers"""
898     p[0] = ListFromConcat(p[1], p[2])
899
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])
906
907   # [94]
908   def p_ExtendedAttributeNoArgs(self, p):
909     """ExtendedAttributeNoArgs : identifier"""
910     p[0] = self.BuildNamed('ExtAttribute', p, 1)
911
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)
917
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)
923
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)
929
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)
936
937 #
938 # Parser Errors
939 #
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.
945 #
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()
962
963     # Attempt to remap the error to a friendlier form
964     if msg in ERROR_REMAP:
965       msg = ERROR_REMAP[msg]
966
967     self._last_error_msg = msg
968     self._last_error_lineno = lineno
969     self._last_error_pos = pos
970
971   def Warn(self, node, msg):
972     sys.stdout.write(node.GetLogLine(msg))
973     self.parse_warnings += 1
974
975   def LastToken(self):
976     return self.lexer.last
977
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
991
992
993 #
994 # BuildProduction
995 #
996 # Production is the set of items sent to a grammar rule resulting in a new
997 # item being returned.
998 #
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 = []
1007
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
1020
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)
1025
1026   def BuildComment(self, cls, p, index):
1027     name = p[index]
1028
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)
1054
1055 #
1056 # BuildError
1057 #
1058 # Build and Errror node as part of the recovery process.
1059 #
1060 #
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)
1067
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)
1072
1073     return node
1074
1075 #
1076 # BuildAttribute
1077 #
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.
1081 #
1082   def BuildAttribute(self, key, val):
1083     return IDLAttribute(key, val)
1084
1085   def BuildFalse(self, key):
1086     return IDLAttribute(key, Boolean(False))
1087
1088   def BuildTrue(self, key):
1089     return IDLAttribute(key, Boolean(True))
1090
1091   def GetErrors(self):
1092     # Access lexer errors, despite being private
1093     # pylint: disable=W0212
1094     return self._parse_errors + self.lexer._lex_errors
1095
1096 #
1097 # ParseData
1098 #
1099 # Attempts to parse the current data loaded in the lexer.
1100 #
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
1107
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])
1113
1114     except lex.LexError as lexError:
1115       sys.stderr.write('Error in token: %s\n' % str(lexError))
1116     return None
1117
1118
1119
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
1128
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)))
1133
1134
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)
1144
1145   ast = IDLNode('AST', '__AST__', 0, 0, nodes)
1146
1147   print '\n'.join(ast.Tree(accept_props=['PROD']))
1148   if errors:
1149     print '\nFound %d errors.\n' % errors
1150
1151   return errors
1152
1153
1154 if __name__ == '__main__':
1155   sys.exit(main(sys.argv[1:]))