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.
Files changed (52) hide show
  1. infinity_data/__init__.py +41 -0
  2. infinity_data/emit/__init__.py +9 -0
  3. infinity_data/emit/converter.py +55 -0
  4. infinity_data/frontend.py +37 -0
  5. infinity_data/infra/__init__.py +4 -0
  6. infinity_data/infra/diagnostics.py +212 -0
  7. infinity_data/infra/file.py +83 -0
  8. infinity_data/infra/ll1_stream.py +71 -0
  9. infinity_data/infra/location.py +80 -0
  10. infinity_data/infra/path.py +46 -0
  11. infinity_data/parser/__init__.py +79 -0
  12. infinity_data/parser/diagnostics.py +92 -0
  13. infinity_data/parser/models.py +295 -0
  14. infinity_data/parser/parser.py +988 -0
  15. infinity_data/parser/token_stream.py +128 -0
  16. infinity_data/pipeline.py +252 -0
  17. infinity_data/sandbox/__init__.py +32 -0
  18. infinity_data/sandbox/config.py +61 -0
  19. infinity_data/sandbox/errors.py +106 -0
  20. infinity_data/sandbox/mediator.py +214 -0
  21. infinity_data/sandbox/schema.py +21 -0
  22. infinity_data/semantic/__init__.py +59 -0
  23. infinity_data/semantic/builder/__init__.py +29 -0
  24. infinity_data/semantic/builder/builder.py +523 -0
  25. infinity_data/semantic/builder/models.py +163 -0
  26. infinity_data/semantic/constraints.py +163 -0
  27. infinity_data/semantic/diagnostics.py +172 -0
  28. infinity_data/semantic/executor/__init__.py +10 -0
  29. infinity_data/semantic/executor/executor.py +259 -0
  30. infinity_data/semantic/registry/__init__.py +168 -0
  31. infinity_data/semantic/registry/_core.py +212 -0
  32. infinity_data/semantic/registry/dict_constraints.py +56 -0
  33. infinity_data/semantic/registry/general.py +362 -0
  34. infinity_data/semantic/registry/logic.py +134 -0
  35. infinity_data/semantic/registry/types.py +136 -0
  36. infinity_data/semantic/resolver/__init__.py +20 -0
  37. infinity_data/semantic/resolver/imports.py +191 -0
  38. infinity_data/semantic/resolver/models.py +67 -0
  39. infinity_data/semantic/resolver/resolver.py +345 -0
  40. infinity_data/tokenizer/__init__.py +16 -0
  41. infinity_data/tokenizer/char_stream.py +72 -0
  42. infinity_data/tokenizer/diagnostics.py +62 -0
  43. infinity_data/tokenizer/finalizer.py +271 -0
  44. infinity_data/tokenizer/models/__init__.py +17 -0
  45. infinity_data/tokenizer/models/raw_tokens.py +61 -0
  46. infinity_data/tokenizer/models/tokens.py +245 -0
  47. infinity_data/tokenizer/tokenizer.py +531 -0
  48. infinity_data-1.0.0.dist-info/METADATA +60 -0
  49. infinity_data-1.0.0.dist-info/RECORD +52 -0
  50. infinity_data-1.0.0.dist-info/WHEEL +5 -0
  51. infinity_data-1.0.0.dist-info/licenses/LICENSE +21 -0
  52. 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