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 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