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

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

Issue 15801003: IDL parser rewrite in Python (Closed) Base URL: svn://svn.chromium.org/blink/trunk
Patch Set: [WIP] Full parser Created 7 years, 6 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
« no previous file with comments | « Source/bindings/scripts/generate_bindings.py ('k') | Source/bindings/scripts/test-py-json.sh » ('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 # Copyright (C) 2013 Google Inc. All rights reserved.
dominicc (has gone to gerrit) 2013/06/26 04:20:53 Your comments need work. Fragments do not work we
Nils Barth (inactive) 2013/06/26 04:43:37 Will do.
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 """Blink IDL Intermediate Representation (IR) classes
dominicc (has gone to gerrit) 2013/06/26 04:20:53 End the first line of a docstring comment with a p
30
31 Also JSON export.
32 """
33
34 # Disable attribute hiding check (else JSONEncoder default raises an error)
35 # pylint: disable=E0202
36
37 import abc
38 import json
39 import os.path
40 import re
41
42
43 # Base classes
44
45
46 class BaseIdl():
47 """Abstract base class, used for JSON serialization."""
48 __metaclass__ = abc.ABCMeta
49
50 @abc.abstractmethod
51 def to_json(self):
dominicc (has gone to gerrit) 2013/06/26 04:20:53 Is there a better name for this? It doesn't return
Nils Barth (inactive) 2013/06/26 04:43:37 "json_serializable" is more precise; will change.
52 """Return a serializable form of the object.
dominicc (has gone to gerrit) 2013/06/26 04:20:53 Return -> Returns
53
54 Compatible with Perl IR and JSON import.
dominicc (has gone to gerrit) 2013/06/26 04:20:53 Wrap text; separate paragraphs with blank lines. Y
dominicc (has gone to gerrit) 2013/06/26 04:20:53 This documentation needs to be improved. "Compati
55 In practice a dictionary, whose keys specify the class."""
56 pass
dominicc (has gone to gerrit) 2013/06/26 04:20:53 It would be better to raise an exception here. If
Nils Barth (inactive) 2013/06/26 04:43:37 The ABC (Abstract Base Class) library handles this
57
58
59 class TypedIdlObject(BaseIdl):
dominicc (has gone to gerrit) 2013/06/26 04:20:53 If this class handles typedefs, maybe it should ha
60 """Auxiliary class for handling typedefs."""
dominicc (has gone to gerrit) 2013/06/26 04:20:53 Auxiliary is a meaningless word without more conte
61 data_type = None
62 extended_attributes = {}
dominicc (has gone to gerrit) 2013/06/26 04:20:53 Won't this hash object be shared by all instances
Nils Barth (inactive) 2013/06/26 04:43:37 (Just added this to silence lint errors; probably
63
64 def apply_typedefs(self, typedefs):
65 """Applies typedefs to object itself (e.g., return type of function).
66
67 Override if calling on parameters as well."""
68 new_extended_attributes = {}
69 # Convert string representation to and from a Type object
70 # to handle parsing
71 data_type_object = Type.from_string(self.data_type)
72 base_type = data_type_object.base_type
73 if base_type in typedefs:
74 replacement_type = typedefs[base_type]
75 data_type_object.base_type = replacement_type.data_type
76 new_extended_attributes = replacement_type.extended_attributes
77 self.data_type = str(data_type_object)
78 self.extended_attributes.update(new_extended_attributes)
79 if new_extended_attributes:
80 raise ValueError('Extended attributes in a typedef are untested!')
81
82
83 # IDL classes
84
85
86 class IdlDocument(BaseIdl):
87 def __init__(self, callback_functions=None, enumerations=None, file_name=Non e, interfaces=None, typedefs=None):
88 self.callback_functions = callback_functions or []
89 self.enumerations = enumerations or []
90 if file_name:
91 self.file_name = os.path.abspath(file_name)
92 self.interfaces = interfaces or []
93 if typedefs:
94 self.apply_typedefs(typedefs)
95
96 def apply_typedefs(self, typedefs):
97 for callback_function in self.callback_functions:
98 callback_function.apply_typedefs(typedefs)
99 for interface in self.interfaces:
100 interface.apply_typedefs(typedefs)
101
102 def to_json(self):
103 return {
104 'idlDocument::callbackFunctions': self.callback_functions,
105 'idlDocument::enumerations': self.enumerations,
106 'idlDocument::fileName': self.file_name,
107 'idlDocument::interfaces': self.interfaces,
108 }
109
110
111 class CallbackFunction(TypedIdlObject):
112 def __init__(self, name=None, data_type=None, parameters=None):
113 """parameters: List of DomParameters"""
114 self.data_type = data_type
115 self.name = name or "" # FIXME: is "" needed?
116 self.parameters = parameters or []
117
118 def apply_typedefs(self, typedefs):
119 TypedIdlObject.apply_typedefs(self, typedefs)
120 for parameter in self.parameters:
121 parameter.apply_typedefs(typedefs)
122 raise ValueError('Typedefs in CallbackFunctions are untested!')
123
124 def to_json(self):
125 return {
126 'callbackFunction::name': self.name,
127 'callbackFunction::type': self.data_type,
128 'callbackFunction::parameters': self.parameters,
129 }
130
131
132 class DomEnum(BaseIdl):
133 def __init__(self, name=None, values=None):
134 """name: enumeration identifier
135 values: enumeration values, list of unique strings
136 """
137 self.name = name
138 self.values = values or []
139
140 def to_json(self):
141 return {
142 'domEnum::name': self.name,
143 'domEnum::values': self.values,
144 }
145
146
147 class DomInterface(BaseIdl):
148 def __init__(self, name=None, parents=None, constants=None, functions=None, attributes=None, extended_attributes=None, constructors=None, custom_constructor s=None, is_exception=None, is_callback=None, is_partial=None):
149 """
150 attributes: list of DomAttributes
151 constants: list of DomConstants
152 constructors: list of DomFunctions
153 custom_constructors: list of DomFunctions
154 functions: list of DomFunctions
155 is_exception: used for exceptions
156 parents: list of strings
157 """
158 self.attributes = attributes or []
159 self.constants = constants or []
160 self.constructors = constructors or []
161 self.custom_constructors = custom_constructors or []
162 self.extended_attributes = extended_attributes or {}
163 self.functions = functions or []
164 self.is_exception = is_exception
165 self.is_callback = is_callback
166 self.is_partial = is_partial
167 self.name = name
168 self.parents = parents or []
169
170 def apply_typedefs(self, typedefs):
171 for constant in self.constants:
172 constant.apply_typedefs(typedefs)
173 for attribute in self.attributes:
174 attribute.apply_typedefs(typedefs)
175 for function in self.functions:
176 function.apply_typedefs(typedefs)
177 for constructor in self.constructors:
178 constructor.apply_typedefs(typedefs)
179 for custom_constructor in self.custom_constructors:
180 custom_constructor.apply_typedefs(typedefs)
181
182 def to_json(self):
183 return {
184 'domInterface::name': self.name,
185 'domInterface::parents': self.parents,
186 'domInterface::constants': self.constants,
187 'domInterface::functions': self.functions,
188 'domInterface::attributes': self.attributes,
189 'domInterface::extendedAttributes': none_to_value_is_missing(self.ex tended_attributes),
190 'domInterface::constructors': self.constructors,
191 'domInterface::customConstructors': self.custom_constructors,
192 'domInterface::isException': boolean_to_perl(self.is_exception),
193 'domInterface::isCallback': boolean_to_perl(self.is_callback),
194 'domInterface::isPartial': self.is_partial,
195 }
196
197
198 class DomAttribute(TypedIdlObject):
199 def __init__(self, data_type=None, name=None, is_nullable=None, is_static=No ne, is_read_only=None, getter_exceptions=None, setter_exceptions=None, extended_ attributes=None):
200 """data_type: Attribute type (including namespace), string or UnionType
201 is_nullable: (T?)
202 getter_exceptions: Possibly raised exceptions
203 setter_exceptions: Possibly raised exceptions
204 """
205 self.data_type = data_type
206 self.extended_attributes = extended_attributes or {}
207 self.getter_exceptions = getter_exceptions or []
208 self.is_nullable = is_nullable
209 self.is_static = is_static
210 self.is_read_only = is_read_only
211 self.name = name
212 self.setter_exceptions = setter_exceptions or []
213
214 def to_json(self):
215 return {
216 'domAttribute::extendedAttributes': none_to_value_is_missing(self.ex tended_attributes),
217 'domAttribute::getterExceptions': self.getter_exceptions,
218 'domAttribute::isNullable': self.is_nullable,
219 'domAttribute::isReadOnly': self.is_read_only,
220 'domAttribute::isStatic': self.is_static,
221 'domAttribute::name': self.name,
222 'domAttribute::setterExceptions': self.setter_exceptions,
223 'domAttribute::type': self.data_type,
224 }
225
226
227 class DomConstant(TypedIdlObject):
228 def __init__(self, name=None, data_type=None, value=None, extended_attribute s=None):
229 self.data_type = data_type
230 self.extended_attributes = extended_attributes or {}
231 self.name = name
232 self.value = value
233
234 def to_json(self):
235 return {
236 'domConstant::extendedAttributes': none_to_value_is_missing(self.ext ended_attributes),
237 'domConstant::name': self.name,
238 'domConstant::type': self.data_type,
239 'domConstant::value': self.value,
240 }
241
242
243 class DomFunction(TypedIdlObject):
244 def __init__(self, is_static=None, name=None, data_type=None, extended_attri butes=None, specials=None, parameters=None, overloaded_index=None):
245 """parameters: List of DomParameters"""
246 self.is_static = is_static
247 self.name = name or ""
248 self.data_type = data_type
249 self.extended_attributes = extended_attributes or {}
250 self.specials = specials or []
251 self.parameters = parameters or []
252 self.overloaded_index = overloaded_index
253
254 def apply_typedefs(self, typedefs):
255 TypedIdlObject.apply_typedefs(self, typedefs)
256 for parameter in self.parameters:
257 parameter.apply_typedefs(typedefs)
258
259 def to_json(self):
260 return {
261 'domFunction::extendedAttributes': none_to_value_is_missing(self.ext ended_attributes),
262 'domFunction::isStatic': boolean_to_perl(self.is_static),
263 'domFunction::name': self.name,
264 'domFunction::overloadedIndex': self.overloaded_index,
265 'domFunction::parameters': self.parameters,
266 'domFunction::specials': self.specials,
267 'domFunction::type': self.data_type,
268 }
269
270
271 class DomParameter(TypedIdlObject):
272 def __init__(self, name=None, data_type=None, extended_attributes=None, is_o ptional=False, is_nullable=None, is_variadic=False):
273 """Used to represent a map of 'variable name' <-> 'variable type'
274
275 data_type: string or UnionType
276 is_optional: (optional T)
277 is_nullable: (T?)
278 is_variadic: (long... numbers)
279 """
280 self.data_type = data_type
281 self.extended_attributes = extended_attributes or {}
282 # FIXME: boolean values are inconsistent (due to Perl),
283 # being sometimes True, False, or None (Perl: 1, 0, undef)
284 # Should all default to False, and be either True or False
285 if is_optional is None:
286 is_optional = False
287 if is_variadic is None:
288 is_variadic = False
289 self.is_nullable = is_nullable
290 # FIXME: can these be in to_json instead?
291 self.is_optional = boolean_to_perl(is_optional)
292 self.is_variadic = boolean_to_perl(is_variadic)
293 self.name = name
294
295 def to_json(self):
296 return {
297 'domParameter::extendedAttributes': none_to_value_is_missing(self.ex tended_attributes),
298 'domParameter::isNullable': self.is_nullable,
299 'domParameter::isOptional': self.is_optional,
300 'domParameter::isVariadic': self.is_variadic,
301 'domParameter::name': self.name,
302 'domParameter::type': self.data_type,
303 }
304
305 # Type classes
306
307
308 class Type():
309 # FIXME: replace Type strings with these objects,
310 # so don't need to parse everywhere types are used.
311 # Types are stored internally as strings, not objects,
312 # e.g., as 'sequence<Foo>' or 'Foo[]',
313 # hence need to parse the string whenever a type is used.
314 # FIXME: incorporate Nullable, Variadic, etc.
315 # FIXME: properly should nest types
316 # Formally types are nested, e.g., short?[] vs. short[]?,
317 # but in practice these complex types aren't used and can treat
318 # as orthogonal properties.
319 def __init__(self, base_type=None, is_array=False, is_sequence=False):
320 if is_array and is_sequence:
321 raise ValueError('Array of Sequences not allowed.')
322 self.base_type = base_type
323 self.is_array = is_array
324 self.is_sequence = is_sequence
325
326 def __str__(self):
327 type_string = self.base_type
328 if self.is_array:
329 type_string += '[]'
330 if self.is_sequence:
331 type_string = 'sequence<%s>' % type_string
332 return type_string
333
334 @classmethod
335 def from_string(cls, type_string):
336 is_array = False
337 if type_string.endswith('[]'):
338 is_array = True
339 type_string = type_string[:-2]
340
341 is_sequence = False
342 sequence_re = r'^sequence<([^>]*)>$'
343 sequence_match = re.match(sequence_re, type_string)
344 if sequence_match:
345 is_sequence = True
346 type_string = sequence_match.group(1)
347 base_type = type_string
348 return cls(base_type=base_type, is_array=is_array, is_sequence=is_sequen ce)
349
350
351 class Typedef():
352 # Not exposed in bindings, internal to IDL parsing
353 def __init__(self, extended_attributes=None, data_type=None):
354 self.extended_attributes = extended_attributes or {}
355 self.data_type = data_type
356
357
358 class UnionType(BaseIdl):
359 def __init__(self, union_member_types=None):
360 """union_member_types: list of string or UnionType"""
361 self.union_member_types = union_member_types or []
362
363 def to_json(self):
364 return {
365 'UnionType::unionMemberTypes': self.union_member_types,
366 }
367
368
369 # Perl JSON compatiblity functions
370 # FIXME: remove when Perl removed
371
372 def none_to_value_is_missing(extended_attributes):
373 # Perl IDL Parser uses 'VALUE_IS_MISSING' for null values in
374 # extended attributes, so add this as a filter when exporting to JSON.
375 new_extended_attributes = {}
376 for key, value in extended_attributes.iteritems():
377 if value is None:
378 new_extended_attributes[key] = 'VALUE_IS_MISSING'
379 else:
380 new_extended_attributes[key] = value
381 return new_extended_attributes
382
383
384 def boolean_to_perl(value):
385 # Perl stores booleans as 1, 0, or undefined (JSON null);
386 # convert to this format
387 if value is None:
388 return None
389 return int(value)
390
391 # JSON export
392 # FIXME: remove when Perl removed
393 # (so no longer using JSON as intermediate format)
394
395
396 class IdlEncoder(json.JSONEncoder):
397 def default(self, obj):
398 if isinstance(obj, BaseIdl):
399 return obj.to_json()
400 return json.JSONEncoder.default(self, obj)
401
402
403 def ir_to_json(ir, debug=False):
404 if debug:
405 # More legible
406 return json.dumps(ir, cls=IdlEncoder, sort_keys=True, indent=4)
407 return json.dumps(ir, cls=IdlEncoder, sort_keys=True, separators=(',', ':'))
OLDNEW
« no previous file with comments | « Source/bindings/scripts/generate_bindings.py ('k') | Source/bindings/scripts/test-py-json.sh » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698