1# Copyright 2018 The Chromium Authors. All rights reserved. 2# Use of this source code is governed by a BSD-style license that can be 3# found in the LICENSE file. 4 5from __future__ import print_function 6import collections 7import json 8import os.path 9import re 10import sys 11 12description = '' 13 14 15primitiveTypes = ['integer', 'number', 'boolean', 'string', 'object', 'any', 'array', 'binary'] 16 17 18def assignType(item, type, is_array=False, map_binary_to_string=False): 19 if is_array: 20 item['type'] = 'array' 21 item['items'] = collections.OrderedDict() 22 assignType(item['items'], type, False, map_binary_to_string) 23 return 24 25 if type == 'enum': 26 type = 'string' 27 if map_binary_to_string and type == 'binary': 28 type = 'string' 29 if type in primitiveTypes: 30 item['type'] = type 31 else: 32 item['$ref'] = type 33 34 35def createItem(d, experimental, deprecated, name=None): 36 result = collections.OrderedDict(d) 37 if name: 38 result['name'] = name 39 global description 40 if description: 41 result['description'] = description.strip() 42 if experimental: 43 result['experimental'] = True 44 if deprecated: 45 result['deprecated'] = True 46 return result 47 48 49def parse(data, file_name, map_binary_to_string=False): 50 protocol = collections.OrderedDict() 51 protocol['version'] = collections.OrderedDict() 52 protocol['domains'] = [] 53 domain = None 54 item = None 55 subitems = None 56 nukeDescription = False 57 global description 58 lines = data.split('\n') 59 for i in range(0, len(lines)): 60 if nukeDescription: 61 description = '' 62 nukeDescription = False 63 line = lines[i] 64 trimLine = line.strip() 65 66 if trimLine.startswith('#'): 67 if len(description): 68 description += '\n' 69 description += trimLine[2:] 70 continue 71 else: 72 nukeDescription = True 73 74 if len(trimLine) == 0: 75 continue 76 77 match = re.compile(r'^(experimental )?(deprecated )?domain (.*)').match(line) 78 if match: 79 domain = createItem({'domain' : match.group(3)}, match.group(1), match.group(2)) 80 protocol['domains'].append(domain) 81 continue 82 83 match = re.compile(r'^ depends on ([^\s]+)').match(line) 84 if match: 85 if 'dependencies' not in domain: 86 domain['dependencies'] = [] 87 domain['dependencies'].append(match.group(1)) 88 continue 89 90 match = re.compile(r'^ (experimental )?(deprecated )?type (.*) extends (array of )?([^\s]+)').match(line) 91 if match: 92 if 'types' not in domain: 93 domain['types'] = [] 94 item = createItem({'id': match.group(3)}, match.group(1), match.group(2)) 95 assignType(item, match.group(5), match.group(4), map_binary_to_string) 96 domain['types'].append(item) 97 continue 98 99 match = re.compile(r'^ (experimental )?(deprecated )?(command|event) (.*)').match(line) 100 if match: 101 list = [] 102 if match.group(3) == 'command': 103 if 'commands' in domain: 104 list = domain['commands'] 105 else: 106 list = domain['commands'] = [] 107 else: 108 if 'events' in domain: 109 list = domain['events'] 110 else: 111 list = domain['events'] = [] 112 113 item = createItem({}, match.group(1), match.group(2), match.group(4)) 114 list.append(item) 115 continue 116 117 match = re.compile(r'^ (experimental )?(deprecated )?(optional )?(array of )?([^\s]+) ([^\s]+)').match(line) 118 if match: 119 param = createItem({}, match.group(1), match.group(2), match.group(6)) 120 if match.group(3): 121 param['optional'] = True 122 assignType(param, match.group(5), match.group(4), map_binary_to_string) 123 if match.group(5) == 'enum': 124 enumliterals = param['enum'] = [] 125 subitems.append(param) 126 continue 127 128 match = re.compile(r'^ (parameters|returns|properties)').match(line) 129 if match: 130 subitems = item[match.group(1)] = [] 131 continue 132 133 match = re.compile(r'^ enum').match(line) 134 if match: 135 enumliterals = item['enum'] = [] 136 continue 137 138 match = re.compile(r'^version').match(line) 139 if match: 140 continue 141 142 match = re.compile(r'^ major (\d+)').match(line) 143 if match: 144 protocol['version']['major'] = match.group(1) 145 continue 146 147 match = re.compile(r'^ minor (\d+)').match(line) 148 if match: 149 protocol['version']['minor'] = match.group(1) 150 continue 151 152 match = re.compile(r'^ redirect ([^\s]+)').match(line) 153 if match: 154 item['redirect'] = match.group(1) 155 continue 156 157 match = re.compile(r'^ ( )?[^\s]+$').match(line) 158 if match: 159 # enum literal 160 enumliterals.append(trimLine) 161 continue 162 163 print('Error in %s:%s, illegal token: \t%s' % (file_name, i, line)) 164 sys.exit(1) 165 return protocol 166 167 168def loads(data, file_name, map_binary_to_string=False): 169 if file_name.endswith(".pdl"): 170 return parse(data, file_name, map_binary_to_string) 171 return json.loads(data) 172