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)
# ==================================================================
Binary file not shown.
+87
View File
@@ -4343,6 +4343,7 @@ static PyObject *ParseDelete(Parser *P);
static PyObject *ParseAssert(Parser *P);
static PyObject *ParseDecorated(Parser *P);
static PyObject *ParseVirtualModified(Parser *P, PyObject *Decorators);
static PyObject *ParseStructDef(Parser *P, PyObject *Decorators);
static PyObject *ParseTypeParams(Parser *P);
static PyObject *ParseExceptHandler(Parser *P);
static PyObject *ParseTargetList(Parser *P);
@@ -4429,6 +4430,11 @@ static PyObject *ParseStatement(Parser *P) {
if (TokIsName(T, "class")) {
return ParseClassDef(P, NULL);
}
/* struct 纯数据类型定义(软关键字:仅"语句起始 + 后随标识符"生效,
* 故 `struct = 5` / `x.struct` 作标识符的写法不受影响) */
if (TokIsName(T, "struct") && PeekTok(P, 1)->Kind == TK_NAME) {
return ParseStructDef(P, NULL);
}
if (TokIsName(T, "if")) {
return ParseIf(P);
}
@@ -5161,6 +5167,10 @@ static PyObject *ParseDecorated(Parser *P) {
if (T->Kind == TK_NAME && TokIsName(T, "class")) {
return ParseClassDef(P, DecoratorList);
}
if (T->Kind == TK_NAME && TokIsName(T, "struct")
&& PeekTok(P, 1)->Kind == TK_NAME) {
return ParseStructDef(P, DecoratorList);
}
if (T->Kind == TK_NAME && (TokIsName(T, "virtual") || TokIsName(T, "novirtual"))) {
return ParseVirtualModified(P, DecoratorList);
}
@@ -5189,6 +5199,14 @@ static PyObject *ParseVirtualModified(Parser *P, PyObject *Decorators) {
IsDef = 1;
}
}
if (Nxt->Kind == TK_NAME && TokIsName(Nxt, "struct")) {
/* struct 是纯数据类型:禁止 virtual / novirtual 修饰(用户 2026-10-01 定)。
* struct 解析期已合成 @t.NoVTable,无需也无法再显式声明虚/非虚。 */
SyntaxErrorAt(P, Mod,
"virtual/novirtual is not allowed on struct "
"(struct is data-only, use class for vtable)");
return NULL;
}
if (!IsClass && !IsDef) {
SyntaxErrorAt(P, Mod, "virtual/novirtual must precede class/def");
return NULL;
@@ -5730,6 +5748,75 @@ static PyObject *ParseClassDef(Parser *P, PyObject *Decorators) {
return Node;
}
/* struct 纯数据类型定义(对齐 vast Parser._ParseStructDef):
* `struct X:` / `struct X(Base):` ≡ `@t.NoVTable class X(t.CStruct):`。
* 复用 ParseClassDef 的完整解析路径(类型参数/基类/关键字实参/类体套件)——
* ParseClassDef 的首个 NextTok 消费的正是 `struct` 关键字本身,故此处不预消费。
* 解析前后各补:合成 @t.NoVTable 装饰器(类级强制无虚表)、追加 t.CStruct
* 标记基类(数据类语义,_MARKER_BASES 认它)、校验类体内没有 def。 */
static PyObject *ParseStructDef(Parser *P, PyObject *Decorators) {
Token *Start = PeekTok(P, 0); /* struct */
PyObject *Deco = MakeTAttrNode(P, "NoVTable");
if (Deco == NULL) {
return NULL;
}
FixLoc(P, Deco, Start);
PyObject *DecoList = NewList();
if (DecoList == NULL) {
Py_DECREF(Deco);
return NULL;
}
if (Decorators != NULL) {
Py_ssize_t N = PyList_GET_SIZE(Decorators);
for (Py_ssize_t I = 0; I < N; I++) {
PyList_Append(DecoList, PyList_GET_ITEM(Decorators, I));
}
}
PyList_Append(DecoList, Deco);
Py_DECREF(Deco);
PyObject *Node = ParseClassDef(P, DecoList);
Py_DECREF(DecoList);
if (Node == NULL) {
return NULL;
}
/* 数据类标记基类:追加在用户基类之后(真实父类解析顺序不变) */
PyObject *Bases = PyObject_GetAttrString(Node, "bases");
if (Bases == NULL) {
PyErr_Clear();
} else {
PyObject *MarkerBase = MakeTAttrNode(P, "CStruct");
if (MarkerBase == NULL) {
Py_DECREF(Bases);
Py_DECREF(Node);
return NULL;
}
FixLoc(P, MarkerBase, Start);
PyList_Append(Bases, MarkerBase);
Py_DECREF(MarkerBase);
Py_DECREF(Bases);
}
/* struct 禁止 def:检查类体直接子节点(嵌套 struct 由其自身解析自检) */
PyObject *Body = PyObject_GetAttrString(Node, "body");
if (Body == NULL) {
PyErr_Clear();
return Node;
}
Py_ssize_t BN = PyList_GET_SIZE(Body);
for (Py_ssize_t I = 0; I < BN; I++) {
PyObject *Item = PyList_GET_ITEM(Body, I);
if (Item != NULL && NodeIs(Item, "FunctionDef")) {
SyntaxErrorAt(P, Start,
"def is not allowed inside struct "
"(struct is data-only, use class for methods)");
Py_DECREF(Body);
Py_DECREF(Node);
return NULL;
}
}
Py_DECREF(Body);
return Node;
}
/* 语句块(套件) */
static PyObject *ParseSuiteImpl(Parser *P) {
if (PeekTok(P, 0)->Kind == TK_NEWLINE) {