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

Side by Side Diff: Source/bindings/scripts/blink_idl_parser.py

Issue 15801003: IDL parser rewrite in Python (Closed) Base URL: svn://svn.chromium.org/blink/trunk
Patch Set: Revised. Created 7 years, 5 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 # Copyright (C) 2013 Google Inc. All rights reserved.
2 #
3 # Redistribution and use in source and binary forms, with or without
4 # modification, are permitted provided that the following conditions are
5 # met:
6 #
7 # * Redistributions of source code must retain the above copyright
8 # notice, this list of conditions and the following disclaimer.
9 # * Redistributions in binary form must reproduce the above
10 # copyright notice, this list of conditions and the following disclaimer
11 # in the documentation and/or other materials provided with the
12 # distribution.
13 # * Neither the name of Google Inc. nor the names of its
14 # contributors may be used to endorse or promote products derived from
15 # this software without specific prior written permission.
16 #
17 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18 # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19 # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
20 # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
21 # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
22 # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
23 # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24 # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25 # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28
29 """Parser for Blink IDL.
30
31 The parser uses the PLY (Python Lex-Yacc) library to build a set of parsing
32 rules which understand the Blink dialect of Web IDL.
33 It derives from a standard Web IDL parser, overriding rules where Blink IDL
34 differs syntactically or semantically from the base parser, or where the base
35 parser diverges from the Web IDL standard.
36
37 Web IDL:
38 http://www.w3.org/TR/WebIDL/
39 Web IDL Grammar:
40 http://www.w3.org/TR/WebIDL/#idl-grammar
41 PLY:
42 http://www.dabeaz.com/ply/
43 """
44
45 # Disable check for line length and Member as Function due to how grammar rules
46 # are defined with PLY
47 #
48 # pylint: disable=R0201
49 # pylint: disable=C0301
50 #
51 # Disable attribute validation, as lint can't import parent class to check
52 # pylint: disable=E1101
53
54 import os.path
55 import sys
56
57 # PLY is in Chromium src/third_party/ply
58 module_path, module_name = os.path.split(__file__)
59 third_party = os.path.join(module_path, os.pardir, os.pardir, os.pardir, os.pard ir)
60 sys.path.append(third_party)
61 from ply import yacc
62
63 # Base parser is in Chromium src/tools/idl_parser
64 tools_dir = os.path.join(module_path, os.pardir, os.pardir, os.pardir, os.pardir , os.pardir, 'tools')
65 sys.path.append(tools_dir)
66 from idl_parser.idl_parser import IDLParser, ListFromConcat
67 from idl_parser.idl_parser import ParseFile as parse_file
68
69 from blink_idl_lexer import BlinkIDLLexer
70
71 # We ignore comments, but base parser preserves them
72 # FIXME: Upstream: comments should be removed in base parser
73 REMOVED_RULES = ['Comments', # [0.1]
74 'CommentsRest', # [0.2]
75 ]
76
77
78 class BlinkIDLParser(IDLParser):
79 # Below are grammar rules used by yacc, given by functions named p_<RULE>.
80 # * The docstring is the production rule in BNF (grammar).
haraken 2013/07/22 01:50:23 Help me understand: The docstring is actually used
Nils Barth (inactive) 2013/07/22 06:32:01 Exactly (see above, in lexer). I’ve written a more
81 # * The body is the yacc action (semantics).
82 # Reference:
83 # http://www.dabeaz.com/ply/ply.html#ply_nn23
84 #
85 # Review of yacc:
86 # Yacc parses a token stream, internally producing a Concrete Syntax Tree
87 # (CST), where each node corresponds to a production rule in the grammar.
88 # At each node, it runs an action, which is usually "produce a node in the
89 # Abstract Syntax Tree (AST)" or "ignore this node" (for nodes in the CST
90 # that aren't included in the AST, since only needed for parsing).
91 #
92 # The rules use pseudo-variables; in PLY syntax:
93 # p[0] is the left side: assign return value to p[0] instead of returning,
94 # p[1] ... p[n] are the right side: the values can be accessed, and they
95 # can be modified.
96 # (In yacc these are $$ and $1 ... $n.)
97 #
98 # The rules can look cryptic at first, but there are a few standard
99 # transforms from the CST to AST. With these in mind, the actions should
100 # be reasonably legible.
101 #
102 # * Ignore production
103 # Discard this branch. Primarily used when one alternative is empty.
104 #
105 # Sample code:
106 # if len(p) > 1:
107 # p[0] = ...
108 # # Note no assignment if len(p) == 1
109 #
110 # * Eliminate singleton production
111 # Discard this node in the CST, pass the next level down up the tree.
112 # Used to ignore productions only necessary for parsing, but not needed
113 # in the AST.
114 #
115 # Sample code:
116 # p[0] = p[1]
117 #
118 # * Build node
119 # The key type of rule. In this parser, produces object of class IDLNode.
120 # There are several helper functions:
121 # * BuildProduction: actually builds an IDLNode, based on a production.
122 # * BuildAttribute: builds an IDLAttribute, which is a temporary
123 # object to hold a name-value pair, which is then
124 # set as a Property of the IDLNode when the IDLNode
125 # is built.
126 # * BuildNamedProduction: Same as BuildProduction, and sets the 'NAME'
haraken 2013/07/22 01:50:23 BuildNamedProduction => BuildNamed
Nils Barth (inactive) 2013/07/22 06:32:01 (>.<) Done.
127 # property.
128 # * BuildTrue: BuildAttribute with value True, for flags.
129 # See base idl_parser.py for definitions and more examples of use.
130 #
131 # Sample code:
132 # # Build node of type NodeType, with value p[1], and children.
133 # p[0] = self.BuildProduction('NodeType', p, 1, children)
134 #
135 # # Build named node of type NodeType, with name and value p[1].
136 # # (children optional)
137 # p[0] = self.BuildNamed('NodeType', p, 1)
138 #
139 # # Make a list
140 # # Used if one node has several children.
141 # children = ListFromConcat(p[2], p[3])
142 # p[0] = self.BuildProduction('NodeType', p, 1, children)
143 #
144 # # Also used to collapse the right-associative tree
145 # # produced by parsing a list back into a single list.
146 # """Foos : Foo Foos
147 # |"""
148 # if len(p) > 1:
149 # p[0] = ListFromConcat(p[1], p[2])
150 #
151 # # Add children.
152 # # Primarily used to add attributes, produced via BuildTrue.
153 # # p_StaticAttribute
154 # """StaticAttribute : STATIC Attribute"""
155 # p[2].AddChildren(self.BuildTrue('STATIC'))
156 # p[0] = p[2]
157 #
158 # Numbering scheme for the rules is:
159 # [1] for Web IDL spec (or additions in base parser)
160 # These should all be upstreamed to the base parser.
161 # [b1] for Blink IDL changes (overrides Web IDL)
162 # [b1.1] for Blink IDL additions, auxiliary rules for [b1]
163 # Numbers are as per Candidate Recommendation 19 April 2012:
164 # http://www.w3.org/TR/2012/CR-WebIDL-20120419/
165
166 # [0] Override grammar, since we strip comments
167 # (not in Web IDL)
168 # FIXME: Upstream
169 def p_Top(self, p):
170 """Top : Definitions"""
171 p[0] = p[1]
172
173 # [3] Override action, since we distinguish callbacks
174 # FIXME: Upstream
175 def p_CallbackOrInterface(self, p):
176 """CallbackOrInterface : CALLBACK CallbackRestOrInterface
177 | Interface"""
178 if len(p) > 2:
179 p[2].AddChildren(self.BuildTrue('CALLBACK'))
180 p[0] = p[2]
181 else:
182 p[0] = p[1]
183
184 # [b27] Add strings, more 'Literal' productions
185 # 'Literal's needed because integers and strings are both internally strings
186 def p_ConstValue(self, p):
187 """ConstValue : BooleanLiteral
188 | FloatLiteral
189 | IntegerLiteral
190 | StringLiteral
191 | null"""
192 # Standard is (no 'string', fewer 'Literal's):
193 # ConstValue : BooleanLiteral
194 # | FloatLiteral
195 # | integer
196 # | NULL
197 p[0] = p[1]
198
199 # [b27.1]
200 def p_IntegerLiteral(self, p):
201 """IntegerLiteral : integer"""
202 p[0] = ListFromConcat(self.BuildAttribute('TYPE', 'integer'),
203 self.BuildAttribute('NAME', p[1]))
204
205 # [b27.2]
206 def p_StringLiteral(self, p):
207 """StringLiteral : string"""
208 p[0] = ListFromConcat(self.BuildAttribute('TYPE', 'DOMString'),
209 self.BuildAttribute('NAME', p[1]))
210
211 # [b30] Add StaticAttribute
212 def p_AttributeOrOperation(self, p):
213 """AttributeOrOperation : STRINGIFIER StringifierAttributeOrOperation
214 | Attribute
215 | StaticAttribute
216 | Operation"""
217 # Standard is (no StaticAttribute):
218 # AttributeOrOperation : STRINGIFIER StringifierAttributeOrOperation
219 # | Attribute
220 # | Operation
221 if len(p) > 2:
222 p[0] = p[2]
haraken 2013/07/22 01:50:23 As mentioned in the previous comment, it looks inc
Nils Barth (inactive) 2013/07/22 06:32:01 Yup, done!
223 else:
224 p[0] = p[1]
225
226 # [b30.1]
227 def p_StaticAttribute(self, p):
228 """StaticAttribute : STATIC Attribute"""
229 p[2].AddChildren(self.BuildTrue('STATIC'))
230 p[0] = p[2]
231
232 # [b47]
233 def p_ExceptionMember(self, p):
234 """ExceptionMember : Const
235 | ExceptionField
236 | Attribute
237 | ExceptionOperation"""
238 # Standard is (no Attribute, no ExceptionOperation):
239 # ExceptionMember : Const
240 # | ExceptionField
241 # FIXME: In DOMException.idl, Attributes should be changed to
242 # ExceptionFields, and Attribute removed from this rule.
243 p[0] = p[1]
244
245 # [b47.1]
246 def p_ExceptionOperation(self, p):
247 """ExceptionOperation : Type identifier '(' ')' ';'"""
248 # Needed to handle one case in DOMException.idl:
249 # // Override in a Mozilla compatible format
250 # [NotEnumerable] DOMString toString();
251 # Limited form of Operation to prevent others from being added.
252 p[0] = self.BuildNamed('ExceptionOperation', p, 2, p[1])
253
254 # Extended attributes
255 # [b49] Override base parser: remove comment field, since comments stripped
256 # FIXME: Upstream
257 def p_ExtendedAttributeList(self, p):
258 """ExtendedAttributeList : '[' ExtendedAttribute ExtendedAttributes ']'
259 | '[' ']'
260 | """
261 if len(p) > 3:
262 items = ListFromConcat(p[2], p[3])
263 p[0] = self.BuildProduction('ExtAttributes', p, 1, items)
264
265 # [b50] Allow optional trailing comma
haraken 2013/07/22 01:50:23 Shall we add a FIXME that says this is currently a
Nils Barth (inactive) 2013/07/22 06:32:01 Done. Not sure if it will be fixed, since Cameron
266 def p_ExtendedAttributes(self, p):
267 """ExtendedAttributes : ',' ExtendedAttribute ExtendedAttributes
268 | ','
269 |"""
270 if len(p) > 2:
haraken 2013/07/22 01:50:23 Nit: len(p) > 3
Nils Barth (inactive) 2013/07/22 06:32:01 Technically > 2 is ok (since length is 1, 2, or 4)
271 p[0] = ListFromConcat(p[2], p[3])
272
273 # [b51] Add ExtendedAttributeIdentAndOrIdent
274 def p_ExtendedAttribute(self, p):
275 """ExtendedAttribute : ExtendedAttributeNoArgs
276 | ExtendedAttributeArgList
277 | ExtendedAttributeIdent
278 | ExtendedAttributeIdentAndOrIdent
279 | ExtendedAttributeNamedArgList"""
280 p[0] = p[1]
281
282 # [59]
283 # FIXME: Upstream UnionType
284 def p_UnionType(self, p):
285 """UnionType : '(' UnionMemberType OR UnionMemberType UnionMemberTypes ' )'"""
286 members = ListFromConcat(p[2], p[4], p[5])
287 p[0] = self.BuildProduction('UnionType', p, 1, members)
288
289 # [60]
290 def p_UnionMemberType(self, p):
291 """UnionMemberType : NonAnyType
292 | UnionType TypeSuffix
293 | ANY '[' ']' TypeSuffix"""
294 if len(p) == 2:
295 p[0] = p[1]
296 elif len(p) == 3:
297 p[0] = ListFromConcat(p[1], p[2])
298 else:
299 p[0] = ListFromConcat(self.BuildProduction('Any', p, 1), p[4])
300
301 # [61]
302 def p_UnionMemberTypes(self, p):
303 """UnionMemberTypes : OR UnionMemberType UnionMemberTypes
304 |"""
305 if len(p) > 1:
haraken 2013/07/22 01:50:23 Nit: len(p) > 3
Nils Barth (inactive) 2013/07/22 06:32:01 Ditto, done.
306 p[0] = ListFromConcat(p[2], p[3])
307
308 # [70] Override base parser to remove non-standard sized array
309 # FIXME: Upstream
310 def p_TypeSuffix(self, p):
311 """TypeSuffix : '[' ']' TypeSuffix
312 | '?' TypeSuffixStartingWithArray
313 |"""
314 if len(p) == 4:
315 p[0] = self.BuildProduction('Array', p, 1, p[3])
316 elif len(p) == 3:
317 p[0] = ListFromConcat(self.BuildTrue('NULLABLE'), p[2])
318
319 # [b76.1]
320 def p_ExtendedAttributeIdentAndOrIdent(self, p):
321 """ExtendedAttributeIdentAndOrIdent : identifier '=' identifier '&' iden tifier
haraken 2013/07/22 01:50:23 Help me understand: How is [X=A&B&C] parsed? I tho
Nils Barth (inactive) 2013/07/22 06:32:01 Currently we only ever have 2 flags: A|B or A&B, s
322 | identifier '=' identifier '|' iden tifier"""
323 value = self.BuildAttribute('VALUE', p[3] + p[4] + p[5])
324 p[0] = self.BuildNamed('ExtAttribute', p, 1, value)
325
326 def __dir__(self):
327 # Remove REMOVED_RULES from listing so yacc doesn't parse them
328 # FIXME: Upstream
329 keys = set(self.__dict__.keys() + dir(self.__class__))
330 for rule in REMOVED_RULES:
331 keys.remove('p_' + rule)
332 return list(keys)
333
334 def __init__(self, lexer=None, verbose=False, debug=False, mute_error=False) :
335 lexer = lexer or BlinkIDLLexer()
336 self.lexer = lexer
337 self.tokens = lexer.KnownTokens()
338 # Using SLR (instead of LALR) generates the table faster,
339 # but produces the same output. This is ok b/c Web IDL (and Blink IDL)
340 # is an LL(1) grammar, so SLR can parse it.
341 self.yaccobj = yacc.yacc(module=self, debug=debug, method='SLR')
342 self.parse_debug = debug
343 self.verbose = verbose
344 self.mute_error = mute_error
345 self._parse_errors = 0
346 self._parse_warnings = 0
347 self._last_error_msg = None
348 self._last_error_lineno = 0
349 self._last_error_pos = 0
350
351
352 # If run by itself, attempt to build the parser
353 if __name__ == '__main__':
354 parser = BlinkIDLParser()
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698