Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(39)

Side by Side Diff: tools/idl_parser/idl_parser.py

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

Powered by Google App Engine
This is Rietveld 408576698