infinity-data 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.
- infinity_data/__init__.py +41 -0
- infinity_data/emit/__init__.py +9 -0
- infinity_data/emit/converter.py +55 -0
- infinity_data/frontend.py +37 -0
- infinity_data/infra/__init__.py +4 -0
- infinity_data/infra/diagnostics.py +212 -0
- infinity_data/infra/file.py +83 -0
- infinity_data/infra/ll1_stream.py +71 -0
- infinity_data/infra/location.py +80 -0
- infinity_data/infra/path.py +46 -0
- infinity_data/parser/__init__.py +79 -0
- infinity_data/parser/diagnostics.py +92 -0
- infinity_data/parser/models.py +295 -0
- infinity_data/parser/parser.py +988 -0
- infinity_data/parser/token_stream.py +128 -0
- infinity_data/pipeline.py +252 -0
- infinity_data/sandbox/__init__.py +32 -0
- infinity_data/sandbox/config.py +61 -0
- infinity_data/sandbox/errors.py +106 -0
- infinity_data/sandbox/mediator.py +214 -0
- infinity_data/sandbox/schema.py +21 -0
- infinity_data/semantic/__init__.py +59 -0
- infinity_data/semantic/builder/__init__.py +29 -0
- infinity_data/semantic/builder/builder.py +523 -0
- infinity_data/semantic/builder/models.py +163 -0
- infinity_data/semantic/constraints.py +163 -0
- infinity_data/semantic/diagnostics.py +172 -0
- infinity_data/semantic/executor/__init__.py +10 -0
- infinity_data/semantic/executor/executor.py +259 -0
- infinity_data/semantic/registry/__init__.py +168 -0
- infinity_data/semantic/registry/_core.py +212 -0
- infinity_data/semantic/registry/dict_constraints.py +56 -0
- infinity_data/semantic/registry/general.py +362 -0
- infinity_data/semantic/registry/logic.py +134 -0
- infinity_data/semantic/registry/types.py +136 -0
- infinity_data/semantic/resolver/__init__.py +20 -0
- infinity_data/semantic/resolver/imports.py +191 -0
- infinity_data/semantic/resolver/models.py +67 -0
- infinity_data/semantic/resolver/resolver.py +345 -0
- infinity_data/tokenizer/__init__.py +16 -0
- infinity_data/tokenizer/char_stream.py +72 -0
- infinity_data/tokenizer/diagnostics.py +62 -0
- infinity_data/tokenizer/finalizer.py +271 -0
- infinity_data/tokenizer/models/__init__.py +17 -0
- infinity_data/tokenizer/models/raw_tokens.py +61 -0
- infinity_data/tokenizer/models/tokens.py +245 -0
- infinity_data/tokenizer/tokenizer.py +531 -0
- infinity_data-1.0.0.dist-info/METADATA +60 -0
- infinity_data-1.0.0.dist-info/RECORD +52 -0
- infinity_data-1.0.0.dist-info/WHEEL +5 -0
- infinity_data-1.0.0.dist-info/licenses/LICENSE +21 -0
- infinity_data-1.0.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
"""词法分析阶段诊断构造器与诊断定义。
|
|
2
|
+
|
|
3
|
+
词法错误统一为 :class:`Diagnostic`(纯数据,从不抛异常);
|
|
4
|
+
:func:`diag` 便捷构造词法错误诊断,收集使用 :class:`DiagnosticCollector`。
|
|
5
|
+
诊断定义(``tokenize.*``)导入时注册进全局注册表(:func:`register`)。
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from typing import Any
|
|
9
|
+
|
|
10
|
+
from infinity_data.infra.diagnostics import Diagnostic, Severity, diagnostic_define, register_diagnostic_define
|
|
11
|
+
from infinity_data.infra.location import SourceRange
|
|
12
|
+
|
|
13
|
+
__all__ = ['diag']
|
|
14
|
+
|
|
15
|
+
register_diagnostic_define(
|
|
16
|
+
diagnostic_define(
|
|
17
|
+
'tokenize.unknown_char', '[{location}] 未知字符: {char!r}', en='[{location}] unknown character: {char!r}'
|
|
18
|
+
),
|
|
19
|
+
diagnostic_define(
|
|
20
|
+
'tokenize.unterminated_string', '[{location}] {str_type}未闭合', en='[{location}] unterminated {str_type}'
|
|
21
|
+
),
|
|
22
|
+
diagnostic_define(
|
|
23
|
+
'tokenize.unterminated_bracket',
|
|
24
|
+
'[{location}] 括号未闭合: {bracket}',
|
|
25
|
+
en='[{location}] unclosed bracket: {bracket}',
|
|
26
|
+
),
|
|
27
|
+
diagnostic_define(
|
|
28
|
+
'tokenize.invalid_number',
|
|
29
|
+
'[{location}] 无效的数字字面量: {raw!r}',
|
|
30
|
+
en='[{location}] invalid number literal: {raw!r}',
|
|
31
|
+
),
|
|
32
|
+
diagnostic_define(
|
|
33
|
+
'tokenize.invalid_escape',
|
|
34
|
+
'[{location}] 无效的转义序列: {raw!r}',
|
|
35
|
+
en='[{location}] invalid escape sequence: {raw!r}',
|
|
36
|
+
),
|
|
37
|
+
diagnostic_define(
|
|
38
|
+
'tokenize.invalid_float',
|
|
39
|
+
'[{location}] 无效的浮点字面量: {raw!r}',
|
|
40
|
+
en='[{location}] invalid float literal: {raw!r}',
|
|
41
|
+
),
|
|
42
|
+
diagnostic_define(
|
|
43
|
+
'tokenize.invalid_bang',
|
|
44
|
+
'[{location}] ! 后期望 env/file/from,实际为 {actual}',
|
|
45
|
+
en="[{location}] expected env/file/from after '!', got {actual}",
|
|
46
|
+
),
|
|
47
|
+
diagnostic_define(
|
|
48
|
+
'tokenize.unterminated_comment',
|
|
49
|
+
'[{location}] 多行注释未闭合,期望结束标记: {flag}',
|
|
50
|
+
en='[{location}] unterminated block comment, expected closing: {flag}',
|
|
51
|
+
),
|
|
52
|
+
diagnostic_define(
|
|
53
|
+
'tokenize.bom',
|
|
54
|
+
'[{location}] 文件包含 BOM,规范要求 UTF-8 NO BOM 编码',
|
|
55
|
+
en='[{location}] file contains a BOM; UTF-8 NO BOM is required',
|
|
56
|
+
),
|
|
57
|
+
)
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def diag(code: str, params: dict[str, Any], source: SourceRange | None) -> Diagnostic:
|
|
61
|
+
"""构造词法阶段错误诊断(severity 恒为 ERROR)。"""
|
|
62
|
+
return Diagnostic(Severity.ERROR, code, params, source)
|
|
@@ -0,0 +1,271 @@
|
|
|
1
|
+
"""终遍词法分析器:将 RawToken 流转换为 Token 流。
|
|
2
|
+
|
|
3
|
+
职责:
|
|
4
|
+
- 解析转义序列(字符串)
|
|
5
|
+
- 提取多行字符串的 tags 并修剪空白
|
|
6
|
+
- 将字符串形式的数字解析为 Python 数值类型
|
|
7
|
+
- 区分特殊浮点字面量(nan, +inf, -inf)与普通浮点数
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from __future__ import annotations
|
|
11
|
+
|
|
12
|
+
import decimal
|
|
13
|
+
import json
|
|
14
|
+
from collections.abc import Iterable, Iterator
|
|
15
|
+
|
|
16
|
+
from infinity_data.infra.diagnostics import DiagnosticCollector
|
|
17
|
+
from infinity_data.tokenizer.diagnostics import diag
|
|
18
|
+
from infinity_data.tokenizer.models.raw_tokens import (
|
|
19
|
+
RawToken,
|
|
20
|
+
RawTokenType,
|
|
21
|
+
)
|
|
22
|
+
from infinity_data.tokenizer.models.tokens import (
|
|
23
|
+
BoolToken,
|
|
24
|
+
ColonToken,
|
|
25
|
+
CommaToken,
|
|
26
|
+
DollarToken,
|
|
27
|
+
DotToken,
|
|
28
|
+
EnvImportToken,
|
|
29
|
+
EofToken,
|
|
30
|
+
EqualsToken,
|
|
31
|
+
ExclamationToken,
|
|
32
|
+
FileImportToken,
|
|
33
|
+
FloatToken,
|
|
34
|
+
FromImportToken,
|
|
35
|
+
IdentifierToken,
|
|
36
|
+
IntegerToken,
|
|
37
|
+
LangleToken,
|
|
38
|
+
LbraceToken,
|
|
39
|
+
LbracketToken,
|
|
40
|
+
LparenToken,
|
|
41
|
+
MultilineStringToken,
|
|
42
|
+
NewlineToken,
|
|
43
|
+
NoexistToken,
|
|
44
|
+
NullToken,
|
|
45
|
+
QuestionToken,
|
|
46
|
+
RangleToken,
|
|
47
|
+
RbraceToken,
|
|
48
|
+
RbracketToken,
|
|
49
|
+
RparenToken,
|
|
50
|
+
SinglelineStringToken,
|
|
51
|
+
TildeToken,
|
|
52
|
+
Token,
|
|
53
|
+
)
|
|
54
|
+
|
|
55
|
+
# ── 单字符 token 映射 ──────────────────────────────
|
|
56
|
+
|
|
57
|
+
_SIMPLE_MAP: dict[RawTokenType, type[Token]] = {
|
|
58
|
+
RawTokenType.LBRACE: LbraceToken,
|
|
59
|
+
RawTokenType.RBRACE: RbraceToken,
|
|
60
|
+
RawTokenType.LBRACKET: LbracketToken,
|
|
61
|
+
RawTokenType.RBRACKET: RbracketToken,
|
|
62
|
+
RawTokenType.LPAREN: LparenToken,
|
|
63
|
+
RawTokenType.RPAREN: RparenToken,
|
|
64
|
+
RawTokenType.LANGLE: LangleToken,
|
|
65
|
+
RawTokenType.RANGLE: RangleToken,
|
|
66
|
+
RawTokenType.EQUALS: EqualsToken,
|
|
67
|
+
RawTokenType.COLON: ColonToken,
|
|
68
|
+
RawTokenType.COMMA: CommaToken,
|
|
69
|
+
RawTokenType.TILDE: TildeToken,
|
|
70
|
+
RawTokenType.EXCLAMATION: ExclamationToken,
|
|
71
|
+
RawTokenType.QUESTION: QuestionToken,
|
|
72
|
+
RawTokenType.DOLLAR: DollarToken,
|
|
73
|
+
RawTokenType.DOT: DotToken,
|
|
74
|
+
RawTokenType.NULL: NullToken,
|
|
75
|
+
RawTokenType.NOEXIST: NoexistToken,
|
|
76
|
+
RawTokenType.ENV_IMPORT: EnvImportToken,
|
|
77
|
+
RawTokenType.FILE_IMPORT: FileImportToken,
|
|
78
|
+
RawTokenType.FROM_IMPORT: FromImportToken,
|
|
79
|
+
RawTokenType.NEWLINE: NewlineToken,
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
# ── 多行字符串处理 ─────────────────────────────────
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
def _process_multiline_string(raw: str) -> tuple[str, list[str]]:
|
|
87
|
+
"""处理多行字符串 raw 返回 (content, tags)。
|
|
88
|
+
|
|
89
|
+
raw 格式: ```[tags]\n[content]```
|
|
90
|
+
"""
|
|
91
|
+
# 1. 统计起始反引号数量
|
|
92
|
+
backtick_count = 0
|
|
93
|
+
for ch in raw:
|
|
94
|
+
if ch == '`':
|
|
95
|
+
backtick_count += 1
|
|
96
|
+
else:
|
|
97
|
+
break
|
|
98
|
+
|
|
99
|
+
if backtick_count == 0:
|
|
100
|
+
return raw, []
|
|
101
|
+
|
|
102
|
+
# 2. 提取 tags(起始行剩余部分)
|
|
103
|
+
rest_start: int = backtick_count
|
|
104
|
+
newline_idx: int = raw.find('\n', rest_start)
|
|
105
|
+
if newline_idx == -1: # 无换行:整个同一行为 tags,无内容
|
|
106
|
+
tags_part: str = raw[rest_start:] # 排除结束围栏
|
|
107
|
+
# 找到结束围栏位置(应从尾部去除 backtick_count 个反引号)
|
|
108
|
+
if raw.endswith('`' * backtick_count):
|
|
109
|
+
tags_part = raw[rest_start:-backtick_count]
|
|
110
|
+
tags: list[str] = tags_part.split() if tags_part.strip() else []
|
|
111
|
+
return '', tags
|
|
112
|
+
|
|
113
|
+
tags_part = raw[rest_start:newline_idx]
|
|
114
|
+
tags = tags_part.split() if tags_part.strip() else []
|
|
115
|
+
|
|
116
|
+
# 3. 内容起始(跳过首换行)
|
|
117
|
+
content_start = newline_idx + 1
|
|
118
|
+
|
|
119
|
+
# 4. 查找结束反引号(从尾部反向扫描,跳过尾空白)
|
|
120
|
+
closing_start: int | None = None
|
|
121
|
+
# 从尾部查找连续反引号序列
|
|
122
|
+
idx = len(raw) - 1
|
|
123
|
+
while idx >= content_start:
|
|
124
|
+
if raw[idx] == '`':
|
|
125
|
+
# 向前数连续反引号
|
|
126
|
+
seq_end = idx
|
|
127
|
+
while idx >= content_start and raw[idx] == '`':
|
|
128
|
+
idx -= 1
|
|
129
|
+
seq_len = seq_end - idx
|
|
130
|
+
if seq_len == backtick_count:
|
|
131
|
+
closing_start = idx + 1
|
|
132
|
+
break
|
|
133
|
+
else:
|
|
134
|
+
idx -= 1
|
|
135
|
+
|
|
136
|
+
if closing_start is None:
|
|
137
|
+
# 未找到匹配结束符(不应发生,RawTokenizer 已保证)
|
|
138
|
+
content = raw[content_start:]
|
|
139
|
+
return content.rstrip(), tags
|
|
140
|
+
|
|
141
|
+
# 5. 内容在 content_start 到 closing_start 之间
|
|
142
|
+
content = raw[content_start:closing_start]
|
|
143
|
+
|
|
144
|
+
# 6. 丢弃尾部空白及一个格式化换行
|
|
145
|
+
content: str = content.rstrip(' \t').removesuffix('\n')
|
|
146
|
+
|
|
147
|
+
return content, tags
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
# ═══════════════════════════════════════════════════════════
|
|
151
|
+
# FinalTokenizer
|
|
152
|
+
# ═══════════════════════════════════════════════════════════
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
class FinalTokenizer:
|
|
156
|
+
"""终遍词法分析器:消费 RawToken 流,产出 Token 流。
|
|
157
|
+
|
|
158
|
+
``error_collector``:与 :class:`RawTokenizer` 同一收集器(词法层容错收集)——
|
|
159
|
+
值转换失败(无效转义 / 无效数字)收集为诊断并产出恢复 token,**从不抛异常**。
|
|
160
|
+
"""
|
|
161
|
+
|
|
162
|
+
def __init__(
|
|
163
|
+
self,
|
|
164
|
+
source: Iterable[RawToken],
|
|
165
|
+
error_collector: DiagnosticCollector | None = None,
|
|
166
|
+
) -> None:
|
|
167
|
+
self._iter: Iterator[RawToken] | None = None
|
|
168
|
+
self._source: Iterable[RawToken] = source
|
|
169
|
+
# 注意:不能用 `or` —— 空 DiagnosticCollector 的 __bool__ 为 False,会静默丢弃传入的收集器
|
|
170
|
+
self._errors = error_collector if error_collector is not None else DiagnosticCollector()
|
|
171
|
+
|
|
172
|
+
def __iter__(self) -> 'FinalTokenizer':
|
|
173
|
+
return self
|
|
174
|
+
|
|
175
|
+
def __next__(self) -> Token:
|
|
176
|
+
if self._iter is None:
|
|
177
|
+
self._iter = iter(self._source)
|
|
178
|
+
raw = next(self._iter)
|
|
179
|
+
return self._convert(raw)
|
|
180
|
+
|
|
181
|
+
# ── 转换核心 ──────────────────────────────────────
|
|
182
|
+
|
|
183
|
+
def _convert(self, raw: RawToken) -> Token:
|
|
184
|
+
"""将单个 RawToken 转换为对应的 Token 子类。"""
|
|
185
|
+
token_type = raw.type
|
|
186
|
+
|
|
187
|
+
# EOF
|
|
188
|
+
if token_type == RawTokenType.EOF:
|
|
189
|
+
return EofToken(raw=raw)
|
|
190
|
+
|
|
191
|
+
# 简单映射(无需解析值)
|
|
192
|
+
if token_type in _SIMPLE_MAP:
|
|
193
|
+
return _SIMPLE_MAP[token_type](raw=raw)
|
|
194
|
+
|
|
195
|
+
# 需要解析值的类型
|
|
196
|
+
if token_type == RawTokenType.STRING:
|
|
197
|
+
return self._convert_string(raw)
|
|
198
|
+
|
|
199
|
+
if token_type == RawTokenType.MULTILINE_STRING:
|
|
200
|
+
return self._convert_multiline_string(raw)
|
|
201
|
+
|
|
202
|
+
if token_type == RawTokenType.INTEGER:
|
|
203
|
+
return self._convert_integer(raw)
|
|
204
|
+
|
|
205
|
+
if token_type == RawTokenType.FLOAT:
|
|
206
|
+
return self._convert_float(raw)
|
|
207
|
+
|
|
208
|
+
if token_type == RawTokenType.BOOL:
|
|
209
|
+
return self._convert_bool(raw)
|
|
210
|
+
|
|
211
|
+
if token_type == RawTokenType.IDENTIFIER:
|
|
212
|
+
return IdentifierToken(raw=raw, name=raw.raw)
|
|
213
|
+
|
|
214
|
+
# 未知类型(不应到达)
|
|
215
|
+
raise ValueError(f'未知 RawTokenType: {token_type}')
|
|
216
|
+
|
|
217
|
+
# ── 各类型转换 ────────────────────────────────────
|
|
218
|
+
|
|
219
|
+
def _convert_string(self, raw: RawToken) -> SinglelineStringToken:
|
|
220
|
+
"""处理单行字符串:通过 json.loads 解析转义。
|
|
221
|
+
|
|
222
|
+
无效转义(RawTokenizer 不校验,如 ``\\q`` / ``\\u``)→ 收集诊断,
|
|
223
|
+
恢复为去引号的原始内容(不做转义处理),保证下游继续。
|
|
224
|
+
"""
|
|
225
|
+
try:
|
|
226
|
+
value = json.loads(raw.raw)
|
|
227
|
+
except json.JSONDecodeError:
|
|
228
|
+
self._errors.add(diag('tokenize.invalid_escape', {'raw': raw.raw}, raw.source))
|
|
229
|
+
value = raw.raw[1:-1] if len(raw.raw) >= 2 else raw.raw
|
|
230
|
+
return SinglelineStringToken(raw=raw, value=value)
|
|
231
|
+
|
|
232
|
+
@staticmethod
|
|
233
|
+
def _convert_multiline_string(raw: RawToken) -> MultilineStringToken:
|
|
234
|
+
"""处理多行字符串:提取 tags、修剪空白。"""
|
|
235
|
+
content, tags = _process_multiline_string(raw.raw)
|
|
236
|
+
return MultilineStringToken(raw=raw, value=content, tags=tags)
|
|
237
|
+
|
|
238
|
+
def _convert_integer(self, raw: RawToken) -> IntegerToken:
|
|
239
|
+
"""解析整数字面量;失败收集诊断并回退 0(防御性:RawTokenizer 已校验)。"""
|
|
240
|
+
try:
|
|
241
|
+
value = int(raw.raw)
|
|
242
|
+
except ValueError:
|
|
243
|
+
self._errors.add(diag('tokenize.invalid_number', {'raw': raw.raw}, raw.source))
|
|
244
|
+
value = 0
|
|
245
|
+
return IntegerToken(raw=raw, value=value)
|
|
246
|
+
|
|
247
|
+
def _convert_float(self, raw: RawToken) -> FloatToken:
|
|
248
|
+
"""解析浮点字面量(含 nan, +inf, -inf)。
|
|
249
|
+
|
|
250
|
+
Decimal 拒绝的输入(如 ``1e`` 尾指数无数字)→ 收集诊断并回退 0。
|
|
251
|
+
"""
|
|
252
|
+
s = raw.raw
|
|
253
|
+
match s:
|
|
254
|
+
case 'nan':
|
|
255
|
+
value = decimal.Decimal('NaN')
|
|
256
|
+
case '+inf':
|
|
257
|
+
value = decimal.Decimal('Infinity')
|
|
258
|
+
case '-inf':
|
|
259
|
+
value = decimal.Decimal('-Infinity')
|
|
260
|
+
case _:
|
|
261
|
+
try:
|
|
262
|
+
value = decimal.Decimal(s)
|
|
263
|
+
except decimal.InvalidOperation:
|
|
264
|
+
self._errors.add(diag('tokenize.invalid_float', {'raw': raw.raw}, raw.source))
|
|
265
|
+
value = decimal.Decimal(0)
|
|
266
|
+
return FloatToken(raw=raw, value=value)
|
|
267
|
+
|
|
268
|
+
@staticmethod
|
|
269
|
+
def _convert_bool(raw: RawToken) -> BoolToken:
|
|
270
|
+
"""解析布尔字面量。"""
|
|
271
|
+
return BoolToken(raw=raw, value=(raw.raw == 'true'))
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
"""词法模型:RawToken(容错层)与 Token(值语义层)。"""
|
|
2
|
+
|
|
3
|
+
from infinity_data.tokenizer.models.raw_tokens import (
|
|
4
|
+
RawToken,
|
|
5
|
+
RawTokenType,
|
|
6
|
+
SourceInfo,
|
|
7
|
+
SourceRange,
|
|
8
|
+
)
|
|
9
|
+
from infinity_data.tokenizer.models.tokens import Token
|
|
10
|
+
|
|
11
|
+
__all__ = [
|
|
12
|
+
'RawToken',
|
|
13
|
+
'RawTokenType',
|
|
14
|
+
'SourceInfo',
|
|
15
|
+
'SourceRange',
|
|
16
|
+
'Token',
|
|
17
|
+
]
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from dataclasses import dataclass
|
|
4
|
+
from enum import Enum
|
|
5
|
+
|
|
6
|
+
from infinity_data.infra.location import SourceInfo, SourceRange
|
|
7
|
+
|
|
8
|
+
__all__ = ['RawTokenType', 'RawToken', 'SourceInfo', 'SourceRange']
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class RawTokenType(Enum):
|
|
12
|
+
"""所有 token 类型。"""
|
|
13
|
+
|
|
14
|
+
# ── 结构定界符 ─────────────────────────────────
|
|
15
|
+
LBRACE = '{'
|
|
16
|
+
RBRACE = '}'
|
|
17
|
+
LBRACKET = '['
|
|
18
|
+
RBRACKET = ']'
|
|
19
|
+
LPAREN = '('
|
|
20
|
+
RPAREN = ')'
|
|
21
|
+
LANGLE = '<'
|
|
22
|
+
RANGLE = '>'
|
|
23
|
+
|
|
24
|
+
# ── 运算符 / 分隔符 ────────────────────────────
|
|
25
|
+
EQUALS = '='
|
|
26
|
+
COLON = ':'
|
|
27
|
+
COMMA = ','
|
|
28
|
+
TILDE = '~'
|
|
29
|
+
EXCLAMATION = '!'
|
|
30
|
+
QUESTION = '?'
|
|
31
|
+
DOLLAR = '$' # 导入命名空间引用前缀
|
|
32
|
+
DOT = '.' # 导入命名空间引用分隔符
|
|
33
|
+
|
|
34
|
+
# ── 字面量 ─────────────────────────────────────
|
|
35
|
+
STRING = 'str' # 双引号单行字符串
|
|
36
|
+
MULTILINE_STRING = 'mlstr' # 反引号多行字符串
|
|
37
|
+
INTEGER = 'int'
|
|
38
|
+
FLOAT = 'float'
|
|
39
|
+
BOOL = 'bool'
|
|
40
|
+
|
|
41
|
+
NULL = 'null'
|
|
42
|
+
NOEXIST = 'noexist'
|
|
43
|
+
|
|
44
|
+
# ── 标识符 / 关键字 ────────────────────────────
|
|
45
|
+
IDENTIFIER = 'identifier'
|
|
46
|
+
|
|
47
|
+
# ── 导入关键字(! + 标识符组合,词法阶段识别,避免语法阶段二义)──
|
|
48
|
+
ENV_IMPORT = '!env' # !env import NAME
|
|
49
|
+
FILE_IMPORT = '!file' # !file "path" import ...
|
|
50
|
+
FROM_IMPORT = '!from' # !from "path" import ...
|
|
51
|
+
|
|
52
|
+
# ── 换行 / EOF ─────────────────────────────────
|
|
53
|
+
NEWLINE = 'newline'
|
|
54
|
+
EOF = 'eof'
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
@dataclass
|
|
58
|
+
class RawToken:
|
|
59
|
+
type: RawTokenType
|
|
60
|
+
raw: str
|
|
61
|
+
source: SourceRange
|
|
@@ -0,0 +1,245 @@
|
|
|
1
|
+
import decimal
|
|
2
|
+
from dataclasses import dataclass, field
|
|
3
|
+
from typing import Any
|
|
4
|
+
|
|
5
|
+
from .raw_tokens import RawToken
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
@dataclass
|
|
9
|
+
class Token:
|
|
10
|
+
raw: RawToken
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
@dataclass
|
|
14
|
+
class LbraceToken(Token):
|
|
15
|
+
"""{"""
|
|
16
|
+
|
|
17
|
+
pass
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
@dataclass
|
|
21
|
+
class RbraceToken(Token):
|
|
22
|
+
"""}"""
|
|
23
|
+
|
|
24
|
+
pass
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
@dataclass
|
|
28
|
+
class LbracketToken(Token):
|
|
29
|
+
"""["""
|
|
30
|
+
|
|
31
|
+
pass
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
@dataclass
|
|
35
|
+
class RbracketToken(Token):
|
|
36
|
+
"""]"""
|
|
37
|
+
|
|
38
|
+
pass
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
@dataclass
|
|
42
|
+
class LparenToken(Token):
|
|
43
|
+
"""("""
|
|
44
|
+
|
|
45
|
+
pass
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
@dataclass
|
|
49
|
+
class RparenToken(Token):
|
|
50
|
+
""")"""
|
|
51
|
+
|
|
52
|
+
pass
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
@dataclass
|
|
56
|
+
class LangleToken(Token):
|
|
57
|
+
"""<"""
|
|
58
|
+
|
|
59
|
+
pass
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
@dataclass
|
|
63
|
+
class RangleToken(Token):
|
|
64
|
+
""">"""
|
|
65
|
+
|
|
66
|
+
pass
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
# ── 运算符 / 分隔符 ─────────────────────────────────
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
@dataclass
|
|
73
|
+
class EqualsToken(Token):
|
|
74
|
+
"""="""
|
|
75
|
+
|
|
76
|
+
pass
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
@dataclass
|
|
80
|
+
class ColonToken(Token):
|
|
81
|
+
""":"""
|
|
82
|
+
|
|
83
|
+
pass
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
@dataclass
|
|
87
|
+
class CommaToken(Token):
|
|
88
|
+
""","""
|
|
89
|
+
|
|
90
|
+
pass
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
@dataclass
|
|
94
|
+
class TildeToken(Token):
|
|
95
|
+
"""~"""
|
|
96
|
+
|
|
97
|
+
pass
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
@dataclass
|
|
101
|
+
class ExclamationToken(Token):
|
|
102
|
+
"""!"""
|
|
103
|
+
|
|
104
|
+
pass
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
@dataclass
|
|
108
|
+
class QuestionToken(Token):
|
|
109
|
+
"""?"""
|
|
110
|
+
|
|
111
|
+
pass
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
@dataclass
|
|
115
|
+
class DollarToken(Token):
|
|
116
|
+
"""$"""
|
|
117
|
+
|
|
118
|
+
pass
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
@dataclass
|
|
122
|
+
class DotToken(Token):
|
|
123
|
+
"""."""
|
|
124
|
+
|
|
125
|
+
pass
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
# ── 字符串字面量 ───────────────────────────────────
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
@dataclass(init=False)
|
|
132
|
+
class StringToken(Token):
|
|
133
|
+
"""字符串基类(抽象:拒绝直接实例化,由单行/多行子类承载)"""
|
|
134
|
+
|
|
135
|
+
value: str = ''
|
|
136
|
+
|
|
137
|
+
def __init__(self, *args: Any, **kwargs: Any) -> None:
|
|
138
|
+
raise TypeError(
|
|
139
|
+
'StringToken 是抽象字符串基类,不能直接实例化;请使用 SinglelineStringToken 或 MultilineStringToken'
|
|
140
|
+
)
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
@dataclass
|
|
144
|
+
class SinglelineStringToken(StringToken):
|
|
145
|
+
"""双引号单行字符串(默认值仅用于错误恢复时的合成 token)"""
|
|
146
|
+
|
|
147
|
+
pass
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
@dataclass
|
|
151
|
+
class MultilineStringToken(StringToken):
|
|
152
|
+
"""反引号多行字符串(默认值仅用于错误恢复时的合成 token)"""
|
|
153
|
+
|
|
154
|
+
tags: list[str] = field(default_factory=lambda: [])
|
|
155
|
+
|
|
156
|
+
|
|
157
|
+
# ── 数字字面量 ─────────────────────────────────────
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
@dataclass
|
|
161
|
+
class IntegerToken(Token):
|
|
162
|
+
"""整数字面量(默认值仅用于错误恢复时的合成 token)"""
|
|
163
|
+
|
|
164
|
+
value: int = 0
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+
@dataclass
|
|
168
|
+
class FloatToken(Token):
|
|
169
|
+
"""浮点数字面量(默认值仅用于错误恢复时的合成 token)"""
|
|
170
|
+
|
|
171
|
+
value: decimal.Decimal = field(default_factory=lambda: decimal.Decimal(0))
|
|
172
|
+
|
|
173
|
+
|
|
174
|
+
# ── 布尔字面量 ─────────────────────────────────────
|
|
175
|
+
|
|
176
|
+
|
|
177
|
+
@dataclass
|
|
178
|
+
class BoolToken(Token):
|
|
179
|
+
"""布尔字面量(默认值仅用于错误恢复时的合成 token)"""
|
|
180
|
+
|
|
181
|
+
value: bool = False
|
|
182
|
+
|
|
183
|
+
|
|
184
|
+
# ── 存在性字面量 ───────────────────────────────────
|
|
185
|
+
|
|
186
|
+
|
|
187
|
+
@dataclass
|
|
188
|
+
class NullToken(Token):
|
|
189
|
+
"""null"""
|
|
190
|
+
|
|
191
|
+
pass
|
|
192
|
+
|
|
193
|
+
|
|
194
|
+
@dataclass
|
|
195
|
+
class NoexistToken(Token):
|
|
196
|
+
"""noexist"""
|
|
197
|
+
|
|
198
|
+
pass
|
|
199
|
+
|
|
200
|
+
|
|
201
|
+
# ── 标识符 ─────────────────────────────────────────
|
|
202
|
+
|
|
203
|
+
|
|
204
|
+
@dataclass
|
|
205
|
+
class IdentifierToken(Token):
|
|
206
|
+
"""标识符"""
|
|
207
|
+
|
|
208
|
+
name: str = ''
|
|
209
|
+
|
|
210
|
+
|
|
211
|
+
# ── 导入相关 token ─────────────────────────────────
|
|
212
|
+
|
|
213
|
+
|
|
214
|
+
@dataclass
|
|
215
|
+
class EnvImportToken(Token):
|
|
216
|
+
"""!env"""
|
|
217
|
+
|
|
218
|
+
pass
|
|
219
|
+
|
|
220
|
+
|
|
221
|
+
@dataclass
|
|
222
|
+
class FileImportToken(Token):
|
|
223
|
+
"""!file"""
|
|
224
|
+
|
|
225
|
+
pass
|
|
226
|
+
|
|
227
|
+
|
|
228
|
+
@dataclass
|
|
229
|
+
class FromImportToken(Token):
|
|
230
|
+
"""!from"""
|
|
231
|
+
|
|
232
|
+
pass
|
|
233
|
+
|
|
234
|
+
|
|
235
|
+
# ── 换行 / EOF ─────────────────────────────────────
|
|
236
|
+
|
|
237
|
+
|
|
238
|
+
@dataclass
|
|
239
|
+
class NewlineToken(Token):
|
|
240
|
+
pass
|
|
241
|
+
|
|
242
|
+
|
|
243
|
+
@dataclass
|
|
244
|
+
class EofToken(Token):
|
|
245
|
+
pass
|