feat(struct): 新增 struct 关键字(class 同形,纯数据类型)
语义(用户 2026-10-01 定):与 class 同形,但只能表示数据类型:
① 禁止 virtual / novirtual 修饰(struct 天然无虚表);
② 禁止在 struct 内定义 def;
③ 允许在 REnum 内作为变体(与 class 一样);
④ 允许 struct 内再写 struct(与 class 内写 class 同口径,语法层不额外禁止)。
实现(双端同一套表示,零新增下游判据):
· tokens.vp:Keyword 枚举**表尾**追加 Struct(既有 kw_subtype 编号逐字不变)
+ _kw_intern("struct");struct 在 .vp 源码中不作标识符。
· parser.vp:新增 _parse_structdef + _RecordStructErr,复用 class 的
基类/类型参数/类体解析路径,解析期合成 `@t.NoVTable` 伪装饰器并**追加**
`t.CStruct` 标记基类(真实父类仍在 bases[0])——与手写
`@t.NoVTable class X(t.CStruct):` 逐字段同形;类体解析后检查直接子节点,
遇 FunctionDef 即 fail-fast。
· parser.vp:_ParseVirtualModified 显式拦截 `virtual struct` / `novirtual struct`。
验证:struct 内 def / virtual struct 均报错;`struct = 5` 仍按普通赋值解析。
This commit is contained in:
1 parent
85f67b5558
commit
c27ebc1b69
2 files changed
+169
-8
No files matched your search
+157
-6
@@ -635,11 +635,20 @@ def _ParseVirtualModified(ps: rawptr[Parser], parent: rawptr[AST]):
|
||||
nxt_s: str = _cur_str(ps)
|
||||
if nxt_s is None:
|
||||
nxt_s = "(none)"
|
||||
_RecordVirtualErr(f"`{mod_name}` 后随 '{nxt_s}'(非 class/def)",
|
||||
mod_lineno, mod_col,
|
||||
f"`{mod_name}` 只在 class/def 前生效;"
|
||||
f"若此处本意是普通标识符名,请改名"
|
||||
f"(该词在语句起始会被判为修饰符)。")
|
||||
if kw2 == Keyword.Struct:
|
||||
# struct 是纯数据类型:禁止 virtual / novirtual 修饰(用户 2026-10-01 定)。
|
||||
# 单独报文(而不是落到通用"后随非 class/def")以便直接指出修法:
|
||||
# struct 解析期已内置 @t.NoVTable,不需要也无法再显式声明虚/非虚。
|
||||
_RecordStructErr(f"`{mod_name} struct` 非法:struct 禁止 virtual/novirtual 修饰",
|
||||
mod_lineno, mod_col,
|
||||
"struct 天然无虚表(解析期已合成 @t.NoVTable),"
|
||||
"请直接写 `struct X:`;需要虚表语义请改用 class。")
|
||||
else:
|
||||
_RecordVirtualErr(f"`{mod_name}` 后随 '{nxt_s}'(非 class/def)",
|
||||
mod_lineno, mod_col,
|
||||
f"`{mod_name}` 只在 class/def 前生效;"
|
||||
f"若此处本意是普通标识符名,请改名"
|
||||
f"(该词在语句起始会被判为修饰符)。")
|
||||
ps.error_count += 1
|
||||
# 余下 token 交给通用语句分派消费(不制造死循环,也不覆盖首次错误)。
|
||||
_parse_simple_stmt_into(ps, parent)
|
||||
@@ -2849,7 +2858,8 @@ def _parse_statement_into(ps: rawptr[Parser], parent: rawptr[AST]):
|
||||
parent.append(stmt)
|
||||
return
|
||||
if kw == Keyword.With or _is_funcdef_kw(ps, kw) != 0 or kw == Keyword.Class \
|
||||
or kw == Keyword.Enum or kw == Keyword.Union or kw == Keyword.Renum:
|
||||
or kw == Keyword.Enum or kw == Keyword.Union or kw == Keyword.Renum \
|
||||
or kw == Keyword.Struct:
|
||||
stmt = _parse_compound(ps)
|
||||
if stmt is not None:
|
||||
parent.append(stmt)
|
||||
@@ -3294,6 +3304,8 @@ def _parse_compound(ps: rawptr[Parser]) -> rawptr[AST]:
|
||||
return _parse_uniondef(ps)
|
||||
if kw == Keyword.Renum:
|
||||
return _parse_renumdef(ps)
|
||||
if kw == Keyword.Struct:
|
||||
return _parse_structdef(ps)
|
||||
if kw == Keyword.Async:
|
||||
_advance(ps)
|
||||
if _is_funcdef_kw(ps, _cur_kw(ps)) != 0:
|
||||
@@ -4035,6 +4047,145 @@ def _parse_renumdef(ps: rawptr[Parser]) -> rawptr[AST]:
|
||||
return node
|
||||
|
||||
|
||||
def _RecordStructErr(detail: str, lineno: int, col: int, extra: str):
|
||||
"""记录 struct 的非法形态(fail-fast;只记首次)。
|
||||
|
||||
与 _RecordVirtualErr 同一上报路径(parse_tokens → dump_parse_traceback 补全
|
||||
`文件:行:列` 与源码行 + `^` 列标记)。报文一律用 f-string 构建:本语言的 str
|
||||
是 i8*,没有 `str + str` 的语义,`"a" + x` 会退化成裸指针 add(非法 IR)。
|
||||
"""
|
||||
global _g_parse_err_line, _g_parse_err_col, _g_parse_err_msg
|
||||
if _g_parse_err_line != 0:
|
||||
return
|
||||
_g_parse_err_line = lineno
|
||||
_g_parse_err_col = col
|
||||
buf: str = stdlib.malloc(1536)
|
||||
if buf is not None:
|
||||
stdio.snprintf(buf, 1536,
|
||||
"[TPV] struct 定义非法(%s)。struct 与 class 同形,但**只能表示数据类型**:"
|
||||
"① 禁止 virtual / novirtual 修饰(struct 天然无虚表,已内置 @t.NoVTable);"
|
||||
"② 禁止在 struct 内定义 def(需要方法请改用 class);"
|
||||
"③ 允许在 REnum 内作为变体、也允许 struct 内再写 struct。"
|
||||
"正确示例:struct Point: x: int / struct Point: x: int 换行 4 空格 y: int。%s"
|
||||
"(struct 的约束不会被静默忽略:非法形态一律终止编译)", detail, extra)
|
||||
_g_parse_err_msg = buf
|
||||
else:
|
||||
_g_parse_err_msg = detail
|
||||
|
||||
|
||||
def _parse_structdef(ps: rawptr[Parser]) -> rawptr[AST]:
|
||||
"""struct NAME: / struct NAME(Base): 语法糖 → 与
|
||||
`@t.NoVTable class NAME(t.CStruct):` 逐字段同形的 ClassDef。
|
||||
|
||||
语义(用户 2026-10-01 定):与 class 同形,但只能表示数据类型:
|
||||
· 强制无虚表:合成伪装饰器 @t.NoVTable(与手写 @t.NoVTable 逐字段同形,
|
||||
ClassGenBase._has_decorator / TPC _resolve_class_flags 零新增判据)。
|
||||
即使继承自带虚表的父类,本类也不生成虚表(_detect_vtable_status 被
|
||||
类级 NoVTable 关闭),与"C 的 struct 没有虚表"一致。
|
||||
· 数据类标记:基类列表**追加** Attribute(Name('t'), 'CStruct'),追加在用户
|
||||
基类**之后**(不移位真实父类的解析顺序)。TPV ClassGenBase._is_marker_base
|
||||
与 TPC lib/includes/t.py 的 _MARKER_BASES 都认 CStruct(都不当真实父类),
|
||||
两侧对称;于是"纯数据类(无方法无虚表无 __new__)"的模块级变量保持
|
||||
**值实例**(TranslatorGenMixin 既有口径)。
|
||||
· 禁止类体内定义 def:解析完类体后检查直接子节点,遇 FunctionDef 即
|
||||
fail-fast(_RecordStructErr)。嵌套 struct 由它自己的解析负责自检。
|
||||
· 禁止 virtual/novirtual struct:见 _ParseVirtualModified。
|
||||
允许出现在 REnum 内部(与 class 一样:REnum 的变体本就是嵌套 ClassDef),
|
||||
也允许 struct 内再写 struct —— 与 class 内写 class **完全同口径**(语法层不禁止;
|
||||
内层是否发射为独立结构体由类体 ClassDef 子节点的既有路径决定)。
|
||||
行号/列号继承自语句本身,报错回显定位到真实源码行。
|
||||
"""
|
||||
lineno: int = _cur_lineno(ps)
|
||||
col: int = _cur_col(ps)
|
||||
_advance(ps) # struct
|
||||
if _cur_type(ps) != TokenType.Name:
|
||||
_RecordStructErr("`struct` 后缺少类型名", lineno, col,
|
||||
"正确写法:struct Point:(类型名紧跟 struct)。")
|
||||
ps.error_count += 1
|
||||
return None
|
||||
name: str = _cur_str(ps)
|
||||
_advance(ps)
|
||||
# 类型参数 [T] / [T, U](与 class 同口径,PEP 695 泛型)
|
||||
type_params: rawptr[list[str]] = None
|
||||
if _accept_op(ps, TokOp.Lsqb):
|
||||
type_params = list[str](ps.pool, 8)
|
||||
while not _match_op(ps, TokOp.Rsqb):
|
||||
if _cur_type(ps) == TokenType.EndMarker: break
|
||||
if _cur_type(ps) == TokenType.Name:
|
||||
tp_name: str = _cur_str(ps)
|
||||
_advance(ps)
|
||||
if _match_op(ps, TokOp.Colon):
|
||||
_advance(ps)
|
||||
_parse_test(ps)
|
||||
type_params.append(tp_name)
|
||||
if not _accept_op(ps, TokOp.Comma):
|
||||
break
|
||||
_expect_op(ps, TokOp.Rsqb)
|
||||
bases: rawptr[list[rawptr[AST]]] = list[rawptr[AST]](ps.pool, 8)
|
||||
keywords: rawptr[list[rawptr[AST]]] = list[rawptr[AST]](ps.pool, 8)
|
||||
# bases / keywords(与 _parse_classdef 同一解析路径,含跨行与 keyword=value)
|
||||
if _accept_op(ps, TokOp.LPar):
|
||||
while not _match_op(ps, TokOp.RPar):
|
||||
_skip_nl(ps)
|
||||
if _cur_type(ps) == TokenType.EndMarker: break
|
||||
if _cur_type(ps) == TokenType.Name:
|
||||
e: rawptr[AST] = _parse_or_test(ps)
|
||||
if _match_op(ps, TokOp.Equal):
|
||||
_advance(ps)
|
||||
v: rawptr[AST] = _parse_test(ps)
|
||||
kw_node: rawptr[AST] = KwNode(ps.pool, e, v)
|
||||
keywords.append(kw_node)
|
||||
else:
|
||||
bases.append(e)
|
||||
if not _accept_op(ps, TokOp.Comma):
|
||||
break
|
||||
_skip_nl(ps)
|
||||
_expect_op(ps, TokOp.RPar)
|
||||
# 数据类标记基类:追加在用户基类之后(真实父类仍在 bases[0],解析顺序不变)
|
||||
bases.append(Attribute(ps.pool, Name(ps.pool, "t", ASTCtx.Load),
|
||||
"CStruct", ASTCtx.Load))
|
||||
_expect_op(ps, TokOp.Colon)
|
||||
# 伪装饰器 @t.NoVTable:追加在用户装饰器之后(与 _ParseVirtualModified 同款合并)
|
||||
dec_list: rawptr[list[rawptr[AST]]] = ps.pending_decorators
|
||||
ps.pending_decorators = None
|
||||
marker: rawptr[AST] = Attribute(ps.pool, Name(ps.pool, "t", ASTCtx.Load),
|
||||
"NoVTable", ASTCtx.Load)
|
||||
_set_pos(marker, lineno, col, 0, 0)
|
||||
merged: rawptr[list[rawptr[AST]]] = list[rawptr[AST]](ps.pool, 8)
|
||||
if merged is not None:
|
||||
if dec_list is not None:
|
||||
dn_s: sizet = len(dec_list)
|
||||
di_s: sizet = 0
|
||||
while di_s < dn_s:
|
||||
merged.append(dec_list.get(di_s))
|
||||
di_s += 1
|
||||
merged.append(marker)
|
||||
node: rawptr[ClassDef] = ClassDef(ps.pool, name, bases, keywords, merged, type_params)
|
||||
# owner_is_class=1:与 class 同口径(类体期间 ps.in_class_body=1)
|
||||
_parse_suite_into_children(ps, node, 1)
|
||||
# struct 禁止 def:检查直接子节点
|
||||
chk: rawptr[list[rawptr[AST]]] = node.children
|
||||
if chk is not None:
|
||||
cn_s: sizet = len(chk)
|
||||
ci_s: sizet = 0
|
||||
while ci_s < cn_s:
|
||||
it_s: rawptr[AST] = chk.get(ci_s)
|
||||
if it_s is not None and it_s.kind() == ASTKind.FunctionDef:
|
||||
fd_s: rawptr[FunctionDef] = (rawptr[FunctionDef])(it_s)
|
||||
fname_s: str = "(anonymous)"
|
||||
if fd_s is not None and fd_s.name is not None:
|
||||
fname_s = fd_s.name
|
||||
_RecordStructErr(f"struct '{name}' 内定义 def '{fname_s}'",
|
||||
it_s.lineno, it_s.col_offset,
|
||||
"struct 只表示数据类型(与 C 的 struct 同口径);"
|
||||
"若确实需要方法与虚表,请把该类型写成 class。")
|
||||
ps.error_count += 1
|
||||
break
|
||||
ci_s += 1
|
||||
_set_pos(node, lineno, col, 0, 0)
|
||||
return node
|
||||
|
||||
|
||||
def _parse_match(ps: rawptr[Parser]) -> rawptr[AST]:
|
||||
"""match statement: match subject ':' NewLine Indent case+ Dedent"""
|
||||
lineno: int = _cur_lineno(ps)
|
||||
|
||||
+12
-2
@@ -134,7 +134,14 @@ class Keyword(t.CEnum):
|
||||
# renum 变体类型定义关键字(语法糖:`renum X:` 与 `class X(t.REnum):` 等价,
|
||||
# 见 parser.vp 的 _parse_renumdef)。同样追加在**表尾**:既有关键字编号
|
||||
# (kw_subtype)逐字不变;全仓 .vp 源码中 `renum` 不作标识符使用。
|
||||
Renum: t.State
|
||||
Renum: t.State
|
||||
# struct 纯数据类型定义关键字(语法糖:`struct X:` 与
|
||||
# `class X(t.CStruct):` + `@t.NoVTable` 等价,见 parser.vp 的 _parse_structdef):
|
||||
# 与 class 同形,但**只能表示数据类型**——禁止 virtual/novirtual 修饰、禁止类体内
|
||||
# 定义 def。同样追加在**表尾**:既有关键字编号(kw_subtype)逐字不变;
|
||||
# 全仓 .vp 源码中 `struct` 不作标识符使用(仅出现在注释/字符串与
|
||||
# build_gep_struct 之类复合标识符里)。
|
||||
Struct: t.State
|
||||
|
||||
|
||||
# ============================================================
|
||||
@@ -409,7 +416,10 @@ def _init_keywords(pool: rawptr[memhub.MemManager]):
|
||||
_kw_intern(pool, "novirtual", Keyword.Novirtual)
|
||||
_kw_intern(pool, "union", Keyword.Union)
|
||||
# renum 变体类型定义关键字(语法糖,同 enum/union,见 parser.vp 的 _parse_renumdef)
|
||||
_kw_intern(pool, "renum", Keyword.Renum)
|
||||
_kw_intern(pool, "renum", Keyword.Renum)
|
||||
# struct 纯数据类型定义关键字(语法糖,同 enum/union/renum,见 parser.vp 的
|
||||
# _parse_structdef:基类 t.CStruct + 伪装饰器 @t.NoVTable,禁 virtual/novirtual、禁 def)
|
||||
_kw_intern(pool, "struct", Keyword.Struct)
|
||||
|
||||
|
||||
# ============================================================
|
||||
|
||||
Reference in new issue
Block a user