|  | # Copyright 2018 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. | 
|  |  | 
|  | from __future__ import print_function | 
|  | import collections | 
|  | import json | 
|  | import os.path | 
|  | import re | 
|  | import sys | 
|  |  | 
|  | description = '' | 
|  |  | 
|  |  | 
|  | primitiveTypes = ['integer', 'number', 'boolean', 'string', 'object', | 
|  | 'any', 'array', 'binary'] | 
|  |  | 
|  |  | 
|  | def assignType(item, type, is_array=False, map_binary_to_string=False): | 
|  | if is_array: | 
|  | item['type'] = 'array' | 
|  | item['items'] = collections.OrderedDict() | 
|  | assignType(item['items'], type, False, map_binary_to_string) | 
|  | return | 
|  |  | 
|  | if type == 'enum': | 
|  | type = 'string' | 
|  | if map_binary_to_string and type == 'binary': | 
|  | type = 'string' | 
|  | if type in primitiveTypes: | 
|  | item['type'] = type | 
|  | else: | 
|  | item['$ref'] = type | 
|  |  | 
|  |  | 
|  | def createItem(d, experimental, deprecated, name=None): | 
|  | result = collections.OrderedDict(d) | 
|  | if name: | 
|  | result['name'] = name | 
|  | global description | 
|  | if description: | 
|  | result['description'] = description.strip() | 
|  | if experimental: | 
|  | result['experimental'] = True | 
|  | if deprecated: | 
|  | result['deprecated'] = True | 
|  | return result | 
|  |  | 
|  |  | 
|  | def parse(data, file_name, map_binary_to_string=False): | 
|  | protocol = collections.OrderedDict() | 
|  | protocol['version'] = collections.OrderedDict() | 
|  | protocol['domains'] = [] | 
|  | domain = None | 
|  | item = None | 
|  | subitems = None | 
|  | nukeDescription = False | 
|  | global description | 
|  | lines = data.split('\n') | 
|  | for i in range(0, len(lines)): | 
|  | if nukeDescription: | 
|  | description = '' | 
|  | nukeDescription = False | 
|  | line = lines[i] | 
|  | trimLine = line.strip() | 
|  |  | 
|  | if trimLine.startswith('#'): | 
|  | if len(description): | 
|  | description += '\n' | 
|  | description += trimLine[2:] | 
|  | continue | 
|  | else: | 
|  | nukeDescription = True | 
|  |  | 
|  | if len(trimLine) == 0: | 
|  | continue | 
|  |  | 
|  | match = re.compile( | 
|  | r'^(experimental )?(deprecated )?domain (.*)').match(line) | 
|  | if match: | 
|  | domain = createItem({'domain' : match.group(3)}, match.group(1), | 
|  | match.group(2)) | 
|  | protocol['domains'].append(domain) | 
|  | continue | 
|  |  | 
|  | match = re.compile(r'^  depends on ([^\s]+)').match(line) | 
|  | if match: | 
|  | if 'dependencies' not in domain: | 
|  | domain['dependencies'] = [] | 
|  | domain['dependencies'].append(match.group(1)) | 
|  | continue | 
|  |  | 
|  | match = re.compile(r'^  (experimental )?(deprecated )?type (.*) ' | 
|  | r'extends (array of )?([^\s]+)').match(line) | 
|  | if match: | 
|  | if 'types' not in domain: | 
|  | domain['types'] = [] | 
|  | item = createItem({'id': match.group(3)}, match.group(1), match.group(2)) | 
|  | assignType(item, match.group(5), match.group(4), map_binary_to_string) | 
|  | domain['types'].append(item) | 
|  | continue | 
|  |  | 
|  | match = re.compile( | 
|  | r'^  (experimental )?(deprecated )?(command|event) (.*)').match(line) | 
|  | if match: | 
|  | list = [] | 
|  | if match.group(3) == 'command': | 
|  | if 'commands' in domain: | 
|  | list = domain['commands'] | 
|  | else: | 
|  | list = domain['commands'] = [] | 
|  | else: | 
|  | if 'events' in domain: | 
|  | list = domain['events'] | 
|  | else: | 
|  | list = domain['events'] = [] | 
|  |  | 
|  | item = createItem({}, match.group(1), match.group(2), match.group(4)) | 
|  | list.append(item) | 
|  | continue | 
|  |  | 
|  | match = re.compile( | 
|  | r'^      (experimental )?(deprecated )?(optional )?' | 
|  | r'(array of )?([^\s]+) ([^\s]+)').match(line) | 
|  | if match: | 
|  | param = createItem({}, match.group(1), match.group(2), match.group(6)) | 
|  | if match.group(3): | 
|  | param['optional'] = True | 
|  | assignType(param, match.group(5), match.group(4), map_binary_to_string) | 
|  | if match.group(5) == 'enum': | 
|  | enumliterals = param['enum'] = [] | 
|  | subitems.append(param) | 
|  | continue | 
|  |  | 
|  | match = re.compile(r'^    (parameters|returns|properties)').match(line) | 
|  | if match: | 
|  | subitems = item[match.group(1)] = [] | 
|  | continue | 
|  |  | 
|  | match = re.compile(r'^    enum').match(line) | 
|  | if match: | 
|  | enumliterals = item['enum'] = [] | 
|  | continue | 
|  |  | 
|  | match = re.compile(r'^version').match(line) | 
|  | if match: | 
|  | continue | 
|  |  | 
|  | match = re.compile(r'^  major (\d+)').match(line) | 
|  | if match: | 
|  | protocol['version']['major'] = match.group(1) | 
|  | continue | 
|  |  | 
|  | match = re.compile(r'^  minor (\d+)').match(line) | 
|  | if match: | 
|  | protocol['version']['minor'] = match.group(1) | 
|  | continue | 
|  |  | 
|  | match = re.compile(r'^    redirect ([^\s]+)').match(line) | 
|  | if match: | 
|  | item['redirect'] = match.group(1) | 
|  | continue | 
|  |  | 
|  | match = re.compile(r'^      (  )?[^\s]+$').match(line) | 
|  | if match: | 
|  | # enum literal | 
|  | enumliterals.append(trimLine) | 
|  | continue | 
|  |  | 
|  | print('Error in %s:%s, illegal token: \t%s' % (file_name, i, line)) | 
|  | sys.exit(1) | 
|  | return protocol | 
|  |  | 
|  |  | 
|  | def loads(data, file_name, map_binary_to_string=False): | 
|  | if file_name.endswith(".pdl"): | 
|  | return parse(data, file_name, map_binary_to_string) | 
|  | return json.loads(data) |