feat(struct): TPC 前端支持 struct 关键字(含 C 加速解析器)

与 TransPyV/includes 侧(c27ebc1)逐点对齐:`struct X:` / `struct X(Base):` 脱糖为
`@t.NoVTable class X(t.CStruct):`,禁止 virtual/novirtual 修饰、禁止类体内 def。

- lib/vast/Parser.py:软关键字分派(语句起始 + 后随标识符;`struct = 5` 不受影响)、
  _ParseStructDef(复用 _ParseClassDef 全路径 + 合成 @t.NoVTable + 追加 t.CStruct
  标记基类 + 类体禁 def 校验)、_StructSyntaxError 报文、_ParseVirtualModified
  显式拦截 `virtual/novirtual struct`;@deco 前缀同样支持。
- lib/vast_c/vast_c.c:同构实现(ParseStatement / ParseDecorated 分派、
  ParseStructDef、ParseVirtualModified 拦截)。
- lib/vast_c/_parser.pyd:按 README §4.3 的命令重建(gcc 15.2.0)。

验证(两条路径逐一对照):bases=[t.CStruct] / [Base, t.CStruct]、
decos=[t.NoVTable];`virtual struct` 与 `struct 内 def` 均 fail-fast;
`struct = 5` 仍解析为普通赋值。
This commit is contained in:
TermiNexus committed 2026-10-01 23:07:49 +08:00
1 parent 85d68e5913
commit a8f6ca006d
3 files changed
+177

No files matched your search

