| Index: tools/json_schema_compiler/idl_schema.py
|
| diff --git a/tools/json_schema_compiler/idl_schema.py b/tools/json_schema_compiler/idl_schema.py
|
| deleted file mode 100755
|
| index a9137569cc348f0583110e279e9a523b016c7543..0000000000000000000000000000000000000000
|
| --- a/tools/json_schema_compiler/idl_schema.py
|
| +++ /dev/null
|
| @@ -1,488 +0,0 @@
|
| -#! /usr/bin/env python
|
| -# 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 itertools
|
| -import json
|
| -import os.path
|
| -import re
|
| -import sys
|
| -
|
| -from json_parse import OrderedDict
|
| -
|
| -# 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 in sys.path:
|
| - import idl_parser
|
| -else:
|
| - sys.path.insert(0, _idl_generators_path)
|
| - try:
|
| - import idl_parser
|
| - finally:
|
| - sys.path.pop(0)
|
| -
|
| -def ProcessComment(comment):
|
| - '''
|
| - Convert a comment into a parent comment and a list of parameter comments.
|
| -
|
| - Function comments are of the form:
|
| - Function documentation. May contain HTML and multiple lines.
|
| -
|
| - |arg1_name|: Description of arg1. Use <var>argument</var> to refer
|
| - to other arguments.
|
| - |arg2_name|: Description of arg2...
|
| -
|
| - Newlines are removed, and leading and trailing whitespace is stripped.
|
| -
|
| - Args:
|
| - comment: The string from a Comment node.
|
| -
|
| - Returns: A tuple that looks like:
|
| - (
|
| - "The processed comment, minus all |parameter| mentions.",
|
| - {
|
| - 'parameter_name_1': "The comment that followed |parameter_name_1|:",
|
| - ...
|
| - }
|
| - )
|
| - '''
|
| - def add_paragraphs(content):
|
| - paragraphs = content.split('\n\n')
|
| - if len(paragraphs) < 2:
|
| - return content
|
| - return '<p>' + '</p><p>'.join(p.strip() for p in paragraphs) + '</p>'
|
| -
|
| - # Find all the parameter comments of the form '|name|: comment'.
|
| - parameter_starts = list(re.finditer(r' *\|([^|]*)\| *: *', comment))
|
| -
|
| - # Get the parent comment (everything before the first parameter comment.
|
| - first_parameter_location = (parameter_starts[0].start()
|
| - if parameter_starts else len(comment))
|
| - parent_comment = (add_paragraphs(comment[:first_parameter_location].strip())
|
| - .replace('\n', ''))
|
| -
|
| - params = OrderedDict()
|
| - for (cur_param, next_param) in itertools.izip_longest(parameter_starts,
|
| - parameter_starts[1:]):
|
| - param_name = cur_param.group(1)
|
| -
|
| - # A parameter's comment goes from the end of its introduction to the
|
| - # beginning of the next parameter's introduction.
|
| - param_comment_start = cur_param.end()
|
| - param_comment_end = next_param.start() if next_param else len(comment)
|
| - params[param_name] = (
|
| - add_paragraphs(comment[param_comment_start:param_comment_end].strip())
|
| - .replace('\n', ''))
|
| -
|
| - return (parent_comment, params)
|
| -
|
| -
|
| -class Callspec(object):
|
| - '''
|
| - Given a Callspec node representing an IDL function declaration, converts into
|
| - a tuple:
|
| - (name, list of function parameters, return type)
|
| - '''
|
| - def __init__(self, callspec_node, comment):
|
| - self.node = callspec_node
|
| - self.comment = comment
|
| -
|
| - def process(self, callbacks):
|
| - parameters = []
|
| - return_type = None
|
| - if self.node.GetProperty('TYPEREF') not in ('void', None):
|
| - return_type = Typeref(self.node.GetProperty('TYPEREF'),
|
| - self.node.parent,
|
| - {'name': self.node.GetName()}).process(callbacks)
|
| - # The IDL parser doesn't allow specifying return types as optional.
|
| - # Instead we infer any object return values to be optional.
|
| - # TODO(asargent): fix the IDL parser to support optional return types.
|
| - if return_type.get('type') == 'object' or '$ref' in return_type:
|
| - return_type['optional'] = True
|
| - for node in self.node.GetChildren():
|
| - parameter = Param(node).process(callbacks)
|
| - if parameter['name'] in self.comment:
|
| - parameter['description'] = self.comment[parameter['name']]
|
| - parameters.append(parameter)
|
| - return (self.node.GetName(), parameters, return_type)
|
| -
|
| -
|
| -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, callbacks):
|
| - return Typeref(self.node.GetProperty('TYPEREF'),
|
| - self.node,
|
| - {'name': self.node.GetName()}).process(callbacks)
|
| -
|
| -
|
| -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, callbacks):
|
| - properties = OrderedDict()
|
| - for node in self.node.GetChildren():
|
| - if node.cls == 'Member':
|
| - k, v = Member(node).process(callbacks)
|
| - properties[k] = v
|
| - result = {'id': self.node.GetName(),
|
| - 'properties': properties,
|
| - 'type': 'object'}
|
| - if self.node.GetProperty('nodoc'):
|
| - result['nodoc'] = True
|
| - elif self.node.GetProperty('inline_doc'):
|
| - result['inline_doc'] = True
|
| - elif self.node.GetProperty('noinline_doc'):
|
| - result['noinline_doc'] = True
|
| - return result
|
| -
|
| -
|
| -
|
| -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, callbacks):
|
| - properties = OrderedDict()
|
| - name = self.node.GetName()
|
| - if self.node.GetProperty('deprecated'):
|
| - properties['deprecated'] = self.node.GetProperty('deprecated')
|
| - if self.node.GetProperty('allowAmbiguousOptionalArguments'):
|
| - properties['allowAmbiguousOptionalArguments'] = True
|
| - for property_name in ('OPTIONAL', 'nodoc', 'nocompile', 'nodart'):
|
| - if self.node.GetProperty(property_name):
|
| - properties[property_name.lower()] = True
|
| - for option_name, sanitizer in [
|
| - ('maxListeners', int),
|
| - ('supportsFilters', lambda s: s == 'true'),
|
| - ('supportsListeners', lambda s: s == 'true'),
|
| - ('supportsRules', lambda s: s == 'true')]:
|
| - if self.node.GetProperty(option_name):
|
| - if 'options' not in properties:
|
| - properties['options'] = {}
|
| - properties['options'][option_name] = sanitizer(self.node.GetProperty(
|
| - option_name))
|
| - is_function = False
|
| - parameter_comments = OrderedDict()
|
| - for node in self.node.GetChildren():
|
| - if node.cls == 'Comment':
|
| - (parent_comment, parameter_comments) = ProcessComment(node.GetName())
|
| - properties['description'] = parent_comment
|
| - elif node.cls == 'Callspec':
|
| - is_function = True
|
| - name, parameters, return_type = (Callspec(node, parameter_comments)
|
| - .process(callbacks))
|
| - properties['parameters'] = parameters
|
| - if return_type is not None:
|
| - properties['returns'] = return_type
|
| - properties['name'] = name
|
| - if is_function:
|
| - properties['type'] = 'function'
|
| - else:
|
| - properties = Typeref(self.node.GetProperty('TYPEREF'),
|
| - self.node, properties).process(callbacks)
|
| - enum_values = self.node.GetProperty('legalValues')
|
| - if enum_values:
|
| - if properties['type'] == 'integer':
|
| - enum_values = map(int, enum_values)
|
| - elif properties['type'] == 'double':
|
| - enum_values = map(float, enum_values)
|
| - properties['enum'] = enum_values
|
| - 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, callbacks):
|
| - properties = self.additional_properties
|
| - result = properties
|
| -
|
| - if self.parent.GetPropertyLocal('OPTIONAL'):
|
| - properties['optional'] = True
|
| -
|
| - # The IDL parser denotes array types by adding a child 'Array' node onto
|
| - # the Param node in the Callspec.
|
| - for sibling in self.parent.GetChildren():
|
| - if sibling.cls == 'Array' and sibling.GetName() == self.parent.GetName():
|
| - properties['type'] = 'array'
|
| - properties['items'] = OrderedDict()
|
| - properties = properties['items']
|
| - break
|
| -
|
| - if self.typeref == 'DOMString':
|
| - properties['type'] = 'string'
|
| - elif self.typeref == 'boolean':
|
| - properties['type'] = 'boolean'
|
| - elif self.typeref == 'double':
|
| - properties['type'] = 'number'
|
| - elif self.typeref == 'long':
|
| - properties['type'] = 'integer'
|
| - elif self.typeref == 'any':
|
| - properties['type'] = 'any'
|
| - elif self.typeref == 'object':
|
| - properties['type'] = 'object'
|
| - if 'additionalProperties' not in properties:
|
| - properties['additionalProperties'] = OrderedDict()
|
| - properties['additionalProperties']['type'] = 'any'
|
| - instance_of = self.parent.GetProperty('instanceOf')
|
| - if instance_of:
|
| - properties['isInstanceOf'] = instance_of
|
| - elif self.typeref == 'ArrayBuffer':
|
| - properties['type'] = 'binary'
|
| - properties['isInstanceOf'] = 'ArrayBuffer'
|
| - elif self.typeref == 'FileEntry':
|
| - properties['type'] = 'object'
|
| - properties['isInstanceOf'] = 'FileEntry'
|
| - if 'additionalProperties' not in properties:
|
| - properties['additionalProperties'] = OrderedDict()
|
| - properties['additionalProperties']['type'] = 'any'
|
| - elif self.parent.GetPropertyLocal('Union'):
|
| - choices = []
|
| - properties['choices'] = [Typeref(node.GetProperty('TYPEREF'),
|
| - node,
|
| - OrderedDict()).process(callbacks)
|
| - for node in self.parent.GetChildren()
|
| - if node.cls == 'Option']
|
| - elif self.typeref is None:
|
| - properties['type'] = 'function'
|
| - else:
|
| - if self.typeref in callbacks:
|
| - # Do not override name and description if they are already specified.
|
| - name = properties.get('name', None)
|
| - description = properties.get('description', None)
|
| - properties.update(callbacks[self.typeref])
|
| - if description is not None:
|
| - properties['description'] = description
|
| - if name is not None:
|
| - properties['name'] = name
|
| - else:
|
| - properties['$ref'] = self.typeref
|
| - return result
|
| -
|
| -
|
| -class Enum(object):
|
| - '''
|
| - Given an IDL Enum node, converts into a Python dictionary that the JSON
|
| - schema compiler expects to see.
|
| - '''
|
| - def __init__(self, enum_node):
|
| - self.node = enum_node
|
| - self.description = ''
|
| -
|
| - def process(self, callbacks):
|
| - enum = []
|
| - for node in self.node.GetChildren():
|
| - if node.cls == 'EnumItem':
|
| - enum_value = {'name': node.GetName()}
|
| - for child in node.GetChildren():
|
| - if child.cls == 'Comment':
|
| - enum_value['description'] = ProcessComment(child.GetName())[0]
|
| - else:
|
| - raise ValueError('Did not process %s %s' % (child.cls, child))
|
| - enum.append(enum_value)
|
| - elif node.cls == 'Comment':
|
| - self.description = ProcessComment(node.GetName())[0]
|
| - else:
|
| - sys.exit('Did not process %s %s' % (node.cls, node))
|
| - result = {'id' : self.node.GetName(),
|
| - 'description': self.description,
|
| - 'type': 'string',
|
| - 'enum': enum}
|
| - for property_name in (
|
| - 'inline_doc', 'noinline_doc', 'nodoc', 'cpp_enum_prefix_override',):
|
| - if self.node.GetProperty(property_name):
|
| - result[property_name] = self.node.GetProperty(property_name)
|
| - if self.node.GetProperty('deprecated'):
|
| - result[deprecated] = self.node.GetProperty('deprecated')
|
| - return result
|
| -
|
| -
|
| -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,
|
| - description,
|
| - nodoc=False,
|
| - internal=False,
|
| - platforms=None,
|
| - compiler_options=None,
|
| - deprecated=None):
|
| - self.namespace = namespace_node
|
| - self.nodoc = nodoc
|
| - self.internal = internal
|
| - self.platforms = platforms
|
| - self.compiler_options = compiler_options
|
| - self.events = []
|
| - self.functions = []
|
| - self.types = []
|
| - self.callbacks = OrderedDict()
|
| - self.description = description
|
| - self.deprecated = deprecated
|
| -
|
| - def process(self):
|
| - for node in self.namespace.GetChildren():
|
| - if node.cls == 'Dictionary':
|
| - self.types.append(Dictionary(node).process(self.callbacks))
|
| - elif node.cls == 'Callback':
|
| - k, v = Member(node).process(self.callbacks)
|
| - self.callbacks[k] = v
|
| - elif node.cls == 'Interface' and node.GetName() == 'Functions':
|
| - self.functions = self.process_interface(node)
|
| - elif node.cls == 'Interface' and node.GetName() == 'Events':
|
| - self.events = self.process_interface(node)
|
| - elif node.cls == 'Enum':
|
| - self.types.append(Enum(node).process(self.callbacks))
|
| - else:
|
| - sys.exit('Did not process %s %s' % (node.cls, node))
|
| - if self.compiler_options is not None:
|
| - compiler_options = self.compiler_options
|
| - else:
|
| - compiler_options = {}
|
| - return {'namespace': self.namespace.GetName(),
|
| - 'description': self.description,
|
| - 'nodoc': self.nodoc,
|
| - 'types': self.types,
|
| - 'functions': self.functions,
|
| - 'internal': self.internal,
|
| - 'events': self.events,
|
| - 'platforms': self.platforms,
|
| - 'compiler_options': compiler_options,
|
| - 'deprecated': self.deprecated}
|
| -
|
| - def process_interface(self, node):
|
| - members = []
|
| - for member in node.GetChildren():
|
| - if member.cls == 'Member':
|
| - name, properties = Member(member).process(self.callbacks)
|
| - 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 = []
|
| - nodoc = False
|
| - internal = False
|
| - description = None
|
| - platforms = None
|
| - compiler_options = {}
|
| - deprecated = None
|
| - for node in self.idl:
|
| - if node.cls == 'Namespace':
|
| - if not description:
|
| - # TODO(kalman): Go back to throwing an error here.
|
| - print('%s must have a namespace-level comment. This will '
|
| - 'appear on the API summary page.' % node.GetName())
|
| - description = ''
|
| - namespace = Namespace(node, description, nodoc, internal,
|
| - platforms=platforms,
|
| - compiler_options=compiler_options or None,
|
| - deprecated=deprecated)
|
| - namespaces.append(namespace.process())
|
| - nodoc = False
|
| - internal = False
|
| - platforms = None
|
| - compiler_options = None
|
| - elif node.cls == 'Copyright':
|
| - continue
|
| - elif node.cls == 'Comment':
|
| - description = node.GetName()
|
| - elif node.cls == 'ExtAttribute':
|
| - if node.name == 'nodoc':
|
| - nodoc = bool(node.value)
|
| - elif node.name == 'internal':
|
| - internal = bool(node.value)
|
| - elif node.name == 'platforms':
|
| - platforms = list(node.value)
|
| - elif node.name == 'implemented_in':
|
| - compiler_options['implemented_in'] = node.value
|
| - elif node.name == 'camel_case_enum_to_string':
|
| - compiler_options['camel_case_enum_to_string'] = node.value
|
| - elif node.name == 'deprecated':
|
| - deprecated = str(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()
|
| -
|
| -
|
| -def Main():
|
| - '''
|
| - Dump a json serialization of parse result for the IDL files whose names
|
| - were passed in on the command line.
|
| - '''
|
| - if len(sys.argv) > 1:
|
| - for filename in sys.argv[1:]:
|
| - schema = Load(filename)
|
| - print json.dumps(schema, indent=2)
|
| - else:
|
| - contents = sys.stdin.read()
|
| - idl = idl_parser.IDLParser().ParseData(contents, '<stdin>')
|
| - schema = IDLSchema(idl).process()
|
| - print json.dumps(schema, indent=2)
|
| -
|
| -
|
| -if __name__ == '__main__':
|
| - Main()
|
|
|