infinity-data 2.1.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.1.0/src/infinity_data.egg-info → infinity_data-2.2.1}/PKG-INFO +1 -1
- {infinity_data-2.1.0 → infinity_data-2.2.1}/pyproject.toml +3 -2
- {infinity_data-2.1.0 → infinity_data-2.2.1}/src/infinity_data/parser/__init__.py +2 -0
- {infinity_data-2.1.0 → infinity_data-2.2.1}/src/infinity_data/parser/models.py +66 -32
- {infinity_data-2.1.0 → infinity_data-2.2.1}/src/infinity_data/parser/parser.py +23 -19
- {infinity_data-2.1.0 → infinity_data-2.2.1}/src/infinity_data/pipeline.py +1 -1
- {infinity_data-2.1.0 → infinity_data-2.2.1}/src/infinity_data/semantic/builder/__init__.py +4 -0
- {infinity_data-2.1.0 → infinity_data-2.2.1}/src/infinity_data/semantic/builder/builder.py +211 -150
- {infinity_data-2.1.0 → infinity_data-2.2.1}/src/infinity_data/semantic/builder/models.py +10 -4
- {infinity_data-2.1.0 → infinity_data-2.2.1}/src/infinity_data/semantic/constraints.py +1 -1
- {infinity_data-2.1.0 → infinity_data-2.2.1}/src/infinity_data/semantic/executor/executor.py +15 -6
- {infinity_data-2.1.0 → infinity_data-2.2.1}/src/infinity_data/semantic/registry/dict_constraints.py +3 -1
- {infinity_data-2.1.0 → infinity_data-2.2.1}/src/infinity_data/semantic/registry/general.py +8 -3
- {infinity_data-2.1.0 → infinity_data-2.2.1}/src/infinity_data/semantic/resolver/identity.py +29 -7
- {infinity_data-2.1.0 → infinity_data-2.2.1}/src/infinity_data/semantic/resolver/imports.py +55 -5
- {infinity_data-2.1.0 → infinity_data-2.2.1}/src/infinity_data/semantic/resolver/models.py +28 -13
- {infinity_data-2.1.0 → infinity_data-2.2.1}/src/infinity_data/semantic/resolver/resolver.py +53 -16
- {infinity_data-2.1.0 → infinity_data-2.2.1}/src/infinity_data/semantic/std.py +40 -10
- {infinity_data-2.1.0 → infinity_data-2.2.1/src/infinity_data.egg-info}/PKG-INFO +1 -1
- {infinity_data-2.1.0 → infinity_data-2.2.1}/LICENSE +0 -0
- {infinity_data-2.1.0 → infinity_data-2.2.1}/README.md +0 -0
- {infinity_data-2.1.0 → infinity_data-2.2.1}/setup.cfg +0 -0
- {infinity_data-2.1.0 → infinity_data-2.2.1}/src/infinity_data/__init__.py +0 -0
- {infinity_data-2.1.0 → infinity_data-2.2.1}/src/infinity_data/emit/__init__.py +0 -0
- {infinity_data-2.1.0 → infinity_data-2.2.1}/src/infinity_data/emit/converter.py +0 -0
- {infinity_data-2.1.0 → infinity_data-2.2.1}/src/infinity_data/frontend.py +0 -0
- {infinity_data-2.1.0 → infinity_data-2.2.1}/src/infinity_data/infra/__init__.py +0 -0
- {infinity_data-2.1.0 → infinity_data-2.2.1}/src/infinity_data/infra/diagnostics.py +0 -0
- {infinity_data-2.1.0 → infinity_data-2.2.1}/src/infinity_data/infra/file.py +0 -0
- {infinity_data-2.1.0 → infinity_data-2.2.1}/src/infinity_data/infra/ll1_stream.py +0 -0
- {infinity_data-2.1.0 → infinity_data-2.2.1}/src/infinity_data/infra/location.py +0 -0
- {infinity_data-2.1.0 → infinity_data-2.2.1}/src/infinity_data/infra/path.py +0 -0
- {infinity_data-2.1.0 → infinity_data-2.2.1}/src/infinity_data/parser/diagnostics.py +0 -0
- {infinity_data-2.1.0 → infinity_data-2.2.1}/src/infinity_data/parser/token_stream.py +0 -0
- {infinity_data-2.1.0 → infinity_data-2.2.1}/src/infinity_data/py.typed +0 -0
- {infinity_data-2.1.0 → infinity_data-2.2.1}/src/infinity_data/sandbox/__init__.py +0 -0
- {infinity_data-2.1.0 → infinity_data-2.2.1}/src/infinity_data/sandbox/config.py +0 -0
- {infinity_data-2.1.0 → infinity_data-2.2.1}/src/infinity_data/sandbox/errors.py +0 -0
- {infinity_data-2.1.0 → infinity_data-2.2.1}/src/infinity_data/sandbox/mediator.py +0 -0
- {infinity_data-2.1.0 → infinity_data-2.2.1}/src/infinity_data/sandbox/schema.py +0 -0
- {infinity_data-2.1.0 → infinity_data-2.2.1}/src/infinity_data/semantic/__init__.py +0 -0
- {infinity_data-2.1.0 → infinity_data-2.2.1}/src/infinity_data/semantic/diagnostics.py +0 -0
- {infinity_data-2.1.0 → infinity_data-2.2.1}/src/infinity_data/semantic/executor/__init__.py +0 -0
- {infinity_data-2.1.0 → infinity_data-2.2.1}/src/infinity_data/semantic/jsonpath.py +0 -0
- {infinity_data-2.1.0 → infinity_data-2.2.1}/src/infinity_data/semantic/registry/__init__.py +0 -0
- {infinity_data-2.1.0 → infinity_data-2.2.1}/src/infinity_data/semantic/registry/_core.py +0 -0
- {infinity_data-2.1.0 → infinity_data-2.2.1}/src/infinity_data/semantic/registry/logic.py +0 -0
- {infinity_data-2.1.0 → infinity_data-2.2.1}/src/infinity_data/semantic/registry/types.py +0 -0
- {infinity_data-2.1.0 → infinity_data-2.2.1}/src/infinity_data/semantic/resolver/__init__.py +0 -0
- {infinity_data-2.1.0 → infinity_data-2.2.1}/src/infinity_data/tokenizer/__init__.py +0 -0
- {infinity_data-2.1.0 → infinity_data-2.2.1}/src/infinity_data/tokenizer/char_stream.py +0 -0
- {infinity_data-2.1.0 → infinity_data-2.2.1}/src/infinity_data/tokenizer/diagnostics.py +0 -0
- {infinity_data-2.1.0 → infinity_data-2.2.1}/src/infinity_data/tokenizer/finalizer.py +0 -0
- {infinity_data-2.1.0 → infinity_data-2.2.1}/src/infinity_data/tokenizer/models/__init__.py +0 -0
- {infinity_data-2.1.0 → infinity_data-2.2.1}/src/infinity_data/tokenizer/models/raw_tokens.py +0 -0
- {infinity_data-2.1.0 → infinity_data-2.2.1}/src/infinity_data/tokenizer/models/tokens.py +0 -0
- {infinity_data-2.1.0 → infinity_data-2.2.1}/src/infinity_data/tokenizer/tokenizer.py +0 -0
- {infinity_data-2.1.0 → infinity_data-2.2.1}/src/infinity_data.egg-info/SOURCES.txt +0 -0
- {infinity_data-2.1.0 → infinity_data-2.2.1}/src/infinity_data.egg-info/dependency_links.txt +0 -0
- {infinity_data-2.1.0 → infinity_data-2.2.1}/src/infinity_data.egg-info/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "infinity-data"
|
|
3
|
-
version = "2.1
|
|
3
|
+
version = "2.2.1"
|
|
4
4
|
description = "InfinityData:声明式配置语言(.infd/.inft)的 Python 编译器库"
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
license = { text = "MIT" }
|
|
@@ -35,6 +35,7 @@ Documentation = "https://github.com/yinbailiang/infinity_data#readme"
|
|
|
35
35
|
[dependency-groups]
|
|
36
36
|
# 测试组:仅运行测试所需(CI test job 只装这组,更快)
|
|
37
37
|
test = [
|
|
38
|
+
"pyyaml",
|
|
38
39
|
"pytest>=8.0",
|
|
39
40
|
"pytest-cov>=5.0",
|
|
40
41
|
]
|
|
@@ -89,7 +90,7 @@ line-ending = "lf"
|
|
|
89
90
|
typeCheckingMode = "strict"
|
|
90
91
|
reportMissingTypeStubs = false
|
|
91
92
|
reportMissingImports = "error"
|
|
92
|
-
exclude = ["**/node_modules", "**/__pycache__", "**/.*", ".venv"]
|
|
93
|
+
exclude = ["**/node_modules", "**/__pycache__", "**/.*", ".venv", "build"]
|
|
93
94
|
venvPath = "."
|
|
94
95
|
venv = ".venv"
|
|
95
96
|
|
|
@@ -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),
|
|
@@ -137,7 +137,7 @@ def _compile(file: File, options: CompileOptions) -> CompilationResult:
|
|
|
137
137
|
if resolver.schema.from_file and context.schema_scope is not None
|
|
138
138
|
else context.root_scope
|
|
139
139
|
)
|
|
140
|
-
key = scope.get(resolver.schema.template)
|
|
140
|
+
key = scope.visible.get(resolver.schema.template)
|
|
141
141
|
if key is None:
|
|
142
142
|
raise SchemaError('schema.undefined_template', {'template': resolver.schema.template})
|
|
143
143
|
tpl = std.templates[key]
|
|
@@ -13,8 +13,10 @@ from infinity_data.semantic.builder.models import (
|
|
|
13
13
|
StdDocument,
|
|
14
14
|
StdField,
|
|
15
15
|
StdLiteral,
|
|
16
|
+
StdNode,
|
|
16
17
|
StdObject,
|
|
17
18
|
StdValue,
|
|
19
|
+
is_std_node,
|
|
18
20
|
)
|
|
19
21
|
|
|
20
22
|
__all__ = [
|
|
@@ -24,6 +26,8 @@ __all__ = [
|
|
|
24
26
|
'StdDocument',
|
|
25
27
|
'StdField',
|
|
26
28
|
'StdLiteral',
|
|
29
|
+
'StdNode',
|
|
27
30
|
'StdObject',
|
|
28
31
|
'StdValue',
|
|
32
|
+
'is_std_node',
|
|
29
33
|
]
|