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

Unified 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 side-by-side diff with in-line comments
Download patch
« 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 »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: Source/bindings/scripts/ir.py
diff --git a/Source/bindings/scripts/ir.py b/Source/bindings/scripts/ir.py
new file mode 100644
index 0000000000000000000000000000000000000000..0cf0fcebf36601cdd7cc42f4634acc878fa36737
--- /dev/null
+++ b/Source/bindings/scripts/ir.py
@@ -0,0 +1,407 @@
+# 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.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above
+# copyright notice, this list of conditions and the following disclaimer
+# in the documentation and/or other materials provided with the
+# distribution.
+# * Neither the name of Google Inc. nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+"""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
+
+Also JSON export.
+"""
+
+# Disable attribute hiding check (else JSONEncoder default raises an error)
+# pylint: disable=E0202
+
+import abc
+import json
+import os.path
+import re
+
+
+# Base classes
+
+
+class BaseIdl():
+ """Abstract base class, used for JSON serialization."""
+ __metaclass__ = abc.ABCMeta
+
+ @abc.abstractmethod
+ 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.
+ """Return a serializable form of the object.
dominicc (has gone to gerrit) 2013/06/26 04:20:53 Return -> Returns
+
+ 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
+ In practice a dictionary, whose keys specify the class."""
+ 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
+
+
+class TypedIdlObject(BaseIdl):
dominicc (has gone to gerrit) 2013/06/26 04:20:53 If this class handles typedefs, maybe it should ha
+ """Auxiliary class for handling typedefs."""
dominicc (has gone to gerrit) 2013/06/26 04:20:53 Auxiliary is a meaningless word without more conte
+ data_type = None
+ 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
+
+ def apply_typedefs(self, typedefs):
+ """Applies typedefs to object itself (e.g., return type of function).
+
+ Override if calling on parameters as well."""
+ new_extended_attributes = {}
+ # Convert string representation to and from a Type object
+ # to handle parsing
+ data_type_object = Type.from_string(self.data_type)
+ base_type = data_type_object.base_type
+ if base_type in typedefs:
+ replacement_type = typedefs[base_type]
+ data_type_object.base_type = replacement_type.data_type
+ new_extended_attributes = replacement_type.extended_attributes
+ self.data_type = str(data_type_object)
+ self.extended_attributes.update(new_extended_attributes)
+ if new_extended_attributes:
+ raise ValueError('Extended attributes in a typedef are untested!')
+
+
+# IDL classes
+
+
+class IdlDocument(BaseIdl):
+ def __init__(self, callback_functions=None, enumerations=None, file_name=None, interfaces=None, typedefs=None):
+ self.callback_functions = callback_functions or []
+ self.enumerations = enumerations or []
+ if file_name:
+ self.file_name = os.path.abspath(file_name)
+ self.interfaces = interfaces or []
+ if typedefs:
+ self.apply_typedefs(typedefs)
+
+ def apply_typedefs(self, typedefs):
+ for callback_function in self.callback_functions:
+ callback_function.apply_typedefs(typedefs)
+ for interface in self.interfaces:
+ interface.apply_typedefs(typedefs)
+
+ def to_json(self):
+ return {
+ 'idlDocument::callbackFunctions': self.callback_functions,
+ 'idlDocument::enumerations': self.enumerations,
+ 'idlDocument::fileName': self.file_name,
+ 'idlDocument::interfaces': self.interfaces,
+ }
+
+
+class CallbackFunction(TypedIdlObject):
+ def __init__(self, name=None, data_type=None, parameters=None):
+ """parameters: List of DomParameters"""
+ self.data_type = data_type
+ self.name = name or "" # FIXME: is "" needed?
+ self.parameters = parameters or []
+
+ def apply_typedefs(self, typedefs):
+ TypedIdlObject.apply_typedefs(self, typedefs)
+ for parameter in self.parameters:
+ parameter.apply_typedefs(typedefs)
+ raise ValueError('Typedefs in CallbackFunctions are untested!')
+
+ def to_json(self):
+ return {
+ 'callbackFunction::name': self.name,
+ 'callbackFunction::type': self.data_type,
+ 'callbackFunction::parameters': self.parameters,
+ }
+
+
+class DomEnum(BaseIdl):
+ def __init__(self, name=None, values=None):
+ """name: enumeration identifier
+ values: enumeration values, list of unique strings
+ """
+ self.name = name
+ self.values = values or []
+
+ def to_json(self):
+ return {
+ 'domEnum::name': self.name,
+ 'domEnum::values': self.values,
+ }
+
+
+class DomInterface(BaseIdl):
+ def __init__(self, name=None, parents=None, constants=None, functions=None, attributes=None, extended_attributes=None, constructors=None, custom_constructors=None, is_exception=None, is_callback=None, is_partial=None):
+ """
+ attributes: list of DomAttributes
+ constants: list of DomConstants
+ constructors: list of DomFunctions
+ custom_constructors: list of DomFunctions
+ functions: list of DomFunctions
+ is_exception: used for exceptions
+ parents: list of strings
+ """
+ self.attributes = attributes or []
+ self.constants = constants or []
+ self.constructors = constructors or []
+ self.custom_constructors = custom_constructors or []
+ self.extended_attributes = extended_attributes or {}
+ self.functions = functions or []
+ self.is_exception = is_exception
+ self.is_callback = is_callback
+ self.is_partial = is_partial
+ self.name = name
+ self.parents = parents or []
+
+ def apply_typedefs(self, typedefs):
+ for constant in self.constants:
+ constant.apply_typedefs(typedefs)
+ for attribute in self.attributes:
+ attribute.apply_typedefs(typedefs)
+ for function in self.functions:
+ function.apply_typedefs(typedefs)
+ for constructor in self.constructors:
+ constructor.apply_typedefs(typedefs)
+ for custom_constructor in self.custom_constructors:
+ custom_constructor.apply_typedefs(typedefs)
+
+ def to_json(self):
+ return {
+ 'domInterface::name': self.name,
+ 'domInterface::parents': self.parents,
+ 'domInterface::constants': self.constants,
+ 'domInterface::functions': self.functions,
+ 'domInterface::attributes': self.attributes,
+ 'domInterface::extendedAttributes': none_to_value_is_missing(self.extended_attributes),
+ 'domInterface::constructors': self.constructors,
+ 'domInterface::customConstructors': self.custom_constructors,
+ 'domInterface::isException': boolean_to_perl(self.is_exception),
+ 'domInterface::isCallback': boolean_to_perl(self.is_callback),
+ 'domInterface::isPartial': self.is_partial,
+ }
+
+
+class DomAttribute(TypedIdlObject):
+ def __init__(self, data_type=None, name=None, is_nullable=None, is_static=None, is_read_only=None, getter_exceptions=None, setter_exceptions=None, extended_attributes=None):
+ """data_type: Attribute type (including namespace), string or UnionType
+ is_nullable: (T?)
+ getter_exceptions: Possibly raised exceptions
+ setter_exceptions: Possibly raised exceptions
+ """
+ self.data_type = data_type
+ self.extended_attributes = extended_attributes or {}
+ self.getter_exceptions = getter_exceptions or []
+ self.is_nullable = is_nullable
+ self.is_static = is_static
+ self.is_read_only = is_read_only
+ self.name = name
+ self.setter_exceptions = setter_exceptions or []
+
+ def to_json(self):
+ return {
+ 'domAttribute::extendedAttributes': none_to_value_is_missing(self.extended_attributes),
+ 'domAttribute::getterExceptions': self.getter_exceptions,
+ 'domAttribute::isNullable': self.is_nullable,
+ 'domAttribute::isReadOnly': self.is_read_only,
+ 'domAttribute::isStatic': self.is_static,
+ 'domAttribute::name': self.name,
+ 'domAttribute::setterExceptions': self.setter_exceptions,
+ 'domAttribute::type': self.data_type,
+ }
+
+
+class DomConstant(TypedIdlObject):
+ def __init__(self, name=None, data_type=None, value=None, extended_attributes=None):
+ self.data_type = data_type
+ self.extended_attributes = extended_attributes or {}
+ self.name = name
+ self.value = value
+
+ def to_json(self):
+ return {
+ 'domConstant::extendedAttributes': none_to_value_is_missing(self.extended_attributes),
+ 'domConstant::name': self.name,
+ 'domConstant::type': self.data_type,
+ 'domConstant::value': self.value,
+ }
+
+
+class DomFunction(TypedIdlObject):
+ def __init__(self, is_static=None, name=None, data_type=None, extended_attributes=None, specials=None, parameters=None, overloaded_index=None):
+ """parameters: List of DomParameters"""
+ self.is_static = is_static
+ self.name = name or ""
+ self.data_type = data_type
+ self.extended_attributes = extended_attributes or {}
+ self.specials = specials or []
+ self.parameters = parameters or []
+ self.overloaded_index = overloaded_index
+
+ def apply_typedefs(self, typedefs):
+ TypedIdlObject.apply_typedefs(self, typedefs)
+ for parameter in self.parameters:
+ parameter.apply_typedefs(typedefs)
+
+ def to_json(self):
+ return {
+ 'domFunction::extendedAttributes': none_to_value_is_missing(self.extended_attributes),
+ 'domFunction::isStatic': boolean_to_perl(self.is_static),
+ 'domFunction::name': self.name,
+ 'domFunction::overloadedIndex': self.overloaded_index,
+ 'domFunction::parameters': self.parameters,
+ 'domFunction::specials': self.specials,
+ 'domFunction::type': self.data_type,
+ }
+
+
+class DomParameter(TypedIdlObject):
+ def __init__(self, name=None, data_type=None, extended_attributes=None, is_optional=False, is_nullable=None, is_variadic=False):
+ """Used to represent a map of 'variable name' <-> 'variable type'
+
+ data_type: string or UnionType
+ is_optional: (optional T)
+ is_nullable: (T?)
+ is_variadic: (long... numbers)
+ """
+ self.data_type = data_type
+ self.extended_attributes = extended_attributes or {}
+ # FIXME: boolean values are inconsistent (due to Perl),
+ # being sometimes True, False, or None (Perl: 1, 0, undef)
+ # Should all default to False, and be either True or False
+ if is_optional is None:
+ is_optional = False
+ if is_variadic is None:
+ is_variadic = False
+ self.is_nullable = is_nullable
+ # FIXME: can these be in to_json instead?
+ self.is_optional = boolean_to_perl(is_optional)
+ self.is_variadic = boolean_to_perl(is_variadic)
+ self.name = name
+
+ def to_json(self):
+ return {
+ 'domParameter::extendedAttributes': none_to_value_is_missing(self.extended_attributes),
+ 'domParameter::isNullable': self.is_nullable,
+ 'domParameter::isOptional': self.is_optional,
+ 'domParameter::isVariadic': self.is_variadic,
+ 'domParameter::name': self.name,
+ 'domParameter::type': self.data_type,
+ }
+
+# Type classes
+
+
+class Type():
+ # FIXME: replace Type strings with these objects,
+ # so don't need to parse everywhere types are used.
+ # Types are stored internally as strings, not objects,
+ # e.g., as 'sequence<Foo>' or 'Foo[]',
+ # hence need to parse the string whenever a type is used.
+ # FIXME: incorporate Nullable, Variadic, etc.
+ # FIXME: properly should nest types
+ # Formally types are nested, e.g., short?[] vs. short[]?,
+ # but in practice these complex types aren't used and can treat
+ # as orthogonal properties.
+ def __init__(self, base_type=None, is_array=False, is_sequence=False):
+ if is_array and is_sequence:
+ raise ValueError('Array of Sequences not allowed.')
+ self.base_type = base_type
+ self.is_array = is_array
+ self.is_sequence = is_sequence
+
+ def __str__(self):
+ type_string = self.base_type
+ if self.is_array:
+ type_string += '[]'
+ if self.is_sequence:
+ type_string = 'sequence<%s>' % type_string
+ return type_string
+
+ @classmethod
+ def from_string(cls, type_string):
+ is_array = False
+ if type_string.endswith('[]'):
+ is_array = True
+ type_string = type_string[:-2]
+
+ is_sequence = False
+ sequence_re = r'^sequence<([^>]*)>$'
+ sequence_match = re.match(sequence_re, type_string)
+ if sequence_match:
+ is_sequence = True
+ type_string = sequence_match.group(1)
+ base_type = type_string
+ return cls(base_type=base_type, is_array=is_array, is_sequence=is_sequence)
+
+
+class Typedef():
+ # Not exposed in bindings, internal to IDL parsing
+ def __init__(self, extended_attributes=None, data_type=None):
+ self.extended_attributes = extended_attributes or {}
+ self.data_type = data_type
+
+
+class UnionType(BaseIdl):
+ def __init__(self, union_member_types=None):
+ """union_member_types: list of string or UnionType"""
+ self.union_member_types = union_member_types or []
+
+ def to_json(self):
+ return {
+ 'UnionType::unionMemberTypes': self.union_member_types,
+ }
+
+
+# Perl JSON compatiblity functions
+# FIXME: remove when Perl removed
+
+def none_to_value_is_missing(extended_attributes):
+ # Perl IDL Parser uses 'VALUE_IS_MISSING' for null values in
+ # extended attributes, so add this as a filter when exporting to JSON.
+ new_extended_attributes = {}
+ for key, value in extended_attributes.iteritems():
+ if value is None:
+ new_extended_attributes[key] = 'VALUE_IS_MISSING'
+ else:
+ new_extended_attributes[key] = value
+ return new_extended_attributes
+
+
+def boolean_to_perl(value):
+ # Perl stores booleans as 1, 0, or undefined (JSON null);
+ # convert to this format
+ if value is None:
+ return None
+ return int(value)
+
+# JSON export
+# FIXME: remove when Perl removed
+# (so no longer using JSON as intermediate format)
+
+
+class IdlEncoder(json.JSONEncoder):
+ def default(self, obj):
+ if isinstance(obj, BaseIdl):
+ return obj.to_json()
+ return json.JSONEncoder.default(self, obj)
+
+
+def ir_to_json(ir, debug=False):
+ if debug:
+ # More legible
+ return json.dumps(ir, cls=IdlEncoder, sort_keys=True, indent=4)
+ return json.dumps(ir, cls=IdlEncoder, sort_keys=True, separators=(',', ':'))
« 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