+90
View File
@@ -62,6 +62,26 @@ _VIRTUAL_EXAMPLE: str = (
'novirtual class B: novirtual def helper(self) -> int: ...'
)
# ======================================================================
# struct 纯数据类型定义关键字(软关键字)
# ======================================================================
# 语法:`struct X:` / `struct X(Base):`(与 class 同形,含类型参数 [T] 与关键字实参)
# 语义(用户 2026-10-01 定):与 class 同形,但**只能表示数据类型**:
# - 禁止 virtual / novirtual 修饰(见 _ParseVirtualModified 的显式拦截);
# - 禁止类体内定义 def(见 _ParseStructDef 的 fail-fast);
# - 解析期合成 `@t.NoVTable` 装饰器 + 追加 `t.CStruct` 标记基类,
# 与手写 `@t.NoVTable class X(t.CStruct):` 逐字段同形 ⇒ 下游
# (HandlesClassDef._resolve_class_flags / _MARKER_BASES)零新增判据。
# - 允许出现在 REnum 内部(REnum 变体本就是嵌套 ClassDef),也允许 struct 内
# 再写 struct(与 class 内写 class 同口径)。
# 软关键字边界:只在"语句起始 + 后随标识符"时判定,故 `struct = 5` / `x.struct`
# 等作标识符的写法逐字不变。
_STRUCT_KEYWORD: str = 'struct'
_STRUCT_EXAMPLE: str = (
'struct Point: x: int / struct Point: x: int(换行缩进)y: int / '
'struct Node(Base): value: int'
)
class Parser(ExprParserMixin):
"""Viper/Python 完整语法分析器。"""
@@ -145,6 +165,10 @@ class Parser(ExprParserMixin):
return self._ParseFunctionDef()
if v == 'class':
return self._ParseClassDef()
# struct 纯数据类型定义(软关键字):仅"语句起始 + 后随标识符"时判定,
# 故 `struct = 5` / `x.struct` 等作标识符的写法逐字不变。
if v == _STRUCT_KEYWORD and self._Peek(1).Type == NAME:
return self._ParseStructDef()
if v == 'if':
return self._ParseIf()
if v == 'while':
@@ -586,6 +610,8 @@ class Parser(ExprParserMixin):
return self._ParseFunctionDef(is_async=True, decorators=decorator_list)
if tok.Type == NAME and tok.Value == 'class':
return self._ParseClassDef(decorators=decorator_list)
if tok.Type == NAME and tok.Value == _STRUCT_KEYWORD and self._Peek(1).Type == NAME:
return self._ParseStructDef(decorators=decorator_list)
if tok.Type == NAME and tok.Value in _VIRTUAL_MARKERS:
# `@deco` 与 `virtual class`/`virtual def` 组合:合成标记追加在既有装饰器之后
return self._ParseVirtualModified(decorators=decorator_list)
@@ -643,6 +669,13 @@ class Parser(ExprParserMixin):
and nxt.Value not in _DEF_ALIASES):
nxt_text: str = ('(行尾)' if nxt.Type in (NEWLINE, NL, ENDMARKER)
else repr(nxt.Value))
if nxt.Type == NAME and nxt.Value == _STRUCT_KEYWORD:
# struct 是纯数据类型:禁止 virtual / novirtual 修饰(用户 2026-10-01 定)。
# 单独报文(而不是落到通用"后随非 class/def")以便直接给出修法。
raise self._StructSyntaxError(
f'{mod.Value} struct 非法:struct 禁止 virtual/novirtual 修饰', mod,
'struct 天然无虚表(解析期已合成 @t.NoVTable),请直接写 `struct X:`;'
'需要虚表语义请改用 class。')
raise self._VirtualSyntaxError(
f"{mod.Value!r} 后随 {nxt_text}(非 class/def)", mod,
f"`{mod.Value}` 只在 class/def(def 的别名 fn/fun/func/function)"
@@ -663,6 +696,63 @@ class Parser(ExprParserMixin):
return self._ParseClassDef(decorators=deco_list)
return self._ParseFunctionDef(decorators=deco_list)
# ==================================================================
# struct 纯数据类型定义(软关键字:`struct X:` / `struct X(Base):`)
# ==================================================================
def _StructSyntaxError(self, detail: str, tok: Token, extra: str = '') -> SyntaxError:
"""构造 struct 非法形态的 fail-fast 报文(与 _VirtualSyntaxError 同结构)。
含 行:列 + 源码片段 + struct 的三条约束 + 正确示例;行尾附 `at line N`
供 TPC 既有诊断路径兜底提取行号(见 HandlesImports._ParseFailureSite)。
"""
return SyntaxError(
f"[TPC] struct 定义非法({detail}):行 {tok.Line} 列 {tok.Col},"
f"源码片段 {self._SourceLine(tok.Line).strip()!r}。"
f"struct 与 class 同形,但**只能表示数据类型**:"
f"① 禁止 virtual / novirtual 修饰(struct 天然无虚表,已内置 @t.NoVTable);"
f"② 禁止在 struct 内定义 def(需要方法请改用 class);"
f"③ 允许在 REnum 内作为变体、也允许 struct 内再写 struct。"
f"正确示例:{_STRUCT_EXAMPLE}。"
+ (f" {extra}" if extra else '')
+ " (struct 的约束不会被静默忽略:非法形态一律终止编译)"
+ f" at line {tok.Line}"
)
def _ParseStructDef(self, decorators: list[N.expr] | None = None) -> N.ClassDef:
"""`struct X:` / `struct X(Base):` → 与 `@t.NoVTable class X(t.CStruct):`
逐字段同形的 ClassDef(与 TPV 的 _parse_structdef 同口径)。
实现:复用 _ParseClassDef 的完整解析路径(类型参数 / 基类 / 关键字实参 /
类体套件),只在**解析前后**补三件事:
· 解析前把合成的 `@t.NoVTable` 追加进装饰器列表(与 virtual/novirtual
同款表示)⇒ 类级强制无虚表,即使父类带虚表;
· 解析后把 `t.CStruct` 标记基类**追加**到 bases 末尾(不提位用户基类,
真实父类解析顺序不变)⇒ 数据类语义(_MARKER_BASES 认它,不当真实父类);
· 解析后校验类体**直接子节点**里没有 def —— struct 只能表示数据类型。
_ParseClassDef 的首个 `_Next()` 消费的正是 `struct` 关键字本身,故本函数
不预先消费,行号/列号与写法均继承自该 token。
"""
start: Token = self._Peek() # struct(由 _ParseClassDef 的 _Next() 消费)
deco: N.expr = N.Attribute(value=N.Name(id='t', ctx=N.Load()),
attr='NoVTable', ctx=N.Load())
self._FixLoc(deco, start)
deco_list: list[N.expr] = list(decorators or [])
deco_list.append(deco)
node: N.ClassDef = self._ParseClassDef(decorators=deco_list)
marker_base: N.expr = N.Attribute(value=N.Name(id='t', ctx=N.Load()),
attr='CStruct', ctx=N.Load())
self._FixLoc(marker_base, start)
node.bases.append(marker_base)
for item in node.body:
if isinstance(item, N.FunctionDef):
bad: Token = start
raise self._StructSyntaxError(
f"struct {node.name!r} 内定义 def {getattr(item, 'name', '(anonymous)')!r}",
bad,
'struct 只表示数据类型(与 C 的 struct 同口径);'
'若确实需要方法与虚表,请把该类型写成 class。')
return node
# ==================================================================
# 函数定义(含软关键字 fn/fun/func/function)
# ==================================================================