diff options
author | miket@chromium.org <miket@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-03-07 02:33:35 +0000 |
---|---|---|
committer | miket@chromium.org <miket@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-03-07 02:33:35 +0000 |
commit | 750b83600136c3b9bfaf33bb60ec61c41010a150 (patch) | |
tree | aead05297ea1f7ba6840c414917be091a5970dfb /tools/json_schema_compiler | |
parent | 8a59a087de2604becfc5fb4fb691b45c37ca8795 (diff) | |
download | chromium_src-750b83600136c3b9bfaf33bb60ec61c41010a150.zip chromium_src-750b83600136c3b9bfaf33bb60ec61c41010a150.tar.gz chromium_src-750b83600136c3b9bfaf33bb60ec61c41010a150.tar.bz2 |
- Extend compiler.py to recognize .idl files.
- Extend .gyp to pass certain .idl files to compiler.py.
- Implement a translator taking IDL parser output and creating
a schema that the JSON compiler recognizes.
- Convert one API to IDL using asargent's JSON cat JSON-to-IDL
converter, and adapt API's implementation to use the newly generated
output.
TEST=no changes
BUG=none
Review URL: http://codereview.chromium.org/9600050
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@125300 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'tools/json_schema_compiler')
-rw-r--r-- | tools/json_schema_compiler/compiler.py | 16 | ||||
-rw-r--r-- | tools/json_schema_compiler/idl_schema.py | 211 | ||||
-rw-r--r-- | tools/json_schema_compiler/json_schema.py | 2 | ||||
-rw-r--r-- | tools/json_schema_compiler/model.py | 3 |
4 files changed, 225 insertions, 7 deletions
diff --git a/tools/json_schema_compiler/compiler.py b/tools/json_schema_compiler/compiler.py index fb84a6c..f782f72 100644 --- a/tools/json_schema_compiler/compiler.py +++ b/tools/json_schema_compiler/compiler.py @@ -19,7 +19,8 @@ Usage example: import cc_generator import cpp_type_generator import h_generator -from json_schema import LoadJSON +import idl_schema +import json_schema import model import optparse import os.path @@ -47,17 +48,24 @@ if __name__ == '__main__': api_model = model.Model() - # Actually generate for source file. - api_defs = LoadJSON(schema) + schema_filename, schema_extension = os.path.splitext(schema) + if schema_extension == '.json': + api_defs = json_schema.Load(schema) + elif schema_extension == '.idl': + api_defs = idl_schema.Load(schema) + else: + sys.exit("Did not recognize file extension %s for schema %s" % + (schema_extension, schema)) for target_namespace in api_defs: referenced_schemas = target_namespace.get('dependencies', []) # Load type dependencies into the model. + # TODO(miket): do we need this in IDL? for referenced_schema in referenced_schemas: referenced_schema_path = os.path.join( os.path.dirname(schema), referenced_schema + '.json') - referenced_api_defs = LoadJSON(referenced_schema_path) + referenced_api_defs = json_schema.Load(referenced_schema_path) for namespace in referenced_api_defs: api_model.AddNamespace(namespace, diff --git a/tools/json_schema_compiler/idl_schema.py b/tools/json_schema_compiler/idl_schema.py new file mode 100644 index 0000000..d64f111 --- /dev/null +++ b/tools/json_schema_compiler/idl_schema.py @@ -0,0 +1,211 @@ +# Copyright (c) 2012 The Chromium Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +import os.path +import sys + +# This file is a peer to json_schema.py. Each of these files understands a +# certain format describing APIs (either JSON or IDL), reads files written +# in that format into memory, and emits them as a Python array of objects +# corresponding to those APIs, where the objects are formatted in a way that +# the JSON schema compiler understands. compiler.py drives both idl_schema.py +# and json_schema.py. + +# idl_parser expects to be able to import certain files in its directory, +# so let's set things up the way it wants. +idl_generators_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), + os.pardir, os.pardir, 'ppapi', 'generators') +if idl_generators_path not in sys.path: + sys.path.insert(0, idl_generators_path) +import idl_parser + +class Callspec(object): + ''' + Given a Callspec node representing an IDL function declaration, converts into + a name/value pair where the value is a list of function parameters. + ''' + def __init__(self, callspec_node): + self.node = callspec_node + + def process(self, refs): + parameters = [] + for node in self.node.children: + parameters.append(Param(node).process(refs)) + return self.node.GetName(), parameters + +class Param(object): + ''' + Given a Param node representing a function parameter, converts into a Python + dictionary that the JSON schema compiler expects to see. + ''' + def __init__(self, param_node): + self.node = param_node + + def process(self, refs): + return Typeref(self.node.GetProperty( 'TYPEREF'), + self.node, + { 'name': self.node.GetName() }).process(refs) + +class Dictionary(object): + ''' + Given an IDL Dictionary node, converts into a Python dictionary that the JSON + schema compiler expects to see. + ''' + def __init__(self, dictionary_node): + self.node = dictionary_node + + def process(self, refs): + properties = {} + for node in self.node.children: + if node.cls == 'Member': + k, v = Member(node).process(refs) + properties[k] = v + return { 'id': self.node.GetName(), + 'properties': properties, + 'type': 'object' } + +class Member(object): + ''' + Given an IDL dictionary or interface member, converts into a name/value pair + where the value is a Python dictionary that the JSON schema compiler expects + to see. + ''' + def __init__(self, member_node): + self.node = member_node + + def process(self, refs): + properties = {} + name = self.node.GetName() + if self.node.GetProperty('OPTIONAL'): + properties['optional'] = True + if self.node.GetProperty('nodoc'): + properties['nodoc'] = True + is_function = False + for node in self.node.children: + if node.cls == 'Callspec': + is_function = True + name, parameters = Callspec(node).process(refs) + properties['parameters'] = parameters + properties['name'] = name + if is_function: + properties['type'] = 'function' + else: + properties = Typeref(self.node.GetProperty('TYPEREF'), + self.node, properties).process(refs) + return name, properties + +class Typeref(object): + ''' + Given a TYPEREF property representing the type of dictionary member or + function parameter, converts into a Python dictionary that the JSON schema + compiler expects to see. + ''' + def __init__(self, typeref, parent, additional_properties={}): + self.typeref = typeref + self.parent = parent + self.additional_properties = additional_properties + + def process(self, refs): + properties = self.additional_properties + if self.typeref == 'DOMString': + properties['type'] = 'string' + elif self.typeref == 'boolean': + properties['type'] = 'boolean' + elif self.typeref == 'long': + properties['type'] = 'integer' + elif self.typeref is None: + properties['type'] = 'function' + else: + try: + properties = refs[self.typeref] + except KeyError, e: + properties['$ref'] = self.typeref + return properties + +class Namespace(object): + ''' + Given an IDLNode representing an IDL namespace, converts into a Python + dictionary that the JSON schema compiler expects to see. + ''' + + def __init__(self, namespace_node, nodoc=False): + self.namespace = namespace_node + self.nodoc = nodoc + self.events = [] + self.functions = [] + self.types = [] + self.refs = {} + + def process(self): + for node in self.namespace.children: + cls = node.cls + if cls == "Dictionary": + self.types.append(Dictionary(node).process(self.refs)) + elif cls == "Callback": + k, v = Member(node).process(self.refs) + self.refs[k] = v + elif cls == "Interface" and node.GetName() == "Functions": + self.functions = self.process_interface(node) + elif cls == "Interface" and node.GetName() == "Events": + self.events = self.process_interface(node) + else: + sys.exit("Did not process %s %s" % (node.cls, node)) + + return { 'events': self.events, + 'functions': self.functions, + 'types': self.types, + 'namespace': self.namespace.GetName(), + 'nodoc': self.nodoc } + + def process_interface(self, node): + members = [] + for member in node.children: + if member.cls == 'Member': + name, properties = Member(member).process(self.refs) + members.append(properties) + return members + +class IDLSchema(object): + ''' + Given a list of IDLNodes and IDLAttributes, converts into a Python list + of api_defs that the JSON schema compiler expects to see. + ''' + + def __init__(self, idl): + self.idl = idl + + def process(self): + namespaces = [] + for node in self.idl: + nodoc = False + cls = node.cls + if cls == 'Namespace': + namespace = Namespace(node, nodoc) + namespaces.append(namespace.process()) + elif cls == 'Copyright': + continue + elif cls == 'Comment': + continue + elif cls == 'ExtAttribute': + if node.name == 'nodoc': + nodoc = bool(node.value) + else: + continue + else: + sys.exit("Did not process %s %s" % (node.cls, node)) + return namespaces + +def Load(filename): + ''' + Given the filename of an IDL file, parses it and returns an equivalent + Python dictionary in a format that the JSON schema compiler expects to see. + ''' + + f = open(filename, 'r') + contents = f.read() + f.close() + + idl = idl_parser.IDLParser().ParseData(contents, filename) + idl_schema = IDLSchema(idl) + return idl_schema.process() diff --git a/tools/json_schema_compiler/json_schema.py b/tools/json_schema_compiler/json_schema.py index aa192c0..779ec87 100644 --- a/tools/json_schema_compiler/json_schema.py +++ b/tools/json_schema_compiler/json_schema.py @@ -14,6 +14,6 @@ if third_party_path not in sys.path: sys.path.insert(0, third_party_path) import json_minify as minify -def LoadJSON(filename): +def Load(filename): with open(filename, 'r') as handle: return json.loads(minify.json_minify(handle.read())) diff --git a/tools/json_schema_compiler/model.py b/tools/json_schema_compiler/model.py index de392d2..cb569c6 100644 --- a/tools/json_schema_compiler/model.py +++ b/tools/json_schema_compiler/model.py @@ -90,7 +90,7 @@ class Type(object): props = [] for prop_name, prop_json in json.get('properties', {}).items(): # TODO(calamity): support functions (callbacks) as properties. The model - # doesn't support it yet because to h/cc generators don't -- this is + # doesn't support it yet because the h/cc generators don't -- this is # because we'd need to hook it into a base::Callback or something. # # However, pragmatically it's not necessary to support them anyway, since @@ -328,4 +328,3 @@ def GetModelHierarchy(entity): entity = entity.parent hierarchy.reverse() return hierarchy - |