pyVHDLModelTreesitter 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- pyvhdlmodeltreesitter-0.1.0/.gitignore +5 -0
- pyvhdlmodeltreesitter-0.1.0/LICENSE +13 -0
- pyvhdlmodeltreesitter-0.1.0/PKG-INFO +34 -0
- pyvhdlmodeltreesitter-0.1.0/README.md +4 -0
- pyvhdlmodeltreesitter-0.1.0/pyproject.toml +119 -0
- pyvhdlmodeltreesitter-0.1.0/src/pyVHDLModelTreesitter/Base.py +29 -0
- pyvhdlmodeltreesitter-0.1.0/src/pyVHDLModelTreesitter/DesignUnit.py +278 -0
- pyvhdlmodeltreesitter-0.1.0/src/pyVHDLModelTreesitter/Expression.py +80 -0
- pyvhdlmodeltreesitter-0.1.0/src/pyVHDLModelTreesitter/Interface.py +298 -0
- pyvhdlmodeltreesitter-0.1.0/src/pyVHDLModelTreesitter/Name.py +46 -0
- pyvhdlmodeltreesitter-0.1.0/src/pyVHDLModelTreesitter/Symbol.py +62 -0
- pyvhdlmodeltreesitter-0.1.0/src/pyVHDLModelTreesitter/__init__.py +228 -0
- pyvhdlmodeltreesitter-0.1.0/src/pyVHDLModelTreesitter/_error.py +39 -0
- pyvhdlmodeltreesitter-0.1.0/src/pyVHDLModelTreesitter/py.typed +0 -0
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
Copyright 2025 Dominic Adam Walters
|
|
2
|
+
|
|
3
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
you may not use this file except in compliance with the License.
|
|
5
|
+
You may obtain a copy of the License at
|
|
6
|
+
|
|
7
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
|
|
9
|
+
Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
See the License for the specific language governing permissions and
|
|
13
|
+
limitations under the License.
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: pyVHDLModelTreesitter
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: An implementation of pyVHDLModel using treesitter
|
|
5
|
+
Project-URL: Repository, https://gitlab.com/dawalters/pyVHDLModelTreesitter
|
|
6
|
+
Author: Dominic Adam Walters
|
|
7
|
+
License-File: LICENSE
|
|
8
|
+
Keywords: language,model,treesitter,vhdl
|
|
9
|
+
Classifier: Development Status :: 3 - Alpha
|
|
10
|
+
Classifier: Intended Audience :: Developers
|
|
11
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
12
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
14
|
+
Requires-Python: >=3.12
|
|
15
|
+
Requires-Dist: pyvhdlmodel==0.33.1
|
|
16
|
+
Requires-Dist: tree-sitter-vhdl==1.5.0
|
|
17
|
+
Requires-Dist: tree-sitter==0.25.2
|
|
18
|
+
Provides-Extra: dev
|
|
19
|
+
Requires-Dist: coverage==7.13.5; extra == 'dev'
|
|
20
|
+
Requires-Dist: hatch==1.16.5; extra == 'dev'
|
|
21
|
+
Requires-Dist: mypy==1.20.1; extra == 'dev'
|
|
22
|
+
Requires-Dist: pre-commit==4.5.1; extra == 'dev'
|
|
23
|
+
Requires-Dist: pytest-cov==7.1.0; extra == 'dev'
|
|
24
|
+
Requires-Dist: pytest-sugar==1.1.1; extra == 'dev'
|
|
25
|
+
Requires-Dist: pytest-xdist==3.8.0; extra == 'dev'
|
|
26
|
+
Requires-Dist: pytest==9.0.3; extra == 'dev'
|
|
27
|
+
Requires-Dist: ruff==0.15.10; extra == 'dev'
|
|
28
|
+
Requires-Dist: sphinx==9.1.0; extra == 'dev'
|
|
29
|
+
Description-Content-Type: text/markdown
|
|
30
|
+
|
|
31
|
+
# pyVHDLModelTreesitter
|
|
32
|
+
|
|
33
|
+
`pyVHDLModelTreesitter` is an implementation of the `pyVHDLModel` API using
|
|
34
|
+
`tree-sitter-vhdl`.
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["hatchling"]
|
|
3
|
+
build-backend = "hatchling.build"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "pyVHDLModelTreesitter"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
description = "An implementation of pyVHDLModel using treesitter"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.12"
|
|
11
|
+
dependencies = [
|
|
12
|
+
"pyVHDLModel == 0.33.1",
|
|
13
|
+
"tree-sitter == 0.25.2",
|
|
14
|
+
"tree-sitter-vhdl == 1.5.0", # TODO: try and update this?
|
|
15
|
+
]
|
|
16
|
+
authors = [
|
|
17
|
+
{ name = "Dominic Adam Walters" },
|
|
18
|
+
]
|
|
19
|
+
keywords = ["language", "model", "treesitter", "vhdl"]
|
|
20
|
+
classifiers = [
|
|
21
|
+
"Development Status :: 3 - Alpha",
|
|
22
|
+
"License :: OSI Approved :: MIT License",
|
|
23
|
+
"Programming Language :: Python :: 3 :: Only",
|
|
24
|
+
"Programming Language :: Python :: 3.12",
|
|
25
|
+
"Intended Audience :: Developers",
|
|
26
|
+
]
|
|
27
|
+
|
|
28
|
+
[project.optional-dependencies]
|
|
29
|
+
dev = [
|
|
30
|
+
"coverage == 7.13.5",
|
|
31
|
+
"hatch == 1.16.5",
|
|
32
|
+
"mypy == 1.20.1",
|
|
33
|
+
"pre-commit == 4.5.1",
|
|
34
|
+
"pytest == 9.0.3",
|
|
35
|
+
"pytest-cov == 7.1.0",
|
|
36
|
+
"pytest-sugar == 1.1.1",
|
|
37
|
+
"pytest-xdist == 3.8.0",
|
|
38
|
+
"ruff == 0.15.10",
|
|
39
|
+
"sphinx == 9.1.0",
|
|
40
|
+
]
|
|
41
|
+
|
|
42
|
+
[project.urls]
|
|
43
|
+
Repository = "https://gitlab.com/dawalters/pyVHDLModelTreesitter"
|
|
44
|
+
|
|
45
|
+
[tool.codespell]
|
|
46
|
+
ignore-words-list = "inout"
|
|
47
|
+
|
|
48
|
+
[tool.coverage.html]
|
|
49
|
+
directory = ".htmlcov"
|
|
50
|
+
|
|
51
|
+
[tool.coverage.report]
|
|
52
|
+
exclude_also = [
|
|
53
|
+
"@overload",
|
|
54
|
+
"if TYPE_CHECKING:",
|
|
55
|
+
]
|
|
56
|
+
|
|
57
|
+
[tool.coverage.run]
|
|
58
|
+
branch = true
|
|
59
|
+
|
|
60
|
+
[tool.coverage.xml]
|
|
61
|
+
output = ".coverage.xml"
|
|
62
|
+
|
|
63
|
+
[tool.hatch.build]
|
|
64
|
+
include = [
|
|
65
|
+
"src/pyVHDLModelTreesitter/*.py",
|
|
66
|
+
"src/pyVHDLModelTreesitter/py.typed",
|
|
67
|
+
]
|
|
68
|
+
|
|
69
|
+
[tool.hatch.build.targets.wheel]
|
|
70
|
+
packages = ["src/pyVHDLModelTreesitter"]
|
|
71
|
+
|
|
72
|
+
[tool.hatch.envs.hatch-test]
|
|
73
|
+
dependencies = [
|
|
74
|
+
"coverage == 7.13.5",
|
|
75
|
+
"pytest == 9.0.3",
|
|
76
|
+
"pytest-cov == 7.1.0",
|
|
77
|
+
"pytest-sugar == 1.1.1",
|
|
78
|
+
"pytest-xdist == 3.8.0",
|
|
79
|
+
]
|
|
80
|
+
|
|
81
|
+
[[tool.hatch.envs.hatch-test.matrix]]
|
|
82
|
+
python = ["3.12"]
|
|
83
|
+
|
|
84
|
+
[tool.mypy]
|
|
85
|
+
strict = true
|
|
86
|
+
warn_unreachable = true
|
|
87
|
+
implicit_reexport = true
|
|
88
|
+
|
|
89
|
+
[tool.pytest.ini_options]
|
|
90
|
+
addopts = "--capture=fd -n 0 --cov=pyVHDLModelTreesitter --cov-report html --cov-report xml --verbose --junitxml=.junit.xml -o junit_family=legacy"
|
|
91
|
+
testpaths = [
|
|
92
|
+
"tests",
|
|
93
|
+
]
|
|
94
|
+
|
|
95
|
+
[tool.ruff]
|
|
96
|
+
unsafe-fixes = true
|
|
97
|
+
|
|
98
|
+
[tool.ruff.lint]
|
|
99
|
+
fixable = ["ALL"]
|
|
100
|
+
ignore = [
|
|
101
|
+
"CPY001", # I don't want copyright notices in every file
|
|
102
|
+
"D203", # I don't want blank lines before class docstrings
|
|
103
|
+
"D212", # I want docstring opening lines to be after the """
|
|
104
|
+
"DOC201", # I don't want to mandate documenting return type with typehints
|
|
105
|
+
"FIX002", # I want to be able to type TODO
|
|
106
|
+
"N999", # I want to use the same structure as pyVHDLModel
|
|
107
|
+
"TD002", # I don't want TODOs to need authors
|
|
108
|
+
"TD003", # I don't want TODOs to need issue IDs
|
|
109
|
+
]
|
|
110
|
+
preview = true
|
|
111
|
+
select = [
|
|
112
|
+
"ALL",
|
|
113
|
+
]
|
|
114
|
+
unfixable = [
|
|
115
|
+
"T201", # Don't fix print statements
|
|
116
|
+
]
|
|
117
|
+
|
|
118
|
+
[tool.ruff.lint.flake8-self]
|
|
119
|
+
ignore-names = ["_AddEntity"]
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
"""Submodule mirroring modified aspects of pyVHDLModel.Base."""
|
|
2
|
+
|
|
3
|
+
__all__ = [
|
|
4
|
+
"parse_as_mode",
|
|
5
|
+
]
|
|
6
|
+
|
|
7
|
+
import pyVHDLModel
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def parse_as_mode(string: str) -> pyVHDLModel.Base.Mode | None:
|
|
11
|
+
"""
|
|
12
|
+
Try and convert incorrect inputs.
|
|
13
|
+
|
|
14
|
+
This will accept any mix of the following:
|
|
15
|
+
- Incorrect capitalisation
|
|
16
|
+
- "IN"
|
|
17
|
+
- "iN"
|
|
18
|
+
- "in"
|
|
19
|
+
- Split words with whitespace
|
|
20
|
+
- "in out"
|
|
21
|
+
"""
|
|
22
|
+
# TODO: make a PR on pyVHDLModel to add this
|
|
23
|
+
string = "".join(string.split()).lower().capitalize()
|
|
24
|
+
if string == "Inout":
|
|
25
|
+
string = "InOut"
|
|
26
|
+
try:
|
|
27
|
+
return pyVHDLModel.Base.Mode[string]
|
|
28
|
+
except IndexError:
|
|
29
|
+
return None
|
|
@@ -0,0 +1,278 @@
|
|
|
1
|
+
"""Submodule mirroring modified aspects of pyVHDLModel.DesignUnit."""
|
|
2
|
+
|
|
3
|
+
__all__ = [
|
|
4
|
+
"Entity",
|
|
5
|
+
"LibraryClause",
|
|
6
|
+
"UseClause",
|
|
7
|
+
]
|
|
8
|
+
|
|
9
|
+
from collections.abc import Iterable
|
|
10
|
+
from functools import cmp_to_key
|
|
11
|
+
from typing import Self, cast
|
|
12
|
+
|
|
13
|
+
import pyVHDLModel
|
|
14
|
+
import tree_sitter
|
|
15
|
+
from pyVHDLModel.DesignUnit import LibraryClause as BaseLibraryClause
|
|
16
|
+
from pyVHDLModel.DesignUnit import UseClause as BaseUseClause
|
|
17
|
+
|
|
18
|
+
from pyVHDLModelTreesitter._error import ExtraNodeError, NodeTextIsNoneError
|
|
19
|
+
from pyVHDLModelTreesitter.Interface import (
|
|
20
|
+
GenericConstantInterfaceItem,
|
|
21
|
+
PortSignalInterfaceItem,
|
|
22
|
+
)
|
|
23
|
+
from pyVHDLModelTreesitter.Name import SelectedName, SimpleName
|
|
24
|
+
from pyVHDLModelTreesitter.Symbol import LibraryReferenceSymbol, PackageReferenceSymbol
|
|
25
|
+
from pyVHDLModelTreesitter.treesitter import get_node_or_raise, get_nodes_or_raise
|
|
26
|
+
from pyVHDLModelTreesitter.treesitter.query import (
|
|
27
|
+
EntityDeclaration,
|
|
28
|
+
InterfaceDeclarations,
|
|
29
|
+
LibraryClauses,
|
|
30
|
+
LibraryNamespaces,
|
|
31
|
+
SelectedNames,
|
|
32
|
+
UseClauses,
|
|
33
|
+
)
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
class LibraryClause(BaseLibraryClause): # noqa: D101
|
|
37
|
+
@classmethod
|
|
38
|
+
def from_treesitter_node(
|
|
39
|
+
cls,
|
|
40
|
+
node: tree_sitter.Node,
|
|
41
|
+
) -> list[Self]:
|
|
42
|
+
"""
|
|
43
|
+
Create one or more LibraryClauses from a treesitter node.
|
|
44
|
+
|
|
45
|
+
Raises
|
|
46
|
+
------
|
|
47
|
+
NodeTextIsNoneError:
|
|
48
|
+
If the `library_namespace` treesitter node contains no text.
|
|
49
|
+
|
|
50
|
+
"""
|
|
51
|
+
captures = LibraryClauses.capture(node)
|
|
52
|
+
library_clause_nodes = get_nodes_or_raise(
|
|
53
|
+
node,
|
|
54
|
+
captures,
|
|
55
|
+
"library-clause",
|
|
56
|
+
LibraryClauses.TEXT,
|
|
57
|
+
)
|
|
58
|
+
ret = []
|
|
59
|
+
for library_clause_node in library_clause_nodes:
|
|
60
|
+
captures = LibraryNamespaces.capture(library_clause_node)
|
|
61
|
+
library_namespace_nodes = get_nodes_or_raise(
|
|
62
|
+
node,
|
|
63
|
+
captures,
|
|
64
|
+
"library",
|
|
65
|
+
LibraryNamespaces.TEXT,
|
|
66
|
+
)
|
|
67
|
+
symbols = []
|
|
68
|
+
for library_namespace_node in library_namespace_nodes:
|
|
69
|
+
if library_namespace_node.text is None:
|
|
70
|
+
raise NodeTextIsNoneError(node, LibraryNamespaces.TEXT, "library")
|
|
71
|
+
library = library_namespace_node.text.decode()
|
|
72
|
+
symbols.append(LibraryReferenceSymbol(SimpleName(library)))
|
|
73
|
+
ret.append(cls(symbols))
|
|
74
|
+
return ret
|
|
75
|
+
|
|
76
|
+
def __eq__(self, other: object) -> bool: # noqa: D105
|
|
77
|
+
if not isinstance(other, LibraryClause):
|
|
78
|
+
return False
|
|
79
|
+
# Required for LSP completions
|
|
80
|
+
other = cast("LibraryClause", other) # type: ignore[redundant-cast]
|
|
81
|
+
return self._symbols == other._symbols
|
|
82
|
+
|
|
83
|
+
def __hash__(self) -> int: # noqa: D105
|
|
84
|
+
return hash(
|
|
85
|
+
(self._symbols,),
|
|
86
|
+
)
|
|
87
|
+
|
|
88
|
+
def __repr__(self) -> str: # noqa: D105
|
|
89
|
+
return f"{self.__class__.__name__}(_symbols={self._symbols!r}, )"
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
class UseClause(BaseUseClause): # noqa: D101
|
|
93
|
+
@classmethod
|
|
94
|
+
def from_treesitter_node(
|
|
95
|
+
cls,
|
|
96
|
+
node: tree_sitter.Node,
|
|
97
|
+
) -> list[Self]:
|
|
98
|
+
"""
|
|
99
|
+
Create one or more UseClauses from a treesitter node.
|
|
100
|
+
|
|
101
|
+
Raises
|
|
102
|
+
------
|
|
103
|
+
NodeTextIsNoneError:
|
|
104
|
+
If the `library_namespace` treesitter node contains no text.
|
|
105
|
+
|
|
106
|
+
"""
|
|
107
|
+
captures = UseClauses.capture(node)
|
|
108
|
+
use_clause_nodes = get_nodes_or_raise(
|
|
109
|
+
node,
|
|
110
|
+
captures,
|
|
111
|
+
"use-clause",
|
|
112
|
+
UseClauses.TEXT,
|
|
113
|
+
)
|
|
114
|
+
ret = []
|
|
115
|
+
for use_clause_node in use_clause_nodes:
|
|
116
|
+
captures = SelectedNames.capture(use_clause_node)
|
|
117
|
+
selected_name_nodes = get_nodes_or_raise(
|
|
118
|
+
node,
|
|
119
|
+
captures,
|
|
120
|
+
"selected-name",
|
|
121
|
+
SelectedNames.TEXT,
|
|
122
|
+
)
|
|
123
|
+
symbols = []
|
|
124
|
+
for selected_name_node in selected_name_nodes:
|
|
125
|
+
if selected_name_node.text is None:
|
|
126
|
+
raise NodeTextIsNoneError(node, SelectedNames.TEXT, "selected-name")
|
|
127
|
+
selected_name = selected_name_node.text.decode()
|
|
128
|
+
selected_name_parts = selected_name.split(".")
|
|
129
|
+
selected_name_obj: SimpleName | SelectedName = SimpleName(
|
|
130
|
+
selected_name_parts[0],
|
|
131
|
+
)
|
|
132
|
+
for part in selected_name_parts[1:]:
|
|
133
|
+
selected_name_obj = SelectedName(part, selected_name_obj)
|
|
134
|
+
symbols.append(PackageReferenceSymbol(selected_name_obj))
|
|
135
|
+
ret.append(cls(symbols))
|
|
136
|
+
return ret
|
|
137
|
+
|
|
138
|
+
def __eq__(self, other: object) -> bool: # noqa: D105
|
|
139
|
+
if not isinstance(other, UseClause):
|
|
140
|
+
return False
|
|
141
|
+
# Required for LSP completions
|
|
142
|
+
other = cast("UseClause", other) # type: ignore[redundant-cast]
|
|
143
|
+
return self._symbols == other._symbols
|
|
144
|
+
|
|
145
|
+
def __hash__(self) -> int: # noqa: D105
|
|
146
|
+
return hash(
|
|
147
|
+
(self._symbols,),
|
|
148
|
+
)
|
|
149
|
+
|
|
150
|
+
def __repr__(self) -> str: # noqa: D105
|
|
151
|
+
return f"{self.__class__.__name__}(_symbols={self._symbols!r}, )"
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
class Entity(pyVHDLModel.Entity): # noqa: D101
|
|
155
|
+
@classmethod
|
|
156
|
+
def from_treesitter_node(
|
|
157
|
+
cls,
|
|
158
|
+
node: tree_sitter.Node,
|
|
159
|
+
*,
|
|
160
|
+
contextItems: Iterable[LibraryClause | UseClause] = [], # noqa: N803
|
|
161
|
+
) -> Self:
|
|
162
|
+
"""
|
|
163
|
+
Create an Entity from a treesitter node.
|
|
164
|
+
|
|
165
|
+
Raises
|
|
166
|
+
------
|
|
167
|
+
ExtraNodeError:
|
|
168
|
+
If there is more than one `generics` or `ports` capture.
|
|
169
|
+
NodeTextIsNoneError:
|
|
170
|
+
If the `name` capture has no text.
|
|
171
|
+
|
|
172
|
+
"""
|
|
173
|
+
captures = EntityDeclaration.capture(node)
|
|
174
|
+
name_node = get_node_or_raise(
|
|
175
|
+
node,
|
|
176
|
+
captures,
|
|
177
|
+
"name",
|
|
178
|
+
EntityDeclaration.TEXT,
|
|
179
|
+
)
|
|
180
|
+
if name_node.text is None:
|
|
181
|
+
raise NodeTextIsNoneError(node, EntityDeclaration.TEXT, "name")
|
|
182
|
+
name_text = name_node.text.decode()
|
|
183
|
+
generics_nodes = captures.get("generics", [])
|
|
184
|
+
if len(generics_nodes) > 1:
|
|
185
|
+
raise ExtraNodeError(node, EntityDeclaration.TEXT, "generics")
|
|
186
|
+
ports_nodes = captures.get("ports", [])
|
|
187
|
+
if len(ports_nodes) > 1:
|
|
188
|
+
raise ExtraNodeError(node, EntityDeclaration.TEXT, "generics")
|
|
189
|
+
generic_items = None
|
|
190
|
+
if len(generics_nodes) == 1:
|
|
191
|
+
generics_node = generics_nodes[0]
|
|
192
|
+
captures = InterfaceDeclarations.capture(generics_node)
|
|
193
|
+
generic_items_with_nodes = [
|
|
194
|
+
(
|
|
195
|
+
GenericConstantInterfaceItem.from_treesitter_node(interface_node),
|
|
196
|
+
interface_node,
|
|
197
|
+
)
|
|
198
|
+
for interface_node in captures.get("interface", [])
|
|
199
|
+
]
|
|
200
|
+
generic_items = [
|
|
201
|
+
obj
|
|
202
|
+
for obj, _ in sorted(
|
|
203
|
+
generic_items_with_nodes,
|
|
204
|
+
key=cmp_to_key(lambda l, r: l[1].start_byte - r[1].start_byte), # type: ignore[index] # noqa: E741
|
|
205
|
+
)
|
|
206
|
+
]
|
|
207
|
+
port_items = None
|
|
208
|
+
if len(ports_nodes) == 1:
|
|
209
|
+
ports_node = ports_nodes[0]
|
|
210
|
+
captures = InterfaceDeclarations.capture(ports_node)
|
|
211
|
+
port_items_with_nodes = [
|
|
212
|
+
(
|
|
213
|
+
PortSignalInterfaceItem.from_treesitter_node(interface_node),
|
|
214
|
+
interface_node,
|
|
215
|
+
)
|
|
216
|
+
for interface_node in captures.get("interface", [])
|
|
217
|
+
]
|
|
218
|
+
port_items = [
|
|
219
|
+
obj
|
|
220
|
+
for obj, _ in sorted(
|
|
221
|
+
port_items_with_nodes,
|
|
222
|
+
key=cmp_to_key(lambda l, r: l[1].start_byte - r[1].start_byte), # type: ignore[index] # noqa: E741
|
|
223
|
+
)
|
|
224
|
+
]
|
|
225
|
+
return cls(
|
|
226
|
+
identifier=name_text,
|
|
227
|
+
contextItems=contextItems,
|
|
228
|
+
genericItems=generic_items,
|
|
229
|
+
portItems=port_items,
|
|
230
|
+
declaredItems=None, # TODO: implement
|
|
231
|
+
statements=None, # TODO: implement
|
|
232
|
+
documentation=None, # TODO: implement
|
|
233
|
+
allowBlackbox=None, # TODO: implement
|
|
234
|
+
)
|
|
235
|
+
|
|
236
|
+
def __eq__(self, other: object) -> bool: # noqa: D105
|
|
237
|
+
if not isinstance(other, Entity):
|
|
238
|
+
return False
|
|
239
|
+
# Required for LSP completions
|
|
240
|
+
other = cast("Entity", other) # type: ignore[redundant-cast]
|
|
241
|
+
return (
|
|
242
|
+
self._identifier == other._identifier
|
|
243
|
+
and self._contextItems == other._contextItems
|
|
244
|
+
and self._genericItems == other._genericItems
|
|
245
|
+
and self._portItems == other._portItems
|
|
246
|
+
and self._declaredItems == other._declaredItems
|
|
247
|
+
and self._statements == other._statements
|
|
248
|
+
and self._documentation == other._documentation
|
|
249
|
+
and self._allowBlackbox == other._allowBlackbox
|
|
250
|
+
)
|
|
251
|
+
|
|
252
|
+
def __hash__(self) -> int: # noqa: D105
|
|
253
|
+
return hash(
|
|
254
|
+
(
|
|
255
|
+
self._identifier,
|
|
256
|
+
self._contextItems,
|
|
257
|
+
self._genericItems,
|
|
258
|
+
self._portItems,
|
|
259
|
+
self._declaredItems,
|
|
260
|
+
self._statements,
|
|
261
|
+
self._documentation,
|
|
262
|
+
self._allowBlackbox,
|
|
263
|
+
),
|
|
264
|
+
)
|
|
265
|
+
|
|
266
|
+
def __repr__(self) -> str: # noqa: D105
|
|
267
|
+
return (
|
|
268
|
+
f"{self.__class__.__name__}("
|
|
269
|
+
f"_identifier={self._identifier!r}, "
|
|
270
|
+
f"_contextItems={self._contextItems!r}, "
|
|
271
|
+
f"_genericItems={self._genericItems!r}, "
|
|
272
|
+
f"_portItems={self._portItems!r}, "
|
|
273
|
+
f"_declaredItems={self._declaredItems}, "
|
|
274
|
+
f"_statements={self._statements}, "
|
|
275
|
+
f"_documentation={self._documentation}, "
|
|
276
|
+
f"_allowBlackbox={self._allowBlackbox}, "
|
|
277
|
+
")"
|
|
278
|
+
)
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
"""Submodule mirroring modified aspects of pyVHDLModel.Expression."""
|
|
2
|
+
|
|
3
|
+
__all__ = [
|
|
4
|
+
"EnumerationLiteral",
|
|
5
|
+
"FloatingPointLiteral",
|
|
6
|
+
"IntegerLiteral",
|
|
7
|
+
"StringLiteral",
|
|
8
|
+
]
|
|
9
|
+
|
|
10
|
+
from typing import cast
|
|
11
|
+
|
|
12
|
+
import pyVHDLModel
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class EnumerationLiteral(pyVHDLModel.Expression.EnumerationLiteral): # noqa: D101
|
|
16
|
+
def __eq__(self, other: object) -> bool: # noqa: D105
|
|
17
|
+
if not isinstance(other, EnumerationLiteral):
|
|
18
|
+
return False
|
|
19
|
+
# Required for LSP completions
|
|
20
|
+
other = cast("EnumerationLiteral", other) # type: ignore[redundant-cast]
|
|
21
|
+
return self._value.lower() == other._value.lower()
|
|
22
|
+
|
|
23
|
+
def __hash__(self) -> int: # noqa: D105
|
|
24
|
+
return hash(
|
|
25
|
+
(self._value,),
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
def __repr__(self) -> str: # noqa: D105
|
|
29
|
+
return f"{self.__class__.__name__}(_value={self._value!r}, )"
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
class FloatingPointLiteral(pyVHDLModel.Expression.FloatingPointLiteral): # noqa: D101
|
|
33
|
+
def __eq__(self, other: object) -> bool: # noqa: D105
|
|
34
|
+
if not isinstance(other, FloatingPointLiteral):
|
|
35
|
+
return False
|
|
36
|
+
# Required for LSP completions
|
|
37
|
+
other = cast("FloatingPointLiteral", other) # type: ignore[redundant-cast]
|
|
38
|
+
return self._value == other._value
|
|
39
|
+
|
|
40
|
+
def __hash__(self) -> int: # noqa: D105
|
|
41
|
+
return hash(
|
|
42
|
+
(self._value,),
|
|
43
|
+
)
|
|
44
|
+
|
|
45
|
+
def __repr__(self) -> str: # noqa: D105
|
|
46
|
+
return f"{self.__class__.__name__}(_value={self._value!r}, )"
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
class IntegerLiteral(pyVHDLModel.Expression.IntegerLiteral): # noqa: D101
|
|
50
|
+
def __eq__(self, other: object) -> bool: # noqa: D105
|
|
51
|
+
if not isinstance(other, IntegerLiteral):
|
|
52
|
+
return False
|
|
53
|
+
# Required for LSP completions
|
|
54
|
+
other = cast("IntegerLiteral", other) # type: ignore[redundant-cast]
|
|
55
|
+
return self._value == other._value
|
|
56
|
+
|
|
57
|
+
def __hash__(self) -> int: # noqa: D105
|
|
58
|
+
return hash(
|
|
59
|
+
(self._value,),
|
|
60
|
+
)
|
|
61
|
+
|
|
62
|
+
def __repr__(self) -> str: # noqa: D105
|
|
63
|
+
return f"{self.__class__.__name__}(_value={self._value!r}, )"
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
class StringLiteral(pyVHDLModel.Expression.StringLiteral): # noqa: D101
|
|
67
|
+
def __eq__(self, other: object) -> bool: # noqa: D105
|
|
68
|
+
if not isinstance(other, StringLiteral):
|
|
69
|
+
return False
|
|
70
|
+
# Required for LSP completions
|
|
71
|
+
other = cast("StringLiteral", other) # type: ignore[redundant-cast]
|
|
72
|
+
return self._value == other._value
|
|
73
|
+
|
|
74
|
+
def __hash__(self) -> int: # noqa: D105
|
|
75
|
+
return hash(
|
|
76
|
+
(self._value,),
|
|
77
|
+
)
|
|
78
|
+
|
|
79
|
+
def __repr__(self) -> str: # noqa: D105
|
|
80
|
+
return f"{self.__class__.__name__}(_value={self._value!r}, )"
|
|
@@ -0,0 +1,298 @@
|
|
|
1
|
+
"""Submodule defining the various interfaces that are available."""
|
|
2
|
+
|
|
3
|
+
__all__ = [
|
|
4
|
+
"GenericConstantInterfaceItem",
|
|
5
|
+
"PortSignalInterfaceItem",
|
|
6
|
+
]
|
|
7
|
+
|
|
8
|
+
from typing import Self, cast
|
|
9
|
+
|
|
10
|
+
import pyVHDLModel
|
|
11
|
+
from tree_sitter import Node
|
|
12
|
+
|
|
13
|
+
from pyVHDLModelTreesitter._error import (
|
|
14
|
+
ExtraNodeError,
|
|
15
|
+
MissingNodeError,
|
|
16
|
+
NodeTextIsNoneError,
|
|
17
|
+
)
|
|
18
|
+
from pyVHDLModelTreesitter.Base import parse_as_mode
|
|
19
|
+
from pyVHDLModelTreesitter.Expression import (
|
|
20
|
+
EnumerationLiteral,
|
|
21
|
+
FloatingPointLiteral,
|
|
22
|
+
IntegerLiteral,
|
|
23
|
+
StringLiteral,
|
|
24
|
+
)
|
|
25
|
+
from pyVHDLModelTreesitter.Name import SimpleName
|
|
26
|
+
from pyVHDLModelTreesitter.Symbol import SimpleSubtypeSymbol
|
|
27
|
+
from pyVHDLModelTreesitter.treesitter import get_node_or_raise
|
|
28
|
+
from pyVHDLModelTreesitter.treesitter.query import InterfaceDeclaration
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def parse_simple_expression(node: Node) -> pyVHDLModel.Expression.BaseExpression:
|
|
32
|
+
"""
|
|
33
|
+
Parse a 'simple_expression' to a model object.
|
|
34
|
+
|
|
35
|
+
Raises
|
|
36
|
+
------
|
|
37
|
+
MissingNodeError:
|
|
38
|
+
If `node` doesn't have exactly one child.
|
|
39
|
+
|
|
40
|
+
"""
|
|
41
|
+
if node.child_count != 1:
|
|
42
|
+
raise MissingNodeError(
|
|
43
|
+
node,
|
|
44
|
+
InterfaceDeclaration.TEXT,
|
|
45
|
+
"simple_expression",
|
|
46
|
+
)
|
|
47
|
+
expression_child_node = node.children[0]
|
|
48
|
+
assert expression_child_node.text is not None # noqa: S101
|
|
49
|
+
# TODO: support more simple expressions
|
|
50
|
+
if expression_child_node.type == "decimal_float":
|
|
51
|
+
return FloatingPointLiteral(
|
|
52
|
+
float(expression_child_node.text.decode()),
|
|
53
|
+
)
|
|
54
|
+
if expression_child_node.type == "decimal_integer":
|
|
55
|
+
return IntegerLiteral(
|
|
56
|
+
int(expression_child_node.text.decode()),
|
|
57
|
+
)
|
|
58
|
+
if expression_child_node.type == "library_constant_boolean":
|
|
59
|
+
return EnumerationLiteral(
|
|
60
|
+
expression_child_node.text.decode(),
|
|
61
|
+
)
|
|
62
|
+
if expression_child_node.type == "name":
|
|
63
|
+
assert expression_child_node.child_count == 1 # noqa: S101
|
|
64
|
+
name_node = expression_child_node.children[0]
|
|
65
|
+
assert name_node.text is not None # noqa: S101
|
|
66
|
+
if name_node.type == "library_constant_std_logic":
|
|
67
|
+
return EnumerationLiteral(
|
|
68
|
+
name_node.text.decode()[1:-1],
|
|
69
|
+
)
|
|
70
|
+
if expression_child_node.type == "string_literal":
|
|
71
|
+
return StringLiteral(
|
|
72
|
+
expression_child_node.text.decode()[1:-1],
|
|
73
|
+
)
|
|
74
|
+
message = f"Default of type '{expression_child_node.type}' is not yet implemented"
|
|
75
|
+
raise NotImplementedError(message)
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
class GenericConstantInterfaceItem(pyVHDLModel.Interface.GenericConstantInterfaceItem): # noqa: D101
|
|
79
|
+
@classmethod
|
|
80
|
+
def from_treesitter_node(
|
|
81
|
+
cls,
|
|
82
|
+
node: Node,
|
|
83
|
+
) -> Self:
|
|
84
|
+
"""
|
|
85
|
+
Create a GenericConstantInterfaceItem from a treesitter node.
|
|
86
|
+
|
|
87
|
+
Raises
|
|
88
|
+
------
|
|
89
|
+
MissingNodeError:
|
|
90
|
+
If `default` is present but isn't a simple expression.
|
|
91
|
+
NodeTextIsNoneError:
|
|
92
|
+
If the `name`, `type`, or `default` capture has no text.
|
|
93
|
+
ExtraNodeError:
|
|
94
|
+
If there is more than one `default`.
|
|
95
|
+
|
|
96
|
+
"""
|
|
97
|
+
captures = InterfaceDeclaration.capture(node)
|
|
98
|
+
name_node = get_node_or_raise(
|
|
99
|
+
node,
|
|
100
|
+
captures,
|
|
101
|
+
"name",
|
|
102
|
+
InterfaceDeclaration.TEXT,
|
|
103
|
+
)
|
|
104
|
+
type_node = get_node_or_raise(
|
|
105
|
+
node,
|
|
106
|
+
captures,
|
|
107
|
+
"type",
|
|
108
|
+
InterfaceDeclaration.TEXT,
|
|
109
|
+
)
|
|
110
|
+
default_nodes = captures.get("default")
|
|
111
|
+
|
|
112
|
+
if name_node.text is None:
|
|
113
|
+
raise NodeTextIsNoneError(node, InterfaceDeclaration.TEXT, "name")
|
|
114
|
+
name_text = name_node.text.decode()
|
|
115
|
+
|
|
116
|
+
mode = pyVHDLModel.Base.Mode.In
|
|
117
|
+
|
|
118
|
+
if type_node.text is None:
|
|
119
|
+
raise NodeTextIsNoneError(node, InterfaceDeclaration.TEXT, "type")
|
|
120
|
+
type_text = type_node.text.decode()
|
|
121
|
+
|
|
122
|
+
default_expression: pyVHDLModel.Expression.BaseExpression | None = None
|
|
123
|
+
if default_nodes is not None:
|
|
124
|
+
if len(default_nodes) > 1:
|
|
125
|
+
raise ExtraNodeError(node, InterfaceDeclaration.TEXT, "default")
|
|
126
|
+
default_node = default_nodes[0]
|
|
127
|
+
# TODO: support non simple expressions
|
|
128
|
+
assert default_node.child_count == 1 # noqa: S101
|
|
129
|
+
simple_expression_node = default_node.children[0]
|
|
130
|
+
assert simple_expression_node.type == "simple_expression" # noqa: S101
|
|
131
|
+
if simple_expression_node is None:
|
|
132
|
+
raise MissingNodeError(
|
|
133
|
+
default_node,
|
|
134
|
+
InterfaceDeclaration.TEXT,
|
|
135
|
+
"simple_expression",
|
|
136
|
+
)
|
|
137
|
+
default_expression = parse_simple_expression(simple_expression_node)
|
|
138
|
+
|
|
139
|
+
return cls(
|
|
140
|
+
# TODO: why is there a list of identifiers?
|
|
141
|
+
identifiers=[name_text],
|
|
142
|
+
mode=mode,
|
|
143
|
+
# TODO: support types that aren't simple
|
|
144
|
+
subtype=SimpleSubtypeSymbol(SimpleName(type_text)),
|
|
145
|
+
defaultExpression=default_expression,
|
|
146
|
+
documentation=None,
|
|
147
|
+
)
|
|
148
|
+
|
|
149
|
+
def __eq__(self, other: object) -> bool: # noqa: D105
|
|
150
|
+
if not isinstance(other, GenericConstantInterfaceItem):
|
|
151
|
+
return False
|
|
152
|
+
# Required for LSP completions
|
|
153
|
+
other = cast("GenericConstantInterfaceItem", other) # type: ignore[redundant-cast]
|
|
154
|
+
return (
|
|
155
|
+
self._identifiers == other._identifiers
|
|
156
|
+
and self._mode == other._mode
|
|
157
|
+
and self._subtype == other._subtype
|
|
158
|
+
and self._defaultExpression == other._defaultExpression
|
|
159
|
+
)
|
|
160
|
+
|
|
161
|
+
def __hash__(self) -> int: # noqa: D105
|
|
162
|
+
return hash(
|
|
163
|
+
(
|
|
164
|
+
self._identifiers,
|
|
165
|
+
self._mode,
|
|
166
|
+
self._subtype,
|
|
167
|
+
self._defaultExpression,
|
|
168
|
+
),
|
|
169
|
+
)
|
|
170
|
+
|
|
171
|
+
def __repr__(self) -> str: # noqa: D105
|
|
172
|
+
return (
|
|
173
|
+
f"{self.__class__.__name__}("
|
|
174
|
+
f"_identifiers={self._identifiers}, "
|
|
175
|
+
f"_mode={self._mode}, "
|
|
176
|
+
f"_subtype={self._subtype!r}, "
|
|
177
|
+
f"_defaultExpression={self._defaultExpression!r}, "
|
|
178
|
+
")"
|
|
179
|
+
)
|
|
180
|
+
|
|
181
|
+
|
|
182
|
+
class PortSignalInterfaceItem(pyVHDLModel.Interface.PortSignalInterfaceItem): # noqa: D101
|
|
183
|
+
@classmethod
|
|
184
|
+
def from_treesitter_node(
|
|
185
|
+
cls,
|
|
186
|
+
node: Node,
|
|
187
|
+
) -> Self:
|
|
188
|
+
"""
|
|
189
|
+
Create a PortSignalInterfaceItem from a treesitter node.
|
|
190
|
+
|
|
191
|
+
Raises
|
|
192
|
+
------
|
|
193
|
+
ExtraNodeError:
|
|
194
|
+
If `default` is captured more than once.
|
|
195
|
+
MissingNodeError:
|
|
196
|
+
If `default` is present but isn't a simple expression.
|
|
197
|
+
NodeTextIsNoneError:
|
|
198
|
+
If the `name`, `type`, `mode`, or `default` capture has no text.
|
|
199
|
+
ValueError:
|
|
200
|
+
If `mode` is not a valid mode.
|
|
201
|
+
|
|
202
|
+
"""
|
|
203
|
+
captures = InterfaceDeclaration.capture(node)
|
|
204
|
+
name_node = get_node_or_raise(
|
|
205
|
+
node,
|
|
206
|
+
captures,
|
|
207
|
+
"name",
|
|
208
|
+
InterfaceDeclaration.TEXT,
|
|
209
|
+
)
|
|
210
|
+
mode_node = get_node_or_raise(
|
|
211
|
+
node,
|
|
212
|
+
captures,
|
|
213
|
+
"mode",
|
|
214
|
+
InterfaceDeclaration.TEXT,
|
|
215
|
+
)
|
|
216
|
+
type_node = get_node_or_raise(
|
|
217
|
+
node,
|
|
218
|
+
captures,
|
|
219
|
+
"type",
|
|
220
|
+
InterfaceDeclaration.TEXT,
|
|
221
|
+
)
|
|
222
|
+
default_nodes = captures.get("default")
|
|
223
|
+
|
|
224
|
+
if name_node.text is None:
|
|
225
|
+
raise NodeTextIsNoneError(node, InterfaceDeclaration.TEXT, "name")
|
|
226
|
+
name_text = name_node.text.decode()
|
|
227
|
+
|
|
228
|
+
if mode_node.text is None:
|
|
229
|
+
raise NodeTextIsNoneError(node, InterfaceDeclaration.TEXT, "mode")
|
|
230
|
+
mode_text = mode_node.text.decode()
|
|
231
|
+
|
|
232
|
+
if type_node.text is None:
|
|
233
|
+
raise NodeTextIsNoneError(node, InterfaceDeclaration.TEXT, "type")
|
|
234
|
+
type_text = type_node.text.decode()
|
|
235
|
+
|
|
236
|
+
default_expression: pyVHDLModel.Expression.BaseExpression | None = None
|
|
237
|
+
if default_nodes is not None:
|
|
238
|
+
if len(default_nodes) > 1:
|
|
239
|
+
raise ExtraNodeError(node, InterfaceDeclaration.TEXT, "default")
|
|
240
|
+
default_node = default_nodes[0]
|
|
241
|
+
# TODO: support non simple expressions
|
|
242
|
+
assert default_node.child_count == 1 # noqa: S101
|
|
243
|
+
simple_expression_node = default_node.children[0]
|
|
244
|
+
assert simple_expression_node.type == "simple_expression" # noqa: S101
|
|
245
|
+
if simple_expression_node is None:
|
|
246
|
+
raise MissingNodeError(
|
|
247
|
+
default_node,
|
|
248
|
+
InterfaceDeclaration.TEXT,
|
|
249
|
+
"simple_expression",
|
|
250
|
+
)
|
|
251
|
+
default_expression = parse_simple_expression(simple_expression_node)
|
|
252
|
+
|
|
253
|
+
mode = parse_as_mode(mode_text)
|
|
254
|
+
if mode is None:
|
|
255
|
+
message = f"'{mode_text}' is not a valid VHDL interface mode"
|
|
256
|
+
raise ValueError(message)
|
|
257
|
+
|
|
258
|
+
return cls(
|
|
259
|
+
# TODO: why is there a list of identifiers?
|
|
260
|
+
identifiers=[name_text],
|
|
261
|
+
mode=mode,
|
|
262
|
+
# TODO: support types that aren't simple
|
|
263
|
+
subtype=SimpleSubtypeSymbol(SimpleName(type_text)),
|
|
264
|
+
defaultExpression=default_expression,
|
|
265
|
+
documentation=None,
|
|
266
|
+
)
|
|
267
|
+
|
|
268
|
+
def __eq__(self, other: object) -> bool: # noqa: D105
|
|
269
|
+
if not isinstance(other, PortSignalInterfaceItem):
|
|
270
|
+
return False
|
|
271
|
+
# Required for LSP completions
|
|
272
|
+
other = cast("PortSignalInterfaceItem", other) # type: ignore[redundant-cast]
|
|
273
|
+
return (
|
|
274
|
+
self._identifiers == other._identifiers
|
|
275
|
+
and self._mode == other._mode
|
|
276
|
+
and self._subtype == other._subtype
|
|
277
|
+
and self._defaultExpression == other._defaultExpression
|
|
278
|
+
)
|
|
279
|
+
|
|
280
|
+
def __hash__(self) -> int: # noqa: D105
|
|
281
|
+
return hash(
|
|
282
|
+
(
|
|
283
|
+
self._identifiers,
|
|
284
|
+
self._mode,
|
|
285
|
+
self._subtype,
|
|
286
|
+
self._defaultExpression,
|
|
287
|
+
),
|
|
288
|
+
)
|
|
289
|
+
|
|
290
|
+
def __repr__(self) -> str: # noqa: D105
|
|
291
|
+
return (
|
|
292
|
+
f"{self.__class__.__name__}("
|
|
293
|
+
f"_identifiers={self._identifiers}, "
|
|
294
|
+
f"_mode={self._mode}, "
|
|
295
|
+
f"_subtype={self._subtype!r}, "
|
|
296
|
+
f"_defaultExpression={self._defaultExpression!r}, "
|
|
297
|
+
")"
|
|
298
|
+
)
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
"""Submodule mirroring modified aspects of pyVHDLModel.Name."""
|
|
2
|
+
|
|
3
|
+
__all__ = [
|
|
4
|
+
"SelectedName",
|
|
5
|
+
"SimpleName",
|
|
6
|
+
]
|
|
7
|
+
|
|
8
|
+
from typing import cast
|
|
9
|
+
|
|
10
|
+
from pyVHDLModel.Name import (
|
|
11
|
+
SelectedName as BaseSelectedName,
|
|
12
|
+
)
|
|
13
|
+
from pyVHDLModel.Name import (
|
|
14
|
+
SimpleName as BaseSimpleName,
|
|
15
|
+
)
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class SimpleName(BaseSimpleName): # noqa: D101
|
|
19
|
+
def __eq__(self, other: object) -> bool: # noqa: D105
|
|
20
|
+
if not isinstance(other, SimpleName):
|
|
21
|
+
return False
|
|
22
|
+
# Required for LSP completions
|
|
23
|
+
other = cast("SimpleName", other) # type: ignore[redundant-cast]
|
|
24
|
+
return self._identifier == other._identifier
|
|
25
|
+
|
|
26
|
+
def __hash__(self) -> int: # noqa: D105
|
|
27
|
+
return hash(
|
|
28
|
+
(self._identifier,),
|
|
29
|
+
)
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
class SelectedName(BaseSelectedName): # noqa: D101
|
|
33
|
+
def __eq__(self, other: object) -> bool: # noqa: D105
|
|
34
|
+
if not isinstance(other, SelectedName):
|
|
35
|
+
return False
|
|
36
|
+
# Required for LSP completions
|
|
37
|
+
other = cast("SelectedName", other) # type: ignore[redundant-cast]
|
|
38
|
+
return self._identifier == other._identifier and self._prefix == other._prefix
|
|
39
|
+
|
|
40
|
+
def __hash__(self) -> int: # noqa: D105
|
|
41
|
+
return hash(
|
|
42
|
+
(
|
|
43
|
+
self._identifier,
|
|
44
|
+
self._prefix,
|
|
45
|
+
),
|
|
46
|
+
)
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
"""Submodule mirroring modified aspects of pyVHDLModel.Symbol."""
|
|
2
|
+
|
|
3
|
+
__all__ = [
|
|
4
|
+
"LibraryReferenceSymbol",
|
|
5
|
+
"PackageReferenceSymbol",
|
|
6
|
+
"SimpleSubtypeSymbol",
|
|
7
|
+
]
|
|
8
|
+
|
|
9
|
+
from typing import cast
|
|
10
|
+
|
|
11
|
+
import pyVHDLModel
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class LibraryReferenceSymbol(pyVHDLModel.Symbol.LibraryReferenceSymbol): # noqa: D101
|
|
15
|
+
def __eq__(self, other: object) -> bool: # noqa: D105
|
|
16
|
+
if not isinstance(other, LibraryReferenceSymbol):
|
|
17
|
+
return False
|
|
18
|
+
# Required for LSP completions
|
|
19
|
+
other = cast("LibraryReferenceSymbol", other) # type: ignore[redundant-cast]
|
|
20
|
+
return self._name == other._name
|
|
21
|
+
|
|
22
|
+
def __hash__(self) -> int: # noqa: D105
|
|
23
|
+
return hash(
|
|
24
|
+
(self._name,),
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
def __repr__(self) -> str: # noqa: D105
|
|
28
|
+
return f"{self.__class__.__name__}(_name={self._name}, )"
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
class PackageReferenceSymbol(pyVHDLModel.Symbol.PackageReferenceSymbol): # noqa: D101
|
|
32
|
+
def __eq__(self, other: object) -> bool: # noqa: D105
|
|
33
|
+
if not isinstance(other, PackageReferenceSymbol):
|
|
34
|
+
return False
|
|
35
|
+
# Required for LSP completions
|
|
36
|
+
other = cast("PackageReferenceSymbol", other) # type: ignore[redundant-cast]
|
|
37
|
+
return self._name == other._name
|
|
38
|
+
|
|
39
|
+
def __hash__(self) -> int: # noqa: D105
|
|
40
|
+
return hash(
|
|
41
|
+
(self._name,),
|
|
42
|
+
)
|
|
43
|
+
|
|
44
|
+
def __repr__(self) -> str: # noqa: D105
|
|
45
|
+
return f"{self.__class__.__name__}(_name={self._name}, )"
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
class SimpleSubtypeSymbol(pyVHDLModel.Symbol.SimpleSubtypeSymbol): # noqa: D101
|
|
49
|
+
def __eq__(self, other: object) -> bool: # noqa: D105
|
|
50
|
+
if not isinstance(other, SimpleSubtypeSymbol):
|
|
51
|
+
return False
|
|
52
|
+
# Required for LSP completions
|
|
53
|
+
other = cast("SimpleSubtypeSymbol", other) # type: ignore[redundant-cast]
|
|
54
|
+
return self._name == other._name
|
|
55
|
+
|
|
56
|
+
def __hash__(self) -> int: # noqa: D105
|
|
57
|
+
return hash(
|
|
58
|
+
(self._name,),
|
|
59
|
+
)
|
|
60
|
+
|
|
61
|
+
def __repr__(self) -> str: # noqa: D105
|
|
62
|
+
return f"{self.__class__.__name__}(_name={self._name}, )"
|
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
"""The pyVHDLModelTreesitter package."""
|
|
2
|
+
|
|
3
|
+
__all__: list[str] = [
|
|
4
|
+
"Design",
|
|
5
|
+
"DesignUnit",
|
|
6
|
+
"Document",
|
|
7
|
+
"Entity",
|
|
8
|
+
"Expression",
|
|
9
|
+
"ExtraNodeError",
|
|
10
|
+
"Interface",
|
|
11
|
+
"Library",
|
|
12
|
+
"MissingNodeError",
|
|
13
|
+
"Name",
|
|
14
|
+
"NodeTextIsNoneError",
|
|
15
|
+
"Symbol",
|
|
16
|
+
]
|
|
17
|
+
|
|
18
|
+
from ._error import ExtraNodeError, MissingNodeError, NodeTextIsNoneError # noqa: I001
|
|
19
|
+
from . import DesignUnit, Expression, Interface, Name, Symbol
|
|
20
|
+
from .DesignUnit import Entity
|
|
21
|
+
|
|
22
|
+
from pathlib import Path
|
|
23
|
+
from typing import Self, cast
|
|
24
|
+
|
|
25
|
+
from pyVHDLModel import (
|
|
26
|
+
Design as BaseDesign,
|
|
27
|
+
Document as BaseDocument,
|
|
28
|
+
Library as BaseLibrary,
|
|
29
|
+
)
|
|
30
|
+
from pyVHDLModelTreesitter.DesignUnit import LibraryClause, UseClause
|
|
31
|
+
from pyVHDLModelTreesitter.treesitter.query import PARSER, DesignUnits
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
class Library(BaseLibrary): # noqa: D101
|
|
35
|
+
def __eq__(self, other: object) -> bool: # noqa: D105
|
|
36
|
+
if not isinstance(other, Library):
|
|
37
|
+
return False
|
|
38
|
+
# Required for LSP completions
|
|
39
|
+
other = cast("Library", other) # type: ignore[redundant-cast]
|
|
40
|
+
return (
|
|
41
|
+
self._allowBlackbox == other._allowBlackbox
|
|
42
|
+
and self._contexts == other._contexts
|
|
43
|
+
and self._configurations == other._configurations
|
|
44
|
+
and self._entities == other._entities
|
|
45
|
+
and self._architectures == other._architectures
|
|
46
|
+
and self._packages == other._packages
|
|
47
|
+
and self._packageBodies == other._packageBodies
|
|
48
|
+
)
|
|
49
|
+
|
|
50
|
+
def __hash__(self) -> int: # noqa: D105
|
|
51
|
+
return hash(
|
|
52
|
+
(
|
|
53
|
+
self._allowBlackbox,
|
|
54
|
+
self._contexts,
|
|
55
|
+
self._configurations,
|
|
56
|
+
self._entities,
|
|
57
|
+
self._architectures,
|
|
58
|
+
self._packages,
|
|
59
|
+
self._packageBodies,
|
|
60
|
+
),
|
|
61
|
+
)
|
|
62
|
+
|
|
63
|
+
def __repr__(self) -> str: # noqa: D105
|
|
64
|
+
return (
|
|
65
|
+
f"{self.__class__.__name__}("
|
|
66
|
+
f"_allowBlackbox={self._allowBlackbox}, "
|
|
67
|
+
f"_contexts={self._contexts}, "
|
|
68
|
+
f"_configurations={self._configurations}, "
|
|
69
|
+
f"_entities={list(self._entities.values())!r}, "
|
|
70
|
+
f"_architectures={self._architectures}, "
|
|
71
|
+
f"_packages={self._packages}, "
|
|
72
|
+
f"_packageBodies={self._packageBodies}, "
|
|
73
|
+
")"
|
|
74
|
+
)
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
class Document(BaseDocument): # noqa: D101
|
|
78
|
+
@classmethod
|
|
79
|
+
def from_file(cls, path: str | Path) -> Self: # noqa: C901 # TODO: refactor
|
|
80
|
+
"""
|
|
81
|
+
Create a Document, and all ModelEntities it contains.
|
|
82
|
+
|
|
83
|
+
Raises
|
|
84
|
+
------
|
|
85
|
+
MissingNodeError:
|
|
86
|
+
- There are no `design_unit` nodes.
|
|
87
|
+
- Any `design_unit` node has no children.
|
|
88
|
+
NotImplementedError:
|
|
89
|
+
- If a node is encountered that isn't implemented.
|
|
90
|
+
|
|
91
|
+
"""
|
|
92
|
+
if isinstance(path, str):
|
|
93
|
+
path = Path(path)
|
|
94
|
+
document = cls(path)
|
|
95
|
+
|
|
96
|
+
tree = PARSER.parse(path.read_bytes())
|
|
97
|
+
node = tree.root_node
|
|
98
|
+
captures = DesignUnits.capture(node)
|
|
99
|
+
design_unit_captures = captures.get("design-unit")
|
|
100
|
+
if design_unit_captures is None:
|
|
101
|
+
raise MissingNodeError(node, DesignUnits.TEXT, "design-unit")
|
|
102
|
+
for design_unit_capture in design_unit_captures:
|
|
103
|
+
if len(design_unit_capture.children) == 0:
|
|
104
|
+
raise MissingNodeError(node, DesignUnits.TEXT, "design-unit")
|
|
105
|
+
design_unit_child = design_unit_capture.children[-1]
|
|
106
|
+
context_children = design_unit_capture.children[:-1]
|
|
107
|
+
context_items: list[LibraryClause | UseClause] = []
|
|
108
|
+
for context_child in context_children:
|
|
109
|
+
if context_child is None:
|
|
110
|
+
raise MissingNodeError(node, DesignUnits.TEXT, "design-unit")
|
|
111
|
+
if context_child.type == "library_clause":
|
|
112
|
+
context_items.extend(
|
|
113
|
+
LibraryClause.from_treesitter_node(context_child),
|
|
114
|
+
)
|
|
115
|
+
elif context_child.type == "use_clause":
|
|
116
|
+
context_items.extend(
|
|
117
|
+
UseClause.from_treesitter_node(context_child),
|
|
118
|
+
)
|
|
119
|
+
else:
|
|
120
|
+
message = f"Node of type '{context_child.type}' is not supported"
|
|
121
|
+
raise NotImplementedError(message)
|
|
122
|
+
if design_unit_child is None:
|
|
123
|
+
raise MissingNodeError(node, DesignUnits.TEXT, "design-unit")
|
|
124
|
+
if design_unit_child.type == "entity_declaration":
|
|
125
|
+
entity = Entity.from_treesitter_node(node, contextItems=context_items)
|
|
126
|
+
document._AddEntity(entity)
|
|
127
|
+
else:
|
|
128
|
+
message = f"Node of type '{design_unit_child.type}' is not supported"
|
|
129
|
+
raise NotImplementedError(message)
|
|
130
|
+
return document
|
|
131
|
+
|
|
132
|
+
def __eq__(self, other: object) -> bool: # noqa: D105
|
|
133
|
+
if not isinstance(other, Document):
|
|
134
|
+
return False
|
|
135
|
+
# Required for LSP completions
|
|
136
|
+
other = cast("Document", other) # type: ignore[redundant-cast]
|
|
137
|
+
return (
|
|
138
|
+
self._path == other._path
|
|
139
|
+
and self._contexts == other._contexts
|
|
140
|
+
and self._configurations == other._configurations
|
|
141
|
+
and self._entities == other._entities
|
|
142
|
+
and self._architectures == other._architectures
|
|
143
|
+
and self._packages == other._packages
|
|
144
|
+
and self._packageBodies == other._packageBodies
|
|
145
|
+
and self._verificationUnits == other._verificationUnits
|
|
146
|
+
and self._verificationProperties == other._verificationProperties
|
|
147
|
+
and self._verificationModes == other._verificationModes
|
|
148
|
+
)
|
|
149
|
+
|
|
150
|
+
def __hash__(self) -> int: # noqa: D105
|
|
151
|
+
return hash(
|
|
152
|
+
(
|
|
153
|
+
self._path,
|
|
154
|
+
self._contexts,
|
|
155
|
+
self._configurations,
|
|
156
|
+
self._entities,
|
|
157
|
+
self._architectures,
|
|
158
|
+
self._packages,
|
|
159
|
+
self._packageBodies,
|
|
160
|
+
self._verificationUnits,
|
|
161
|
+
self._verificationProperties,
|
|
162
|
+
self._verificationModes,
|
|
163
|
+
),
|
|
164
|
+
)
|
|
165
|
+
|
|
166
|
+
def __repr__(self) -> str: # noqa: D105
|
|
167
|
+
return (
|
|
168
|
+
f"{self.__class__.__name__}("
|
|
169
|
+
f"_path={self._path}, "
|
|
170
|
+
f"_contexts={self._contexts}, "
|
|
171
|
+
f"_configurations={self._configurations}, "
|
|
172
|
+
f"_entities={list(self._entities.values())!r}, "
|
|
173
|
+
f"_architectures={self._architectures}, "
|
|
174
|
+
f"_packages={self._packages}, "
|
|
175
|
+
f"_packageBodies={self._packageBodies}, "
|
|
176
|
+
f"_verificationUnits={self._verificationUnits}, "
|
|
177
|
+
f"_verificationProperties={self._verificationProperties}, "
|
|
178
|
+
f"_verificationModes={self._verificationModes}, "
|
|
179
|
+
")"
|
|
180
|
+
)
|
|
181
|
+
|
|
182
|
+
|
|
183
|
+
class Design(BaseDesign): # noqa: D101
|
|
184
|
+
def GetLibrary( # noqa: D102, N802
|
|
185
|
+
self,
|
|
186
|
+
name: str,
|
|
187
|
+
) -> Library:
|
|
188
|
+
library = self.Libraries.get(name)
|
|
189
|
+
if library is not None:
|
|
190
|
+
return cast("Library", library)
|
|
191
|
+
library = Library(
|
|
192
|
+
name,
|
|
193
|
+
allowBlackbox=self._allowBlackbox,
|
|
194
|
+
)
|
|
195
|
+
self.AddLibrary(library)
|
|
196
|
+
return library
|
|
197
|
+
|
|
198
|
+
def __eq__(self, other: object) -> bool: # noqa: D105
|
|
199
|
+
if not isinstance(other, Design):
|
|
200
|
+
return False
|
|
201
|
+
# Required for LSP completions
|
|
202
|
+
other = cast("Design", other) # type: ignore[redundant-cast]
|
|
203
|
+
return (
|
|
204
|
+
self._name == other._name
|
|
205
|
+
and self._allowBlackbox == other._allowBlackbox
|
|
206
|
+
and self._libraries == other._libraries
|
|
207
|
+
and self._documents == other._documents
|
|
208
|
+
)
|
|
209
|
+
|
|
210
|
+
def __hash__(self) -> int: # noqa: D105
|
|
211
|
+
return hash(
|
|
212
|
+
(
|
|
213
|
+
self._name,
|
|
214
|
+
self._allowBlackbox,
|
|
215
|
+
self._libraries,
|
|
216
|
+
self._documents,
|
|
217
|
+
),
|
|
218
|
+
)
|
|
219
|
+
|
|
220
|
+
def __repr__(self) -> str: # noqa: D105
|
|
221
|
+
return (
|
|
222
|
+
f"{self.__class__.__name__}("
|
|
223
|
+
f"_name={self._name}, "
|
|
224
|
+
f"_allowBlackbox={self._allowBlackbox}, "
|
|
225
|
+
f"_libraries={list(self._libraries.values())!r}, "
|
|
226
|
+
f"_documents={self._documents!r}, "
|
|
227
|
+
")"
|
|
228
|
+
)
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
"""Submodule defining errors."""
|
|
2
|
+
|
|
3
|
+
from tree_sitter import Node
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class BaseQueryCaptureError(RuntimeError):
|
|
7
|
+
"""Error raised when a capture fails for some reason."""
|
|
8
|
+
|
|
9
|
+
def __init__(self, node: Node, query: str, name: str, message: str) -> None:
|
|
10
|
+
"""Construct."""
|
|
11
|
+
assert node.text is not None # noqa: S101
|
|
12
|
+
super().__init__(f"Capture '@{name}': {message}: {node.text.decode()}: {node}")
|
|
13
|
+
self._node = node
|
|
14
|
+
self._query = query
|
|
15
|
+
self._name = name
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class MissingNodeError(BaseQueryCaptureError):
|
|
19
|
+
"""Error raised when an expected node is not found."""
|
|
20
|
+
|
|
21
|
+
def __init__(self, node: Node, query: str, name: str) -> None:
|
|
22
|
+
"""Construct."""
|
|
23
|
+
super().__init__(node, query, name, "Not found in node")
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class ExtraNodeError(BaseQueryCaptureError):
|
|
27
|
+
"""Error raised when an expected node is found too many times."""
|
|
28
|
+
|
|
29
|
+
def __init__(self, node: Node, query: str, name: str) -> None:
|
|
30
|
+
"""Construct."""
|
|
31
|
+
super().__init__(node, query, name, "Found too many times in node")
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
class NodeTextIsNoneError(BaseQueryCaptureError):
|
|
35
|
+
"""Error raised when a node that is expected to have a `text` field doesn't."""
|
|
36
|
+
|
|
37
|
+
def __init__(self, node: Node, query: str, name: str) -> None:
|
|
38
|
+
"""Construct."""
|
|
39
|
+
super().__init__(node, query, name, "`text` attribute not found for node")
|
|
File without changes
|