| OLD | NEW |
| 1 #! /usr/bin/env python | 1 #! /usr/bin/env python |
| 2 # Copyright (c) 2012 The Chromium Authors. All rights reserved. | 2 # Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| 3 # Use of this source code is governed by a BSD-style license that can be | 3 # Use of this source code is governed by a BSD-style license that can be |
| 4 # found in the LICENSE file. | 4 # found in the LICENSE file. |
| 5 | 5 |
| 6 import itertools | 6 import itertools |
| 7 import json | 7 import json |
| 8 import os.path | 8 import os.path |
| 9 import pprint |
| 9 import re | 10 import re |
| 10 import sys | 11 import sys |
| 11 | 12 |
| 12 from json_parse import OrderedDict | 13 from json_parse import OrderedDict |
| 13 | 14 |
| 14 # This file is a peer to json_schema.py. Each of these files understands a | 15 # This file is a peer to json_schema.py. Each of these files understands a |
| 15 # certain format describing APIs (either JSON or IDL), reads files written | 16 # certain format describing APIs (either JSON or IDL), reads files written |
| 16 # in that format into memory, and emits them as a Python array of objects | 17 # in that format into memory, and emits them as a Python array of objects |
| 17 # corresponding to those APIs, where the objects are formatted in a way that | 18 # corresponding to those APIs, where the objects are formatted in a way that |
| 18 # the JSON schema compiler understands. compiler.py drives both idl_schema.py | 19 # the JSON schema compiler understands. compiler.py drives both idl_schema.py |
| (...skipping 141 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 160 | 161 |
| 161 class Member(object): | 162 class Member(object): |
| 162 ''' | 163 ''' |
| 163 Given an IDL dictionary or interface member, converts into a name/value pair | 164 Given an IDL dictionary or interface member, converts into a name/value pair |
| 164 where the value is a Python dictionary that the JSON schema compiler expects | 165 where the value is a Python dictionary that the JSON schema compiler expects |
| 165 to see. | 166 to see. |
| 166 ''' | 167 ''' |
| 167 def __init__(self, member_node): | 168 def __init__(self, member_node): |
| 168 self.node = member_node | 169 self.node = member_node |
| 169 | 170 |
| 170 def process(self, callbacks): | 171 def process(self, callbacks, functions_are_properties=False): |
| 171 properties = OrderedDict() | 172 properties = OrderedDict() |
| 172 name = self.node.GetName() | 173 name = self.node.GetName() |
| 173 if self.node.GetProperty('deprecated'): | 174 if self.node.GetProperty('deprecated'): |
| 174 properties['deprecated'] = self.node.GetProperty('deprecated') | 175 properties['deprecated'] = self.node.GetProperty('deprecated') |
| 175 if self.node.GetProperty('allowAmbiguousOptionalArguments'): | 176 if self.node.GetProperty('allowAmbiguousOptionalArguments'): |
| 176 properties['allowAmbiguousOptionalArguments'] = True | 177 properties['allowAmbiguousOptionalArguments'] = True |
| 177 for property_name in ('OPTIONAL', 'nodoc', 'nocompile', 'nodart'): | 178 for property_name in ('OPTIONAL', 'nodoc', 'nocompile', 'nodart'): |
| 178 if self.node.GetProperty(property_name): | 179 if self.node.GetProperty(property_name): |
| 179 properties[property_name.lower()] = True | 180 properties[property_name.lower()] = True |
| 180 for option_name, sanitizer in [ | 181 for option_name, sanitizer in [ |
| 181 ('maxListeners', int), | 182 ('maxListeners', int), |
| 182 ('supportsFilters', lambda s: s == 'true'), | 183 ('supportsFilters', lambda s: s == 'true'), |
| 183 ('supportsListeners', lambda s: s == 'true'), | 184 ('supportsListeners', lambda s: s == 'true'), |
| 184 ('supportsRules', lambda s: s == 'true')]: | 185 ('supportsRules', lambda s: s == 'true')]: |
| 185 if self.node.GetProperty(option_name): | 186 if self.node.GetProperty(option_name): |
| 186 if 'options' not in properties: | 187 if 'options' not in properties: |
| 187 properties['options'] = {} | 188 properties['options'] = {} |
| 188 properties['options'][option_name] = sanitizer(self.node.GetProperty( | 189 properties['options'][option_name] = sanitizer(self.node.GetProperty( |
| 189 option_name)) | 190 option_name)) |
| 190 is_function = False | 191 type_override = None |
| 191 parameter_comments = OrderedDict() | 192 parameter_comments = OrderedDict() |
| 192 for node in self.node.GetChildren(): | 193 for node in self.node.GetChildren(): |
| 193 if node.cls == 'Comment': | 194 if node.cls == 'Comment': |
| 194 (parent_comment, parameter_comments) = ProcessComment(node.GetName()) | 195 (parent_comment, parameter_comments) = ProcessComment(node.GetName()) |
| 195 properties['description'] = parent_comment | 196 properties['description'] = parent_comment |
| 196 elif node.cls == 'Callspec': | 197 elif node.cls == 'Callspec': |
| 197 is_function = True | |
| 198 name, parameters, return_type = (Callspec(node, parameter_comments) | 198 name, parameters, return_type = (Callspec(node, parameter_comments) |
| 199 .process(callbacks)) | 199 .process(callbacks)) |
| 200 properties['parameters'] = parameters | 200 if functions_are_properties: |
| 201 if return_type is not None: | 201 # If functions are treated as properties (which will happen if the |
| 202 properties['returns'] = return_type | 202 # interface is named Properties) then this isn't a function, it's a |
| 203 # property which is encoded as a function with no arguments. The |
| 204 # property type is the return type. This is an egregious hack in lieu |
| 205 # of the IDL parser supporting 'const'. |
| 206 assert parameters == [], ( |
| 207 'Property "%s" must be no-argument functions ' |
| 208 'with a non-void return type' % name) |
| 209 assert return_type is not None, ( |
| 210 'Property "%s" must be no-argument functions ' |
| 211 'with a non-void return type' % name) |
| 212 assert 'type' in return_type, ( |
| 213 'Property return type "%s" from "%s" must specify a ' |
| 214 'fundamental IDL type.' % (pprint.pformat(return_type), name)) |
| 215 type_override = return_type['type'] |
| 216 else: |
| 217 type_override = 'function' |
| 218 properties['parameters'] = parameters |
| 219 if return_type is not None: |
| 220 properties['returns'] = return_type |
| 203 properties['name'] = name | 221 properties['name'] = name |
| 204 if is_function: | 222 if type_override is not None: |
| 205 properties['type'] = 'function' | 223 properties['type'] = type_override |
| 206 else: | 224 else: |
| 207 properties = Typeref(self.node.GetProperty('TYPEREF'), | 225 properties = Typeref(self.node.GetProperty('TYPEREF'), |
| 208 self.node, properties).process(callbacks) | 226 self.node, properties).process(callbacks) |
| 227 value = self.node.GetProperty('value') |
| 228 if value is not None: |
| 229 # IDL always returns values as strings, so cast to their real type. |
| 230 properties['value'] = self.cast_from_json_type(properties['type'], value) |
| 209 enum_values = self.node.GetProperty('legalValues') | 231 enum_values = self.node.GetProperty('legalValues') |
| 210 if enum_values: | 232 if enum_values: |
| 211 if properties['type'] == 'integer': | 233 # IDL always returns enum values as strings, so cast to their real type. |
| 212 enum_values = map(int, enum_values) | 234 properties['enum'] = [self.cast_from_json_type(properties['type'], enum) |
| 213 elif properties['type'] == 'double': | 235 for enum in enum_values] |
| 214 enum_values = map(float, enum_values) | |
| 215 properties['enum'] = enum_values | |
| 216 return name, properties | 236 return name, properties |
| 217 | 237 |
| 238 def cast_from_json_type(self, json_type, string_value): |
| 239 '''Casts from string |string_value| to a real Python type based on a JSON |
| 240 Schema type |json_type|. For example, a string value of '42' and a JSON |
| 241 Schema type 'integer' will cast to int('42') ==> 42. |
| 242 ''' |
| 243 if json_type == 'integer': |
| 244 return int(string_value) |
| 245 if json_type == 'number': |
| 246 return float(string_value) |
| 247 # Add more as necessary. |
| 248 assert json_type == 'string', ( |
| 249 'No rule exists to cast JSON Schema type "%s" to its equivalent ' |
| 250 'Python type for value "%s". You must add a new rule here.' % |
| 251 (json_type, string_value)) |
| 252 return string_value |
| 253 |
| 218 | 254 |
| 219 class Typeref(object): | 255 class Typeref(object): |
| 220 ''' | 256 ''' |
| 221 Given a TYPEREF property representing the type of dictionary member or | 257 Given a TYPEREF property representing the type of dictionary member or |
| 222 function parameter, converts into a Python dictionary that the JSON schema | 258 function parameter, converts into a Python dictionary that the JSON schema |
| 223 compiler expects to see. | 259 compiler expects to see. |
| 224 ''' | 260 ''' |
| 225 def __init__(self, typeref, parent, additional_properties): | 261 def __init__(self, typeref, parent, additional_properties): |
| 226 self.typeref = typeref | 262 self.typeref = typeref |
| 227 self.parent = parent | 263 self.parent = parent |
| (...skipping 117 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 345 compiler_options=None, | 381 compiler_options=None, |
| 346 deprecated=None, | 382 deprecated=None, |
| 347 documentation_options=None): | 383 documentation_options=None): |
| 348 self.namespace = namespace_node | 384 self.namespace = namespace_node |
| 349 self.nodoc = nodoc | 385 self.nodoc = nodoc |
| 350 self.internal = internal | 386 self.internal = internal |
| 351 self.platforms = platforms | 387 self.platforms = platforms |
| 352 self.compiler_options = compiler_options | 388 self.compiler_options = compiler_options |
| 353 self.events = [] | 389 self.events = [] |
| 354 self.functions = [] | 390 self.functions = [] |
| 391 self.properties = OrderedDict() |
| 355 self.types = [] | 392 self.types = [] |
| 356 self.callbacks = OrderedDict() | 393 self.callbacks = OrderedDict() |
| 357 self.description = description | 394 self.description = description |
| 358 self.deprecated = deprecated | 395 self.deprecated = deprecated |
| 359 self.documentation_options = documentation_options | 396 self.documentation_options = documentation_options |
| 360 | 397 |
| 361 def process(self): | 398 def process(self): |
| 362 for node in self.namespace.GetChildren(): | 399 for node in self.namespace.GetChildren(): |
| 363 if node.cls == 'Dictionary': | 400 if node.cls == 'Dictionary': |
| 364 self.types.append(Dictionary(node).process(self.callbacks)) | 401 self.types.append(Dictionary(node).process(self.callbacks)) |
| 365 elif node.cls == 'Callback': | 402 elif node.cls == 'Callback': |
| 366 k, v = Member(node).process(self.callbacks) | 403 k, v = Member(node).process(self.callbacks) |
| 367 self.callbacks[k] = v | 404 self.callbacks[k] = v |
| 368 elif node.cls == 'Interface' and node.GetName() == 'Functions': | 405 elif node.cls == 'Interface' and node.GetName() == 'Functions': |
| 369 self.functions = self.process_interface(node) | 406 self.functions = self.process_interface(node) |
| 370 elif node.cls == 'Interface' and node.GetName() == 'Events': | 407 elif node.cls == 'Interface' and node.GetName() == 'Events': |
| 371 self.events = self.process_interface(node) | 408 self.events = self.process_interface(node) |
| 409 elif node.cls == 'Interface' and node.GetName() == 'Properties': |
| 410 properties_as_list = self.process_interface( |
| 411 node, functions_are_properties=True) |
| 412 for prop in properties_as_list: |
| 413 # Properties are given as key-value pairs, but IDL will parse |
| 414 # it as a list. Convert back to key-value pairs. |
| 415 prop_name = prop.pop('name') |
| 416 assert not self.properties.has_key(prop_name), ( |
| 417 'Property "%s" cannot be specified more than once.' % |
| 418 prop_name) |
| 419 self.properties[prop_name] = prop |
| 372 elif node.cls == 'Enum': | 420 elif node.cls == 'Enum': |
| 373 self.types.append(Enum(node).process()) | 421 self.types.append(Enum(node).process()) |
| 374 else: | 422 else: |
| 375 sys.exit('Did not process %s %s' % (node.cls, node)) | 423 sys.exit('Did not process %s %s' % (node.cls, node)) |
| 376 compiler_options = self.compiler_options or {} | 424 compiler_options = self.compiler_options or {} |
| 377 documentation_options = self.documentation_options or {} | 425 documentation_options = self.documentation_options or {} |
| 378 return {'namespace': self.namespace.GetName(), | 426 return {'namespace': self.namespace.GetName(), |
| 379 'description': self.description, | 427 'description': self.description, |
| 380 'nodoc': self.nodoc, | 428 'nodoc': self.nodoc, |
| 381 'types': self.types, | 429 'types': self.types, |
| 382 'functions': self.functions, | 430 'functions': self.functions, |
| 431 'properties': self.properties, |
| 383 'internal': self.internal, | 432 'internal': self.internal, |
| 384 'events': self.events, | 433 'events': self.events, |
| 385 'platforms': self.platforms, | 434 'platforms': self.platforms, |
| 386 'compiler_options': compiler_options, | 435 'compiler_options': compiler_options, |
| 387 'deprecated': self.deprecated, | 436 'deprecated': self.deprecated, |
| 388 'documentation_options': documentation_options} | 437 'documentation_options': documentation_options} |
| 389 | 438 |
| 390 def process_interface(self, node): | 439 def process_interface(self, node, functions_are_properties=False): |
| 391 members = [] | 440 members = [] |
| 392 for member in node.GetChildren(): | 441 for member in node.GetChildren(): |
| 393 if member.cls == 'Member': | 442 if member.cls == 'Member': |
| 394 _, properties = Member(member).process(self.callbacks) | 443 _, properties = Member(member).process( |
| 444 self.callbacks, |
| 445 functions_are_properties=functions_are_properties) |
| 395 members.append(properties) | 446 members.append(properties) |
| 396 return members | 447 return members |
| 397 | 448 |
| 398 | 449 |
| 399 class IDLSchema(object): | 450 class IDLSchema(object): |
| 400 ''' | 451 ''' |
| 401 Given a list of IDLNodes and IDLAttributes, converts into a Python list | 452 Given a list of IDLNodes and IDLAttributes, converts into a Python list |
| 402 of api_defs that the JSON schema compiler expects to see. | 453 of api_defs that the JSON schema compiler expects to see. |
| 403 ''' | 454 ''' |
| 404 | 455 |
| (...skipping 92 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 497 print json.dumps(schema, indent=2) | 548 print json.dumps(schema, indent=2) |
| 498 else: | 549 else: |
| 499 contents = sys.stdin.read() | 550 contents = sys.stdin.read() |
| 500 idl = idl_parser.IDLParser().ParseData(contents, '<stdin>') | 551 idl = idl_parser.IDLParser().ParseData(contents, '<stdin>') |
| 501 schema = IDLSchema(idl).process() | 552 schema = IDLSchema(idl).process() |
| 502 print json.dumps(schema, indent=2) | 553 print json.dumps(schema, indent=2) |
| 503 | 554 |
| 504 | 555 |
| 505 if __name__ == '__main__': | 556 if __name__ == '__main__': |
| 506 Main() | 557 Main() |
| OLD | NEW |