pdl.py 5.0 KB

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