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,531 @@
1
+ """词法分析器"""
2
+
3
+ from infinity_data.infra.diagnostics import Diagnostic, DiagnosticCollector, Severity
4
+ from infinity_data.infra.file import File
5
+ from infinity_data.infra.ll1_stream import NoNextType
6
+ from infinity_data.tokenizer.char_stream import CharStream
7
+ from infinity_data.tokenizer.diagnostics import diag
8
+ from infinity_data.tokenizer.models.raw_tokens import (
9
+ RawToken,
10
+ RawTokenType,
11
+ SourceInfo,
12
+ SourceRange,
13
+ )
14
+
15
+
16
+ class RawTokenizer:
17
+ """将 infd/inft 源码转为 RawToken 流。"""
18
+
19
+ _single_char_map: dict[str, RawTokenType] = {
20
+ '{': RawTokenType.LBRACE,
21
+ '}': RawTokenType.RBRACE,
22
+ '[': RawTokenType.LBRACKET,
23
+ ']': RawTokenType.RBRACKET,
24
+ '(': RawTokenType.LPAREN,
25
+ ')': RawTokenType.RPAREN,
26
+ '<': RawTokenType.LANGLE,
27
+ '>': RawTokenType.RANGLE,
28
+ '=': RawTokenType.EQUALS,
29
+ ':': RawTokenType.COLON,
30
+ ',': RawTokenType.COMMA,
31
+ '~': RawTokenType.TILDE,
32
+ '?': RawTokenType.QUESTION,
33
+ '$': RawTokenType.DOLLAR,
34
+ '.': RawTokenType.DOT,
35
+ '\n': RawTokenType.NEWLINE,
36
+ }
37
+
38
+ _keywords_map: dict[str, RawTokenType] = {
39
+ 'null': RawTokenType.NULL,
40
+ 'noexist': RawTokenType.NOEXIST,
41
+ 'true': RawTokenType.BOOL,
42
+ 'false': RawTokenType.BOOL,
43
+ 'nan': RawTokenType.FLOAT,
44
+ }
45
+
46
+ _open_brackets: frozenset[str] = frozenset({'{', '[', '(', '<'})
47
+ _close_brackets: frozenset[str] = frozenset({'}', ']', ')', '>'})
48
+
49
+ def __init__(
50
+ self,
51
+ file: File,
52
+ error_collector: DiagnosticCollector | None = None,
53
+ ) -> None:
54
+ self._file: File = file
55
+ self._stream: CharStream = CharStream(file.chars())
56
+ # 注意:不能用 `or` —— 空 DiagnosticCollector 的 __bool__ 为 False,会静默丢弃传入的收集器
57
+ self._errors = error_collector if error_collector is not None else DiagnosticCollector()
58
+ self._eof_sent: bool = False
59
+ self._open_stack: list[tuple[str, SourceInfo]] = []
60
+ self._detect_bom()
61
+
62
+ def _detect_bom(self) -> None:
63
+ """检测文件 BOM(规范要求 UTF-8 NO BOM):报 tokenize.bom 警告并跳过。"""
64
+ if not self._stream.eof() and self._stream.peek() == '\ufeff':
65
+ self._errors.add(
66
+ Diagnostic(
67
+ Severity.WARNING,
68
+ 'tokenize.bom',
69
+ {},
70
+ SourceRange.at(self._file, self._stream.info()),
71
+ )
72
+ )
73
+ self._stream.advance()
74
+
75
+ def __iter__(self) -> 'RawTokenizer':
76
+ return self
77
+
78
+ def __next__(self) -> RawToken:
79
+ if self._eof_sent:
80
+ raise StopIteration
81
+ tok: RawToken = self.next()
82
+ if tok.type is RawTokenType.EOF:
83
+ self._eof_sent = True
84
+ return tok
85
+
86
+ @property
87
+ def error_collector(self) -> DiagnosticCollector:
88
+ return self._errors
89
+
90
+ @property
91
+ def file(self) -> File:
92
+ return self._file
93
+
94
+ def _current_source_info(self) -> SourceInfo:
95
+ return self._stream.info()
96
+
97
+ def next(self) -> RawToken:
98
+ """返回下一个 token。"""
99
+ while True:
100
+ self._skip_whitespace_and_comments()
101
+
102
+ if self._stream.eof():
103
+ self._report_unclosed_brackets()
104
+ return self._make_token(RawTokenType.EOF, '', self._current_source_info())
105
+
106
+ ch = self._stream.peek()
107
+ assert not isinstance(ch, NoNextType)
108
+
109
+ if ch in self._single_char_map:
110
+ tok = self._single_char(self._single_char_map[ch])
111
+ self._track_bracket(ch, tok)
112
+ return tok
113
+
114
+ if ch == '!':
115
+ tok = self._read_bang()
116
+ if tok is not None:
117
+ return tok
118
+ continue # 词法错误已报,跳过该 ! 序列
119
+
120
+ if ch == '"':
121
+ return self._read_string()
122
+
123
+ if ch == '`':
124
+ return self._read_multiline_string()
125
+
126
+ if ch.isdigit() or ch in ['+', '-']:
127
+ return self._read_number_fallback()
128
+
129
+ # ── 标识符 / 关键字 ───────────────────────
130
+ if ch.isalpha() or ch == '_':
131
+ return self._read_identifier_or_keyword()
132
+
133
+ # ── 无法识别的字符 ────────────────────────
134
+ self._errors.add(
135
+ diag('tokenize.unknown_char', {'char': ch}, SourceRange.at(self._file, self._current_source_info()))
136
+ )
137
+ self._stream.advance()
138
+
139
+ def _make_token(
140
+ self,
141
+ token_type: RawTokenType,
142
+ raw: str,
143
+ start: SourceInfo,
144
+ ) -> RawToken:
145
+ return RawToken(
146
+ type=token_type,
147
+ raw=raw,
148
+ source=SourceRange(
149
+ file=self._file,
150
+ start=start,
151
+ end=self._current_source_info(),
152
+ ),
153
+ )
154
+
155
+ # ── 空白与注释跳过 ────────────────────────────────
156
+
157
+ def _skip_whitespace_and_comments(self) -> None:
158
+ """跳过空白及注释(单行 # 和多行 #+...#-)。"""
159
+ while not self._stream.eof():
160
+ ch = self._stream.peek()
161
+ assert not isinstance(ch, NoNextType)
162
+
163
+ # 跳过除换行外的空白
164
+ if ch != '\n' and ch.isspace():
165
+ self._stream.advance()
166
+ continue
167
+
168
+ # 单行注释: # 到行尾
169
+ if ch == '#':
170
+ self._handle_comment()
171
+ continue
172
+
173
+ break
174
+
175
+ def _handle_comment(self) -> None:
176
+ """处理注释:单行 # 或多行 #+...#-"""
177
+ self._stream.advance() # 消费 '#'
178
+
179
+ if self._stream.eof():
180
+ return
181
+
182
+ ch = self._stream.peek()
183
+ assert not isinstance(ch, NoNextType)
184
+
185
+ # 检查是否为多行注释起始标记 #+
186
+ plus_count = 0
187
+ while ch == '+':
188
+ plus_count += 1
189
+ self._stream.advance()
190
+ if self._stream.eof():
191
+ self._errors.add(
192
+ diag(
193
+ 'tokenize.unterminated_comment',
194
+ {'flag': '#' + '-' * plus_count},
195
+ SourceRange.at(self._file, self._current_source_info()),
196
+ )
197
+ )
198
+ return
199
+ ch = self._stream.peek()
200
+ assert not isinstance(ch, NoNextType)
201
+
202
+ if plus_count > 0:
203
+ # 多行注释模式: 需要找到匹配的 # + '-' * plus_count
204
+ self._skip_multiline_comment(plus_count)
205
+ else:
206
+ # 单行注释: 跳到行尾
207
+ while not self._stream.eof() and self._stream.peek() != '\n':
208
+ self._stream.advance()
209
+
210
+ def _skip_multiline_comment(self, depth: int) -> None:
211
+ """跳过多行注释直到找到匹配的结束标记 # + '-' * depth。"""
212
+ while not self._stream.eof():
213
+ ch = self._stream.peek()
214
+ assert not isinstance(ch, NoNextType)
215
+
216
+ if ch == '#':
217
+ self._stream.advance()
218
+ if self._stream.eof():
219
+ break
220
+ # 检查后续字符是否全是 '-'
221
+ minus_count = 0
222
+ while not self._stream.eof() and self._stream.peek() == '-':
223
+ minus_count += 1
224
+ self._stream.advance()
225
+ if minus_count == depth:
226
+ return
227
+ continue
228
+
229
+ self._stream.advance()
230
+
231
+ self._errors.add(
232
+ diag(
233
+ 'tokenize.unterminated_comment',
234
+ {'flag': '#' + '-' * depth},
235
+ SourceRange.at(self._file, self._current_source_info()),
236
+ )
237
+ )
238
+
239
+ # ── 单字符 token ──────────────────────────────────
240
+ def _single_char(self, token_type: RawTokenType) -> RawToken:
241
+ ch = self._stream.peek()
242
+ assert not isinstance(ch, NoNextType)
243
+ start = self._current_source_info()
244
+ self._stream.advance()
245
+ return self._make_token(token_type, ch, start=start)
246
+
247
+ # ── 括号栈(EOF 时报告未闭合)────────────────────
248
+ def _track_bracket(self, ch: str, tok: RawToken) -> None:
249
+ """维护括号栈:开括号压栈、闭括号弹栈,供 EOF 时报告未闭合括号。"""
250
+ if ch in self._open_brackets:
251
+ self._open_stack.append((ch, tok.source.start))
252
+ elif ch in self._close_brackets:
253
+ if self._open_stack:
254
+ self._open_stack.pop()
255
+
256
+ def _report_unclosed_brackets(self) -> None:
257
+ """EOF 时仍开着的括号 → 报 tokenize.unterminated_bracket(按开括号顺序)。"""
258
+ for bracket, start in self._open_stack:
259
+ self._errors.add(
260
+ diag('tokenize.unterminated_bracket', {'bracket': bracket}, SourceRange.at(self._file, start))
261
+ )
262
+ self._open_stack.clear()
263
+
264
+ # ── ! 导入关键字(词法组合,! 无独立语法)──
265
+
266
+ def _read_bang(self) -> RawToken | None:
267
+ """读取 ``!`` 起始的 token。
268
+
269
+ - ``!env`` / ``!file`` / ``!from`` → 组合导入关键字 token
270
+ - 其他任何情况 → 词法错误(语言不允许单独 ``!``),返回 None 跳过
271
+ """
272
+ start = self._current_source_info()
273
+ self._stream.advance() # 消费 '!'
274
+
275
+ if self._stream.eof():
276
+ self._errors.add(diag('tokenize.invalid_bang', {'actual': 'EOF'}, SourceRange.at(self._file, start)))
277
+ return None
278
+ ch = self._stream.peek()
279
+ assert not isinstance(ch, NoNextType)
280
+ if not (ch.isalpha() or ch == '_'):
281
+ self._errors.add(diag('tokenize.invalid_bang', {'actual': repr(ch)}, SourceRange.at(self._file, start)))
282
+ return None
283
+
284
+ ident_tok = self._read_identifier_or_keyword()
285
+ match ident_tok.raw:
286
+ case 'env':
287
+ return self._make_token(RawTokenType.ENV_IMPORT, '!env', start=start)
288
+ case 'file':
289
+ return self._make_token(RawTokenType.FILE_IMPORT, '!file', start=start)
290
+ case 'from':
291
+ return self._make_token(RawTokenType.FROM_IMPORT, '!from', start=start)
292
+ case _:
293
+ self._errors.add(
294
+ diag('tokenize.invalid_bang', {'actual': repr(ident_tok.raw)}, SourceRange.at(self._file, start))
295
+ )
296
+ return None
297
+
298
+ # ── 单行字符串 ────────────────────────────────────
299
+
300
+ def _read_string(self) -> RawToken:
301
+ """读取双引号包裹的单行字符串"""
302
+ start = self._current_source_info()
303
+ raw_parts: list[str] = [self._stream.advance()] # 消费 '"'
304
+
305
+ while not self._stream.eof():
306
+ ch = self._stream.peek()
307
+ assert not isinstance(ch, NoNextType)
308
+
309
+ if ch == '\\':
310
+ raw_parts.append(self._stream.advance())
311
+ if self._stream.eof():
312
+ self._errors.add(
313
+ diag('tokenize.unterminated_string', {'str_type': '字符串'}, SourceRange.at(self._file, start))
314
+ )
315
+ return self._make_token(RawTokenType.STRING, ''.join(raw_parts), start=start)
316
+ raw_parts.append(self._stream.advance())
317
+ continue
318
+
319
+ if ch == '"':
320
+ raw_parts.append(ch)
321
+ self._stream.advance()
322
+ return self._make_token(RawTokenType.STRING, ''.join(raw_parts), start=start)
323
+
324
+ if ch == '\n':
325
+ self._errors.add(
326
+ diag('tokenize.unterminated_string', {'str_type': '字符串'}, SourceRange.at(self._file, start))
327
+ )
328
+ return self._make_token(RawTokenType.STRING, ''.join(raw_parts), start=start)
329
+
330
+ raw_parts.append(ch)
331
+ self._stream.advance()
332
+
333
+ self._errors.add(
334
+ diag('tokenize.unterminated_string', {'str_type': '字符串'}, SourceRange.at(self._file, start))
335
+ )
336
+ raw_parts.append('"') # 补上缺失的结束引号
337
+ return self._make_token(RawTokenType.STRING, ''.join(raw_parts), start=start)
338
+
339
+ # ── 多行字符串(Markdown 风格) ────────────────────
340
+
341
+ def _read_multiline_string(self) -> RawToken:
342
+ """读取反引号包裹的多行字符串。
343
+
344
+ 语法: `...`
345
+ - 起始 ` 可变长(>= 1 个反引号)
346
+ """
347
+ start = self._current_source_info()
348
+
349
+ # 统计起始反引号数量
350
+ backtick_count = 0
351
+ while not self._stream.eof():
352
+ ch = self._stream.peek()
353
+ if ch == '`':
354
+ backtick_count += 1
355
+ self._stream.advance()
356
+ else:
357
+ break
358
+
359
+ # 读取内容直到匹配的结束反引号
360
+ raw = '`' * backtick_count
361
+ while not self._stream.eof():
362
+ ch = self._stream.peek()
363
+ assert not isinstance(ch, NoNextType)
364
+
365
+ if ch == '`':
366
+ # 检查是否有足够的反引号匹配
367
+ temp_count = 0
368
+ while temp_count < backtick_count and not self._stream.eof() and self._stream.peek() == '`':
369
+ temp_count += 1
370
+ self._stream.advance()
371
+ if temp_count == backtick_count:
372
+ raw += '`' * temp_count
373
+ return self._make_token(RawTokenType.MULTILINE_STRING, raw, start=start)
374
+ else:
375
+ raw += '`' * temp_count
376
+ continue
377
+
378
+ raw += ch
379
+ self._stream.advance()
380
+
381
+ self._errors.add(
382
+ diag('tokenize.unterminated_string', {'str_type': '多行字符串'}, SourceRange.at(self._file, start))
383
+ )
384
+ raw += '`' * backtick_count
385
+ return self._make_token(RawTokenType.MULTILINE_STRING, raw, start=start)
386
+
387
+ # ── 数字 / 特殊浮点字面量 ─────────────────────────
388
+
389
+ def _read_number_fallback(self) -> RawToken:
390
+ """读取数字或特殊浮点字面量(nan, +inf, -inf)。
391
+
392
+ 支持的格式:
393
+ - 整数: 42, -80
394
+ - 浮点: 3.14, 5.0, 1e10, 2.5e-3
395
+ - 特殊: +inf, -inf(nan 由关键字路径处理)
396
+ """
397
+ start = self._current_source_info()
398
+ raw_parts: list[str] = []
399
+ is_float = False
400
+
401
+ # ── 1. 可选正负号 ──
402
+ ch = self._stream.peek()
403
+ if not isinstance(ch, NoNextType) and ch in '+-':
404
+ raw_parts.append(ch)
405
+ self._stream.advance()
406
+
407
+ # ── 检查是否为特殊字面量 ──
408
+ if raw_parts and raw_parts[0] in '+-':
409
+ ch = self._stream.peek()
410
+ if not isinstance(ch, NoNextType) and ch.isalpha():
411
+ # 尝试读取标识符 (如 +inf, -inf)
412
+ ident_parts: list[str] = []
413
+ while not self._stream.eof():
414
+ c = self._stream.peek()
415
+ if not isinstance(c, NoNextType) and (c.isalnum() or c == '_'):
416
+ ident_parts.append(c)
417
+ self._stream.advance()
418
+ else:
419
+ break
420
+ ident = ''.join(ident_parts)
421
+ full = raw_parts[0] + ident
422
+ if full == '+inf':
423
+ return self._make_token(RawTokenType.FLOAT, full, start=start)
424
+ if full == '-inf':
425
+ return self._make_token(RawTokenType.FLOAT, full, start=start)
426
+ self._errors.add(diag('tokenize.invalid_number', {'raw': full}, SourceRange.at(self._file, start)))
427
+ return self._make_token(RawTokenType.IDENTIFIER, full, start=start)
428
+
429
+ # ── 2. 整数部分 ──
430
+ ch = self._stream.peek()
431
+ if not isinstance(ch, NoNextType) and ch.isdigit():
432
+ raw_parts.append(ch)
433
+ self._stream.advance()
434
+ while not self._stream.eof():
435
+ ch = self._stream.peek()
436
+ if not isinstance(ch, NoNextType) and ch.isdigit():
437
+ raw_parts.append(ch)
438
+ self._stream.advance()
439
+ else:
440
+ break
441
+
442
+ # ── 3. 可选小数部分 ──
443
+ ch = self._stream.peek()
444
+ if ch == '.':
445
+ self._stream.advance() # 消费 '.'
446
+ if not self._stream.eof():
447
+ next_ch = self._stream.peek()
448
+ if not isinstance(next_ch, NoNextType) and next_ch.isdigit():
449
+ is_float = True
450
+ raw_parts.append('.')
451
+ raw_parts.append(next_ch)
452
+ self._stream.advance()
453
+ while not self._stream.eof():
454
+ ch = self._stream.peek()
455
+ if not isinstance(ch, NoNextType) and ch.isdigit():
456
+ raw_parts.append(ch)
457
+ self._stream.advance()
458
+ else:
459
+ break
460
+ else:
461
+ # 有前置数字但 . 后无数字 (如 "42.") → 记录错误
462
+ self._errors.add(
463
+ diag(
464
+ 'tokenize.invalid_number',
465
+ {'raw': ''.join(raw_parts) + '.'},
466
+ SourceRange.at(self._file, start),
467
+ )
468
+ )
469
+
470
+ # ── 4. 可选指数部分 ──
471
+ ch = self._stream.peek()
472
+ if not isinstance(ch, NoNextType) and ch in ['e', 'E']:
473
+ raw_parts.append(ch)
474
+ self._stream.advance()
475
+ is_float = True
476
+
477
+ if not self._stream.eof():
478
+ ch = self._stream.peek()
479
+ if not isinstance(ch, NoNextType) and ch in '+-':
480
+ raw_parts.append(ch)
481
+ self._stream.advance()
482
+
483
+ if self._stream.eof():
484
+ self._errors.add(
485
+ diag('tokenize.invalid_number', {'raw': ''.join(raw_parts)}, SourceRange.at(self._file, start))
486
+ )
487
+ else:
488
+ ch = self._stream.peek()
489
+ if not isinstance(ch, NoNextType) and ch.isdigit():
490
+ raw_parts.append(ch)
491
+ self._stream.advance()
492
+ while not self._stream.eof():
493
+ ch = self._stream.peek()
494
+ if not isinstance(ch, NoNextType) and ch.isdigit():
495
+ raw_parts.append(ch)
496
+ self._stream.advance()
497
+ else:
498
+ break
499
+ else:
500
+ self._errors.add(
501
+ diag('tokenize.invalid_number', {'raw': ''.join(raw_parts)}, SourceRange.at(self._file, start))
502
+ )
503
+
504
+ # ── 5. 确保至少有一位数字 ──
505
+ raw = ''.join(raw_parts)
506
+ if not any(c.isdigit() for c in raw):
507
+ self._errors.add(
508
+ diag('tokenize.invalid_number', {'raw': ''.join(raw_parts)}, SourceRange.at(self._file, start))
509
+ )
510
+
511
+ token_type = RawTokenType.FLOAT if is_float else RawTokenType.INTEGER
512
+ return self._make_token(token_type, raw, start=start)
513
+
514
+ # ── 标识符 / 关键字 ───────────────────────────────
515
+
516
+ def _read_identifier_or_keyword(self) -> RawToken:
517
+ """读取标识符,识别关键字。"""
518
+ start = self._current_source_info()
519
+ raw_parts: list[str] = []
520
+
521
+ while not self._stream.eof():
522
+ ch = self._stream.peek()
523
+ if not isinstance(ch, NoNextType) and (ch.isalnum() or ch == '_'):
524
+ raw_parts.append(ch)
525
+ self._stream.advance()
526
+ else:
527
+ break
528
+
529
+ raw = ''.join(raw_parts)
530
+ token_type = self._keywords_map.get(raw, RawTokenType.IDENTIFIER)
531
+ return self._make_token(token_type, raw, start=start)
@@ -0,0 +1,60 @@
1
+ Metadata-Version: 2.4
2
+ Name: infinity-data
3
+ Version: 1.0.0
4
+ Summary: InfinityData:声明式配置语言(.infd/.inft)的 Python 编译器库
5
+ Author: infinity_system: yin_bailiang
6
+ License: MIT
7
+ Project-URL: Repository, https://github.com/yinbailiang/infinity_data
8
+ Project-URL: Documentation, https://github.com/yinbailiang/infinity_data#readme
9
+ Keywords: config,configuration,declarative,dsl,compiler,infinity-data
10
+ Classifier: Development Status :: 5 - Production/Stable
11
+ Classifier: Intended Audience :: Developers
12
+ Classifier: License :: OSI Approved :: MIT License
13
+ Classifier: Operating System :: OS Independent
14
+ Classifier: Programming Language :: Python :: 3
15
+ Classifier: Programming Language :: Python :: 3.12
16
+ Classifier: Programming Language :: Python :: 3.13
17
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
18
+ Requires-Python: >=3.12
19
+ Description-Content-Type: text/markdown
20
+ License-File: LICENSE
21
+ Dynamic: license-file
22
+
23
+ # InfinityData
24
+
25
+ 声明式配置语言(`.infd` / `.inft`)的 Python 编译器库。
26
+
27
+ - **编译优先**:`.infd` 是源码,JSON/YAML/TOML 是构建产物
28
+ - **模板即约束**:模板定义自动注册为约束校验器
29
+ - **零信任默认**:库默认 `deny_all` 沙盒,显式开放而非事后限制
30
+ - **结构化错误**:稳定错误码 + 结构化参数 + 多语言渲染
31
+
32
+ ## 快速使用
33
+
34
+ ```python
35
+ from infinity_data import load, safe_load, SandboxConfig, Schema
36
+
37
+ result = load('app.infd') # 默认零信任
38
+ if result.has_errors:
39
+ for d in result.diagnostics:
40
+ print(d.location, d.code, d.message)
41
+ else:
42
+ print(result.value) # 降维后的 dict
43
+ ```
44
+
45
+ ## 文档导航
46
+
47
+ | 文档 | 内容 |
48
+ |---|---|
49
+ | [neo_desg.md](./neo_desg.md) | 语言基础设计(语法 / 类型 / 模板 / 约束) |
50
+ | [extra_desg.md](./extra_desg.md) | 库 API / CLI / 生态设计 |
51
+ | [impl_desg.md](./impl_desg.md) | 编译器实现层的取舍与假设 |
52
+
53
+ ## 开发
54
+
55
+ ```bash
56
+ uv run pytest -q # 测试
57
+ uv run ruff check src tests # lint
58
+ uv run pyright # 类型检查(strict)
59
+ uv run python test.py # 错误报告演示
60
+ ```
@@ -0,0 +1,52 @@
1
+ infinity_data/__init__.py,sha256=soRyEbA8B2BvU8H_VQJgx3HDFR6-dwT188du-sxF-sU,979
2
+ infinity_data/frontend.py,sha256=3OTZOkpgM8uI1eTAjwrsvo_Q3jNemT0Sd7rW1dEU0zY,1577
3
+ infinity_data/pipeline.py,sha256=VpOnnX8LYf6xgs3QwS2kYzI49QUuodypQtscE1X-b5Q,9827
4
+ infinity_data/emit/__init__.py,sha256=VaLebPtYb4_lHF8b_19FTLc9gkWUUYMbMueUoq4IFqQ,398
5
+ infinity_data/emit/converter.py,sha256=26fdvzfKlvxinEJ79px6fMZ6fbM4PNRsEMxJXGPTe_M,1918
6
+ infinity_data/infra/__init__.py,sha256=LRAUkn-2gQKCOhPj4MGsjwZgYBAnBHre6amzZOnhug4,213
7
+ infinity_data/infra/diagnostics.py,sha256=MsS4cn_KzTq_gBhTV6LGQRF11XCe3SKe32QXGoGWewY,7274
8
+ infinity_data/infra/file.py,sha256=ty0VSDKBcdCT-QcO1x8-NGqsRs9bMU-JN5T1KqrZJbc,2668
9
+ infinity_data/infra/ll1_stream.py,sha256=U3bAMAVfV3I-uui-EvWN2JeL_7ZQGz6PP107WTDh1Uk,2029
10
+ infinity_data/infra/location.py,sha256=5ej8mr2QCG7lbNynKz9AvSi3B3WzpvRuZirH-pZHrBI,2058
11
+ infinity_data/infra/path.py,sha256=S1rj5M60kNKQqJYh3Pod_P4NFD7WDBbGrqdY9SjtwO8,1930
12
+ infinity_data/parser/__init__.py,sha256=4g5x2Gp6qCSF6ChXd6bfhZoxKa1qWqw8LqkOy3ACbOg,1830
13
+ infinity_data/parser/diagnostics.py,sha256=sU2RntPnzv7c7VWZaneXnDHpluq_9CEko5WzYgYJ9BU,4254
14
+ infinity_data/parser/models.py,sha256=sXi6yb7pkWT3AMwB7p_9edZxzTCMbIANdLl4-fSNXbg,9055
15
+ infinity_data/parser/parser.py,sha256=rI25qZDSeUOi-7LhCWSQYkNdVItwj7W5_880ptld3_s,40479
16
+ infinity_data/parser/token_stream.py,sha256=mwbFjoBG_4cR-j1KFs5b7kkShT4vU52HcOZ_FSFyk6A,5191
17
+ infinity_data/sandbox/__init__.py,sha256=qFgTsZhAzFTtRKu-RJEJLNtQNLWWU_uk9WJjhWI5VnA,909
18
+ infinity_data/sandbox/config.py,sha256=MgFgxMEKvUk6J0S9mNTPCp8JRxcv83r6OmL_qec7lV0,2235
19
+ infinity_data/sandbox/errors.py,sha256=D48DnWicB5fCT9h3c2cce34eue5CA-Oq0w38DrC2i0k,3781
20
+ infinity_data/sandbox/mediator.py,sha256=4HWENRUORaMhvjgRf0irCThpTQ8TZjqNeJudIHi-Szg,8148
21
+ infinity_data/sandbox/schema.py,sha256=P_rvBBEixeThYw-kgpoLImlpf4u4K7_FpytDncteZmw,686
22
+ infinity_data/semantic/__init__.py,sha256=eazCCWg8o_P-atwgKbzrfPDQGjlCMRTMlr3W1ulbUWc,1632
23
+ infinity_data/semantic/constraints.py,sha256=3Hh-LjOkWh8wexzbZmjTb1K3tunQ_t3LV2P5TNNYXsU,5896
24
+ infinity_data/semantic/diagnostics.py,sha256=Tmdb0VcA_XPUSWYfyBZZ2EknSowZH22SFBrK1XM-Coc,11762
25
+ infinity_data/semantic/builder/__init__.py,sha256=7aJXEq_lcsyGj8JjbvRBNYx9rmU5INHUAcrSp-aU_M8,735
26
+ infinity_data/semantic/builder/builder.py,sha256=GZeHM6fuowWoyuw6-weRSSHEHgxGO_uBS05BncwB8GM,23717
27
+ infinity_data/semantic/builder/models.py,sha256=EIxc6og4LraTPcX9kSG0EsjtyVbdITyQJw4pwcypRLg,6338
28
+ infinity_data/semantic/executor/__init__.py,sha256=b-XF7P88jmaqBovLaU8YMsYOGHxw0GHyaDm3P2KrMWo,364
29
+ infinity_data/semantic/executor/executor.py,sha256=vvtSj5lykRGpsuaFPyi0-qrvj-PWfedPIMwKlCA3QSI,11439
30
+ infinity_data/semantic/registry/__init__.py,sha256=mEmCXO_MNaBpdN3FcPfawrW7eT3osZ-HB55OQ71pBQ4,7162
31
+ infinity_data/semantic/registry/_core.py,sha256=YWXv8Z6RCOkvDu_rbooF-PGLlV_fVbmNrkE41t9DBkI,7167
32
+ infinity_data/semantic/registry/dict_constraints.py,sha256=PtER1nYL1_ScVPduyF_B-_pFG9T9W-UbYveHiOnE-II,1572
33
+ infinity_data/semantic/registry/general.py,sha256=kLCOKTV46LioS-SeLzoUcVzeO9t90rnlCfHcbK12OK0,11579
34
+ infinity_data/semantic/registry/logic.py,sha256=AVKepdmyQ_YSx6VDDw8p_0rzRNQLf5vYS9CRH-n5v5E,3683
35
+ infinity_data/semantic/registry/types.py,sha256=SkePeIXmimpVzPKOsPnzNLM6Zgn6y1hvzXrAhXOuBZM,4086
36
+ infinity_data/semantic/resolver/__init__.py,sha256=uue2RoEOe0F7a4MZ7C2fBPyMbOXFdqOqMY689JdE-FQ,755
37
+ infinity_data/semantic/resolver/imports.py,sha256=Xi4WeVO6S2puiX478gs2rGOMLeyL106PaOgmWididmc,7418
38
+ infinity_data/semantic/resolver/models.py,sha256=hpKNEHYPkWJK3u9AuP53sIsebxbW3ukHpdD2hh5EGIw,2767
39
+ infinity_data/semantic/resolver/resolver.py,sha256=8Hr-d9f1Q7dwWq_o6B1pSEJRdCosQvUabgMNmKijgLg,15207
40
+ infinity_data/tokenizer/__init__.py,sha256=dkPkCtnPNVNzHjepCPsxhwJUeLJsl13vIvPPbv_06Hw,532
41
+ infinity_data/tokenizer/char_stream.py,sha256=hn6putg7374lDGR6dSIZTuDWwEnJz4CpQmV-dcygv3I,2026
42
+ infinity_data/tokenizer/diagnostics.py,sha256=pRiNKFh6-I2Vt9HSkoBKUgsKy1z7s9-Fq6oZ9OZlG5s,2433
43
+ infinity_data/tokenizer/finalizer.py,sha256=3Sxhd7PIYoY_jw8HrO6BaPo9uapbWGpwqtd9B9hQ9cQ,9652
44
+ infinity_data/tokenizer/tokenizer.py,sha256=wccZs7vOQDu_lcA_kEqMRvkkcriNDzuUXEVMB9A23XA,20521
45
+ infinity_data/tokenizer/models/__init__.py,sha256=vr4OWsEb0-5cNmWJb15qY2O4MwJ3hAPM4Zd5a6phK70,356
46
+ infinity_data/tokenizer/models/raw_tokens.py,sha256=YmX1KF8BzFjxazQAzKMT2UXQJNNXiXDijW1sAi7cIVw,1896
47
+ infinity_data/tokenizer/models/tokens.py,sha256=Ozdrrc06bhfiu04y1-AC6kMU6nSApExyNvglYMwFEYQ,3975
48
+ infinity_data-1.0.0.dist-info/licenses/LICENSE,sha256=SQtaU1Umg4scJDkot4OxhqzIB26vkK50CvciBhaGJyc,1086
49
+ infinity_data-1.0.0.dist-info/METADATA,sha256=mcn9RNs6zMJmaCk0-TUCyyTnaYngry_AjTsFwzZmUHQ,2119
50
+ infinity_data-1.0.0.dist-info/WHEEL,sha256=YVMoNqKzERt-wjUZwJ33xBGAwnFl-4cqbYkTtWa4itE,91
51
+ infinity_data-1.0.0.dist-info/top_level.txt,sha256=aHZLBFrKFdz2uxSgNYqD5Q7enbDMIzorit96IMU0VL0,14
52
+ infinity_data-1.0.0.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (84.0.0)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 InfinitySystem (yin_bailiang)
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1 @@
1
+ infinity_data