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

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

Issue 13498002: Add WebIDL compliant parser plus tests (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Merge using updated lexer, fix error msgs Created 7 years, 8 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 | Annotate | Revision Log
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://dev.w3.org/2006/webapi/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 glob
33 import optparse
34 import os.path
35 import sys
36 import time
37
38 from idl_lexer import IDLLexer
39 from idl_node import IDLAttribute, IDLNode
40
41 from ply import lex
42 from ply import yacc
43
44 #
45 # ERROR_REMAP
46 #
47 # Maps the standard error formula into a more friendly error message.
48 #
49 ERROR_REMAP = {
50 'Unexpected ")" after "(".' : 'Empty argument list.',
51 'Unexpected ")" after ",".' : 'Missing argument.',
52 'Unexpected "}" after ",".' : 'Trailing comma in block.',
53 'Unexpected "}" after "{".' : 'Unexpected empty block.',
54 'Unexpected comment after "}".' : 'Unexpected trailing comment.',
55 'Unexpected "{" after keyword "enum".' : 'Enum missing name.',
56 'Unexpected "{" after keyword "struct".' : 'Struct missing name.',
57 'Unexpected "{" after keyword "interface".' : 'Interface missing name.',
58 }
59
60
61 def Boolean(val):
62 """Convert to strict boolean type."""
63 if val:
64 return True
65 return False
66
67
68 def ListFromConcat(*items):
69 """Generate list by concatenating inputs"""
70 itemsout = []
71 for item in items:
72 if item is None:
73 continue
74 if type(item) is not type([]):
75 itemsout.append(item)
76 else:
77 itemsout.extend(item)
78
79 return itemsout
80
81 def ExpandProduction(p):
82 if type(p) == list:
83 return '[' + ', '.join([ExpandProduction(x) for x in p]) + ']'
84 if type(p) == IDLNode:
85 return 'Node:' + str(p)
86 if type(p) == IDLAttribute:
87 return 'Attr:' + str(p)
88 if type(p) == str:
89 return 'str:' + p
90 return '%s:%s' % (p.__class__.__name__, str(p))
91
92 # TokenTypeName
93 #
94 # Generate a string which has the type and value of the token.
95 #
96 def TokenTypeName(t):
97 if t.type == 'SYMBOL':
98 return 'symbol %s' % t.value
99 if t.type in ['HEX', 'INT', 'OCT', 'FLOAT']:
100 return 'value %s' % t.value
101 if t.type == 'string' :
102 return 'string "%s"' % t.value
103 if t.type == 'COMMENT' :
104 return 'comment'
105 if t.type == t.value:
106 return '"%s"' % t.value
107 if t.type == ',':
108 return 'Comma'
109 if t.type == 'identifier':
110 return 'identifier "%s"' % t.value
111 return 'keyword "%s"' % t.value
112
113
114 #
115 # IDL Parser
116 #
117 # The Parser inherits the from the Lexer to provide PLY with the tokenizing
118 # definitions. Parsing patterns are encoded as functions where p_<name> is
119 # is called any time a patern matching the function documentation is found.
120 # Paterns are expressed in the form of:
121 # """ <new item> : <item> ....
122 # | <item> ...."""
123 #
124 # Where new item is the result of a match against one or more sets of items
125 # separated by the "|".
126 #
127 # The function is called with an object 'p' where p[0] is the output object
128 # and p[n] is the set of inputs for positive values of 'n'. Len(p) can be
129 # used to distinguish between multiple item sets in the pattern.
130 #
131 # For more details on parsing refer to the PLY documentation at
132 # http://www.dabeaz.com/ply/
133 #
134 # The parser is based on the WebIDL standard. See:
135 # http://www.w3.org/TR/WebIDL/#idl-grammar
136 #
137 # The various productions are annotated so that the WHOLE number greater than
138 # zero in the comment denotes the matching WebIDL grammar definition.
139 #
140 # Productions with a fractional component in the comment denote additions to
141 # the WebIDL spec, such as comments.
142 #
143
144
145 class IDLParser(IDLLexer):
146 #
147 # We force all input files to start with two comments. The first comment is a
148 # Copyright notice followed by a file comment and finally by file level
149 # productions.
150 #
151 # [0] Insert a TOP definition for Copyright and Comments
152 def p_Top(self, p):
153 """Top : COMMENT COMMENT Definitions"""
154 Copyright = self.BuildComment('Copyright', p, 1)
155 Filedoc = self.BuildComment('Comment', p, 2)
156 p[0] = ListFromConcat(Copyright, Filedoc, p[3])
157
158 # [0.1] Add support for Multiple COMMENTS
159 def p_Comments(self, p):
160 """Comments : CommentsRest"""
161 if len(p) > 1:
162 p[0] = p[1]
163
164 # [0.2] Produce a COMMENT and aggregate sibling comments
165 def p_CommentsRest(self, p):
166 """CommentsRest : COMMENT CommentsRest
167 | """
168 if len(p) > 1:
169 p[0] = ListFromConcat(self.BuildComment('Comment', p, 1), p[2])
170
171
172 #
173 #The parser is based on the WebIDL standard. See:
174 # http://www.w3.org/TR/WebIDL/#idl-grammar
175 #
176 # [1]
177 def p_Definitions(self, p):
178 """Definitions : ExtendedAttributeList Definition Definitions
179 | """
180 if len(p) > 1:
181 p[2].AddChildren(p[1])
182 p[0] = ListFromConcat(p[2], p[3])
183
184 # [2] Add INLINE definition
185 def p_Definition(self, p):
186 """Definition : CallbackOrInterface
187 | Partial
188 | Dictionary
189 | Exception
190 | Enum
191 | Typedef
192 | ImplementsStatement"""
193 p[0] = p[1]
194
195 # [2.1] Error recovery for definition
196 def p_DefinitionError(self, p):
197 """Definition : error ';'"""
198 p[0] = self.BuildError(p, 'Definition')
199
200 # [3]
201 def p_CallbackOrInterface(self, p):
202 """CallbackOrInterface : CALLBACK CallbackRestOrInterface
203 | Interface"""
204 if len(p) > 2:
205 p[0] = p[2]
206 else:
207 p[0] = p[1]
208
209 # [4]
210 def p_CallbackRestOrInterface(self, p):
211 """CallbackRestOrInterface : CallbackRest
212 | Interface"""
213 p[0] = p[1]
214
215 # [5]
216 def p_Interface(self, p):
217 """Interface : INTERFACE identifier Inheritance '{' InterfaceMembers '}' ';' """
218 p[0] = self.BuildNamed('Interface', p, 2, ListFromConcat(p[3], p[5]))
219
220 # [6] Error recovery for PARTIAL
221 def p_Partial(self, p):
222 """Partial : PARTIAL PartialDefinition"""
223 p[2].AddChildren(self.BuildTrue('Partial'))
224 p[0] = p[2]
225
226 # [6.1] Error recovery for Enums
227 def p_PartialError(self, p):
228 """Partial : PARTIAL error"""
229 p[0] = self.BuildError(p, 'Partial')
230
231 # [7]
232 def p_PartialDefinition(self, p):
233 """PartialDefinition : PartialDictionary
234 | PartialInterface"""
235 p[0] = p[1]
236
237 # [8]
238 def p_PartialInterface(self, p):
239 """PartialInterface : INTERFACE identifier '{' InterfaceMembers '}' ';'"""
240 p[0] = self.BuildNamed('Interface', p, 2, p[4])
241
242 # [9]
243 def p_InterfaceMembers(self, p):
244 """InterfaceMembers : ExtendedAttributeList InterfaceMember InterfaceMembers
245 |"""
246 if len(p) > 1:
247 p[2].AddChildren(p[1])
248 p[0] = ListFromConcat(p[2], p[3])
249
250 # [10]
251 def p_InterfaceMember(self, p):
252 """InterfaceMember : Const
253 | AttributeOrOperation"""
254 p[0] = p[1]
255
256 # [11]
257 def p_Dictionary(self, p):
258 """Dictionary : DICTIONARY identifier Inheritance '{' DictionaryMembers '}' ';'"""
259 p[0] = self.BuildNamed('Dictionary', p, 2, ListFromConcat(p[3], p[5]))
260
261 # [11.1] Error recovery for regular Dictionary
262 def p_DictionaryError(self, p):
263 """Dictionary : DICTIONARY error ';'"""
264 p[0] = self.BuildError(p, 'Dictionary')
265
266 # [12]
267 def p_DictionaryMembers(self, p):
268 """DictionaryMembers : ExtendedAttributeList DictionaryMember DictionaryMemb ers
269 |"""
270 if len(p) > 1:
271 p[2].AddChildren(p[1])
272 p[0] = ListFromConcat(p[2], p[3])
273
274 # [13]
275 def p_DictionaryMember(self, p):
276 """DictionaryMember : Type identifier Default ';'"""
277 p[0] = self.BuildNamed('Key', p, 2, ListFromConcat(p[1], p[3]))
278
279 # [14]
280 def p_PartialDictionary(self, p):
281 """PartialDictionary : DICTIONARY identifier '{' DictionaryMembers '}' ';'"" "
282 partial = self.BuildTrue('Partial')
283 p[0] = self.BuildNamed('Dictionary', p, 2, ListFromConcat(p[4], partial))
284
285 # [14.1] Error recovery for Partial Dictionary
286 def p_PartialDictionaryError(self, p):
287 """PartialDictionary : DICTIONARY error ';'"""
288 p[0] = self.BuildError(p, 'PartialDictionary')
289
290 # [15]
291 def p_Default(self, p):
292 """Default : '=' DefaultValue
293 |"""
294 if len(p) > 1:
295 p[0] = self.BuildProduction('Default', p, 2, p[2])
296
297 # [16]
298 def p_DefaultValue(self, p):
299 """DefaultValue : ConstValue
300 | string"""
301 if type(p[1]) == str:
302 p[0] = ListFromConcat(self.BuildAttribute('TYPE', 'DOMString'),
303 self.BuildAttribute('NAME', p[1]))
304 else:
305 p[0] = p[1]
306
307 # [17]
308 def p_Exception(self, p):
309 """Exception : EXCEPTION identifier Inheritance '{' ExceptionMembers '}' ';' """
310 p[0] = self.BuildNamed('Exception', p, 2, ListFromConcat(p[3], p[5]))
311
312 # [18]
313 def p_ExceptionMembers(self, p):
314 """ExceptionMembers : ExtendedAttributeList ExceptionMember ExceptionMembers
315 |"""
316 if len(p) > 1:
317 p[2].AddChildren(p[1])
318 p[0] = ListFromConcat(p[2], p[3])
319
320 # [18.1] Error recovery for ExceptionMembers
321 def p_ExceptionMembersError(self, p):
322 """ExceptionMembers : error"""
323 p[0] = self.BuildError(p, 'ExceptionMembers')
324
325 # [19]
326 def p_Inheritance(self, p):
327 """Inheritance : ':' identifier
328 |"""
329 if len(p) > 1:
330 p[0] = self.BuildNamed('Inherit', p, 2)
331
332 # [20]
333 def p_Enum(self, p):
334 """Enum : ENUM identifier '{' EnumValueList '}' ';'"""
335 p[0] = self.BuildNamed('Enum', p, 2, p[4])
336
337 # [20.1] Error recovery for Enums
338 def p_EnumError(self, p):
339 """Enum : ENUM error ';'"""
340 p[0] = self.BuildError(p, 'Enum')
341
342 # [21]
343 def p_EnumValueList(self, p):
344 """EnumValueList : ExtendedAttributeList string EnumValues"""
345 enum = self.BuildNamed('EnumItem', p, 2, p[1])
346 p[0] = ListFromConcat(enum, p[3])
347
348 # [22]
349 def p_EnumValues(self, p):
350 """EnumValues : ',' ExtendedAttributeList string EnumValues
351 |"""
352 if len(p) > 1:
353 enum = self.BuildNamed('EnumItem', p, 3, p[2])
354 p[0] = ListFromConcat(enum, p[4])
355
356 # [23]
357 def p_CallbackRest(self, p):
358 """CallbackRest : identifier '=' ReturnType '(' ArgumentList ')' ';'"""
359 arguments = self.BuildProduction('Arguments', p, 4, p[5])
360 p[0] = self.BuildNamed('Callback', p, 1, ListFromConcat(p[3], arguments))
361
362 # [24]
363 def p_Typedef(self, p):
364 """Typedef : TYPEDEF ExtendedAttributeList Type identifier ';'"""
365 p[0] = self.BuildNamed('Typedef', p, 4, ListFromConcat(p[2], p[3]))
366
367 # [24.1] Error recovery for Typedefs
368 def p_TypedefError(self, p):
369 """Typedef : TYPEDEF error ';'"""
370 p[0] = self.BuildError(p, 'Typedef')
371
372 # [25]
373 def p_ImplementsStatement(self, p):
374 """ImplementsStatement : identifier IMPLEMENTS identifier ';'"""
375 name = self.BuildAttribute('REFERENCE', p[3])
376 p[0] = self.BuildNamed('Implements', p, 1, name)
377
378 # [26]
379 def p_Const(self, p):
380 """Const : CONST ConstType identifier '=' ConstValue ';'"""
381 value = self.BuildProduction('Value', p, 5, p[5])
382 p[0] = self.BuildNamed('Const', p, 3, ListFromConcat(p[2], value))
383
384 # [27]
385 def p_ConstValue(self, p):
386 """ConstValue : BooleanLiteral
387 | FloatLiteral
388 | integer
389 | null"""
390 if type(p[1]) == str:
391 p[0] = ListFromConcat(self.BuildAttribute('TYPE', 'integer'),
392 self.BuildAttribute('NAME', p[1]))
393 else:
394 p[0] = p[1]
395
396 # [27.1] Add definition for NULL
397 def p_null(self, p):
398 """null : NULL"""
399 p[0] = ListFromConcat(self.BuildAttribute('TYPE', 'NULL'),
400 self.BuildAttribute('NAME', 'NULL'))
401
402 # [28]
403 def p_BooleanLiteral(self, p):
404 """BooleanLiteral : TRUE
405 | FALSE"""
406 value = self.BuildAttribute('NAME', Boolean(p[1] == 'true'))
407 p[0] = ListFromConcat(self.BuildAttribute('TYPE', 'boolean'), value)
408
409 # [29]
410 def p_FloatLiteral(self, p):
411 """FloatLiteral : float
412 | '-' INFINITY
413 | INFINITY
414 | NAN """
415 if len(p) > 2:
416 val = '-Infinity'
417 else:
418 val = p[1]
419 p[0] = ListFromConcat(self.BuildAttribute('TYPE', 'float'),
420 self.BuildAttribute('VALUE', val))
421
422 # [30]
423 def p_AttributeOrOperation(self, p):
424 """AttributeOrOperation : STRINGIFIER StringifierAttributeOrOperation
425 | Attribute
426 | Operation"""
427 if len(p) > 2:
428 p[0] = p[2]
429 else:
430 p[0] = p[1]
431
432 # [31]
433 def p_StringifierAttributeOrOperation(self, p):
434 """StringifierAttributeOrOperation : Attribute
435 | OperationRest
436 | ';'"""
437 if p[1] == ';':
438 p[0] = self.BuildAttribute('STRINGIFIER', Boolean(True))
439 else:
440 p[0] = ListFromConcat(self.BuildAttribute('STRINGIFIER', p[1]), p[1])
441
442 # [32]
443 def p_Attribute(self, p):
444 """Attribute : Inherit ReadOnly ATTRIBUTE Type identifier ';'"""
445 p[0] = self.BuildNamed('Attribute', p, 5,
446 ListFromConcat(p[1], p[2], p[4]))
447
448 # [33]
449 def p_Inherit(self, p):
450 """Inherit : INHERIT
451 |"""
452 if len(p) > 1:
453 p[0] = self.BuildTrue('INHERIT')
454
455 # [34]
456 def p_ReadOnly(self, p):
457 """ReadOnly : READONLY
458 |"""
459 if len(p) > 1:
460 p[0] = self.BuildTrue('READONLY')
461
462 # [35]
463 def p_Operation(self, p):
464 """Operation : Qualifiers OperationRest"""
465 p[2].AddChildren(p[1])
466 p[0] = p[2]
467
468 # [36]
469 def p_Qualifiers(self, p):
470 """Qualifiers : STATIC
471 | Specials"""
472 if p[1] == 'static':
473 p[0] = self.BuildTrue('STATIC')
474 else:
475 p[0] = p[1]
476
477 # [37]
478 def p_Specials(self, p):
479 """Specials : Special Specials
480 | """
481 if len(p) > 1:
482 p[0] = ListFromConcat(p[1], p[2])
483
484 # [38]
485 def p_Special(self, p):
486 """Special : GETTER
487 | SETTER
488 | CREATOR
489 | DELETER
490 | LEGACYCALLER"""
491 p[0] = self.BuildTrue(p[1].upper())
492
493
494 # [39]
495 def p_OperationRest(self, p):
496 """OperationRest : ReturnType OptionalIdentifier '(' ArgumentList ')' ';'"""
497 arguments = self.BuildProduction('Arguments', p, 3, p[4])
498 p[0] = self.BuildNamed('Operation', p, 2, ListFromConcat(p[1], arguments))
499
500 # [40]
501 def p_OptionalIdentifier(self, p):
502 """OptionalIdentifier : identifier
503 |"""
504 if len(p) > 1:
505 p[0] = p[1]
506 else:
507 p[0] = '_unnamed_'
508
509 # [41]
510 def p_ArgumentList(self, p):
511 """ArgumentList : Argument Arguments
512 |"""
513 if len(p) > 1:
514 p[0] = ListFromConcat(p[1], p[2])
515
516 # [41.1] ArgumentList error recovery
517 def p_ArgumentListError(self, p):
518 """ArgumentList : error """
519 p[0] = self.BuildError(p, 'ArgumentList')
520
521 # [42]
522 def p_Arguments(self, p):
523 """Arguments : ',' Argument Arguments
524 |"""
525 if len(p) > 1:
526 p[0] = ListFromConcat(p[2], p[3])
527
528 # [43]
529 def p_Argument(self, p):
530 """Argument : ExtendedAttributeList OptionalOrRequiredArgument"""
531 p[2].AddChildren(p[1])
532 p[0] = p[2]
533
534
535 # [44]
536 def p_OptionalOrRequiredArgument(self, p):
537 """OptionalOrRequiredArgument : OPTIONAL Type ArgumentName Default
538 | Type Ellipsis ArgumentName"""
539 if len(p) > 4:
540 arg = self.BuildNamed('Argument', p, 3, ListFromConcat(p[2], p[4]))
541 arg.AddChildren(self.BuildTrue('OPTIONAL'))
542 else:
543 arg = self.BuildNamed('Argument', p, 3, ListFromConcat(p[1], p[2]))
544 p[0] = arg
545
546 # [45]
547 def p_ArgumentName(self, p):
548 """ArgumentName : ArgumentNameKeyword
549 | identifier"""
550 p[0] = p[1]
551
552 # [46]
553 def p_Ellipsis(self, p):
554 """Ellipsis : ELLIPSIS
555 |"""
556 if len(p) > 1:
557 p[0] = self.BuildNamed('Argument', p, 1)
558 p[0].AddChildren(self.BuildTrue('ELLIPSIS'))
559
560 # [47]
561 def p_ExceptionMember(self, p):
562 """ExceptionMember : Const
563 | ExceptionField"""
564 p[0] = p[1]
565
566 # [48]
567 def p_ExceptionField(self, p):
568 """ExceptionField : Type identifier ';'"""
569 p[0] = self.BuildNamed('ExceptionField', p, 2, p[1])
570
571 # [48.1] Error recovery for ExceptionMembers
572 def p_ExceptionFieldError(self, p):
573 """ExceptionField : error"""
574 p[0] = self.BuildError(p, 'ExceptionField')
575
576 # [49] Add optional comment field
577 def p_ExtendedAttributeList(self, p):
578 """ExtendedAttributeList : Comments '[' ExtendedAttribute ExtendedAttributes ']'
579 | Comments """
580 if len(p) > 2:
581 items = ListFromConcat(p[3], p[4])
582 attribs = self.BuildProduction('ExtAttributes', p, 2, items)
583 p[0] = ListFromConcat(p[1], attribs)
584 else:
585 p[0] = p[1]
586
587 # [50]
588 def p_ExtendedAttributes(self, p):
589 """ExtendedAttributes : ',' ExtendedAttribute ExtendedAttributes
590 |"""
591 if len(p) > 1:
592 p[0] = ListFromConcat(p[2], p[3])
593
594 # We only support:
595 # [ identifier ]
596 # [ identifier = identifier ]
597 # [ identifier ( ArgumentList )]
598 # [ identifier = identifier ( ArgumentList )]
599 # [51] map directly to 74-77
600 # [52-54, 56] are unsupported
601 def p_ExtendedAttribute(self, p):
602 """ExtendedAttribute : ExtendedAttributeNoArgs
603 | ExtendedAttributeArgList
604 | ExtendedAttributeIdent
605 | ExtendedAttributeNamedArgList"""
606 p[0] = p[1]
607
608 # [55]
609 def p_ArgumentNameKeyword(self, p):
610 """ArgumentNameKeyword : ATTRIBUTE
611 | CALLBACK
612 | CONST
613 | CREATOR
614 | DELETER
615 | DICTIONARY
616 | ENUM
617 | EXCEPTION
618 | GETTER
619 | IMPLEMENTS
620 | INHERIT
621 | LEGACYCALLER
622 | PARTIAL
623 | SETTER
624 | STATIC
625 | STRINGIFIER
626 | TYPEDEF
627 | UNRESTRICTED"""
628 p[0] = p[1]
629
630 # [57]
631 def p_Type(self, p):
632 """Type : SingleType
633 | UnionType TypeSuffix"""
634 if len(p) == 2:
635 p[0] = self.BuildProduction('Type', p, 1, p[1])
636 else:
637 p[0] = self.BuildProduction('Type', p, 1, ListFromConcat(p[1], p[2]))
638
639 # [58]
640 def p_SingleType(self, p):
641 """SingleType : NonAnyType
642 | ANY TypeSuffixStartingWithArray"""
643 if len(p) == 2:
644 p[0] = p[1]
645 else:
646 p[0] = ListFromConcat(self.BuildProduction('Any', p, 1), p[2])
647
648 # [59]
649 def p_UnionType(self, p):
650 """UnionType : '(' UnionMemberType OR UnionMemberType UnionMemberTypes ')'"" "
651
652 # [60]
653 def p_UnionMemberType(self, p):
654 """UnionMemberType : NonAnyType
655 | UnionType TypeSuffix
656 | ANY '[' ']' TypeSuffix"""
657 # [61]
658 def p_UnionMemberTypes(self, p):
659 """UnionMemberTypes : OR UnionMemberType UnionMemberTypes
660 |"""
661
662 # [62] Moved DATE, DOMSTRING, OBJECT to PrimitiveType
663 def p_NonAnyType(self, p):
664 """NonAnyType : PrimitiveType TypeSuffix
665 | identifier TypeSuffix
666 | SEQUENCE '<' Type '>' Null"""
667 if len(p) == 3:
668 if type(p[1]) == str:
669 typeref = self.BuildNamed('Typeref', p, 1)
670 else:
671 typeref = p[1]
672 p[0] = ListFromConcat(typeref, p[2])
673
674 if len(p) == 6:
675 p[0] = self.BuildProduction('Sequence', p, 1, ListFromConcat(p[3], p[5]))
676
677
678 # [63]
679 def p_ConstType(self, p):
680 """ConstType : PrimitiveType Null
681 | identifier Null"""
682 if type(p[1]) == str:
683 p[0] = self.BuildNamed('Typeref', p, 1, p[2])
684 else:
685 p[1].AddChildren(p[2])
686 p[0] = p[1]
687
688
689 # [64]
690 def p_PrimitiveType(self, p):
691 """PrimitiveType : UnsignedIntegerType
692 | UnrestrictedFloatType
693 | BOOLEAN
694 | BYTE
695 | OCTET
696 | DOMSTRING
697 | DATE
698 | OBJECT"""
699 if type(p[1]) == str:
700 p[0] = self.BuildNamed('PrimitiveType', p, 1)
701 else:
702 p[0] = p[1]
703
704
705 # [65]
706 def p_UnrestrictedFloatType(self, p):
707 """UnrestrictedFloatType : UNRESTRICTED FloatType
708 | FloatType"""
709 if len(p) == 2:
710 typeref = self.BuildNamed('PrimitiveType', p, 1)
711 else:
712 typeref = self.BuildNamed('PrimitiveType', p, 2)
713 typeref.AddChildren(self.BuildTrue('UNRESTRICTED'))
714 p[0] = typeref
715
716
717 # [66]
718 def p_FloatType(self, p):
719 """FloatType : FLOAT
720 | DOUBLE"""
721 p[0] = p[1]
722
723 # [67]
724 def p_UnsignedIntegerType(self, p):
725 """UnsignedIntegerType : UNSIGNED IntegerType
726 | IntegerType"""
727 if len(p) == 2:
728 p[0] = p[1]
729 else:
730 p[0] = 'unsigned ' + p[2]
731
732 # [68]
733 def p_IntegerType(self, p):
734 """IntegerType : SHORT
735 | LONG OptionalLong"""
736 if len(p) == 2:
737 p[0] = p[1]
738 else:
739 p[0] = p[1] + p[2]
740
741 # [69]
742 def p_OptionalLong(self, p):
743 """OptionalLong : LONG
744 | """
745 if len(p) > 1:
746 p[0] = ' ' + p[1]
747 else:
748 p[0] = ''
749
750
751 # [70] Add support for sized array
752 def p_TypeSuffix(self, p):
753 """TypeSuffix : '[' integer ']' TypeSuffix
754 | '[' ']' TypeSuffix
755 | '?' TypeSuffixStartingWithArray
756 |"""
757 if len(p) == 5:
758 p[0] = self.BuildNamed('Array', p, 2, p[4])
759
760 if len(p) == 4:
761 p[0] = self.BuildProduction('Array', p, 1, p[3])
762
763 if len(p) == 3:
764 p[0] = ListFromConcat(self.BuildTrue('NULLABLE'), p[2])
765
766
767 # [71]
768 def p_TypeSuffixStartingWithArray(self, p):
769 """TypeSuffixStartingWithArray : '[' ']' TypeSuffix
770 | """
771 if len(p) > 1:
772 p[0] = self.BuildProduction('Array', p, 0, p[3])
773
774 # [72]
775 def p_Null(self, p):
776 """Null : '?'
777 |"""
778 if len(p) > 1:
779 p[0] = self.BuildTrue('NULLABLE')
780
781 # [73]
782 def p_ReturnType(self, p):
783 """ReturnType : Type
784 | VOID"""
785 if p[1] == 'void':
786 p[0] = self.BuildProduction('Type', p, 1)
787 p[0].AddChildren(self.BuildNamed('PrimitiveType', p, 1))
788 else:
789 p[0] = p[1]
790
791 # [74]
792 def p_ExtendedAttributeNoArgs(self, p):
793 """ExtendedAttributeNoArgs : identifier"""
794 p[0] = self.BuildNamed('ExtAttribute', p, 1)
795
796 # [75]
797 def p_ExtendedAttributeArgList(self, p):
798 """ExtendedAttributeArgList : identifier '(' ArgumentList ')'"""
799 arguments = self.BuildProduction('Arguments', p, 2, p[3])
800 p[0] = self.BuildNamed('ExtAttribute', p, 1, arguments)
801
802 # [76]
803 def p_ExtendedAttributeIdent(self, p):
804 """ExtendedAttributeIdent : identifier '=' identifier"""
805 value = self.BuildAttribute('VALUE', p[3])
806 p[0] = self.BuildNamed('ExtAttribute', p, 1, value)
807
808 # [77]
809 def p_ExtendedAttributeNamedArgList(self, p):
810 """ExtendedAttributeNamedArgList : identifier '=' identifier '(' ArgumentLis t ')'"""
811 args = self.BuildProduction('Arguments', p, 4, p[5])
812 value = self.BuildNamed('Call', p, 3, args)
813 p[0] = self.BuildNamed('ExtAttribute', p, 1, value)
814
815 #
816 # Parser Errors
817 #
818 # p_error is called whenever the parser can not find a pattern match for
819 # a set of items from the current state. The p_error function defined here
820 # is triggered logging an error, and parsing recovery happens as the
821 # p_<type>_error functions defined above are called. This allows the parser
822 # to continue so as to capture more than one error per file.
823 #
824 def p_error(self, t):
825 filename = self.lexer.lexobj.filename
826 if t:
827 lineno = t.lineno
828 pos = t.lexpos
829 prev = self.yaccobj.symstack[-1]
830 if type(prev) == lex.LexToken:
831 msg = "Unexpected %s after %s." % (
832 TokenTypeName(t), TokenTypeName(prev))
833 else:
834 msg = "Unexpected %s." % (t.value)
835 else:
836 lineno = self.last().lineno
837 pos = self.last().lexpos
838 msg = "Unexpected end of file after %s." % TokenTypeName(self.last())
839 self.yaccobj.restart()
840
841 # Attempt to remap the error to a friendlier form
842 if msg in ERROR_REMAP:
843 msg = ERROR_REMAP[msg]
844
845 self._last_error_msg = msg
846 self._last_error_lineno = lineno
847 self._last_error_pos = pos
848
849 def Warn(self, node, msg):
850 sys.stdout.write(node.GetLogLine(msg))
851 self.parse_warnings += 1
852
853 def last(self):
854 return self.lexer.last
855
856 def __init__(self, lexer, verbose=False, debug=False, mute_error=False):
857 self.lexer = lexer
858 self.yaccobj = yacc.yacc(module=self, tabmodule=None, debug=False,
859 optimize=0, write_tables=0)
860 self.parse_debug = debug
861 self.verbose = verbose
862 self.mute_error = mute_error
863 self._parse_errors = 0
864 self._parse_warnings = 0
865 self._last_error_msg = None
866 self._last_error_lineno = 0
867 self._last_error_pos = 0
868
869
870 #
871 # BuildProduction
872 #
873 # Production is the set of items sent to a grammar rule resulting in a new
874 # item being returned.
875 #
876 # p - Is the Yacc production object containing the stack of items
877 # index - Index into the production of the name for the item being produced.
878 # cls - The type of item being producted
879 # childlist - The children of the new item
880 def BuildProduction(self, cls, p, index, childlist=None):
881 try:
882 if not childlist:
883 childlist = []
884
885 filename = self.lexer.lexobj.filename
886 lineno = p.lineno(index)
887 pos = p.lexpos(index)
888 out = IDLNode(cls, filename, lineno, pos, childlist)
889 return out
890 except:
891 print 'Exception while parsing:'
892 for num, item in enumerate(p):
893 print ' [%d] %s' % (num, ExpandProduction(item))
894 if self.last():
895 print 'Last token: %s' % str(self.last())
896 raise
897
898 def BuildNamed(self, cls, p, index, childlist=None):
899 childlist = ListFromConcat(childlist)
900 childlist.append(self.BuildAttribute('NAME', p[index]))
901 return self.BuildProduction(cls, p, index, childlist)
902
903 def BuildComment(self, cls, p, index):
904 name = p[index]
905
906 # Remove comment markers
907 lines = []
908 if name[:2] == '//':
909 # For C++ style, remove any leading whitespace and the '//' marker from
910 # each line.
911 form = 'cc'
912 for line in name.split('\n'):
913 start = line.find('//')
914 lines.append(line[start+2:])
915 else:
916 # For C style, remove ending '*/''
917 form = 'c'
918 for line in name[:-2].split('\n'):
919 # Remove characters until start marker for this line '*' if found
920 # otherwise it should be blank.
921 offs = line.find('*')
922 if offs >= 0:
923 line = line[offs + 1:].rstrip()
924 else:
925 line = ''
926 lines.append(line)
927 name = '\n'.join(lines)
928 childlist = [self.BuildAttribute('NAME', name),
929 self.BuildAttribute('FORM', form)]
930 return self.BuildProduction(cls, p, index, childlist)
931
932 #
933 # BuildError
934 #
935 # Build and Errror node as part of the recovery process.
936 #
937 #
938 def BuildError(self, p, prod):
939 self._parse_errors += 1
940 name = self.BuildAttribute('NAME', self._last_error_msg)
941 line = self.BuildAttribute('LINE', self._last_error_lineno)
942 pos = self.BuildAttribute('POS', self._last_error_pos)
943 prod = self.BuildAttribute('PROD', prod)
944
945 node = self.BuildProduction('Error', p, 1,
946 ListFromConcat(name, line, pos, prod))
947 if not self.mute_error:
948 sys.stderr.write(node.GetLogLine('error: ' + self._last_error_msg))
949 return node
950
951 #
952 # BuildAttribute
953 #
954 # An ExtendedAttribute is a special production that results in a property
955 # which is applied to the adjacent item. Attributes have no children and
956 # instead represent key/value pairs.
957 #
958 def BuildAttribute(self, key, val):
959 return IDLAttribute(key, val)
960
961 def BuildFalse(self, key):
962 return IDLAttribute(key, Boolean(False))
963
964 def BuildTrue(self, key):
965 return IDLAttribute(key, Boolean(True))
966
967 def GetErrors(self):
968 return self._parse_errors + self.lexer._lex_errors
969
970 #
971 # ParseData
972 #
973 # Attempts to parse the current data loaded in the lexer.
974 #
975 def ParseText(self, filename, data):
976 self._parse_errors = 0
977 self._parse_warnings = 0
978 self._last_error_msg = None
979 self._last_error_lineno = 0
980 self._last_error_pos = 0
981
982 try:
983 self.lexer.Tokenize(data, filename)
984 nodes = self.yaccobj.parse(lexer=self.lexer)
985 name = self.BuildAttribute('NAME', filename)
986 return IDLNode('File', filename, 0, 0, nodes + [name])
987
988 except lex.LexError as lexError:
989 sys.stderr.write('Error in token: %s\n' % str(lexError))
990 return None
991
992
993
994 def ParseFile(parser, filename):
995 """Parse a file and return a File type of node."""
996 with open(filename) as fileobject:
997 try:
998 out = parser.ParseText(filename, fileobject.read())
999 out.SetProperty('DATETIME', time.ctime(os.path.getmtime(filename)))
1000 out.SetProperty('ERRORS', parser.GetErrors())
1001 return out
1002
1003 except Exception as e:
1004 sys.stderr.write('%s(%d) : Internal parsing error - %s.' % (
1005 filename, parser.last().lineno, str(e)))
1006
1007
1008 def main(argv):
1009 nodes = []
1010 parser = IDLParser(IDLLexer())
1011 errors = 0
1012 for filename in argv:
1013 filenode = ParseFile(parser, filename)
1014 errors += filenode.GetProperty('ERRORS')
1015 nodes.append(filenode)
1016
1017 ast = IDLNode('AST', '__AST__', 0, 0, nodes)
1018
1019 print '\n'.join(ast.Tree(accept_props=['PROD']))
1020 if errors:
1021 print '\nFound %d errors.\n' % errors
1022
1023
1024 return errors
1025
1026
1027 if __name__ == '__main__':
1028 sys.exit(main(sys.argv[1:]))
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698