python-delphi-lsp 1.0.0__py3-none-any.whl
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.
- delphiast/__init__.py +133 -0
- delphiast/binary.py +251 -0
- delphiast/comment_builder.py +29 -0
- delphiast/consts.py +345 -0
- delphiast/grammar.py +460 -0
- delphiast/lark_builder.py +2674 -0
- delphiast/lark_tokens.py +237 -0
- delphiast/lsp_server.py +1553 -0
- delphiast/nodes.py +371 -0
- delphiast/parser.py +182 -0
- delphiast/preprocessor.py +837 -0
- delphiast/project_indexer.py +313 -0
- delphiast/semantic.py +375 -0
- delphiast/semantic_builder.py +1384 -0
- delphiast/source_reader.py +15 -0
- delphiast/workspace.py +53 -0
- delphiast/writer.py +73 -0
- python_delphi_lsp-1.0.0.dist-info/METADATA +280 -0
- python_delphi_lsp-1.0.0.dist-info/RECORD +23 -0
- python_delphi_lsp-1.0.0.dist-info/WHEEL +5 -0
- python_delphi_lsp-1.0.0.dist-info/entry_points.txt +2 -0
- python_delphi_lsp-1.0.0.dist-info/licenses/LICENSE +373 -0
- python_delphi_lsp-1.0.0.dist-info/top_level.txt +1 -0
delphiast/__init__.py
ADDED
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
from .binary import BinarySerializer, dumps, loads
|
|
2
|
+
from .consts import AttributeName, SyntaxNodeType
|
|
3
|
+
from .nodes import (
|
|
4
|
+
CommentNode,
|
|
5
|
+
CompoundSyntaxNode,
|
|
6
|
+
ParserException,
|
|
7
|
+
SyntaxNode,
|
|
8
|
+
SyntaxTreeException,
|
|
9
|
+
ValuedSyntaxNode,
|
|
10
|
+
)
|
|
11
|
+
from .writer import SyntaxTreeWriter
|
|
12
|
+
from .preprocessor import CommentInfo, IncludeLoader, PreprocessedSource, Preprocessor, PreprocessorOptions
|
|
13
|
+
from .lark_tokens import KEYWORDS, KEYWORD_TERMINALS, BASE_TERMINALS, build_grammar_snippet
|
|
14
|
+
from .grammar import build_grammar, GRAMMAR_RULES
|
|
15
|
+
from .comment_builder import build_comment_nodes
|
|
16
|
+
from .parser import DelphiParser, ParseResult, StringTransform, parse
|
|
17
|
+
from .semantic import (
|
|
18
|
+
ArrayTypeRef,
|
|
19
|
+
ClassOfTypeRef,
|
|
20
|
+
ClassTypeRef,
|
|
21
|
+
EnumTypeRef,
|
|
22
|
+
FileTypeRef,
|
|
23
|
+
GenericInstanceTypeRef,
|
|
24
|
+
InterfaceTypeRef,
|
|
25
|
+
Modifier,
|
|
26
|
+
NamedTypeRef,
|
|
27
|
+
PointerTypeRef,
|
|
28
|
+
ProcTypeRef,
|
|
29
|
+
ReferenceKind,
|
|
30
|
+
ReferenceTypeRef,
|
|
31
|
+
RecordTypeRef,
|
|
32
|
+
Scope,
|
|
33
|
+
ScopeKind,
|
|
34
|
+
SourceRange,
|
|
35
|
+
SubrangeTypeRef,
|
|
36
|
+
SetTypeRef,
|
|
37
|
+
Symbol,
|
|
38
|
+
SymbolIndex,
|
|
39
|
+
SymbolKind,
|
|
40
|
+
SymbolReference,
|
|
41
|
+
TypeRef,
|
|
42
|
+
TypeParameterRef,
|
|
43
|
+
UnknownTypeRef,
|
|
44
|
+
Visibility,
|
|
45
|
+
)
|
|
46
|
+
from .semantic_builder import SemanticBuilder, SemanticModel, SemanticProblem
|
|
47
|
+
from .workspace import WorkspaceSemanticResult, build_workspace_semantics
|
|
48
|
+
from .lsp_server import LspWorkspaceState, create_server
|
|
49
|
+
from .project_indexer import (
|
|
50
|
+
GetUnitSyntaxHook,
|
|
51
|
+
IncludeFileInfo,
|
|
52
|
+
ProjectIndexer,
|
|
53
|
+
ProjectIndexResult,
|
|
54
|
+
ProjectProblem,
|
|
55
|
+
ProjectProblemType,
|
|
56
|
+
UnitErrorInfo,
|
|
57
|
+
UnitInfo,
|
|
58
|
+
UnitParsedHook,
|
|
59
|
+
)
|
|
60
|
+
|
|
61
|
+
__all__ = [
|
|
62
|
+
'AttributeName',
|
|
63
|
+
'BinarySerializer',
|
|
64
|
+
'CommentNode',
|
|
65
|
+
'CompoundSyntaxNode',
|
|
66
|
+
'ParserException',
|
|
67
|
+
'SyntaxNode',
|
|
68
|
+
'SyntaxNodeType',
|
|
69
|
+
'SyntaxTreeException',
|
|
70
|
+
'SyntaxTreeWriter',
|
|
71
|
+
'ValuedSyntaxNode',
|
|
72
|
+
'PreprocessedSource',
|
|
73
|
+
'Preprocessor',
|
|
74
|
+
'PreprocessorOptions',
|
|
75
|
+
'IncludeLoader',
|
|
76
|
+
'CommentInfo',
|
|
77
|
+
'KEYWORDS',
|
|
78
|
+
'KEYWORD_TERMINALS',
|
|
79
|
+
'BASE_TERMINALS',
|
|
80
|
+
'build_grammar_snippet',
|
|
81
|
+
'build_grammar',
|
|
82
|
+
'GRAMMAR_RULES',
|
|
83
|
+
'build_comment_nodes',
|
|
84
|
+
'DelphiParser',
|
|
85
|
+
'ParseResult',
|
|
86
|
+
'StringTransform',
|
|
87
|
+
'parse',
|
|
88
|
+
'Modifier',
|
|
89
|
+
'ArrayTypeRef',
|
|
90
|
+
'ClassOfTypeRef',
|
|
91
|
+
'ClassTypeRef',
|
|
92
|
+
'EnumTypeRef',
|
|
93
|
+
'FileTypeRef',
|
|
94
|
+
'GenericInstanceTypeRef',
|
|
95
|
+
'InterfaceTypeRef',
|
|
96
|
+
'NamedTypeRef',
|
|
97
|
+
'PointerTypeRef',
|
|
98
|
+
'ProcTypeRef',
|
|
99
|
+
'ReferenceKind',
|
|
100
|
+
'ReferenceTypeRef',
|
|
101
|
+
'RecordTypeRef',
|
|
102
|
+
'Scope',
|
|
103
|
+
'ScopeKind',
|
|
104
|
+
'SourceRange',
|
|
105
|
+
'SubrangeTypeRef',
|
|
106
|
+
'SetTypeRef',
|
|
107
|
+
'Symbol',
|
|
108
|
+
'SymbolIndex',
|
|
109
|
+
'SymbolKind',
|
|
110
|
+
'SymbolReference',
|
|
111
|
+
'TypeRef',
|
|
112
|
+
'TypeParameterRef',
|
|
113
|
+
'UnknownTypeRef',
|
|
114
|
+
'Visibility',
|
|
115
|
+
'SemanticBuilder',
|
|
116
|
+
'SemanticModel',
|
|
117
|
+
'SemanticProblem',
|
|
118
|
+
'WorkspaceSemanticResult',
|
|
119
|
+
'build_workspace_semantics',
|
|
120
|
+
'LspWorkspaceState',
|
|
121
|
+
'create_server',
|
|
122
|
+
'ProjectIndexer',
|
|
123
|
+
'ProjectIndexResult',
|
|
124
|
+
'ProjectProblemType',
|
|
125
|
+
'ProjectProblem',
|
|
126
|
+
'UnitInfo',
|
|
127
|
+
'UnitErrorInfo',
|
|
128
|
+
'IncludeFileInfo',
|
|
129
|
+
'GetUnitSyntaxHook',
|
|
130
|
+
'UnitParsedHook',
|
|
131
|
+
'dumps',
|
|
132
|
+
'loads',
|
|
133
|
+
]
|
delphiast/binary.py
ADDED
|
@@ -0,0 +1,251 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from dataclasses import dataclass
|
|
4
|
+
from enum import IntEnum
|
|
5
|
+
import io
|
|
6
|
+
import struct
|
|
7
|
+
from typing import BinaryIO, Optional
|
|
8
|
+
|
|
9
|
+
from .consts import AttributeName, SyntaxNodeType
|
|
10
|
+
from .nodes import CommentNode, CompoundSyntaxNode, SyntaxNode, ValuedSyntaxNode
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class BinarySerializerError(Exception):
|
|
14
|
+
pass
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class NodeClass(IntEnum):
|
|
18
|
+
SYNTAX = 0
|
|
19
|
+
COMPOUND = 1
|
|
20
|
+
VALUED = 2
|
|
21
|
+
COMMENT = 3
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
_SIGNATURE = b'DAST binary file\x1a'
|
|
25
|
+
_VERSION = 0x01000000
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
class BinarySerializer:
|
|
29
|
+
def __init__(self) -> None:
|
|
30
|
+
self._string_list: list[str] = []
|
|
31
|
+
self._string_table: dict[str, int] = {}
|
|
32
|
+
|
|
33
|
+
def read(self, stream: BinaryIO) -> SyntaxNode:
|
|
34
|
+
self._string_list = []
|
|
35
|
+
if not self._check_signature(stream):
|
|
36
|
+
raise BinarySerializerError('invalid signature')
|
|
37
|
+
if not self._check_version(stream):
|
|
38
|
+
raise BinarySerializerError('unsupported version')
|
|
39
|
+
node = self._read_node(stream)
|
|
40
|
+
if node is None:
|
|
41
|
+
raise BinarySerializerError('failed to read node')
|
|
42
|
+
return node
|
|
43
|
+
|
|
44
|
+
def write(self, stream: BinaryIO, root: SyntaxNode) -> None:
|
|
45
|
+
self._string_table = {}
|
|
46
|
+
stream.write(_SIGNATURE)
|
|
47
|
+
stream.write(struct.pack('<I', _VERSION))
|
|
48
|
+
if not self._write_node(stream, root):
|
|
49
|
+
raise BinarySerializerError('failed to write node')
|
|
50
|
+
|
|
51
|
+
def _check_signature(self, stream: BinaryIO) -> bool:
|
|
52
|
+
data = stream.read(len(_SIGNATURE))
|
|
53
|
+
return data == _SIGNATURE
|
|
54
|
+
|
|
55
|
+
def _check_version(self, stream: BinaryIO) -> bool:
|
|
56
|
+
data = stream.read(4)
|
|
57
|
+
if len(data) != 4:
|
|
58
|
+
return False
|
|
59
|
+
version = struct.unpack('<I', data)[0]
|
|
60
|
+
return (version & 0xFFFF0000) == _VERSION
|
|
61
|
+
|
|
62
|
+
def _create_node(self, node_class: NodeClass, node_type: SyntaxNodeType) -> SyntaxNode:
|
|
63
|
+
if node_class == NodeClass.SYNTAX:
|
|
64
|
+
return SyntaxNode(node_type)
|
|
65
|
+
if node_class == NodeClass.COMPOUND:
|
|
66
|
+
return CompoundSyntaxNode(node_type)
|
|
67
|
+
if node_class == NodeClass.VALUED:
|
|
68
|
+
return ValuedSyntaxNode(node_type)
|
|
69
|
+
if node_class == NodeClass.COMMENT:
|
|
70
|
+
return CommentNode(node_type)
|
|
71
|
+
raise BinarySerializerError('unexpected node class')
|
|
72
|
+
|
|
73
|
+
def _read_node(self, stream: BinaryIO) -> Optional[SyntaxNode]:
|
|
74
|
+
node_class_num = self._read_number(stream)
|
|
75
|
+
node_type_num = self._read_number(stream)
|
|
76
|
+
if node_class_num is None or node_type_num is None:
|
|
77
|
+
return None
|
|
78
|
+
try:
|
|
79
|
+
node_class = NodeClass(node_class_num)
|
|
80
|
+
node_type = SyntaxNodeType(node_type_num)
|
|
81
|
+
except ValueError:
|
|
82
|
+
raise BinarySerializerError('invalid node class or type')
|
|
83
|
+
node = self._create_node(node_class, node_type)
|
|
84
|
+
|
|
85
|
+
col = self._read_number(stream)
|
|
86
|
+
line = self._read_number(stream)
|
|
87
|
+
if col is None or line is None:
|
|
88
|
+
return None
|
|
89
|
+
node.col = col
|
|
90
|
+
node.line = line
|
|
91
|
+
|
|
92
|
+
if node_class == NodeClass.COMPOUND:
|
|
93
|
+
end_col = self._read_number(stream)
|
|
94
|
+
end_line = self._read_number(stream)
|
|
95
|
+
if end_col is None or end_line is None:
|
|
96
|
+
return None
|
|
97
|
+
node.end_col = end_col
|
|
98
|
+
node.end_line = end_line
|
|
99
|
+
elif node_class == NodeClass.VALUED:
|
|
100
|
+
value = self._read_string(stream)
|
|
101
|
+
if value is None:
|
|
102
|
+
return None
|
|
103
|
+
node.value = value
|
|
104
|
+
elif node_class == NodeClass.COMMENT:
|
|
105
|
+
text = self._read_string(stream)
|
|
106
|
+
if text is None:
|
|
107
|
+
return None
|
|
108
|
+
node.text = text
|
|
109
|
+
|
|
110
|
+
attr_count = self._read_number(stream)
|
|
111
|
+
if attr_count is None:
|
|
112
|
+
return None
|
|
113
|
+
for _ in range(attr_count):
|
|
114
|
+
attr_key_num = self._read_number(stream)
|
|
115
|
+
if attr_key_num is None:
|
|
116
|
+
return None
|
|
117
|
+
try:
|
|
118
|
+
attr_key = AttributeName(attr_key_num)
|
|
119
|
+
except ValueError:
|
|
120
|
+
raise BinarySerializerError('invalid attribute key')
|
|
121
|
+
attr_value = self._read_string(stream)
|
|
122
|
+
if attr_value is None:
|
|
123
|
+
return None
|
|
124
|
+
node.set_attribute(attr_key, attr_value)
|
|
125
|
+
|
|
126
|
+
child_count = self._read_number(stream)
|
|
127
|
+
if child_count is None:
|
|
128
|
+
return None
|
|
129
|
+
for _ in range(child_count):
|
|
130
|
+
child_node = self._read_node(stream)
|
|
131
|
+
if child_node is None:
|
|
132
|
+
return None
|
|
133
|
+
node.add_child(child_node)
|
|
134
|
+
|
|
135
|
+
return node
|
|
136
|
+
|
|
137
|
+
def _read_number(self, stream: BinaryIO) -> Optional[int]:
|
|
138
|
+
shift = 0
|
|
139
|
+
num = 0
|
|
140
|
+
while True:
|
|
141
|
+
data = stream.read(1)
|
|
142
|
+
if len(data) != 1:
|
|
143
|
+
return None
|
|
144
|
+
low = data[0]
|
|
145
|
+
num |= (low & 0x7F) << shift
|
|
146
|
+
shift += 7
|
|
147
|
+
if (low & 0x80) == 0:
|
|
148
|
+
return num
|
|
149
|
+
|
|
150
|
+
def _read_string(self, stream: BinaryIO) -> Optional[str]:
|
|
151
|
+
length = self._read_number(stream)
|
|
152
|
+
if length is None:
|
|
153
|
+
return None
|
|
154
|
+
if (length >> 24) == 0xFF:
|
|
155
|
+
string_id = length & 0x00FFFFFF
|
|
156
|
+
if string_id >= len(self._string_list):
|
|
157
|
+
return None
|
|
158
|
+
return self._string_list[string_id]
|
|
159
|
+
data = stream.read(length) if length else b''
|
|
160
|
+
if len(data) != length:
|
|
161
|
+
return None
|
|
162
|
+
value = data.decode('utf-8')
|
|
163
|
+
if len(value) > 4:
|
|
164
|
+
self._string_list.append(value)
|
|
165
|
+
return value
|
|
166
|
+
|
|
167
|
+
def _write_node(self, stream: BinaryIO, node: SyntaxNode) -> bool:
|
|
168
|
+
if isinstance(node, CompoundSyntaxNode):
|
|
169
|
+
node_class = NodeClass.COMPOUND
|
|
170
|
+
elif isinstance(node, ValuedSyntaxNode):
|
|
171
|
+
node_class = NodeClass.VALUED
|
|
172
|
+
elif isinstance(node, CommentNode):
|
|
173
|
+
node_class = NodeClass.COMMENT
|
|
174
|
+
else:
|
|
175
|
+
node_class = NodeClass.SYNTAX
|
|
176
|
+
|
|
177
|
+
if not self._write_number(stream, int(node_class)):
|
|
178
|
+
return False
|
|
179
|
+
if not self._write_number(stream, int(node.typ)):
|
|
180
|
+
return False
|
|
181
|
+
if not self._write_number(stream, node.col):
|
|
182
|
+
return False
|
|
183
|
+
if not self._write_number(stream, node.line):
|
|
184
|
+
return False
|
|
185
|
+
|
|
186
|
+
if node_class == NodeClass.COMPOUND:
|
|
187
|
+
if not self._write_number(stream, node.end_col):
|
|
188
|
+
return False
|
|
189
|
+
if not self._write_number(stream, node.end_line):
|
|
190
|
+
return False
|
|
191
|
+
elif node_class == NodeClass.VALUED:
|
|
192
|
+
if not self._write_string(stream, node.value):
|
|
193
|
+
return False
|
|
194
|
+
elif node_class == NodeClass.COMMENT:
|
|
195
|
+
if not self._write_string(stream, node.text):
|
|
196
|
+
return False
|
|
197
|
+
|
|
198
|
+
if not self._write_number(stream, len(node.attributes)):
|
|
199
|
+
return False
|
|
200
|
+
for attr_key, attr_value in node.attributes:
|
|
201
|
+
if not self._write_number(stream, int(attr_key)):
|
|
202
|
+
return False
|
|
203
|
+
if not self._write_string(stream, attr_value):
|
|
204
|
+
return False
|
|
205
|
+
|
|
206
|
+
if not self._write_number(stream, len(node.child_nodes)):
|
|
207
|
+
return False
|
|
208
|
+
for child_node in node.child_nodes:
|
|
209
|
+
if not self._write_node(stream, child_node):
|
|
210
|
+
return False
|
|
211
|
+
|
|
212
|
+
return True
|
|
213
|
+
|
|
214
|
+
def _write_number(self, stream: BinaryIO, num: int) -> bool:
|
|
215
|
+
value = num
|
|
216
|
+
while True:
|
|
217
|
+
low = value & 0x7F
|
|
218
|
+
value >>= 7
|
|
219
|
+
if value != 0:
|
|
220
|
+
low |= 0x80
|
|
221
|
+
if stream.write(bytes([low])) != 1:
|
|
222
|
+
return False
|
|
223
|
+
if value == 0:
|
|
224
|
+
return True
|
|
225
|
+
|
|
226
|
+
def _write_string(self, stream: BinaryIO, value: str) -> bool:
|
|
227
|
+
if len(value) > 4 and value in self._string_table:
|
|
228
|
+
string_id = self._string_table[value]
|
|
229
|
+
return self._write_number(stream, string_id | 0xFF000000)
|
|
230
|
+
|
|
231
|
+
if len(value) > 4:
|
|
232
|
+
self._string_table[value] = len(self._string_table)
|
|
233
|
+
if self._string_table[value] > 0xFFFFFF:
|
|
234
|
+
raise BinarySerializerError('too many strings')
|
|
235
|
+
|
|
236
|
+
data = value.encode('utf-8')
|
|
237
|
+
if not self._write_number(stream, len(data)):
|
|
238
|
+
return False
|
|
239
|
+
if data:
|
|
240
|
+
return stream.write(data) == len(data)
|
|
241
|
+
return True
|
|
242
|
+
|
|
243
|
+
|
|
244
|
+
def loads(data: bytes) -> SyntaxNode:
|
|
245
|
+
return BinarySerializer().read(io.BytesIO(data))
|
|
246
|
+
|
|
247
|
+
|
|
248
|
+
def dumps(node: SyntaxNode) -> bytes:
|
|
249
|
+
stream = io.BytesIO()
|
|
250
|
+
BinarySerializer().write(stream, node)
|
|
251
|
+
return stream.getvalue()
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from typing import Iterable
|
|
4
|
+
|
|
5
|
+
from .consts import SyntaxNodeType
|
|
6
|
+
from .nodes import CommentNode
|
|
7
|
+
from .preprocessor import CommentInfo
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
_KIND_TO_NODE_TYPE = {
|
|
11
|
+
'ansi': SyntaxNodeType.ntAnsiComment,
|
|
12
|
+
'borland': SyntaxNodeType.ntBorComment,
|
|
13
|
+
'slashes': SyntaxNodeType.ntSlashesComment,
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def build_comment_nodes(comments: Iterable[CommentInfo]) -> list[CommentNode]:
|
|
18
|
+
nodes: list[CommentNode] = []
|
|
19
|
+
for info in comments:
|
|
20
|
+
node_type = _KIND_TO_NODE_TYPE.get(info.kind)
|
|
21
|
+
if node_type is None:
|
|
22
|
+
raise ValueError(f'unknown comment kind: {info.kind}')
|
|
23
|
+
node = CommentNode(node_type)
|
|
24
|
+
node.text = info.text
|
|
25
|
+
node.file_name = info.file_name
|
|
26
|
+
node.line = info.line
|
|
27
|
+
node.col = info.col
|
|
28
|
+
nodes.append(node)
|
|
29
|
+
return nodes
|