infinity-data 2.2.0__tar.gz → 2.2.1__tar.gz
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-2.2.0/src/infinity_data.egg-info → infinity_data-2.2.1}/PKG-INFO +1 -1
- {infinity_data-2.2.0 → infinity_data-2.2.1}/pyproject.toml +1 -1
- {infinity_data-2.2.0 → infinity_data-2.2.1}/src/infinity_data/parser/__init__.py +2 -0
- {infinity_data-2.2.0 → infinity_data-2.2.1}/src/infinity_data/parser/models.py +66 -32
- {infinity_data-2.2.0 → infinity_data-2.2.1}/src/infinity_data/parser/parser.py +23 -19
- {infinity_data-2.2.0 → infinity_data-2.2.1}/src/infinity_data/semantic/builder/builder.py +122 -99
- {infinity_data-2.2.0 → infinity_data-2.2.1/src/infinity_data.egg-info}/PKG-INFO +1 -1
- {infinity_data-2.2.0 → infinity_data-2.2.1}/LICENSE +0 -0
- {infinity_data-2.2.0 → infinity_data-2.2.1}/README.md +0 -0
- {infinity_data-2.2.0 → infinity_data-2.2.1}/setup.cfg +0 -0
- {infinity_data-2.2.0 → infinity_data-2.2.1}/src/infinity_data/__init__.py +0 -0
- {infinity_data-2.2.0 → infinity_data-2.2.1}/src/infinity_data/emit/__init__.py +0 -0
- {infinity_data-2.2.0 → infinity_data-2.2.1}/src/infinity_data/emit/converter.py +0 -0
- {infinity_data-2.2.0 → infinity_data-2.2.1}/src/infinity_data/frontend.py +0 -0
- {infinity_data-2.2.0 → infinity_data-2.2.1}/src/infinity_data/infra/__init__.py +0 -0
- {infinity_data-2.2.0 → infinity_data-2.2.1}/src/infinity_data/infra/diagnostics.py +0 -0
- {infinity_data-2.2.0 → infinity_data-2.2.1}/src/infinity_data/infra/file.py +0 -0
- {infinity_data-2.2.0 → infinity_data-2.2.1}/src/infinity_data/infra/ll1_stream.py +0 -0
- {infinity_data-2.2.0 → infinity_data-2.2.1}/src/infinity_data/infra/location.py +0 -0
- {infinity_data-2.2.0 → infinity_data-2.2.1}/src/infinity_data/infra/path.py +0 -0
- {infinity_data-2.2.0 → infinity_data-2.2.1}/src/infinity_data/parser/diagnostics.py +0 -0
- {infinity_data-2.2.0 → infinity_data-2.2.1}/src/infinity_data/parser/token_stream.py +0 -0
- {infinity_data-2.2.0 → infinity_data-2.2.1}/src/infinity_data/pipeline.py +0 -0
- {infinity_data-2.2.0 → infinity_data-2.2.1}/src/infinity_data/py.typed +0 -0
- {infinity_data-2.2.0 → infinity_data-2.2.1}/src/infinity_data/sandbox/__init__.py +0 -0
- {infinity_data-2.2.0 → infinity_data-2.2.1}/src/infinity_data/sandbox/config.py +0 -0
- {infinity_data-2.2.0 → infinity_data-2.2.1}/src/infinity_data/sandbox/errors.py +0 -0
- {infinity_data-2.2.0 → infinity_data-2.2.1}/src/infinity_data/sandbox/mediator.py +0 -0
- {infinity_data-2.2.0 → infinity_data-2.2.1}/src/infinity_data/sandbox/schema.py +0 -0
- {infinity_data-2.2.0 → infinity_data-2.2.1}/src/infinity_data/semantic/__init__.py +0 -0
- {infinity_data-2.2.0 → infinity_data-2.2.1}/src/infinity_data/semantic/builder/__init__.py +0 -0
- {infinity_data-2.2.0 → infinity_data-2.2.1}/src/infinity_data/semantic/builder/models.py +0 -0
- {infinity_data-2.2.0 → infinity_data-2.2.1}/src/infinity_data/semantic/constraints.py +0 -0
- {infinity_data-2.2.0 → infinity_data-2.2.1}/src/infinity_data/semantic/diagnostics.py +0 -0
- {infinity_data-2.2.0 → infinity_data-2.2.1}/src/infinity_data/semantic/executor/__init__.py +0 -0
- {infinity_data-2.2.0 → infinity_data-2.2.1}/src/infinity_data/semantic/executor/executor.py +0 -0
- {infinity_data-2.2.0 → infinity_data-2.2.1}/src/infinity_data/semantic/jsonpath.py +0 -0
- {infinity_data-2.2.0 → infinity_data-2.2.1}/src/infinity_data/semantic/registry/__init__.py +0 -0
- {infinity_data-2.2.0 → infinity_data-2.2.1}/src/infinity_data/semantic/registry/_core.py +0 -0
- {infinity_data-2.2.0 → infinity_data-2.2.1}/src/infinity_data/semantic/registry/dict_constraints.py +0 -0
- {infinity_data-2.2.0 → infinity_data-2.2.1}/src/infinity_data/semantic/registry/general.py +0 -0
- {infinity_data-2.2.0 → infinity_data-2.2.1}/src/infinity_data/semantic/registry/logic.py +0 -0
- {infinity_data-2.2.0 → infinity_data-2.2.1}/src/infinity_data/semantic/registry/types.py +0 -0
- {infinity_data-2.2.0 → infinity_data-2.2.1}/src/infinity_data/semantic/resolver/__init__.py +0 -0
- {infinity_data-2.2.0 → infinity_data-2.2.1}/src/infinity_data/semantic/resolver/identity.py +0 -0
- {infinity_data-2.2.0 → infinity_data-2.2.1}/src/infinity_data/semantic/resolver/imports.py +0 -0
- {infinity_data-2.2.0 → infinity_data-2.2.1}/src/infinity_data/semantic/resolver/models.py +0 -0
- {infinity_data-2.2.0 → infinity_data-2.2.1}/src/infinity_data/semantic/resolver/resolver.py +0 -0
- {infinity_data-2.2.0 → infinity_data-2.2.1}/src/infinity_data/semantic/std.py +0 -0
- {infinity_data-2.2.0 → infinity_data-2.2.1}/src/infinity_data/tokenizer/__init__.py +0 -0
- {infinity_data-2.2.0 → infinity_data-2.2.1}/src/infinity_data/tokenizer/char_stream.py +0 -0
- {infinity_data-2.2.0 → infinity_data-2.2.1}/src/infinity_data/tokenizer/diagnostics.py +0 -0
- {infinity_data-2.2.0 → infinity_data-2.2.1}/src/infinity_data/tokenizer/finalizer.py +0 -0
- {infinity_data-2.2.0 → infinity_data-2.2.1}/src/infinity_data/tokenizer/models/__init__.py +0 -0
- {infinity_data-2.2.0 → infinity_data-2.2.1}/src/infinity_data/tokenizer/models/raw_tokens.py +0 -0
- {infinity_data-2.2.0 → infinity_data-2.2.1}/src/infinity_data/tokenizer/models/tokens.py +0 -0
- {infinity_data-2.2.0 → infinity_data-2.2.1}/src/infinity_data/tokenizer/tokenizer.py +0 -0
- {infinity_data-2.2.0 → infinity_data-2.2.1}/src/infinity_data.egg-info/SOURCES.txt +0 -0
- {infinity_data-2.2.0 → infinity_data-2.2.1}/src/infinity_data.egg-info/dependency_links.txt +0 -0
- {infinity_data-2.2.0 → infinity_data-2.2.1}/src/infinity_data.egg-info/top_level.txt +0 -0
|
@@ -34,6 +34,7 @@ from infinity_data.parser.models import (
|
|
|
34
34
|
JsonPathKey,
|
|
35
35
|
JsonPathSegment,
|
|
36
36
|
LiteralValue,
|
|
37
|
+
NamedArg,
|
|
37
38
|
Statement,
|
|
38
39
|
TemplateCallValue,
|
|
39
40
|
TemplateConfig,
|
|
@@ -72,6 +73,7 @@ __all__ = [
|
|
|
72
73
|
'JsonPathKey',
|
|
73
74
|
'JsonPathSegment',
|
|
74
75
|
'LiteralValue',
|
|
76
|
+
'NamedArg',
|
|
75
77
|
'Parser',
|
|
76
78
|
'Statement',
|
|
77
79
|
'TemplateCallValue',
|
|
@@ -37,6 +37,16 @@ def _canonical_constraint_list(constraints: Iterable['Constraint']) -> str:
|
|
|
37
37
|
return '<' + ', '.join(c.canonical() for c in items) + '>'
|
|
38
38
|
|
|
39
39
|
|
|
40
|
+
def _canonical_error(message: str) -> str:
|
|
41
|
+
"""错误节点容错序列化:``error("...")`` 占位符。
|
|
42
|
+
|
|
43
|
+
错误节点是**解析失败恢复**的产物,对应的源码本就无效,无法还原为等价 AST——
|
|
44
|
+
canonical 仅作**容错**(模板真名哈希 / 调试 / LSP 提示不因错误节点崩溃),
|
|
45
|
+
不承诺 round-trip(``error(...)`` 是显示占位符,不是可解析的模板调用语法)。
|
|
46
|
+
"""
|
|
47
|
+
return f'error({_canonical_str(message)})'
|
|
48
|
+
|
|
49
|
+
|
|
40
50
|
# ═══════════════════════════════════════════════════════════
|
|
41
51
|
# 节点
|
|
42
52
|
# ═══════════════════════════════════════════════════════════
|
|
@@ -457,24 +467,41 @@ class UnpackValue(AstNode):
|
|
|
457
467
|
return ('**' if self.double else '*') + self.value.canonical()
|
|
458
468
|
|
|
459
469
|
|
|
470
|
+
@dataclass
|
|
471
|
+
class NamedArg(AstNode):
|
|
472
|
+
"""模板调用命名参数槽: name = value。
|
|
473
|
+
|
|
474
|
+
与 ``**expr`` 解包项(:class:`UnpackValue`)在 :attr:`TemplateCallValue.named_args`
|
|
475
|
+
中按**源码顺序**混排——键冲突「保留先到者」(disjoint merge,§2.7)与书写顺序一致。
|
|
476
|
+
"""
|
|
477
|
+
|
|
478
|
+
name: str
|
|
479
|
+
value: Value
|
|
480
|
+
|
|
481
|
+
def children(self) -> Iterable[AstNode]:
|
|
482
|
+
return [self.value]
|
|
483
|
+
|
|
484
|
+
def canonical(self) -> str:
|
|
485
|
+
return f'{self.name} = {self.value.canonical()}'
|
|
486
|
+
|
|
487
|
+
|
|
460
488
|
@dataclass
|
|
461
489
|
class DictValue(AstNode):
|
|
462
490
|
"""对象值: { ... }
|
|
463
491
|
|
|
464
492
|
``: <constraint, ...>`` 结构级约束作用于该字面量 dict 的整体。
|
|
465
|
-
``
|
|
493
|
+
``items``:字段(:class:`Field`)与 ``**expr`` 解包项(:class:`UnpackValue`)按
|
|
494
|
+
**源码顺序**混排——键冲突「保留先到者」(disjoint merge,§2.7)与书写顺序一致。
|
|
466
495
|
"""
|
|
467
496
|
|
|
468
|
-
|
|
497
|
+
items: list[Field | UnpackValue]
|
|
469
498
|
constraints: list[Constraint] = field(default_factory=lambda: [])
|
|
470
|
-
unpacks: list[UnpackValue] = field(default_factory=lambda: [])
|
|
471
499
|
|
|
472
500
|
def children(self) -> Iterable[AstNode]:
|
|
473
|
-
return [*self.
|
|
501
|
+
return [*self.items, *self.constraints]
|
|
474
502
|
|
|
475
503
|
def canonical(self) -> str:
|
|
476
|
-
inner: list[str] = [
|
|
477
|
-
inner.extend(u.canonical() for u in self.unpacks)
|
|
504
|
+
inner: list[str] = [i.canonical() for i in self.items]
|
|
478
505
|
cs = _canonical_constraint_list(self.constraints)
|
|
479
506
|
if cs:
|
|
480
507
|
inner.append(': ' + cs)
|
|
@@ -501,18 +528,21 @@ class TemplateCallValue(AstNode):
|
|
|
501
528
|
展开(§2.8):参数值后缀 ``...`` = 展开轴;调用级(``)`` 后)``...`` = 展开传播
|
|
502
529
|
(本调用的展开结果作为包围模板调用的轴,整体模式逐元素重复)、``^`` = 笛卡尔积
|
|
503
530
|
(多轴全组合,首轴最慢变化;与 ``propagate`` 正交,``^...`` 可叠加)。
|
|
504
|
-
- ``
|
|
505
|
-
|
|
506
|
-
- ``
|
|
531
|
+
- ``positional_args``:位置参数槽,**保持源码顺序**;``*expr`` 解包项
|
|
532
|
+
(:class:`UnpackValue`,double=False)也在其中,实例化时展开为多个位置参数(§2.7)
|
|
533
|
+
- ``named_args``:命名参数槽,**保持源码顺序**;显式 ``name = value``(:class:`NamedArg`)
|
|
534
|
+
与 ``**expr`` 解包项(:class:`UnpackValue`,double=True)混排——键冲突「保留先到者」
|
|
535
|
+
与书写顺序一致(§2.7 disjoint merge)
|
|
536
|
+
- ``axis_positional``:位置参数槽中带 ``...`` 的索引(``*expr`` 解包项不是轴)
|
|
537
|
+
- ``axis_named``:命名参数(:class:`NamedArg`)中带 ``...`` 的键
|
|
538
|
+
- ``axis_unpack_kwargs``:``**expr`` 解包项(named_args 槽索引)中带 ``...`` 的索引
|
|
507
539
|
- ``propagate``:调用级 ``...``(展开传播;无 = 展开止于本调用)
|
|
508
540
|
- ``cartesian``:调用级 ``^``(笛卡尔积展开;无 = zip 配对)
|
|
509
541
|
"""
|
|
510
542
|
|
|
511
543
|
template_name: str
|
|
512
|
-
positional_args: list[Value]
|
|
513
|
-
named_args:
|
|
514
|
-
unpack_args: list[UnpackValue] = field(default_factory=lambda: []) # *expr(list → 位置参数)
|
|
515
|
-
unpack_kwargs: list[UnpackValue] = field(default_factory=lambda: []) # **expr(dict → 命名参数)
|
|
544
|
+
positional_args: list[Value | UnpackValue]
|
|
545
|
+
named_args: list[NamedArg | UnpackValue]
|
|
516
546
|
axis_positional: frozenset[int] = field(default_factory=frozenset[int])
|
|
517
547
|
axis_named: frozenset[str] = field(default_factory=frozenset[str])
|
|
518
548
|
axis_unpack_kwargs: frozenset[int] = field(default_factory=frozenset[int])
|
|
@@ -522,27 +552,22 @@ class TemplateCallValue(AstNode):
|
|
|
522
552
|
def children(self) -> Iterable[AstNode]:
|
|
523
553
|
return [
|
|
524
554
|
*self.positional_args,
|
|
525
|
-
*self.named_args
|
|
526
|
-
*self.unpack_args,
|
|
527
|
-
*self.unpack_kwargs,
|
|
555
|
+
*self.named_args,
|
|
528
556
|
]
|
|
529
557
|
|
|
530
558
|
def canonical(self) -> str:
|
|
531
559
|
args: list[str] = []
|
|
532
560
|
for i, a in enumerate(self.positional_args):
|
|
533
561
|
s = a.canonical()
|
|
534
|
-
if i in self.axis_positional:
|
|
535
|
-
s += '...'
|
|
536
|
-
args.append(s)
|
|
537
|
-
args.extend(u.canonical() for u in self.unpack_args)
|
|
538
|
-
for k, v in sorted(self.named_args.items()):
|
|
539
|
-
s = f'{k} = {v.canonical()}'
|
|
540
|
-
if k in self.axis_named:
|
|
562
|
+
if not isinstance(a, UnpackValue) and i in self.axis_positional:
|
|
541
563
|
s += '...'
|
|
542
564
|
args.append(s)
|
|
543
|
-
for
|
|
544
|
-
s =
|
|
545
|
-
if
|
|
565
|
+
for i, n in enumerate(self.named_args):
|
|
566
|
+
s = n.canonical()
|
|
567
|
+
if isinstance(n, UnpackValue):
|
|
568
|
+
if i in self.axis_unpack_kwargs:
|
|
569
|
+
s += '...'
|
|
570
|
+
elif n.name in self.axis_named:
|
|
546
571
|
s += '...'
|
|
547
572
|
args.append(s)
|
|
548
573
|
out = f'{self.template_name}({", ".join(args)})'
|
|
@@ -560,32 +585,41 @@ class TemplateCallValue(AstNode):
|
|
|
560
585
|
|
|
561
586
|
@dataclass
|
|
562
587
|
class ErrorStatement(AstNode):
|
|
563
|
-
"""解析失败的语句。用于错误恢复。
|
|
588
|
+
"""解析失败的语句。用于错误恢复。
|
|
589
|
+
|
|
590
|
+
canonical 容错输出 ``error("message")``(不抛异常),供哈希 / LSP 提示。
|
|
591
|
+
"""
|
|
564
592
|
|
|
565
593
|
message: str
|
|
566
594
|
|
|
567
595
|
def canonical(self) -> str:
|
|
568
|
-
|
|
596
|
+
return _canonical_error(self.message)
|
|
569
597
|
|
|
570
598
|
|
|
571
599
|
@dataclass
|
|
572
600
|
class ErrorValue(AstNode):
|
|
573
|
-
"""解析失败的值。用于错误恢复。
|
|
601
|
+
"""解析失败的值。用于错误恢复。
|
|
602
|
+
|
|
603
|
+
canonical 容错输出 ``error("message")``(不抛异常),供哈希 / LSP 提示。
|
|
604
|
+
"""
|
|
574
605
|
|
|
575
606
|
message: str
|
|
576
607
|
|
|
577
608
|
def canonical(self) -> str:
|
|
578
|
-
|
|
609
|
+
return _canonical_error(self.message)
|
|
579
610
|
|
|
580
611
|
|
|
581
612
|
@dataclass
|
|
582
613
|
class ErrorConstraint(AstNode):
|
|
583
|
-
"""解析失败的约束。用于错误恢复。
|
|
614
|
+
"""解析失败的约束。用于错误恢复。
|
|
615
|
+
|
|
616
|
+
canonical 容错输出 ``error("message")``(不抛异常),供哈希 / LSP 提示。
|
|
617
|
+
"""
|
|
584
618
|
|
|
585
619
|
message: str
|
|
586
620
|
|
|
587
621
|
def canonical(self) -> str:
|
|
588
|
-
|
|
622
|
+
return _canonical_error(self.message)
|
|
589
623
|
|
|
590
624
|
|
|
591
625
|
# ═══════════════════════════════════════════════════════════
|
|
@@ -29,6 +29,7 @@ from infinity_data.parser.models import (
|
|
|
29
29
|
JsonPathKey,
|
|
30
30
|
JsonPathSegment,
|
|
31
31
|
LiteralValue,
|
|
32
|
+
NamedArg,
|
|
32
33
|
Statement,
|
|
33
34
|
TemplateCallValue,
|
|
34
35
|
TemplateConfig,
|
|
@@ -1196,8 +1197,7 @@ class Parser:
|
|
|
1196
1197
|
lbrace_tok = stream.expect(LbraceToken)
|
|
1197
1198
|
stream.skip_separators()
|
|
1198
1199
|
|
|
1199
|
-
|
|
1200
|
-
unpacks: list[UnpackValue] = []
|
|
1200
|
+
items: list[Field | UnpackValue] = []
|
|
1201
1201
|
constraints: list[Constraint] = []
|
|
1202
1202
|
missing_sep_reported = [False]
|
|
1203
1203
|
while not stream.check(RawTokenType.RBRACE) and not stream.eof():
|
|
@@ -1207,12 +1207,13 @@ class Parser:
|
|
|
1207
1207
|
parsed = Parser._parse_constraints(stream, collector)
|
|
1208
1208
|
constraints.extend(parsed.constraints)
|
|
1209
1209
|
elif isinstance(stream.peek(), DoubleStarToken):
|
|
1210
|
-
# **expr:dict
|
|
1210
|
+
# **expr:dict 解包(展开为键值对并入字段集);
|
|
1211
|
+
# 与显式字段同槽存放,保持源码顺序(先到者胜出,§2.7)
|
|
1211
1212
|
tok = stream.expect(DoubleStarToken)
|
|
1212
1213
|
val = Parser._parse_value(stream, collector)
|
|
1213
|
-
|
|
1214
|
+
items.append(UnpackValue(source=stream.span_from(tok), value=val, double=True))
|
|
1214
1215
|
else:
|
|
1215
|
-
|
|
1216
|
+
items.append(Parser._parse_field(stream, collector))
|
|
1216
1217
|
had_sep = stream.skip_separators()
|
|
1217
1218
|
Parser._missing_separator(
|
|
1218
1219
|
stream,
|
|
@@ -1224,7 +1225,7 @@ class Parser:
|
|
|
1224
1225
|
)
|
|
1225
1226
|
|
|
1226
1227
|
stream.expect(RbraceToken)
|
|
1227
|
-
return DictValue(source=stream.span_from(lbrace_tok),
|
|
1228
|
+
return DictValue(source=stream.span_from(lbrace_tok), items=items, constraints=constraints)
|
|
1228
1229
|
|
|
1229
1230
|
@staticmethod
|
|
1230
1231
|
def _parse_array(stream: TokenStream, collector: DiagnosticCollector) -> ArrayValue:
|
|
@@ -1263,13 +1264,11 @@ class Parser:
|
|
|
1263
1264
|
stream.expect(LparenToken)
|
|
1264
1265
|
stream.skip_newlines()
|
|
1265
1266
|
|
|
1266
|
-
positional: list[Value] = []
|
|
1267
|
-
named:
|
|
1268
|
-
|
|
1269
|
-
unpack_kwargs: list[UnpackValue] = [] # **expr(dict → 命名参数)
|
|
1270
|
-
axis_positional: set[int] = set() # 位置参数中带 ... 的索引(展开轴,§2.8)
|
|
1267
|
+
positional: list[Value | UnpackValue] = [] # 位置参数 + *expr 解包项(保持源码顺序,§2.7)
|
|
1268
|
+
named: list[NamedArg | UnpackValue] = [] # 命名参数 + **expr 解包项(保持源码顺序,§2.7)
|
|
1269
|
+
axis_positional: set[int] = set() # 位置参数槽中带 ... 的索引(展开轴,§2.8)
|
|
1271
1270
|
axis_named: set[str] = set() # 命名参数中带 ... 的键
|
|
1272
|
-
axis_unpack_kwargs: set[int] = set() # **expr
|
|
1271
|
+
axis_unpack_kwargs: set[int] = set() # named 槽中 **expr 解包项的索引
|
|
1273
1272
|
saw_named = False
|
|
1274
1273
|
missing_sep_reported = [False]
|
|
1275
1274
|
|
|
@@ -1284,15 +1283,16 @@ class Parser:
|
|
|
1284
1283
|
if isinstance(tok, DoubleStarToken):
|
|
1285
1284
|
stream.advance()
|
|
1286
1285
|
val = Parser._parse_value(stream, collector)
|
|
1287
|
-
idx = len(
|
|
1288
|
-
|
|
1286
|
+
idx = len(named)
|
|
1287
|
+
named.append(UnpackValue(source=stream.single_span(tok), value=val, double=True))
|
|
1289
1288
|
if Parser._consume_ellipsis(stream):
|
|
1290
1289
|
axis_unpack_kwargs.add(idx) # **expr...:list[dict] 逐元素解包(§2.8)
|
|
1291
1290
|
saw_named = True
|
|
1292
1291
|
elif isinstance(tok, StarToken):
|
|
1293
1292
|
stream.advance()
|
|
1294
1293
|
val = Parser._parse_value(stream, collector)
|
|
1295
|
-
|
|
1294
|
+
# *expr 解包项与显式位置参数同槽存放,保持源码顺序(§2.7)
|
|
1295
|
+
positional.append(UnpackValue(source=stream.single_span(tok), value=val, double=False))
|
|
1296
1296
|
# 分支 1:标识符 → 可能是命名参数或模板调用(位置参数)
|
|
1297
1297
|
elif isinstance(tok, IdentifierToken):
|
|
1298
1298
|
ident: IdentifierToken = stream.expect(IdentifierToken) # 消费到缓冲区
|
|
@@ -1300,7 +1300,7 @@ class Parser:
|
|
|
1300
1300
|
|
|
1301
1301
|
if isinstance(nxt, EqualsToken):
|
|
1302
1302
|
stream.advance() # 消费 =
|
|
1303
|
-
if ident.name in named:
|
|
1303
|
+
if ident.name in {s.name for s in named if isinstance(s, NamedArg)}:
|
|
1304
1304
|
collector.add(
|
|
1305
1305
|
diag(
|
|
1306
1306
|
'template.dup_argument',
|
|
@@ -1308,7 +1308,13 @@ class Parser:
|
|
|
1308
1308
|
stream.single_span(ident),
|
|
1309
1309
|
)
|
|
1310
1310
|
)
|
|
1311
|
-
named
|
|
1311
|
+
named.append(
|
|
1312
|
+
NamedArg(
|
|
1313
|
+
source=stream.single_span(ident),
|
|
1314
|
+
name=ident.name,
|
|
1315
|
+
value=Parser._parse_value(stream, collector),
|
|
1316
|
+
)
|
|
1317
|
+
)
|
|
1312
1318
|
if Parser._consume_ellipsis(stream):
|
|
1313
1319
|
axis_named.add(ident.name) # 命名参数轴
|
|
1314
1320
|
saw_named = True
|
|
@@ -1358,8 +1364,6 @@ class Parser:
|
|
|
1358
1364
|
template_name=name_tok.name,
|
|
1359
1365
|
positional_args=positional,
|
|
1360
1366
|
named_args=named,
|
|
1361
|
-
unpack_args=unpack_args,
|
|
1362
|
-
unpack_kwargs=unpack_kwargs,
|
|
1363
1367
|
axis_positional=frozenset(axis_positional),
|
|
1364
1368
|
axis_named=frozenset(axis_named),
|
|
1365
1369
|
axis_unpack_kwargs=frozenset(axis_unpack_kwargs),
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
from __future__ import annotations
|
|
17
17
|
|
|
18
18
|
import decimal
|
|
19
|
-
from collections.abc import Iterator,
|
|
19
|
+
from collections.abc import Iterator, Sequence
|
|
20
20
|
from itertools import product
|
|
21
21
|
from typing import Any, cast
|
|
22
22
|
|
|
@@ -32,6 +32,7 @@ from infinity_data.parser import (
|
|
|
32
32
|
Field,
|
|
33
33
|
FileImportStmt,
|
|
34
34
|
LiteralValue,
|
|
35
|
+
NamedArg,
|
|
35
36
|
TemplateCallValue,
|
|
36
37
|
TemplateDef,
|
|
37
38
|
TemplateImportStmt,
|
|
@@ -448,19 +449,21 @@ class AstBuilder:
|
|
|
448
449
|
return self._convert_literal(tok)
|
|
449
450
|
case DollarValue(name=n, type_cast=tc, source=src):
|
|
450
451
|
return self._resolve_dollar(n, tc, path, src, scope)
|
|
451
|
-
case DictValue(
|
|
452
|
+
case DictValue(items=its, constraints=cs):
|
|
452
453
|
std_fields: list[StdField] = []
|
|
453
|
-
# **expr
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
454
|
+
# **expr 解包与显式字段按源码顺序处理:解包展开为键值对,
|
|
455
|
+
# disjoint merge「保留先到者」查重在 _finalize_object(§2.7)
|
|
456
|
+
for it in its:
|
|
457
|
+
if isinstance(it, UnpackValue):
|
|
458
|
+
rv = self._resolve_value(it.value, path, scope)
|
|
459
|
+
fields = self._unpack_dict(rv, it.source, path)
|
|
460
|
+
if fields is not None:
|
|
461
|
+
std_fields.extend(fields)
|
|
462
|
+
else:
|
|
463
|
+
child = f'{path}.{it.name}' if path else it.name
|
|
464
|
+
sf = self._build_field(it, path=child, scope=scope)
|
|
465
|
+
if sf is not None:
|
|
466
|
+
std_fields.append(sf)
|
|
464
467
|
# dict 结构级约束(作用于该字面量整体):解析后挂节点,不执行
|
|
465
468
|
specs, diags = resolve_constraint_list(cs, scope)
|
|
466
469
|
self._collector.extend(diags)
|
|
@@ -488,17 +491,13 @@ class AstBuilder:
|
|
|
488
491
|
template_name=tn,
|
|
489
492
|
positional_args=pa,
|
|
490
493
|
named_args=na,
|
|
491
|
-
unpack_args=upa,
|
|
492
|
-
unpack_kwargs=upk,
|
|
493
494
|
axis_positional=axp,
|
|
494
495
|
axis_named=axn,
|
|
495
496
|
axis_unpack_kwargs=axu,
|
|
496
497
|
propagate=prop,
|
|
497
498
|
cartesian=cart,
|
|
498
499
|
):
|
|
499
|
-
return self._expand_template_call(
|
|
500
|
-
tn, pa, na, upa, upk, axp, axn, axu, prop, cart, path, raw.source, scope
|
|
501
|
-
)
|
|
500
|
+
return self._expand_template_call(tn, pa, na, axp, axn, axu, prop, cart, path, raw.source, scope)
|
|
502
501
|
case ErrorValue():
|
|
503
502
|
# 值解析失败已在语法层报告(parse.value_field / parse.unrecognized_value),不重复
|
|
504
503
|
return None
|
|
@@ -645,10 +644,8 @@ class AstBuilder:
|
|
|
645
644
|
def _expand_template_call(
|
|
646
645
|
self,
|
|
647
646
|
template_name: str,
|
|
648
|
-
positional_args: list[Value],
|
|
649
|
-
named_args:
|
|
650
|
-
unpack_args: list[UnpackValue],
|
|
651
|
-
unpack_kwargs: list[UnpackValue],
|
|
647
|
+
positional_args: list[Value | UnpackValue],
|
|
648
|
+
named_args: list[NamedArg | UnpackValue],
|
|
652
649
|
axis_positional: frozenset[int],
|
|
653
650
|
axis_named: frozenset[str],
|
|
654
651
|
axis_unpack_kwargs: frozenset[int],
|
|
@@ -667,26 +664,36 @@ class AstBuilder:
|
|
|
667
664
|
|
|
668
665
|
每个展开实例是完整模板实例(默认值注入 + 约束照常)。
|
|
669
666
|
"""
|
|
670
|
-
# 收集本调用自身的轴值(参数级 ...,每个轴解析为 list
|
|
667
|
+
# 收集本调用自身的轴值(参数级 ...,每个轴解析为 list)。
|
|
668
|
+
# 轴只可能标在显式位置/命名参数上(*expr / **expr 解包项不是轴,解析器保证)
|
|
671
669
|
axes: list[tuple[tuple[str, str | int], StdArray]] = []
|
|
672
670
|
for i in sorted(axis_positional):
|
|
673
|
-
|
|
671
|
+
arg = positional_args[i]
|
|
672
|
+
assert not isinstance(arg, UnpackValue), '展开轴不能是 *expr 解包项'
|
|
673
|
+
arr = self._resolve_axis(arg, scope, source, path, template_name)
|
|
674
674
|
if arr is not None:
|
|
675
675
|
axes.append((('pos', i), arr))
|
|
676
|
+
# 显式命名参数轴(name = value...,按名排序保持确定性)
|
|
676
677
|
for k in sorted(axis_named):
|
|
677
|
-
|
|
678
|
+
slot = next((s for s in named_args if isinstance(s, NamedArg) and s.name == k), None)
|
|
679
|
+
if slot is None:
|
|
680
|
+
continue
|
|
681
|
+
arr = self._resolve_axis(slot.value, scope, source, path, template_name)
|
|
678
682
|
if arr is not None:
|
|
679
683
|
axes.append((('named', k), arr))
|
|
684
|
+
# **expr 解包轴(**expr...:list[dict] 逐元素解包)
|
|
680
685
|
for j in sorted(axis_unpack_kwargs):
|
|
681
|
-
|
|
686
|
+
slot = named_args[j]
|
|
687
|
+
assert isinstance(slot, UnpackValue), '** 解包轴槽必须是 UnpackValue'
|
|
688
|
+
arr = self._resolve_axis(slot.value, scope, source, path, template_name)
|
|
682
689
|
if arr is not None:
|
|
683
690
|
axes.append((('unpack', j), arr))
|
|
684
691
|
|
|
685
692
|
# 传播轴:参数值是带调用级 ... 的模板调用 → 其展开结果(list)作为本调用的轴。
|
|
686
693
|
# 内层无展开源(结果非 list,expand_no_source 已在内层报告)→ 用解析值替换参数,
|
|
687
694
|
# 避免外层单次实例化时二次解析、重复报错。
|
|
688
|
-
pos_args: list[Value | StdValue] = list(positional_args)
|
|
689
|
-
named:
|
|
695
|
+
pos_args: list[Value | UnpackValue | StdValue] = list(positional_args)
|
|
696
|
+
named: list[NamedArg | UnpackValue] = list(named_args)
|
|
690
697
|
for i, arg in enumerate(pos_args):
|
|
691
698
|
if i in axis_positional:
|
|
692
699
|
continue
|
|
@@ -696,23 +703,24 @@ class AstBuilder:
|
|
|
696
703
|
axes.append((('pos', i), rv))
|
|
697
704
|
elif rv is not None:
|
|
698
705
|
pos_args[i] = rv
|
|
699
|
-
for
|
|
700
|
-
if
|
|
706
|
+
for i, slot in enumerate(named):
|
|
707
|
+
if isinstance(slot, UnpackValue):
|
|
708
|
+
continue # ** 解包项不参与传播
|
|
709
|
+
if slot.name in axis_named:
|
|
701
710
|
continue
|
|
702
|
-
if isinstance(
|
|
703
|
-
rv = self._resolve_value(
|
|
711
|
+
if isinstance(slot.value, TemplateCallValue) and slot.value.propagate:
|
|
712
|
+
rv = self._resolve_value(slot.value, path, scope)
|
|
704
713
|
if isinstance(rv, StdArray):
|
|
705
|
-
axes.append((('named',
|
|
714
|
+
axes.append((('named', slot.name), rv))
|
|
706
715
|
elif rv is not None:
|
|
707
|
-
|
|
716
|
+
# 用解析值替换该槽(保持槽位顺序)
|
|
717
|
+
named[i] = NamedArg(name=slot.name, value=cast(Value, rv), source=slot.source)
|
|
708
718
|
|
|
709
719
|
if not axes:
|
|
710
720
|
# 调用级 ^ / ... 但无任何展开源(自身无轴、也无内层传播而来)→ 报错
|
|
711
721
|
if propagate or cartesian:
|
|
712
722
|
self._err('template.expand_no_source', {'template': template_name}, source, path)
|
|
713
|
-
return self._instantiate_once(
|
|
714
|
-
template_name, pos_args, named, unpack_args, unpack_kwargs, path, source, scope
|
|
715
|
-
)
|
|
723
|
+
return self._instantiate_once(template_name, pos_args, named, path, source, scope)
|
|
716
724
|
|
|
717
725
|
# 组合:zip(等长配对)或笛卡尔积(全组合,首轴最慢变化)
|
|
718
726
|
if cartesian:
|
|
@@ -751,8 +759,8 @@ class AstBuilder:
|
|
|
751
759
|
|
|
752
760
|
results: list[StdValue] = []
|
|
753
761
|
for combo in combos:
|
|
754
|
-
pa, na
|
|
755
|
-
results.append(self._instantiate_once(template_name, pa, na,
|
|
762
|
+
pa, na = self._build_expand_args(pos_args, named, axes, combo)
|
|
763
|
+
results.append(self._instantiate_once(template_name, pa, na, path, source, scope))
|
|
756
764
|
return StdArray(elements=results, source=source)
|
|
757
765
|
|
|
758
766
|
def _resolve_axis(
|
|
@@ -774,22 +782,20 @@ class AstBuilder:
|
|
|
774
782
|
|
|
775
783
|
@staticmethod
|
|
776
784
|
def _build_expand_args(
|
|
777
|
-
positional_args: list[Value | StdValue],
|
|
778
|
-
named_args:
|
|
779
|
-
unpack_args: list[UnpackValue],
|
|
780
|
-
unpack_kwargs: list[UnpackValue],
|
|
785
|
+
positional_args: list[Value | UnpackValue | StdValue],
|
|
786
|
+
named_args: list[NamedArg | UnpackValue],
|
|
781
787
|
axes: list[tuple[tuple[str, str | int], StdArray]],
|
|
782
788
|
combo: tuple[StdValue, ...],
|
|
783
|
-
) -> tuple[list[Value |
|
|
789
|
+
) -> tuple[list[Value | UnpackValue | StdValue], list[NamedArg | UnpackValue]]:
|
|
784
790
|
"""把组合元素替换进轴参数位,构造单次调用的参数视图。
|
|
785
791
|
|
|
786
|
-
``**`` 解包轴:元素(dict
|
|
792
|
+
``**`` 解包轴:元素(dict)字段并入命名参数,移除该项;
|
|
793
|
+
并入字段在**原槽位**展开(保持源码顺序,§2.8 轴与解包叠加)。
|
|
787
794
|
"""
|
|
788
|
-
pa: list[Value | StdValue] = list(positional_args)
|
|
789
|
-
na:
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
unpack_removed: set[int] = set()
|
|
795
|
+
pa: list[Value | UnpackValue | StdValue] = list(positional_args)
|
|
796
|
+
na: list[NamedArg | UnpackValue] = list(named_args)
|
|
797
|
+
taken = {s.name for s in na if isinstance(s, NamedArg)}
|
|
798
|
+
slot_merges: dict[int, list[NamedArg]] = {}
|
|
793
799
|
for (axis_spec, _arr), elem in zip(axes, combo):
|
|
794
800
|
kind, idx = axis_spec
|
|
795
801
|
if kind == 'pos':
|
|
@@ -797,25 +803,39 @@ class AstBuilder:
|
|
|
797
803
|
pa[idx] = elem
|
|
798
804
|
elif kind == 'named':
|
|
799
805
|
assert isinstance(idx, str)
|
|
800
|
-
|
|
806
|
+
# 按名替换命名参数槽的值(保持槽位顺序)
|
|
807
|
+
for i, slot in enumerate(na):
|
|
808
|
+
if isinstance(slot, NamedArg) and slot.name == idx:
|
|
809
|
+
na[i] = NamedArg(name=slot.name, value=cast(Value, elem), source=slot.source)
|
|
810
|
+
break
|
|
801
811
|
else: # unpack 轴:元素(dict)字段并入命名参数
|
|
802
812
|
assert isinstance(idx, int)
|
|
813
|
+
slot = na[idx]
|
|
814
|
+
assert isinstance(slot, UnpackValue)
|
|
815
|
+
merged: list[NamedArg] = []
|
|
803
816
|
if isinstance(elem, StdObject):
|
|
804
817
|
for f in elem.fields:
|
|
805
|
-
if f.value is not None and f.name not in
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
818
|
+
if f.value is not None and f.name not in taken:
|
|
819
|
+
taken.add(f.name)
|
|
820
|
+
# 并入字段的 source 指向 **expr... 槽位(诊断定位)
|
|
821
|
+
merged.append(NamedArg(name=f.name, value=cast(Value, f.value), source=slot.source))
|
|
822
|
+
slot_merges[idx] = merged
|
|
823
|
+
# 移除 ** 解包轴槽,在其原位置展开合并字段(保持源码顺序)
|
|
824
|
+
if slot_merges:
|
|
825
|
+
out: list[NamedArg | UnpackValue] = []
|
|
826
|
+
for i, s in enumerate(na):
|
|
827
|
+
if i in slot_merges:
|
|
828
|
+
out.extend(slot_merges[i])
|
|
829
|
+
else:
|
|
830
|
+
out.append(s)
|
|
831
|
+
na = out
|
|
832
|
+
return pa, na
|
|
811
833
|
|
|
812
834
|
def _instantiate_once(
|
|
813
835
|
self,
|
|
814
836
|
template_name: str,
|
|
815
|
-
positional_args: Sequence[Value | StdValue],
|
|
816
|
-
named_args:
|
|
817
|
-
unpack_args: list[UnpackValue],
|
|
818
|
-
unpack_kwargs: list[UnpackValue],
|
|
837
|
+
positional_args: Sequence[Value | UnpackValue | StdValue],
|
|
838
|
+
named_args: Sequence[NamedArg | UnpackValue],
|
|
819
839
|
path: str,
|
|
820
840
|
source: SourceRange | None,
|
|
821
841
|
scope: Scope,
|
|
@@ -840,13 +860,19 @@ class AstBuilder:
|
|
|
840
860
|
|
|
841
861
|
required = [tf for tf in template.fields if tf.default_value is None]
|
|
842
862
|
|
|
843
|
-
# ── 位置参数:显式 + *expr 解包(list →
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
863
|
+
# ── 位置参数:显式 + *expr 解包(list → 逐个位置参数),保持源码顺序(§2.7)──
|
|
864
|
+
# 解析器把 *expr 解包项与显式位置参数按源码顺序合并存进 positional_args:
|
|
865
|
+
# 解包项(UnpackValue)在此展开,显式值直接进入 → 顺序与书写一致
|
|
866
|
+
# (如 Test(*[1,2,3], 4, 5, 6) ⟹ [1,2,3,4,5,6],而非 [4,5,6,1,2,3])
|
|
867
|
+
expanded_positional: list[Value | StdValue] = []
|
|
868
|
+
for pa in positional_args:
|
|
869
|
+
if isinstance(pa, UnpackValue):
|
|
870
|
+
rv = self._resolve_value(pa.value, path, scope)
|
|
871
|
+
items = self._unpack_list(rv, pa.source, path)
|
|
872
|
+
if items is not None:
|
|
873
|
+
expanded_positional.extend(items)
|
|
874
|
+
else:
|
|
875
|
+
expanded_positional.append(pa)
|
|
850
876
|
|
|
851
877
|
# 模板配置 positional=false:位置参数违规报错,但值仍绑定必填字段
|
|
852
878
|
# (放宽必填绑定:只报 positional_disabled 一条,避免 missing_required 级联)
|
|
@@ -861,16 +887,25 @@ class AstBuilder:
|
|
|
861
887
|
)
|
|
862
888
|
)
|
|
863
889
|
|
|
864
|
-
# ── 命名参数:显式 + **expr 解包(dict →
|
|
890
|
+
# ── 命名参数:显式 + **expr 解包(dict → 键值对),保持源码顺序(§2.7)──
|
|
891
|
+
# 解析器把 **expr 解包项与显式命名参数按源码顺序合并存进 named_args:
|
|
892
|
+
# 解包项(UnpackValue)在此展开,显式命名参数(NamedArg)直接进入 →
|
|
893
|
+
# 键冲突「保留先到者」(disjoint merge)与书写顺序一致
|
|
865
894
|
# 未知命名参数:extra_named_vars 收集(与 allow_extra 互斥);否则 allow_extra/报错
|
|
866
895
|
declared = {tf.name for tf in template.fields}
|
|
867
896
|
named_vars = template.config.extra_named_vars
|
|
868
897
|
if named_vars is not None and named_vars not in declared:
|
|
869
898
|
named_vars = None # 定义时已报 variadic_target_missing(_check_variadic_config)
|
|
870
|
-
param_values: dict[str, Value | StdValue] =
|
|
899
|
+
param_values: dict[str, Value | StdValue] = {}
|
|
871
900
|
extra_args: dict[str, tuple[Value | StdValue, SourceRange | None]] = {}
|
|
872
901
|
extra_named: dict[str, Value | StdValue] = {}
|
|
873
|
-
|
|
902
|
+
|
|
903
|
+
def _bind_named(name: str, arg_val: Value | StdValue, arg_source: SourceRange | None) -> None:
|
|
904
|
+
"""按名绑定命名参数:已绑定 / 已收集 → 重复键(先到者保留);否则走声明/收集/放行。"""
|
|
905
|
+
child = f'{path}.{name}' if path else name
|
|
906
|
+
if name in param_values or name in extra_args or name in extra_named:
|
|
907
|
+
self._err('dict.duplicate_key', {'name': name}, arg_source, child)
|
|
908
|
+
return
|
|
874
909
|
if name not in declared:
|
|
875
910
|
if named_vars is not None:
|
|
876
911
|
extra_named[name] = arg_val
|
|
@@ -881,36 +916,24 @@ class AstBuilder:
|
|
|
881
916
|
self._err(
|
|
882
917
|
'template.unknown_argument',
|
|
883
918
|
{'template': template_name, 'arg': name},
|
|
884
|
-
|
|
885
|
-
|
|
919
|
+
arg_source,
|
|
920
|
+
child,
|
|
886
921
|
)
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
child = f'{path}.{f.name}' if path else f.name
|
|
896
|
-
if f.name in param_values or f.name in extra_args:
|
|
897
|
-
# 解包键与已有参数冲突 → 重复键(disjoint merge)
|
|
898
|
-
self._err('dict.duplicate_key', {'name': f.name}, up.source, child)
|
|
922
|
+
else:
|
|
923
|
+
param_values[name] = arg_val
|
|
924
|
+
|
|
925
|
+
for slot in named_args:
|
|
926
|
+
if isinstance(slot, UnpackValue):
|
|
927
|
+
rv = self._resolve_value(slot.value, path, scope)
|
|
928
|
+
fields = self._unpack_dict(rv, slot.source, path)
|
|
929
|
+
if fields is None:
|
|
899
930
|
continue
|
|
900
|
-
|
|
901
|
-
if
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
self._err(
|
|
907
|
-
'template.unknown_argument',
|
|
908
|
-
{'template': template_name, 'arg': f.name},
|
|
909
|
-
up.source,
|
|
910
|
-
child,
|
|
911
|
-
)
|
|
912
|
-
else:
|
|
913
|
-
param_values[f.name] = f.value
|
|
931
|
+
for f in fields:
|
|
932
|
+
if f.value is None:
|
|
933
|
+
continue # 防御:解包字段值缺失(理论不可达)
|
|
934
|
+
_bind_named(f.name, f.value, slot.source)
|
|
935
|
+
else: # 显式命名参数(值可能是展开轴替换来的 StdValue)
|
|
936
|
+
_bind_named(slot.name, slot.value, slot.source)
|
|
914
937
|
if named_vars is not None and extra_named:
|
|
915
938
|
# 收集未声明命名参数为 dict 字段(约束由字段声明承担,§2.9)
|
|
916
939
|
param_values[named_vars] = StdObject(
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{infinity_data-2.2.0 → infinity_data-2.2.1}/src/infinity_data/semantic/registry/dict_constraints.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{infinity_data-2.2.0 → infinity_data-2.2.1}/src/infinity_data/tokenizer/models/raw_tokens.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|