# 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 from collections import OrderedDict import json import re import sys from typing import Any description = "" primitiveTypes = [ "integer", "number", "boolean", "string", "object", "any", "array", "binary", ] def assignType( item: dict, type: str, is_array: bool = False, map_binary_to_string: bool = False ) -> None: if is_array: item["type"] = "array" item["items"] = 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: dict, experimental: bool | Any, deprecated: bool | Any, name: str | Any = None ) -> OrderedDict[str, Any]: result = 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: str, file_name: str, map_binary_to_string: bool = False ) -> OrderedDict[str, Any]: protocol = OrderedDict() protocol["version"] = 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: str, file_name: str, map_binary_to_string: bool = False ) -> OrderedDict[str, Any] | Any: if file_name.endswith(".pdl"): return parse(data, file_name, map_binary_to_string) return json.loads(data)