diff --git a/lib/vast/Parser.py b/lib/vast/Parser.py index 7519cbb..f050999 100644 --- a/lib/vast/Parser.py +++ b/lib/vast/Parser.py @@ -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) # ================================================================== diff --git a/lib/vast_c/_parser.pyd b/lib/vast_c/_parser.pyd index 306d403..8041032 100644 Binary files a/lib/vast_c/_parser.pyd and b/lib/vast_c/_parser.pyd differ diff --git a/lib/vast_c/vast_c.c b/lib/vast_c/vast_c.c index bb2fefe..a304044 100644 --- a/lib/vast_c/vast_c.c +++ b/lib/vast_c/vast_c.c @@ -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) {