Add sw/host/vendor/google_verible_verilog_syntax_py
Signed-off-by: Mariusz Glebocki <mglebocki@antmicro.com>
diff --git a/python-requirements.txt b/python-requirements.txt
index 38dca7e..4dedce5 100644
--- a/python-requirements.txt
+++ b/python-requirements.txt
@@ -3,6 +3,7 @@
# SPDX-License-Identifier: Apache-2.0
# Keep sorted.
+anytree # dependency of sw/host/vendor/google_verible_verilog_syntax_py
enlighten
flake8
gitpython
diff --git a/sw/host/vendor/google_verible_verilog_syntax_py.lock.hjson b/sw/host/vendor/google_verible_verilog_syntax_py.lock.hjson
new file mode 100644
index 0000000..dc47172
--- /dev/null
+++ b/sw/host/vendor/google_verible_verilog_syntax_py.lock.hjson
@@ -0,0 +1,15 @@
+// Copyright lowRISC contributors.
+// Licensed under the Apache License, Version 2.0, see LICENSE for details.
+// SPDX-License-Identifier: Apache-2.0
+
+// This file is generated by the util/vendor script. Please do not modify it
+// manually.
+
+{
+ upstream:
+ {
+ url: https://github.com/google/verible.git
+ rev: ed1f3a9577047b801854fb36dd14ebe0aecbdddc
+ only_subdir: verilog/tools/syntax/export_json_examples
+ }
+}
diff --git a/sw/host/vendor/google_verible_verilog_syntax_py.vendor.hjson b/sw/host/vendor/google_verible_verilog_syntax_py.vendor.hjson
new file mode 100644
index 0000000..0aeafca
--- /dev/null
+++ b/sw/host/vendor/google_verible_verilog_syntax_py.vendor.hjson
@@ -0,0 +1,13 @@
+// Copyright lowRISC contributors.
+// Licensed under the Apache License, Version 2.0, see LICENSE for details.
+// SPDX-License-Identifier: Apache-2.0
+{
+ name: "google_verible_verilog_syntax_py",
+ target_dir: "google_verible_verilog_syntax_py",
+
+ upstream: {
+ url: "https://github.com/google/verible.git",
+ rev: "v0.0-943-ged1f3a9",
+ only_subdir: "verilog/tools/syntax/export_json_examples",
+ },
+}
diff --git a/sw/host/vendor/google_verible_verilog_syntax_py/BUILD b/sw/host/vendor/google_verible_verilog_syntax_py/BUILD
new file mode 100644
index 0000000..85da887
--- /dev/null
+++ b/sw/host/vendor/google_verible_verilog_syntax_py/BUILD
@@ -0,0 +1,55 @@
+load("@rules_python//python:defs.bzl", "py_test", "py_library", "py_binary")
+
+licenses(["notice"])
+
+py_library(
+ name = "verible_verilog_syntax_py",
+ srcs = ["verible_verilog_syntax.py"],
+ srcs_version = "PY3",
+ imports = ["."],
+ data = ["//verilog/tools/syntax:verible-verilog-syntax"],
+ deps = [
+ "@python_anytree//:anytree",
+ "//third_party/py/dataclasses",
+ ],
+)
+
+py_test(
+ name = "verible_verilog_syntax_py_test",
+ size = "small",
+ srcs = ["verible_verilog_syntax_test.py"],
+ srcs_version = "PY3",
+ python_version = "PY3",
+ main = "verible_verilog_syntax_test.py",
+ deps = [":verible_verilog_syntax_py"],
+ data = ["//verilog/tools/syntax:verible-verilog-syntax"],
+ args = ["$(location //verilog/tools/syntax:verible-verilog-syntax)"],
+)
+
+py_binary(
+ name = "print_modules",
+ srcs = ["print_modules.py"],
+ srcs_version = "PY3",
+ python_version = "PY3",
+ main = "print_modules.py",
+ deps = [
+ ":verible_verilog_syntax_py",
+ "@python_anytree//:anytree",
+ ],
+ data = ["//verilog/tools/syntax:verible-verilog-syntax"],
+ args = ["$(location //verilog/tools/syntax:verible-verilog-syntax)"],
+)
+
+py_binary(
+ name = "print_tree",
+ srcs = ["print_tree.py"],
+ srcs_version = "PY3",
+ python_version = "PY3",
+ main = "print_tree.py",
+ deps = [
+ ":verible_verilog_syntax_py",
+ "@python_anytree//:anytree",
+ ],
+ data = ["//verilog/tools/syntax:verible-verilog-syntax"],
+ args = ["$(location //verilog/tools/syntax:verible-verilog-syntax)"],
+)
diff --git a/sw/host/vendor/google_verible_verilog_syntax_py/print_modules.py b/sw/host/vendor/google_verible_verilog_syntax_py/print_modules.py
new file mode 100755
index 0000000..2974e66
--- /dev/null
+++ b/sw/host/vendor/google_verible_verilog_syntax_py/print_modules.py
@@ -0,0 +1,134 @@
+#!/usr/bin/env python3
+# Copyright 2017-2020 The Verible Authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+"""Print module name, ports, parameters and imports.
+
+Usage: print_modules.py PATH_TO_VERIBLE_VERILOG_SYNTAX \\
+ VERILOG_FILE [VERILOG_FILE [...]]
+
+This example shows how to use ``verible-verilog-syntax --export_json ...``
+output to extract information about module declarations found in System Verilog
+source files. Extracted information:
+
+* module name
+* module port names
+* module parameter names
+* module imports
+* module header code
+"""
+
+import sys
+
+import anytree
+
+import verible_verilog_syntax
+
+
+def process_file_data(path: str, data: verible_verilog_syntax.SyntaxData):
+ """Print information about modules found in SystemVerilog file.
+
+ This function uses verible_verilog_syntax.Node methods to find module
+ declarations and specific tokens containing following information:
+
+ * module name
+ * module port names
+ * module parameter names
+ * module imports
+ * module header code
+
+ Args:
+ path: Path to source file (used only for informational purposes)
+ data: Parsing results returned by one of VeribleVerilogSyntax' parse_*
+ methods.
+ """
+ if not data.tree:
+ return
+
+ modules_info = []
+
+ # Collect information about each module declaration in the file
+ for module in data.tree.iter_find_all({"tag": "kModuleDeclaration"}):
+ module_info = {
+ "header_text": "",
+ "name": "",
+ "ports": [],
+ "parameters": [],
+ "imports": [],
+ }
+
+ # Find module header
+ header = module.find({"tag": "kModuleHeader"})
+ if not header:
+ continue
+ module_info["header_text"] = header.text
+
+ # Find module name
+ name = header.find({"tag": ["SymbolIdentifier", "EscapedIdentifier"]},
+ iter_=anytree.PreOrderIter)
+ if not name:
+ continue
+ module_info["name"] = name.text
+
+ # Get the list of ports
+ for port in header.iter_find_all({"tag": ["kPortDeclaration", "kPort"]}):
+ port_id = port.find({"tag": ["SymbolIdentifier", "EscapedIdentifier"]})
+ module_info["ports"].append(port_id.text)
+
+ # Get the list of parameters
+ for param in header.iter_find_all({"tag": ["kParamDeclaration"]}):
+ param_id = param.find({"tag": ["SymbolIdentifier", "EscapedIdentifier"]})
+ module_info["parameters"].append(param_id.text)
+
+ # Get the list of imports
+ for pkg in module.iter_find_all({"tag": ["kPackageImportItem"]}):
+ module_info["imports"].append(pkg.text)
+
+ modules_info.append(module_info)
+
+ # Print results
+ if len(modules_info) > 0:
+ print(f"\033[1;97;7m{path} \033[0m\n")
+
+ def print_entry(key, values):
+ fmt_values = [f"\033[92m{v}\033[0m" for v in values]
+ value_part = (f"\n\033[33m// {' '*len(key)}".join(fmt_values)
+ or "\033[90m-\033[0m")
+ print(f"\033[33m// \033[93m{key}{value_part}")
+
+ for module_info in modules_info:
+ print_entry("name: ", [module_info["name"]])
+ print_entry("ports: ", module_info["ports"])
+ print_entry("parameters: ", module_info["parameters"])
+ print_entry("imports: ", module_info["imports"])
+ print(f"\033[97m{module_info['header_text']}\033[0m\n")
+
+
+def main():
+ if len(sys.argv) < 3:
+ print(f"Usage: {sys.argv[0]} PATH_TO_VERIBLE_VERILOG_SYNTAX " +
+ "VERILOG_FILE [VERILOG_FILE [...]]")
+ return 1
+
+ parser_path = sys.argv[1]
+ files = sys.argv[2:]
+
+ parser = verible_verilog_syntax.VeribleVerilogSyntax(executable=parser_path)
+ data = parser.parse_files(files)
+
+ for file_path, file_data in data.items():
+ process_file_data(file_path, file_data)
+
+
+if __name__ == "__main__":
+ sys.exit(main())
diff --git a/sw/host/vendor/google_verible_verilog_syntax_py/print_tree.py b/sw/host/vendor/google_verible_verilog_syntax_py/print_tree.py
new file mode 100755
index 0000000..057c6da
--- /dev/null
+++ b/sw/host/vendor/google_verible_verilog_syntax_py/print_tree.py
@@ -0,0 +1,69 @@
+#!/usr/bin/env python3
+# Copyright 2017-2020 The Verible Authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+"""Pretty-print Verilog Concrete Syntax Tree
+
+Usage: print_tree.py PATH_TO_VERIBLE_VERILOG_SYNTAX \\
+ VERILOG_FILE [VERILOG_FILE [...]]
+
+Visualizes tree generated by ``verible-verilog-syntax --export_json ...``.
+Values enclosed in ``[]`` are node tags. ``@(S-E)`` marks token's start (S)
+and end (E) byte offsets in source code. When a token's text in source code
+is not the same as its tag, the text is printed in single quotes.
+"""
+
+import sys
+
+import anytree
+
+import verible_verilog_syntax
+
+
+def process_file_data(path: str, data: verible_verilog_syntax.SyntaxData):
+ """Print tree representation to the console.
+
+ The function uses anytree module (which is a base of a tree implementation
+ used in verible_verilog_syntax) method to print syntax tree representation
+ to the console.
+
+ Args:
+ path: Path to source file (used only for informational purposes)
+ data: Parsing results returned by one of VeribleVerilogSyntax' parse_*
+ methods.
+ """
+ print(f"\033[1;97;7m{path} \033[0m\n")
+ if data.tree:
+ for prefix, _, node in anytree.RenderTree(data.tree):
+ print(f"\033[90m{prefix}\033[0m{node.to_formatted_string()}")
+ print()
+
+
+def main():
+ if len(sys.argv) < 3:
+ print(f"Usage: {sys.argv[0]} PATH_TO_VERIBLE_VERILOG_SYNTAX " +
+ "VERILOG_FILE [VERILOG_FILE [...]]")
+ return 1
+
+ parser_path = sys.argv[1]
+ files = sys.argv[2:]
+
+ parser = verible_verilog_syntax.VeribleVerilogSyntax(executable=parser_path)
+ data = parser.parse_files(files, options={"gen_tree": True})
+
+ for file_path, file_data in data.items():
+ process_file_data(file_path, file_data)
+
+
+if __name__ == "__main__":
+ sys.exit(main())
diff --git a/sw/host/vendor/google_verible_verilog_syntax_py/verible_verilog_syntax.py b/sw/host/vendor/google_verible_verilog_syntax_py/verible_verilog_syntax.py
new file mode 100644
index 0000000..f7cb4f0
--- /dev/null
+++ b/sw/host/vendor/google_verible_verilog_syntax_py/verible_verilog_syntax.py
@@ -0,0 +1,531 @@
+# Copyright 2017-2020 The Verible Authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+"""Wrapper for ``verible-verilog-syntax --export_json``"""
+
+import collections
+import json
+import re
+import subprocess
+from typing import Any, Callable, Dict, Iterable, List, Optional, Union
+
+import anytree
+import dataclasses
+
+_CSI_SEQUENCE = re.compile("\033\\[.*?m")
+
+
+def _colorize(formats: List[str], strings: List[str]) -> str:
+ result = ""
+ fi = 0
+ for s in strings:
+ result += f"\033[{formats[fi]}m{s}\033[0m"
+ fi = (fi+1) % len(formats)
+ return result
+
+
+# Type aliases
+
+CallableFilter = Callable[["Node"], bool]
+KeyValueFilter = Dict[str, Union[str, List[str]]]
+TreeIterator = Union["_TreeIteratorBase", anytree.iterators.AbstractIter]
+
+
+# Custom tree iterators with an option for reverse children iteration
+
+class _TreeIteratorBase:
+ def __init__(self, tree: "Node",
+ filter_: Optional[CallableFilter] = None,
+ reverse_children: bool = False):
+ self.tree = tree
+ self.reverse_children = reverse_children
+ self.filter_ = filter_ if filter_ else lambda n: True
+
+ def __iter__(self) -> Iterable["Node"]:
+ yield from self._iter_tree(self.tree)
+
+ def _iter_children(self, tree: Optional["Node"]) -> Iterable["Node"]:
+ if not tree or not hasattr(tree, "children"):
+ return []
+ return tree.children if not self.reverse_children \
+ else reversed(tree.children)
+
+ def _iter_tree(self, tree: Optional["Node"]) -> Iterable["Node"]:
+ raise NotImplementedError("Subclass must implement '_iter_tree' method")
+
+
+class PreOrderTreeIterator(_TreeIteratorBase):
+ def _iter_tree(self, tree: Optional["Node"]) -> Iterable["Node"]:
+ if self.filter_(tree):
+ yield tree
+ for child in self._iter_children(tree):
+ yield from self._iter_tree(child)
+
+
+class PostOrderTreeIterator(_TreeIteratorBase):
+ def _iter_tree(self, tree: Optional["Node"]) -> Iterable["Node"]:
+ for child in self._iter_children(tree):
+ yield from self._iter_tree(child)
+ if self.filter_(tree):
+ yield tree
+
+
+class LevelOrderTreeIterator(_TreeIteratorBase):
+ def _iter_tree(self, tree: Optional["Node"]) -> Iterable["Node"]:
+ queue = collections.deque([tree])
+ while len(queue) > 0:
+ n = queue.popleft()
+ if self.filter_(n):
+ yield n
+ queue.extend(self._iter_children(n))
+
+
+class Node(anytree.NodeMixin):
+ """Base VeribleVerilogSyntax syntax tree node.
+
+ Attributes:
+ parent (Optional[Node]): Parent node.
+ """
+ def __init__(self, parent: Optional["Node"] = None):
+ self.parent = parent
+
+ @property
+ def syntax_data(self) -> Optional["SyntaxData"]:
+ """Parent SyntaxData"""
+ return self.parent.syntax_data if self.parent else None
+
+ @property
+ def start(self) -> Optional[int]:
+ """Byte offset of node's first character in source text"""
+ raise NotImplementedError("Subclass must implement 'start' property")
+
+ @property
+ def end(self) -> Optional[int]:
+ """Byte offset of a character just past the node in source text."""
+ raise NotImplementedError("Subclass must implement 'end' property")
+
+ @property
+ def text(self) -> str:
+ """Source code fragment spanning all tokens in a node."""
+ start = self.start
+ end = self.end
+ sd = self.syntax_data
+ if ((start is not None) and (end is not None) and sd and sd.source_code
+ and end <= len(sd.source_code)):
+ return sd.source_code[start:end].decode("utf-8")
+ return ""
+
+ def __repr__(self) -> str:
+ return _CSI_SEQUENCE.sub("", self.to_formatted_string())
+
+ def to_formatted_string(self) -> str:
+ """Print node representation formatted for printing in terminal."""
+ return super().__repr__()
+
+
+class BranchNode(Node):
+ """Syntax tree branch node
+
+ Attributes:
+ tag (str): Node tag.
+ children (Optional[Node]): Child nodes.
+ """
+ def __init__(self, tag: str, parent: Optional[Node] = None,
+ children: Optional[List[Node]] = None):
+ super().__init__(parent)
+ self.tag = tag
+ self.children = children if children is not None else []
+
+ @property
+ def start(self) -> Optional[int]:
+ first_token = self.find(lambda n: isinstance(n, TokenNode),
+ iter_=PostOrderTreeIterator)
+ return first_token.start if first_token else None
+
+ @property
+ def end(self) -> Optional[int]:
+ last_token = self.find(lambda n: isinstance(n, TokenNode),
+ iter_=PostOrderTreeIterator, reverse_children=True)
+ return last_token.end if last_token else None
+
+ def iter_find_all(self, filter_: Union[CallableFilter, KeyValueFilter, None],
+ max_count: int = 0,
+ iter_: TreeIterator = LevelOrderTreeIterator,
+ **kwargs) -> Iterable[Node]:
+ """Iterate all nodes matching specified filter.
+
+ Args:
+ filter_: Describes what to search for. Might be:
+ * Callable taking Node as an argument and returning True for accepted
+ nodes.
+ * Dict mapping Node attribute names to searched value or list of
+ searched values.
+ max_count: Stop searching after finding that many matching nodes.
+ iter_: Tree iterator. Decides in what order nodes are visited.
+
+ Yields:
+ Nodes matching specified filter.
+ """
+ def as_list(v):
+ return v if isinstance(v, list) else [v]
+
+ if filter_ and not callable(filter_):
+ filters = filter_
+ def f(node):
+ for attr,value in filters.items():
+ if not hasattr(node, attr):
+ return False
+ if getattr(node, attr) not in as_list(value):
+ return False
+ return True
+ filter_ = f
+
+ for node in iter_(self, filter_, **kwargs):
+ yield node
+ max_count -= 1
+ if max_count == 0:
+ break
+
+ def find(self, filter_: Union[CallableFilter, KeyValueFilter, None],
+ iter_: TreeIterator = LevelOrderTreeIterator, **kwargs) \
+ -> Optional[Node]:
+ """Find node matching specified filter.
+
+ Args:
+ filter_: Describes what to search for. Might be:
+ * Callable taking Node as an argument and returning True for accepted
+ node.
+ * Dict mapping Node attribute names to searched value or list of
+ searched values.
+ iter_: Tree iterator. Decides in what order nodes are visited.
+
+ Returns:
+ First Node matching filter.
+ """
+ return next(self.iter_find_all(filter_, max_count=1, iter_=iter_,
+ **kwargs), None)
+
+ def find_all(self, filter_: Union[CallableFilter, KeyValueFilter, None],
+ max_count: int = 0, iter_: TreeIterator = LevelOrderTreeIterator,
+ **kwargs) -> List[Node]:
+ """Find all nodes matching specified filter.
+
+ Args:
+ filter_: Describes what to search for. Might be:
+ * Callable taking Node as an argument and returning True for accepted
+ nodes.
+ * Dict mapping Node attribute names to searched value or list of
+ searched values.
+ max_count: Stop searching after finding that many matching nodes.
+ iter_: Tree iterator. Decides in what order nodes are visited.
+
+ Returns:
+ List of nodes matching specified filter.
+ """
+ return list(self.iter_find_all(filter_, max_count=max_count, iter_=iter_,
+ **kwargs))
+
+ def to_formatted_string(self) -> str:
+ tag = self.tag if self.tag == repr(self.tag)[1:-1] else repr(self.tag)
+ return _colorize(["37", "1;97"], ["[", tag, "]"])
+
+
+class RootNode(BranchNode):
+ """Syntax tree root node."""
+ def __init__(self, tag: str, syntax_data: Optional["SyntaxData"] = None,
+ children: Optional[List[Node]] = None):
+ super().__init__(tag, None, children)
+ self._syntax_data = syntax_data
+
+ @property
+ def syntax_data(self) -> Optional["SyntaxData"]:
+ return self._syntax_data
+
+
+class LeafNode(Node):
+ """Syntax tree leaf node.
+
+ This specific class is used for null nodes.
+ """
+ @property
+ def start(self) -> None:
+ """Byte offset of token's first character in source text"""
+ return None
+
+ @property
+ def end(self) -> None:
+ """Byte offset of a character just past the token in source text."""
+ return None
+
+ def to_formatted_string(self) -> str:
+ return _colorize(["90"], ["null"])
+
+
+class TokenNode(LeafNode):
+ """Tree node with token data
+
+ Represents single token in a syntax tree.
+
+ Attributes:
+ tag (str): Token tag.
+ """
+
+ def __init__(self, tag: str, start: int, end: int,
+ parent: Optional[Node] = None):
+ super().__init__(parent)
+ self.tag = tag
+ self._start = start
+ self._end = end
+
+ @property
+ def start(self) -> int:
+ return self._start
+
+ @property
+ def end(self) -> int:
+ return self._end
+
+ def to_formatted_string(self) -> str:
+ tag = self.tag if self.tag == repr(self.tag)[1:-1] else repr(self.tag)
+ parts = [
+ _colorize(["37", "1;97"], ["[", tag, "]"]),
+ _colorize(["33", "93"], ["@(", self.start, "-", self.end, ")"]),
+ ]
+ text = self.text
+ if self.tag != text:
+ parts.append(_colorize(["32", "92"], ["'", repr(text)[1:-1], "'"]))
+ return " ".join(parts)
+
+
+class Token:
+ """Token data
+
+ Represents single token in tokens and rawtokens lists.
+
+ Attributes:
+ tag (str): Token tag.
+ start (int): Byte offset of token's first character in source text.
+ end (int): Byte offset of a character just past the token in source text.
+ syntax_data (Optional["SyntaxData"]): Parent SyntaxData.
+ """
+
+ def __init__(self, tag: str, start: int, end: int,
+ syntax_data: Optional["SyntaxData"] = None):
+ self.tag = tag
+ self.start = start
+ self.end = end
+ self.syntax_data = syntax_data
+
+ @property
+ def text(self) -> str:
+ """Token text in source code."""
+ sd = self.syntax_data
+ if sd and sd.source_code and self.end <= len(sd.source_code):
+ return sd.source_code[self.start:self.end].decode("utf-8")
+ return ""
+
+ def __repr__(self) -> str:
+ return _CSI_SEQUENCE.sub("", self.to_formatted_string())
+
+ def to_formatted_string(self) -> str:
+ tag = self.tag if self.tag == repr(self.tag)[1:-1] else repr(self.tag)
+ parts = [
+ _colorize(["37", "1;97"], ["[", tag, "]"]),
+ _colorize(["33", "93"], ["@(", self.start, "-", self.end, ")"]),
+ _colorize(["32", "92"], ["'", repr(self.text)[1:-1], "'"]),
+ ]
+ return " ".join(parts)
+
+
+@dataclasses.dataclass
+class Error:
+ line: int
+ column: int
+ phase: str
+ message: str = ""
+
+
+@dataclasses.dataclass
+class SyntaxData:
+ source_code: Optional[str] = None
+ tree: Optional[RootNode] = None
+ tokens: Optional[List[Token]] = None
+ rawtokens: Optional[List[Token]] = None
+ errors: Optional[List[Error]] = None
+
+
+class VeribleVerilogSyntax:
+ """``verible-verilog-syntax`` wrapper.
+
+ This class provides methods for running ``verible-verilog-syntax`` and
+ transforming its output into Python data structures.
+
+ Args:
+ executable: path to ``verible-verilog-syntax`` binary.
+ """
+
+ def __init__(self, executable: str = "verible-verilog-syntax"):
+ self.executable = executable
+
+ @staticmethod
+ def _transform_tree(tree, data: SyntaxData, skip_null: bool) -> RootNode:
+ def transform(tree):
+ if tree is None:
+ return None
+ if "children" in tree:
+ children = [
+ transform(child) or LeafNode()
+ for child in tree["children"]
+ if not (skip_null and child is None)
+ ]
+ tag = tree["tag"]
+ return BranchNode(tag, children=children)
+ tag = tree["tag"]
+ start = tree["start"]
+ end = tree["end"]
+ return TokenNode(tag, start, end)
+
+ if "children" not in tree:
+ return None
+
+ children = [
+ transform(child) or LeafNode()
+ for child in tree["children"]
+ if not (skip_null and child is None)
+ ]
+ tag = tree["tag"]
+ return RootNode(tag, syntax_data=data, children=children)
+
+
+ @staticmethod
+ def _transform_tokens(tokens, data: SyntaxData) -> List[Token]:
+ return [Token(t["tag"], t["start"], t["end"], data) for t in tokens]
+
+
+ @staticmethod
+ def _transform_errors(tokens) -> List[Error]:
+ return [Error(t["line"], t["column"], t["phase"], t.get("message", None))
+ for t in tokens]
+
+ def _parse(self, paths: List[str], input_: str = None,
+ options: Dict[str, Any] = None) -> Dict[str, SyntaxData]:
+ """Common implementation of parse_* methods"""
+ options = {
+ "gen_tree": True,
+ "skip_null": False,
+ "gen_tokens": False,
+ "gen_rawtokens": False,
+ **(options or {}),
+ }
+
+ args = ["-export_json"]
+ if options["gen_tree"]:
+ args.append("-printtree")
+ if options["gen_tokens"]:
+ args.append("-printtokens")
+ if options["gen_rawtokens"]:
+ args.append("-printrawtokens")
+
+ proc = subprocess.run([self.executable, *args , *paths],
+ stdout=subprocess.PIPE,
+ input=input_,
+ encoding="utf-8",
+ check=False)
+
+ json_data = json.loads(proc.stdout)
+ data = {}
+ for file_path, file_json in json_data.items():
+ file_data = SyntaxData()
+
+ if file_path == "-":
+ file_data.source_code = input_.encode("utf-8")
+ else:
+ with open(file_path, "rb") as f:
+ file_data.source_code = f.read()
+
+ if "tree" in file_json:
+ file_data.tree = VeribleVerilogSyntax._transform_tree(
+ file_json["tree"], file_data, options["skip_null"])
+
+ if "tokens" in file_json:
+ file_data.tokens = VeribleVerilogSyntax._transform_tokens(
+ file_json["tokens"], file_data)
+
+ if "rawtokens" in file_json:
+ file_data.rawtokens = VeribleVerilogSyntax._transform_tokens(
+ file_json["rawtokens"], file_data)
+
+ if "errors" in file_json:
+ file_data.errors = VeribleVerilogSyntax._transform_errors(
+ file_json["errors"])
+
+ data[file_path] = file_data
+
+ return data
+
+ def parse_files(self, paths: List[str], options: Dict[str, Any] = None) \
+ -> Dict[str, SyntaxData]:
+ """Parse multiple SystemVerilog files.
+
+ Args:
+ paths: list of paths to files to parse.
+ options: dict with parsing options.
+ Available options:
+ gen_tree (boolean): whether to generate syntax tree.
+ skip_null (boolean): null nodes won't be stored in a tree if True.
+ gen_tokens (boolean): whether to generate tokens list.
+ gen_rawtokens (boolean): whether to generate raw token list.
+ By default only ``gen_tree`` is True.
+
+ Returns:
+ A dict that maps file names to their parsing results in SyntaxData object.
+ """
+ return self._parse(paths, options = options)
+
+ def parse_file(self, path: str, options: Dict[str, Any] = None) \
+ -> Optional[SyntaxData]:
+ """Parse single SystemVerilog file.
+
+ Args:
+ path: path to a file to parse.
+ options: dict with parsing options.
+ Available options:
+ gen_tree (boolean): whether to generate syntax tree.
+ skip_null (boolean): null nodes won't be stored in a tree if True.
+ gen_tokens (boolean): whether to generate tokens list.
+ gen_rawtokens (boolean): whether to generate raw token list.
+ By default only ``gen_tree`` is True.
+
+ Returns:
+ Parsing results in SyntaxData object.
+ """
+ return self._parse([path], options = options).get(path, None)
+
+ def parse_string(self, string: str, options: Dict[str, Any] = None) \
+ -> Optional[SyntaxData]:
+ """Parse a string with SystemVerilog code.
+
+ Args:
+ string: SystemVerilog code to parse.
+ options: dict with parsing options.
+ Available options:
+ gen_tree (boolean): whether to generate syntax tree.
+ skip_null (boolean): null nodes won't be stored in a tree if True.
+ gen_tokens (boolean): whether to generate tokens list.
+ gen_rawtokens (boolean): whether to generate raw token list.
+ By default only ``gen_tree`` is True.
+
+ Returns:
+ Parsing results in SyntaxData object.
+ """
+ return self._parse(["-"], input_=string, options=options).get("-", None)
diff --git a/sw/host/vendor/google_verible_verilog_syntax_py/verible_verilog_syntax_test.py b/sw/host/vendor/google_verible_verilog_syntax_py/verible_verilog_syntax_test.py
new file mode 100755
index 0000000..c183bee
--- /dev/null
+++ b/sw/host/vendor/google_verible_verilog_syntax_py/verible_verilog_syntax_test.py
@@ -0,0 +1,272 @@
+#!/usr/bin/env python3
+# Copyright 2017-2020 The Verible Authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+"""VeribleVerilogSyntax test"""
+
+
+import sys
+import tempfile
+import unittest
+
+import verible_verilog_syntax
+
+
+class VeribleVerilogSyntaxTest(unittest.TestCase):
+ def setUp(self):
+ if len(sys.argv) > 1:
+ self.parser = verible_verilog_syntax.VeribleVerilogSyntax(
+ executable=sys.argv[1])
+ else:
+ self.parser = verible_verilog_syntax.VeribleVerilogSyntax()
+
+ self.assertIsNotNone(self.parser)
+
+ def create_temp_file(self, content):
+ tmp = tempfile.NamedTemporaryFile(mode="w", suffix=".sv")
+ tmp.write(content)
+ tmp.flush()
+ return tmp
+
+
+class TestParseMethods(VeribleVerilogSyntaxTest):
+ def test_parse_string(self):
+ data = self.parser.parse_string("module X(); endmodule;")
+ self.assertIsNotNone(data)
+ self.assertIsNotNone(data.tree)
+ self.assertIsNone(data.errors)
+
+ def test_parse_string_with_all_options(self):
+ data = self.parser.parse_string("module X(); endmodule;", options={
+ "gen_tree": True,
+ "gen_tokens": True,
+ "gen_rawtokens": True,
+ })
+ self.assertIsNotNone(data)
+ self.assertIsNotNone(data.tree)
+ self.assertIsNotNone(data.tokens)
+ self.assertIsNotNone(data.rawtokens)
+ self.assertIsNone(data.errors)
+
+ def test_parse_string_error(self):
+ data = self.parser.parse_string("endmodule X(); module;")
+ self.assertIsNotNone(data)
+ self.assertIsNotNone(data.errors)
+
+ def test_parse_file(self):
+ file_ = self.create_temp_file("module X(); endmodule;")
+ data = self.parser.parse_file(file_.name)
+ self.assertIsNotNone(data)
+ self.assertIsNotNone(data.tree)
+ self.assertIsNone(data.errors)
+
+ def test_parse_file_with_all_options(self):
+ file_ = self.create_temp_file("module X(); endmodule;")
+ data = self.parser.parse_file(file_.name, options={
+ "gen_tree": True,
+ "gen_tokens": True,
+ "gen_rawtokens": True,
+ })
+ self.assertIsNotNone(data)
+ self.assertIsNotNone(data.tree)
+ self.assertIsNotNone(data.tokens)
+ self.assertIsNotNone(data.rawtokens)
+ self.assertIsNone(data.errors)
+
+ def test_parse_file_error(self):
+ file_ = self.create_temp_file("endmodule X(); module;")
+ data = self.parser.parse_file(file_.name)
+ self.assertIsNotNone(data)
+ self.assertIsNotNone(data.errors)
+
+ def test_parse_files(self):
+ files = [
+ self.create_temp_file("module X(); endmodule;"),
+ self.create_temp_file("module Y(); endmodule;"),
+ ]
+ data = self.parser.parse_files([f.name for f in files])
+ self.assertIsNotNone(data)
+ for f in files:
+ self.assertIsNotNone(data[f.name])
+ self.assertIsNotNone(data[f.name].tree)
+ self.assertIsNone(data[f.name].errors)
+
+ def test_parse_files_with_all_options(self):
+ files = [
+ self.create_temp_file("module X(); endmodule;"),
+ self.create_temp_file("module Y(); endmodule;"),
+ ]
+ data = self.parser.parse_files([f.name for f in files], options={
+ "gen_tree": True,
+ "gen_tokens": True,
+ "gen_rawtokens": True,
+ })
+ self.assertIsNotNone(data)
+ for f in files:
+ self.assertIsNotNone(data[f.name])
+ self.assertIsNotNone(data[f.name].tree)
+ self.assertIsNotNone(data[f.name].tokens)
+ self.assertIsNotNone(data[f.name].rawtokens)
+ self.assertIsNone(data[f.name].errors)
+
+ def test_parse_files_error(self):
+ # One file with, and one without errors
+ files = [
+ self.create_temp_file("endmodule X(); module;"),
+ self.create_temp_file("module Y(); endmodule;"),
+ ]
+ data = self.parser.parse_files([f.name for f in files])
+ self.assertIsNotNone(data)
+ for f in files:
+ self.assertIsNotNone(data[f.name])
+
+ self.assertIsNotNone(data[files[0].name].errors)
+ self.assertIsNone(data[files[1].name].errors)
+
+
+class TestTree(VeribleVerilogSyntaxTest):
+ def setUp(self):
+ super().setUp()
+ data = self.parser.parse_string(
+ "module ModuleName#(parameter PARAM_NAME=42)\n" +
+ " (input portIn, output portOut);\n" +
+ " import import_pkg_name::*;\n" +
+ " wire wireName;\n" +
+ "endmodule;\n" +
+ "module OtherModule; endmodule;\n")
+ self.assertIsNotNone(data)
+ self.assertIsNotNone(data.tree)
+ self.tree = data.tree
+
+ def test_find(self):
+ header = self.tree.find({"tag": "kModuleHeader"})
+ self.assertIsNotNone(header)
+ module_name = header.find({"tag": "SymbolIdentifier"})
+ self.assertIsNotNone(module_name)
+ self.assertEqual(module_name.text, "ModuleName")
+ nonexistent = header.find({"tag": "SomeUndefinedTag"})
+ self.assertIsNone(nonexistent)
+
+ def test_find_all(self):
+ headers = self.tree.find_all({"tag": "kModuleHeader"})
+ self.assertEqual(len(headers), 2)
+
+ identifiers = self.tree.find_all({"tag": "SymbolIdentifier"})
+ self.assertEqual(len(identifiers), 7)
+
+ some_identifiers = self.tree.find_all({"tag": "SymbolIdentifier"},
+ max_count=4)
+ self.assertEqual(len(some_identifiers), 4)
+
+ def test_iter_find_all(self):
+ identifiers = [n.text
+ for n
+ in self.tree.iter_find_all({"tag": "SymbolIdentifier"})]
+ self.assertIn("ModuleName", identifiers)
+ self.assertIn("PARAM_NAME", identifiers)
+ self.assertIn("OtherModule", identifiers)
+
+ def test_custom_filter(self):
+ def tokens_past_135_byte(node):
+ return (isinstance(node, verible_verilog_syntax.TokenNode)
+ and node.start > 135)
+
+ other_module_tokens = self.tree.find_all(tokens_past_135_byte)
+ self.assertGreaterEqual(len(other_module_tokens), 5)
+ for token in other_module_tokens:
+ self.assertGreater(token.start, 135)
+
+ def test_search_order(self):
+ level_order = self.tree.find_all({"tag": "SymbolIdentifier"})
+ depth_order = self.tree.find_all({"tag": "SymbolIdentifier"},
+ iter_=verible_verilog_syntax.PreOrderTreeIterator)
+
+ def check_items_order(iterable, items_to_check):
+ index = 0
+ for item in iterable:
+ if items_to_check[index] == item:
+ index += 1
+ if index == len(items_to_check):
+ return True
+ return False
+
+ self.assertTrue(check_items_order([n.text for n in level_order],
+ ["ModuleName", "OtherModule", "portIn"]))
+ self.assertTrue(check_items_order([n.text for n in depth_order],
+ ["ModuleName", "portIn", "OtherModule"]))
+
+ def test_node_properties(self):
+ header = self.tree.find({"tag": "kModuleHeader"})
+ self.assertIsNotNone(header)
+
+ module_kw = header.find({"tag": "module"})
+ self.assertEqual(module_kw.text, "module")
+ self.assertEqual(module_kw.start, 0)
+ self.assertEqual(module_kw.end, 6)
+
+ semicolon = header.find({"tag": ";"})
+ self.assertEqual(semicolon.text, ";")
+ self.assertEqual(semicolon.start, 78)
+ self.assertEqual(semicolon.end, 79)
+
+ self.assertEqual(header.start, module_kw.start)
+ self.assertEqual(header.end, semicolon.end)
+ self.assertTrue(header.text.startswith("module"))
+ self.assertTrue(header.text.endswith(");"))
+
+
+class TestTokens(VeribleVerilogSyntaxTest):
+ def test_tokens(self):
+ data = self.parser.parse_string(
+ "module X(input portIn, output portOut); endmodule;", options={
+ "gen_tree": False,
+ "gen_tokens": True,
+ })
+
+ self.assertIsNotNone(data)
+ self.assertIsNotNone(data.tokens)
+
+ identifiers = [t for t in data.tokens if t.tag == "SymbolIdentifier"]
+
+ module_name = identifiers[0]
+ self.assertEqual(module_name.text, "X")
+ self.assertEqual(module_name.start, 7)
+ self.assertEqual(module_name.end, 8)
+
+ texts = [t.text for t in identifiers]
+ self.assertSequenceEqual(texts, ["X", "portIn", "portOut"])
+
+
+ def test_rawtokens(self):
+ data = self.parser.parse_string(
+ "module X(input portIn, output portOut); endmodule;", options={
+ "gen_tree": False,
+ "gen_rawtokens": True,
+ })
+
+ self.assertIsNotNone(data)
+ self.assertIsNotNone(data.rawtokens)
+
+ identifiers = [t for t in data.rawtokens if t.tag == "SymbolIdentifier"]
+
+ module_name = identifiers[0]
+ self.assertEqual(module_name.text, "X")
+ self.assertEqual(module_name.start, 7)
+ self.assertEqual(module_name.end, 8)
+
+ texts = [t.text for t in identifiers]
+ self.assertSequenceEqual(texts, ["X", "portIn", "portOut"])
+
+
+if __name__ == "__main__":
+ unittest.main(argv=sys.argv[0:1], verbosity=2)