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 |