diff --git a/lib/core/SymbolUtils.py b/lib/core/SymbolUtils.py index a684ec4..2ea47bd 100644 --- a/lib/core/SymbolUtils.py +++ b/lib/core/SymbolUtils.py @@ -163,7 +163,49 @@ def _ExpandUnionBlocks(Code: str) -> str: return ''.join(out) -# --------------------------------------------------------------------------- +# Viper soft keyword `renum` (statement-level reserved word): `renum X:` is exactly +# `class X(t.REnum):` (same desugar target as TPV includes/ast/parser.vp:_parse_renumdef). +# REnum members are ordinary class-body statements (nested `class V:` variants and +# bare assignments like `None_ = 0`), so only the head line is rewritten. +# `renum` is not used as an identifier anywhere in .vp sources. +_RENUM_HEAD_RE: re.Pattern[str] = re.compile( + r'^(\s*)renum(\s+)([A-Za-z_]\w*)(\s*):([ \t]*(?:#.*)?)$' +) +_RENUM_PREFIX_RE: re.Pattern[str] = re.compile(r'^(\s*)renum\b') + + +def _ExpandRenumBlocks(Code: str) -> str: + """Expand `renum Name:` into `class Name(t.REnum):` (same-line, line numbers kept). + + Mirrors TPV _parse_renumdef: the body is an ordinary class body (variant classes + written as nested `class V:` plus payload-less assignments such as `None_ = 0`), + so only the head line is rewritten. A line starting with `renum` that is not the + exact `renum Name:` form fails loudly (renum is never an identifier in .vp sources). + """ + if 'renum' not in Code: + return Code + out: list[str] = [] + for Lineno, raw in enumerate(Code.splitlines(keepends=True), 1): + k: int = len(raw) + while k > 0 and (raw[k - 1] == '\n' or raw[k - 1] == '\r'): + k -= 1 + body: str = raw[:k] + term: str = raw[k:] + head = _RENUM_HEAD_RE.match(body) + if head is not None: + out.append(f'{head.group(1)}class {head.group(3)}(t.REnum):{head.group(5)}{term}') + continue + if _RENUM_PREFIX_RE.match(body) is not None: + raise SyntaxError( + f"renum 语法错误(行 {Lineno},原文: {body.strip()!r}):应为 `renum 名字:`" + f"(等价于 `class 名字(t.REnum):`)。renum 是语句级保留字,成员写成嵌套 " + f"`class 变体:` 或无载荷赋值(如 `None_ = 0`);非法形态一律终止编译(非静默)。" + ) + out.append(raw) + return ''.join(out) + + +# --------------------------------------------------------------------------- # define 常量声明(Viper 语法糖)——与 `NAME: t.CDefine = 值` **完全等价** # --------------------------------------------------------------------------- # 带类型 define NAME: T = <表达式> → NAME: T | t.CDefine = <表达式> @@ -1248,7 +1290,7 @@ def EnumMemberCountMismatchError(ClassName: str, Visible: list[tuple[str, object ) -# ExpandSoftKeywords 是 Code 的纯函数(全程无全局可变状态,见其"五种展开"实现): +# ExpandSoftKeywords 是 Code 的纯函数(全程无全局可变状态,见其"八种展开"实现): # 同一文件内容在一次编译里会被多处重复展开(Phase1 / Phase2 / 依赖扫描), # 缓存结果避免重复正则替换与 tokenize(cProfile:_ExpandTypeDescriptors 里的 # tokenize.generate_tokens 全程 ~590 万次 TokenInfo._make)。纯函数缓存不改输出;有界防膨胀。 @@ -1282,7 +1324,11 @@ def ExpandSoftKeywords(Code: str) -> str: _ExpandArrayPrefixAnnotations):只在注解位置生效(AnnAssign 的 ':' 之后 / 形参 / '->' 之后 / 类字段),右值列表字面量**绝不改动**;无法脱糖的注解位置 数组前缀**响亮失败**(SyntaxError,绝不静默产出 List 注解)。 - 七种展开都同行替换(长度可增,不增删换行),AST 行号映射保持有效。 + 8) `renum NAME:` → `class NAME(t.REnum):`:与手写 `class NAME(t.REnum):` 产出 + **同一 AST/同一注册路径**(见 _ExpandRenumBlocks)。成员为普通类体(嵌套 + `class 变体:` + 无载荷赋值)。行首 `renum` 的非 `renum 名字:` 形态**响亮失败** + (该词不在任何源码中作标识符)。 + 八种展开都同行替换(长度可增,不增删换行),AST 行号映射保持有效。 """ if not Code: return Code @@ -1303,6 +1349,8 @@ def _ExpandSoftKeywordsImpl(Code: str) -> str: Code = _ExpandEnumBlocks(Code) if 'union' in Code: Code = _ExpandUnionBlocks(Code) + if 'renum' in Code: + Code = _ExpandRenumBlocks(Code) if 'define' in Code: Code = _ExpandDefineConsts(Code) if 'typedef' in Code: