"""共享工具函数:符号表处理中使用的 AST 分析工具""" from __future__ import annotations import io import tokenize import vast as ast import re from lib.includes import t from lib.includes.t import CTypeRegistry _T_MODULE_NAMES: frozenset[str] = frozenset({'t', t.__name__}) # Viper 软关键字 fn/fun/func/function 是 def 的别名(行首 + 空白 + 标识符才算函数定义, # 变量名/调用/注解中的 fn 不受影响)。展开为 def 后行号不变,供 ast.parse 使用。 _SOFT_KEYWORD_RE: re.Pattern[str] = re.compile( r'^(\s*)(function|func|fun|fn)(\s+)(?=[A-Za-z_])', re.MULTILINE ) # enum 语句(Viper 语法糖):`enum Name:` + 成员行,与 # `class Name(t.CEnum):` **完全等价**(同一实现路径——展开后再解析, # 下游 EnumTable/符号表注册只看 ClassDef + t.CEnum 基类,不存在第二套枚举实现)。 # 头行 enum Name: → class Name(t.CEnum): # 裸成员 Name → Name: t.State(默认 i32;必须落为 AnnAssign, # 否则 Enum/REnum 成员收集按 Assign/AnnAssign 过滤会漏掉裸名语句) # 带注解 Name: T → 原样保留(自定义具体类型) # 带值 Name = 0 → 原样保留(Assign 形态,两侧枚举收集均支持) # 多名行 A, B, C → A: t.State; B: t.State; C: t.State(同行分号分隔,见下; # 可带尾随逗号 A, B,,与 TPV 的 AST 口径一致) _ENUM_HEAD_RE: re.Pattern[str] = re.compile( r'^(\s*)enum(\s+)([A-Za-z_]\w*)(\s*):([ \t]*(?:#.*)?)$' ) _ENUM_MEMBER_BARE_RE: re.Pattern[str] = re.compile( r'^(\s*)([A-Za-z_]\w*)([ \t]*(?:#.*)?)$' ) # 逗号分组成员行(`Add, Sub` / `A, B, C` / 尾随逗号 `A, B,`):与"每个名字各占一行"等价。 # 只匹配"纯标识符列表 + 可选尾随逗号 + 可选注释",故 `A, B = 1`(Assign)、 # `A, B: int`(带注解)与 `f(a, b)`(调用)都不命中,原样保留(由注册处的 # EnumBodyMemberNames 兜底判据 fail-fast,不再静默丢成员)。 # 尾随逗号必须支持:TPV 侧走 AST(`class X(t.CEnum): A, B,` 与 `A, B` 在 AST 上 # 完全同形,无法区分),TPC 若在此拒绝会造成"同源两种编译器两种结果"。group(3) # 只收注释正文(不含前导空白/逗号),展开时另行补一个空格。 _ENUM_MEMBER_LIST_RE: re.Pattern[str] = re.compile( r'^(\s*)([A-Za-z_]\w*(?:[ \t]*,[ \t]*[A-Za-z_]\w*)+)[ \t]*,?[ \t]*((?:#.*)?)$' ) # 裸名语句保留集:这些行是语句而非枚举成员,展开时不得追加 `: t.State` _ENUM_MEMBER_SKIP: frozenset[str] = frozenset({ 'pass', 'return', 'break', 'continue', 'global', 'nonlocal', 'raise', 'yield', 'import', 'from', 'del', 'assert', 'if', 'while', 'for', 'with', 'try', 'match', 'case', 'lambda', 'def', 'class', 'enum', 'None', 'True', 'False', }) # --------------------------------------------------------------- # `_AttributeMarkerText` 的属性实参形态判定正则**预编译**(性能:避免每次 # `re.fullmatch`/`re.match(字面量, ...)` 的"模式串哈希 → re 模块级缓存查找"。 # 语义与逐字写法完全一致,模式串一字未改)。 # --------------------------------------------------------------- # 裸标识符(`always_inline` 形态) _RE_ATTR_BARE_IDENT: re.Pattern[str] = re.compile(r'[A-Za-z_][A-Za-z0-9_]*') # 标识符后紧跟调用括号(`section(...)` 形态) _RE_ATTR_CALL_IDENT: re.Pattern[str] = re.compile(r'^[A-Za-z_][A-Za-z0-9_]*\s*\(') def _ExpandEnumBlocks(Code: str) -> str: """把 `enum Name:` 块展开为等价的 `class Name(t.CEnum):` 块(行号不变)。 只在"行首缩进 + enum + 空白 + 标识符 + 冒号(行尾)"的枚举头形态触发: `enum = 5` / `import enum` / `x.enum` / 注解 `x: enum` 均不匹配。 成员行只对块内"更深缩进"的行做展开,块内空行与注释行原样保留; 缩进回到枚举头同级(或更浅)即块结束。全程不增删换行,行号映射保持有效。 成员行两种形态:单名(`Name`)追加 `: t.State`;逗号分组多名(`Add, Sub`) 在同一行内展开为分号分隔的多个 `Name: t.State`(简单语句,`vast` 与 Python 均按多个 AnnAssign 解析)。未展开的一行多名语句会被当作元组表达式,下游 枚举成员收集(仅认 Assign/AnnAssign)会静默漏掉这些成员。 """ lines: list[str] = Code.splitlines(keepends=True) out: list[str] = [] i: int = 0 n: int = len(lines) while i < n: raw: str = lines[i] # 行终止符单独拆出(兼容 LF / CRLF / CR):正文参与匹配,终止符原样回写, # 保证"不增删换行、行号不变"。CRLF 源(本仓 .vp 大量为 CRLF,但 TPC 各 # 调用点多为文本模式读入已被通用换行归一,双保险)若不做此拆分, # 行尾 '\r' 会让 `$` 锚定的头行正则失配 → 静默不展开。 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 = _ENUM_HEAD_RE.match(body) if head is None: out.append(raw) i += 1 continue indent: str = head.group(1) out.append(f'{indent}class {head.group(3)}(t.CEnum):{head.group(5)}{term}') i += 1 while i < n: raw2: str = lines[i] k2: int = len(raw2) while k2 > 0 and (raw2[k2 - 1] == '\n' or raw2[k2 - 1] == '\r'): k2 -= 1 body2: str = raw2[:k2] term2: str = raw2[k2:] stripped: str = body2.strip() if stripped == '' or stripped.startswith('#'): out.append(raw2) i += 1 continue if len(body2) - len(body2.lstrip()) <= len(indent): break # 缩进回到枚举头同级:块结束(该行留给外层循环) member = _ENUM_MEMBER_BARE_RE.match(body2) if member is not None and member.group(2) not in _ENUM_MEMBER_SKIP: out.append(f'{member.group(1)}{member.group(2)}: t.State{member.group(3)}{term2}') else: # 逗号分组成员行:同一行展开为"分号分隔的多个 AnnAssign"。 # `Name: t.State` 是简单语句,分号分隔在 Python 与 vast 中均合法 # ⇒ 与单名成员行产出同一批 AnnAssign(枚举成员/宽度/编号全部一致), # 且不增删换行,行号映射保持有效。group(3) 是注释正文(可为空), # 需要时补一个空格再接回(尾随逗号已在正则里吃掉,不参与拼接)。 member_list = _ENUM_MEMBER_LIST_RE.match(body2) names: list[str] = ([nm.strip() for nm in member_list.group(2).split(',')] if member_list is not None else []) if names and all(nm not in _ENUM_MEMBER_SKIP for nm in names): joined: str = '; '.join(f'{nm}: t.State' for nm in names) tail: str = member_list.group(3) comment: str = f' {tail}' if tail else '' out.append(f'{member_list.group(1)}{joined}{comment}{term2}') else: out.append(raw2) i += 1 return ''.join(out) # Viper soft keyword `union` (statement-level reserved word): `union X:` is exactly # `class X(t.CUnion):` (same desugar target as TPV includes/ast/parser.vp:_parse_uniondef). # Unlike enum, union members are ordinary annotated fields, so no member rewriting. # `union` is not used as an identifier anywhere in .vp sources. _UNION_HEAD_RE: re.Pattern[str] = re.compile( r'^(\s*)union(\s+)([A-Za-z_]\w*)(\s*):([ \t]*(?:#.*)?)$' ) _UNION_PREFIX_RE: re.Pattern[str] = re.compile(r'^(\s*)union\b') def _ExpandUnionBlocks(Code: str) -> str: """Expand `union Name:` into `class Name(t.CUnion):` (same-line, line numbers kept). Mirrors TPV _parse_uniondef: the body is ordinary AnnAssign fields, so only the head line is rewritten. A line starting with `union` that is not the exact `union Name:` form fails loudly (union is never an identifier in .vp sources). """ if 'union' 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 = _UNION_HEAD_RE.match(body) if head is not None: out.append(f'{head.group(1)}class {head.group(3)}(t.CUnion):{head.group(5)}{term}') continue if _UNION_PREFIX_RE.match(body) is not None: raise SyntaxError( f"union 语法错误(行 {Lineno},原文: {body.strip()!r}):应为 `union 名字:`" f"(等价于 `class 名字(t.CUnion):`)。union 是语句级保留字,成员必须写成 " f"`名字: 类型`;非法形态一律终止编译(非静默)。" ) out.append(raw) 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 = <表达式> # 不带类型 define NAME = <表达式> → NAME: t.CDefine = <表达式> # # 语义:编译期常量声明,落到**既有的** t.CDefine 注册路径(TPC: # LlvmGenerator._register_cdefine_constant / HandlesAnnAssign 的 IsDefine 分支; # 跨模块:SymbolExtractor → SymbolTable → .vpi/.pyi → HandlesImports), # 不新增第二套常量实现。可见性与 `t.CDefine` 常量完全一致: # 模块级 / 类体内 / 函数内 三种位置均可(各自走既有注册点)。 # # 为什么带类型写成 `T | t.CDefine`(**类型在前**)而不是既有手写惯用法的 # `t.CDefine | T`:HandlesTypeMerge._MergeCTypeInfoInto 的 BaseType 采用 # "首个非 void 基类型胜出"(`if Target.BaseType is None: Target.BaseType = Source.BaseType`), # 而 _CollectAllOperandTypes 按**书写顺序**左→右展开 ⇒ `t.CDefine | T` 的 # BaseType 会停在 t.CDefine,显式类型 T 被**丢弃**(既有手写形态的既有事实, # 见 Test/DefineTest/App/main.vp:30-32);把 T 写在前面才能让 BaseType = T, # 从而让"值必须能放进 T"这条判据有据可依(同时也是显式类型的真实含义)。 # # 只在"行首缩进 + define + 标识符 + (`=` 或 `: … =`)"形态触发: # - `define NAME(...)`(既有宏函数软关键字,返回注解自动追加 t.CDefine, # 见 lib/vast/Parser.py:_ParseDefineDef)后跟 `(`,两个正则都不命中 ⇒ 原样保留; # - 把 define 当标识符的写法(`define = 5` / `define(x)`)同样不命中; # - 同行替换、不增删换行 ⇒ 行号映射保持有效(与 fn/enum 同约定)。 _DEFINE_CONST_TYPED_RE: re.Pattern[str] = re.compile( r'^(\s*)define[ \t]+([A-Za-z_]\w*)[ \t]*:[ \t]*([^=#]+?)[ \t]*=(?!=)(.*)$' ) _DEFINE_CONST_PLAIN_RE: re.Pattern[str] = re.compile( r'^(\s*)define[ \t]+([A-Za-z_]\w*)[ \t]*=(?!=)(.*)$' ) def _ExpandDefineConsts(Code: str) -> str: """把 `define NAME[: T] = 值` 展开为等价的 `NAME: [T |] t.CDefine = 值`(行号不变)。 逐行处理:行终止符(LF/CRLF/CR)单独拆出后原样回写(与 _ExpandEnumBlocks 同约定,避免行尾 '\\r' 让 `$` 锚定的正则失配而**静默不展开**)。 未命中的行(含 `define NAME(...)` 宏函数、注释、字符串内容以外的行) 逐字节原样保留。缺 `=` 的 `define NAME: T`(无初始值)不在本展开范围, 会原样落到解析器并**响亮失败**(非静默)。 """ if 'define' not in Code: return Code out: list[str] = [] for raw in Code.splitlines(keepends=True): 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:] typed = _DEFINE_CONST_TYPED_RE.match(body) if typed is not None: out.append(f'{typed.group(1)}{typed.group(2)}: ' f'{typed.group(3).strip()} | t.CDefine ={typed.group(4)}{term}') continue plain = _DEFINE_CONST_PLAIN_RE.match(body) if plain is not None: out.append(f'{plain.group(1)}{plain.group(2)}: t.CDefine ={plain.group(3)}{term}') continue out.append(raw) return ''.join(out) # --------------------------------------------------------------------------- # typedef 类型别名声明(Viper 语法糖)——与 `T1: t.CTypedef = T2` **完全等价** # --------------------------------------------------------------------------- # typedef T1 = T2 → T1: t.CTypedef = T2 # # 语义:与手写 `T1: t.CTypedef = T2` 落到**同一条** typedef 注册/查找路径,不新增 # 第二套实现(TPC:符号表 SymbolTypeResolver._resolve_ctypedef_typedef → # _resolve_typedef_from_value;翻译期 HandlesAnnAssign / DeclarationGenerator 的 # typedef 表。TPV:AssignGenMixin.translate_annassign 的 CTypedef 分支 → # TypeGenCType.register_ctypedef)。RHS 即"类型表达式"(书写在值位): # 裸名 `int32` / `t.X` / 并集 `t.CChar | t.CPtr` / 泛型 `array[E, N]` / 链式 `A = B`。 # # `typedef` 是**语句级保留字**:行首出现即必须是 `typedef 名字 = 类型` 形态。 # - 合法形态:同行替换为 `名字: t.CTypedef = 类型`(不增删换行,行号映射保持有效, # 与 fn/enum/define 同约定); # - 非法形态(`typedef = 1` / `typedef A` / `typedef A = ` 缺 RHS)⇒ **响亮失败** # (SyntaxError,非静默);`typedef A = 1`(RHS 不是类型)通过语法层,落到既有 # typedef 注册/解析路径处理(见 Debugger/define-constants.md 同族口径)。 # - 全仓冲突普查:`typedef` 仅在注释/文档字符串中出现,**无任何**变量/参数/字段/ # 函数/类/模块以其命名 ⇒ 作为语句级保留字不会误伤既有源码。 _TYPEDEF_DECL_RE: re.Pattern[str] = re.compile( r'^(\s*)typedef[ \t]+([A-Za-z_]\w*)[ \t]*=(?!=)(.*)$' ) _TYPEDEF_HEAD_RE: re.Pattern[str] = re.compile(r'^(\s*)typedef\b') def _ExpandTypedefs(Code: str) -> str: """把 `typedef T1 = T2` 展开为等价的 `T1: t.CTypedef = T2`(行号不变)。 逐行处理:行终止符(LF/CRLF/CR)单独拆出后原样回写(与 _ExpandEnumBlocks / _ExpandDefineConsts 同约定,避免行尾 '\\r' 让 `$` 锚定的正则失配而**静默不展开**)。 行首 `typedef` 但不符合 `typedef 名字 = …` 形态 ⇒ 响亮失败(SyntaxError); `typedef_x` / `typedefA` 等以 typedef 为前缀的标识符不命中(`\\b` 词边界)。 """ if 'typedef' not in Code: return Code out: list[str] = [] for raw in Code.splitlines(keepends=True): 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:] decl = _TYPEDEF_DECL_RE.match(body) if decl is not None: out.append(f'{decl.group(1)}{decl.group(2)}: t.CTypedef ={decl.group(3)}{term}') continue if _TYPEDEF_HEAD_RE.match(body) is not None: raise SyntaxError( f"typedef 语法错误(原文: {body.strip()!r}):应为 " f"`typedef 名字 = 类型`(等价于 `名字: t.CTypedef = 类型`)。" f"合法的类型写法:裸名 / t.X / a | t.CPtr / array[E, N] / 另一 typedef 名。" ) out.append(raw) return ''.join(out) # --------------------------------------------------------------------------- # 类型描述符 const / volatile(Viper 语法糖:描述符写在**类型前**) # --------------------------------------------------------------------------- # const int → t.CConst | int # volatile int → t.CVolatile | int # const volatile int → t.CConst | t.CVolatile | int # volatile const int → t.CVolatile | t.CConst | int(顺序任意,两个限定符都累积) # const t.CInt32T → t.CConst | t.CInt32T # const rawptr[Foo] → t.CConst | rawptr[Foo] (描述符只管注解最前面) # a: const int → a: t.CConst | int (局部 / 类字段) # def f(p: const int) → def f(p: t.CConst | int) (形参) # def f() -> const int → ... -> t.CConst | int (返回) # # 语义:与**手写** `t.CConst | T` / `t.CVolatile | T` 逐点等价——**同一表示**, # 不新造第二套(描述符不是新类型,只是 `|` 联合里的既有 flag 载体 t.CConst / # t.CVolatile): # - TPC:展开文本落到既有识别路径(HandlesTypeMerge.GetCTypeInfo 的 # ModulePath=='t' 分支 → Info.DataConst/DataVolatile(HandlesTypeMerge.py:192-201); # TypeAnnotationResolver 的类型限定符分支 → DataConst/DataVolatile # (TypeAnnotationResolver.py:934-938)),再由 _MergeCTypeInfoInto 累积 # (HandlesTypeMerge.py:1064-1071)、TypeAnnotationResolver.from_node 的 # BitOr 合并分支累积(TypeAnnotationResolver.py:626-633/640-674); # **BaseType 取非限定符一侧**(TypeAnnotationResolver.py:642-644),故 # `t.CConst | int` 与 `int | t.CConst` 等价,本层统一产出**描述符在前** # (与全仓既有真实写法一致:includes/stdio.vp:4 `fmt: t.CConst | str`、 # includes/spinlock.vp:6 `locked: t.CVolatile | int`、 # ViperApps/ViperOS/VKernel/Kernel/main.vp:301 `t.CConst | int | t.CPtr`)。 # - TPV:includes/ast/parser.vp 的 `_parse_type_ann` 合成**同形** AST # (BinOp(BitOr, Attribute(t,'CConst'), <类型>)),产出与手写形态逐字段同形。 # # 软关键字边界(唯一识别面 = "注解位置 + token 相邻",与 array 那次同口径): # const/volatile 只在该位置生效——修饰符串的前一个 token 是 `:` 且再前一个是 # Name(注解目标:`a:` / `self.x:` / 形参名),或前一个 token 是 `->`(返回注解)。 # **`static` 已从注解位置迁出**(不再与 const/volatile 同源):注解位置不再接受 # `名字: static 类型`(如旧的 `pack_head: static uint32 = 0`),必须改写为**语句 # 起始前置**形式——变量 `static 名字: 类型 = 值`、函数 `static def 名字(...)` # (见 _ExpandExportExternVar 的 static 前置分支与 vast.Parser._ParseStaticDef); # 旧注解位置一律**响亮失败**(见下),不再脱糖为 `t.CStatic | T`。 # 非类型位置**原样保留**为普通标识符(不做词法保留字): # - `t.attr.const()`(TransPyV/Test/App/attr_test.vp:142,全仓 .vp 代码内唯一出现) # - `const = 5` / `x = const` / `d = {"k": const}` # 注释与字符串内容**不参与**:本层用 `tokenize` 做词法切分(不是行正则), # 故 openssl/*.vp 等 C 原型注释里的 `const char *`(全仓 275 处)逐字不变。 # # 非法形态**响亮失败**(SyntaxError,非静默,不丢描述符): # `a: const`(缺类型)/ `a: const = 1` / `a: const 1`(后随非类型)/ 裸 `const` 语句; # **注解位置的 `static`**(如 `memory_regions: static memory_region* = None`): # 已迁移为前置形式,旧写法一律终止编译。 # 报文含 行:列 + 源码片段 + 期望形态 + 正确示例(文件名由调用方的 SyntaxError # 上报路径补全,与 _ExpandTypedefs / TPV dump_parse_traceback 同约定)。 _TYPE_DESCRIPTOR_MARKERS: dict[str, str] = { 'const': 'CConst', 'volatile': 'CVolatile', } _DESCRIPTOR_SKIP_TYPES: tuple[int, ...] = (tokenize.COMMENT, tokenize.NL) _DESCRIPTOR_HINT: str = ( '`名字: const 类型` / `名字: volatile 类型`' '(可组合:`名字: const volatile 类型`)' ) _DESCRIPTOR_EXAMPLE: str = ( 'a: const int / a: volatile int / a: const volatile t.CInt32T / ' 'def f(p: const rawptr[Foo]) -> const int' ) def _PrevSignificantTokenIndex(Tokens: list, Index: int) -> int: """取 Index 之前第一个"有意义"的 token 下标(跳过注释与 NL;-1=无)。""" j: int = Index - 1 while j >= 0 and Tokens[j].type in _DESCRIPTOR_SKIP_TYPES: j -= 1 return j def _NextSignificantTokenIndex(Tokens: list, Index: int) -> int: """取 Index 之后第一个"有意义"的 token 下标(跳过注释与 NL;-1=无)。 与 `_PrevSignificantTokenIndex` 配对:`export 名字 :` 的 lookahead 与 注解类型终点后的 `=` 判定(见 `_ExpandExportExternVar`)共用本函数。 """ j: int = Index + 1 n: int = len(Tokens) while j < n and Tokens[j].type in _DESCRIPTOR_SKIP_TYPES: j += 1 return j if j < n else -1 def _TypeDescriptorSyntaxError(Detail: str, Lineno: int, Col: int, Snippet: str) -> SyntaxError: """构造类型描述符非法形态的 fail-fast 报文(行:列 + 源码片段 + 期望形态 + 正确示例)。""" return SyntaxError( f"[TPC] 类型描述符非法({Detail}):行 {Lineno} 列 {Col},源码片段 {Snippet.strip()!r}。" f"const/volatile 是只在**类型位置**生效的软关键字,必须写成 {_DESCRIPTOR_HINT};" f"正确示例:{_DESCRIPTOR_EXAMPLE}。" f"(描述符不会被静默忽略:缺类型 / 后随非类型 / 裸用一律终止编译)" ) def _StaticPositionSyntaxError(Lineno: int, Col: int, Snippet: str) -> SyntaxError: """构造旧位置 static(注解位置)的 fail-fast 报文。 注解位置的 `static` 已迁移为**语句起始前置**修饰符(`static 名字: 类型 = 值`); 旧写法 `名字: static 类型 = 值` 一律终止编译,绝不静默改写。 """ return SyntaxError( f"[TPC] 注解位置的 static 已不再支持:行 {Lineno} 列 {Col}," f"源码片段 {Snippet.strip()!r}。" f"请写成前置形式 `static 名字: 类型 = 值`" f"(等价 `名字: t.CStatic | 类型 = 值`);" f"正确示例:`static memory_regions: memory_region* = None` / " f"`static pack_head: uint32 = 0`。" f"(旧写法 `名字: static 类型` 已删除,绝不静默改写)" ) def _InStaticOldAnnotationPosition(Tokens: list, Index: int) -> bool: """`Index` 处的 `static` 是否处于**旧**注解位置(`名字: static 类型` / `-> static 类型`)。 与 _ExpandTypeDescriptors 的注解位置判据同口径,并额外回溯 `const` / `volatile` / `static` / `|`,以覆盖 `a: const static T` 这类组合旧形态。 """ j: int = Index Guard: int = 0 while j >= 0 and Guard < 64: PrevIdx: int = _PrevSignificantTokenIndex(Tokens, j) if PrevIdx < 0: return False Prev = Tokens[PrevIdx] if Prev.string == '->': return True if Prev.string == ':': BeforeIdx: int = _PrevSignificantTokenIndex(Tokens, PrevIdx) return BeforeIdx >= 0 and Tokens[BeforeIdx].type == tokenize.NAME if Prev.string == '|': j = PrevIdx Guard += 1 continue if Prev.type == tokenize.NAME and ( Prev.string in _TYPE_DESCRIPTOR_MARKERS or Prev.string == 'static'): j = PrevIdx Guard += 1 continue return False return False # C-style leading array declarator `const [N]T`: textual desugar into # `t.CArray[T, N]` (same shape as TPV includes/ast/parser.vp:_parse_array_prefix_declarator). _TYPE_BRACKET_OPENERS: frozenset[str] = frozenset({'(', '[', '{'}) _TYPE_BRACKET_CLOSERS: frozenset[str] = frozenset({')', ']', '}'}) # Annotation type expression ends at these top-level operators. _TYPE_EXPR_STOP_TOKENS: frozenset[str] = frozenset({'=', ';', ',', ':'}) def _ScanAnnotationTypeEnd(Tokens: list, Start: int, Count: int) -> int: """Return the index just past an annotation type expression starting at Start. Bracket-aware: separators inside ()/[]/{} do not end the type; at top level the type ends at '=' / ';' / ',' / ':' or NEWLINE / ENDMARKER. """ k: int = Start Depth: int = 0 while k < Count: Tok = Tokens[k] if Tok.type == tokenize.COMMENT: # 行尾注释不属于类型文本。若当普通注释跳过继续扫,ElemEnd - 1 会落到注释上, # 使数组前缀脱糖把注释并进 `t.CArray[...]` 内部(`data: [512]uint8 # 注释` # 会变成 `t.CArray[uint8 # 注释, 512]` ⇒ 方括号失衡、整段注解作废)。 break if Tok.type in _DESCRIPTOR_SKIP_TYPES: k += 1 continue if Tok.type in (tokenize.NEWLINE, tokenize.ENDMARKER): break s: str = Tok.string if s in _TYPE_BRACKET_OPENERS: Depth += 1 elif s in _TYPE_BRACKET_CLOSERS: if Depth == 0: break Depth -= 1 elif Depth == 0 and s in _TYPE_EXPR_STOP_TOKENS: break k += 1 return k def _DesugarArrayPrefix(Tokens: list, BracketIdx: int, Count: int, Code: str) -> tuple[int, str] | None: """Desugar a leading array prefix `[N]...T` into the text `t.CArray[T, N]`. `Tokens[BracketIdx]` is '['. Returns `(End, TypeText)` where End is the index just past the element type. Supports `[N][M]T` (outer-to-inner) and `[]T` (length omitted -> None). Cross-line / missing element -> None (caller keeps the existing fail-fast). """ Lines0: list[str] = Code.splitlines() Line: int = Tokens[BracketIdx].start[0] Groups: list[str] = [] k: int = BracketIdx while k < Count and Tokens[k].type == tokenize.OP and Tokens[k].string == '[': k += 1 First: int = k Depth: int = 1 while k < Count and Depth > 0: Tok = Tokens[k] if Tok.type in _DESCRIPTOR_SKIP_TYPES: k += 1 continue if Tok.string in _TYPE_BRACKET_OPENERS: Depth += 1 elif Tok.string in _TYPE_BRACKET_CLOSERS: Depth -= 1 if Depth == 0: break k += 1 if k >= Count or Tokens[k].string != ']': return None Last: int = k - 1 if Tokens[k].start[0] != Line or (Last >= First and Tokens[Last].end[0] != Line): return None # cross-line array prefix: same-line only if Last < First: Groups.append('None') # `[]`: length omitted else: Groups.append(Lines0[Line - 1][Tokens[First].start[1]:Tokens[Last].end[1]]) k += 1 if not Groups: return None ElemStart: int = k ElemEnd: int = _ScanAnnotationTypeEnd(Tokens, k, Count) if ElemEnd <= ElemStart: return None if (Tokens[ElemStart].start[0] != Line or Tokens[ElemEnd - 1].end[0] != Line): return None ElemText: str = Lines0[Line - 1][Tokens[ElemStart].start[1]:Tokens[ElemEnd - 1].end[1]] Result: str = ElemText for GroupText in reversed(Groups): # inner first (right-to-left) Result = f't.CArray[{Result}, {GroupText}]' return ElemEnd, Result # Bare C-style leading array declarator `[N]T` / `[]T` at annotation positions (no # descriptor), desugared to `t.CArray[T, N]` / `t.CArray[T, None]` — same shape as the # const/volatile path and TPV _parse_array_prefix_declarator. _ARRAY_PREFIX_HINT: str = ( '`名字: [长度]元素类型`(长度可省略:`[]元素类型`);' '形参 / 返回值写作 `def f(p: [N]T) -> [M]U`' ) _ARRAY_PREFIX_EXAMPLE: str = ( 'buf: [128]uint8 / HEX_VALS: [256]int / def transform(self, block: [64]uint8)' ) _ARRAY_STMT_BOUNDARY: tuple[int, ...] = ( tokenize.NEWLINE, tokenize.INDENT, tokenize.DEDENT ) def _ArrayPrefixSyntaxError(Detail: str, Lineno: int, Col: int, Snippet: str) -> SyntaxError: """Build the fail-fast error for an array prefix that cannot be desugared.""" return SyntaxError( f"[TPC] 数组前缀注解非法({Detail}):行 {Lineno} 列 {Col},源码片段 {Snippet.strip()!r}。" f"`[N]T` 只在**注解位置**生效(AnnAssign 的 ':' 之后 / 形参 / '->' 之后 / 类字段)," f"期望形态:{_ARRAY_PREFIX_HINT};正确示例:{_ARRAY_PREFIX_EXAMPLE}。" f"(注解位置的数组前缀无法脱糖时一律终止编译,绝不静默产出 List 注解)" ) def _AtAnnotationColon(Tokens: list, ColonIdx: int, Stack: list) -> bool: """':' 是否位于注解分隔位置(AnnAssign / 类字段 / def 形参)——注解位置判据真源。 被 `_InArrayAnnotationPosition`(`[N]T` 数组前缀)与 `_InAttributeAnnotationPosition` (`__attribute__(...)` 描述符)共用:两处若各留一份副本,判据迟早分叉。 dict / slice / lambda 的冒号在此被排除(右值字面量绝不被改写)。 """ TargetIdx: int = _PrevSignificantTokenIndex(Tokens, ColonIdx) if TargetIdx < 0 or Tokens[TargetIdx].type != tokenize.NAME: return False if Stack: Char, OpenIdx = Stack[-1] if Char != '(': return False FnIdx: int = _PrevSignificantTokenIndex(Tokens, OpenIdx) if FnIdx < 0 or Tokens[FnIdx].type != tokenize.NAME: return False KwIdx: int = _PrevSignificantTokenIndex(Tokens, FnIdx) if KwIdx < 0 or Tokens[KwIdx].string != 'def': return False for Tok in Tokens[OpenIdx + 1:ColonIdx]: # a default lambda is not an annotation if Tok.type == tokenize.NAME and Tok.string == 'lambda': return False return True j: int = TargetIdx while True: # walk back over a dotted target chain DotIdx: int = _PrevSignificantTokenIndex(Tokens, j) if DotIdx < 0 or Tokens[DotIdx].string != '.': break HeadIdx: int = _PrevSignificantTokenIndex(Tokens, DotIdx) if HeadIdx < 0 or Tokens[HeadIdx].type != tokenize.NAME: break j = HeadIdx BoundIdx: int = _PrevSignificantTokenIndex(Tokens, j) if BoundIdx < 0: return True Bound = Tokens[BoundIdx] # `export 名字: T` / `extern 名字: T` / `static 名字: T`(变量声明的存储类 # 修饰符,见 _ExpandExportExternVar):修饰符与名字之间已构成语句起始,同样算 # 注解位置。本判据必须在修饰符展开**之前**成立——`[N]T` 数组前缀与 # `__attribute__(...)` 两条路径都先于该展开运行,靠它把 # `export gdt: [7]gdt_entry = []` / `static g: [7]int = []` 正常脱糖成 # `t.CArray[...]` 形态(若等到展开后再插入 ` | t.CExport` / 前置 `t.CStatic | `, # `|` 不在 _TYPE_EXPR_STOP_TOKENS 中,会被并进数组元素类型 → # `t.CArray[gdt_entry | t.CExport, 7]`)。 if Bound.type == tokenize.NAME and Bound.string in _EXPORT_EXTERN_MODIFIERS: return True return Bound.type in _ARRAY_STMT_BOUNDARY or Bound.string == ';' def _InArrayAnnotationPosition(Tokens: list, Index: int, Stack: list) -> bool: """True if the '[' at Index begins an annotation-position type. Stack holds open brackets before Index as (char, open_index). AnnAssign / class field: ':' preceded by a (possibly dotted) target name at a statement boundary. Parameter: ':' inside a def's parameter paren. Return annotation: '->' before '['. Dict / slice / lambda colons are excluded so right-hand `[...]` literals stay put. """ PrevIdx: int = _PrevSignificantTokenIndex(Tokens, Index) if PrevIdx < 0: return False Prev = Tokens[PrevIdx] if Prev.string == '->': return True if Prev.string != ':': return False return _AtAnnotationColon(Tokens, PrevIdx, Stack) def _ApplySameLineEdits(Code: str, Edits: list[tuple[int, int, int, str, str]]) -> str: """Apply (lineno, col_start, col_end, text, orig) edits right-to-left, keeping line endings. Reverse order so multiple edits on one line keep their column offsets valid; the line terminator is split off and written back verbatim. A position-check failure (odd line ending / encoding) leaves the text untouched so the later ast/vast.parse fails loudly (never silently rewrites the wrong span). """ Lines: list[str] = Code.splitlines(keepends=True) for Lineno, ColStart, ColEnd, Text, Orig in sorted( Edits, key=lambda e: (e[0], e[1]), reverse=True): raw: str = Lines[Lineno - 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:] if Body[ColStart:ColEnd] != Orig: # Position check failed: leave untouched so the later ast/vast.parse fails loudly. continue Lines[Lineno - 1] = f'{Body[:ColStart]}{Text}{Body[ColEnd:]}{Term}' return ''.join(Lines) def _ExpandArrayPrefixAnnotations(Code: str) -> str: """Desugar bare `[N]T` array-prefix annotations into `t.CArray[T, N]` (same line). Only fires when '[' sits at an annotation position (see _InArrayAnnotationPosition); right-hand list literals are never touched. A '[' at an annotation position that cannot be desugared (missing element type / cross-line / incomplete) fails loudly (never silently becomes a List annotation). """ if '[' not in Code: return Code try: Tokens: list = list(tokenize.generate_tokens(io.StringIO(Code).readline)) except Exception: return Code n: int = len(Tokens) Lines0: list[str] = Code.splitlines() Edits: list[tuple[int, int, int, str, str]] = [] Stack: list[tuple[str, int]] = [] i: int = 0 while i < n: Tok = Tokens[i] if Tok.type == tokenize.OP: if Tok.string in _TYPE_BRACKET_OPENERS: if Tok.string == '[' and _InArrayAnnotationPosition(Tokens, i, Stack): Desugared = _DesugarArrayPrefix(Tokens, i, n, Code) if Desugared is None: raise _ArrayPrefixSyntaxError( '无法脱糖(缺元素类型 / 跨行 / 不完整)', Tok.start[0], Tok.start[1], Lines0[Tok.start[0] - 1]) End, ArrayText = Desugared Line0: str = Lines0[Tok.start[0] - 1] Edits.append((Tok.start[0], Tok.start[1], Tokens[End - 1].end[1], ArrayText, Line0[Tok.start[1]:Tokens[End - 1].end[1]])) i = End continue Stack.append((Tok.string, i)) elif Tok.string in _TYPE_BRACKET_CLOSERS: if Stack: Stack.pop() i += 1 if not Edits: return Code return _ApplySameLineEdits(Code, Edits) # --------------------------------------------------------------------------- # 变量声明的存储类修饰符 export / extern / static(语句级软关键字)——与 TPV # includes/ast/parser.vp **同一标记、同一消费路径**: # export 名字: T [= 初值] → 名字: T | t.CExport [= 初值] # (本模块**定义**:定义端裸名,不加模块 SHA1 头;内联汇编 / C 符号对接必需。 # 无初值时按既有零初始化全局定义处理,如 `export tss_buf: [104]uint8`。) # extern 名字: T → 名字: T | t.State # (**仅声明**:t.State = t.CExtern | t.CExport ⇒ 定义端 `@裸名 = external global T`。 # extern 是外部定义,**不允许**初值 ⇒ 带 `= 初值` 一律终止编译。) # static 名字: T [= 初值] → 名字: t.CStatic | T [= 初值] # (存储类标记**前置**在类型联合最左,与 TPV `_ParseStaticModified` 的 # `pending_static` 通道同源——`static 名字: T` → `名字: t.CStatic | T`;允许初值。 # 产出与手写 `名字: t.CStatic | T` / 装饰器 `@t.CStatic` 逐字段同形。 # 旧注解位置写法 `名字: static T` 已删除,见 _StaticPositionSyntaxError。) # 与函数形态(`export def f()` / `export f()` / `extern def f()` / `static def f()`, # vast 既有处理)由第三个 token 区分:`:` 是变量,`(` / `[` 是函数 ⇒ 函数语义逐字不变。 # `export` / `extern` / `static` 不在任何 .vp 源码中作标识符(仅本语法与注释使用)。 # --------------------------------------------------------------------------- _EXPORT_EXTERN_MODIFIERS: frozenset[str] = frozenset({'export', 'extern', 'static'}) _EXPORT_EXTERN_HINT: str = ( '`export 名字: 类型 [= 初值]`(裸名导出,等价 `名字: 类型 | t.CExport`);' '`extern 名字: 类型`(仅声明,等价 `名字: 类型 | t.State`,**不允许初值**);' '`static 名字: 类型 [= 初值]`(等价 `名字: t.CStatic | 类型`)' ) _EXPORT_EXTERN_EXAMPLE: str = ( 'export gdt: [7]gdt_entry = [] / export tss_buf: [104]uint8 / extern a: int / ' 'static memory_regions: memory_region* = None' ) def _ExportExternSyntaxError(Detail: str, Lineno: int, Col: int, Snippet: str) -> SyntaxError: """构造 export/extern 变量声明非法形态的 fail-fast 报文。""" return SyntaxError( f"[TPC] export/extern 变量声明非法({Detail}):行 {Lineno} 列 {Col}," f"源码片段 {Snippet.strip()!r}。" f"期望形态:{_EXPORT_EXTERN_HINT};正确示例:{_EXPORT_EXTERN_EXAMPLE}。" f"(非法形态一律终止编译,绝不静默改写)" ) def _ExpandExportExternVar(Code: str) -> str: """把 `export 名字: T [= 初值]` / `extern 名字: T` / `static 名字: T [= 初值]` 展开为带存储类标记的注解。 与 TPV 同口径:export → 类型**右侧追加** `| t.CExport`;extern → 追加 `| t.State` (extern 带初值 ⇒ **响亮失败** SyntaxError);static → 类型**左侧前置** `t.CStatic | ` (与 TransPyV `_ParseStaticModified` / `pending_static` 通道同源——`static 名字: T` → `名字: t.CStatic | T`,亦与手写 `名字: t.CStatic | T` / `@t.CStatic` 逐字段同形; static **允许**初值)。 - 只在"语句起始(可缩进)+ export/extern/static + 标识符 + `:`"形态触发;函数形态 (`export def f()` / `export f()` / `extern def f()` / `static def f()`)第三个 token 不是 `:` ⇒ 不命中,vast 的既有函数处理逐字不变。 - **必须排在 `_ExpandArrayPrefixAnnotations` 之后**:此时注解已是规范形态 (`t.CArray[T, N]` / `t.CConst | ...`),插入点由 `_ScanAnnotationTypeEnd` 确定;`|` 不在 `_TYPE_EXPR_STOP_TOKENS` 中,若在数组前缀展开前插入会被 并进数组元素类型(`t.CArray[gdt_entry | t.CExport, 7]`)。数组前缀之所以 仍能脱糖,靠的是 `_AtAnnotationColon` 已把 export/extern/static 认作语句边界。 - 同行编辑(删修饰符 + 插/前置标记),不增删换行 ⇒ 行号映射保持有效。 """ if 'export' not in Code and 'extern' not in Code and 'static' not in Code: return Code try: Tokens: list = list(tokenize.generate_tokens(io.StringIO(Code).readline)) except Exception: return Code n: int = len(Tokens) Lines0: list[str] = Code.splitlines() Edits: list[tuple[int, int, int, str, str]] = [] i: int = 0 while i < n: Tok = Tokens[i] if Tok.type == tokenize.NAME and Tok.string in _EXPORT_EXTERN_MODIFIERS: NameIdx: int = _NextSignificantTokenIndex(Tokens, i) if NameIdx >= 0 and Tokens[NameIdx].type == tokenize.NAME: ColonIdx: int = _NextSignificantTokenIndex(Tokens, NameIdx) if ColonIdx >= 0 and Tokens[ColonIdx].string == ':': NameTok = Tokens[NameIdx] TypeStart: int = _NextSignificantTokenIndex(Tokens, ColonIdx) Line: int = Tok.start[0] Snippet: str = Lines0[Line - 1] if TypeStart < 0 or Tokens[TypeStart].start[0] != Line: raise _ExportExternSyntaxError( '缺少类型注解', Line, Tok.start[1] + 1, Snippet) TypeEnd: int = _ScanAnnotationTypeEnd(Tokens, TypeStart, n) if TypeEnd <= TypeStart: raise _ExportExternSyntaxError( '缺少类型注解', Line, Tok.start[1] + 1, Snippet) IsExtern: bool = Tok.string == 'extern' IsStatic: bool = Tok.string == 'static' AfterIdx: int = _NextSignificantTokenIndex(Tokens, TypeEnd - 1) if (IsExtern and AfterIdx >= 0 and Tokens[AfterIdx].string == '='): raise _ExportExternSyntaxError( 'extern 变量声明不能带初始值(符号定义在外部)', Line, Tok.start[1] + 1, Snippet) # 编辑 1:删掉修饰符及其后的空白(保留缩进;只删 `export` 会 # 在顶格行留下前导空格 ⇒ IndentationError)。 DelStart: int = Tok.start[1] DelEnd: int = NameTok.start[1] DelOrig: str = Snippet[DelStart:DelEnd] Edits.append((Line, DelStart, DelEnd, '', DelOrig)) if IsStatic: # 编辑 2(static):存储类标记**前置**在类型左侧(联合最左)。 # 与 TransPyV `_ParseStaticModified` / `pending_static` 通道 # 逐字同形:`static 名字: T` → `名字: t.CStatic | T`,产出与 # 手写 `名字: t.CStatic | T` / `@t.CStatic` 逐字段同形。 InsAt: tuple[int, int] = Tokens[TypeStart].start Edits.append((InsAt[0], InsAt[1], InsAt[1], 't.CStatic | ', '')) else: # 编辑 2(export/extern):存储类标记**追加**在注解类型终点 # (右侧):export → `| t.CExport`;extern → `| t.State`。 InsAt = Tokens[TypeEnd - 1].end Marker: str = ' | t.State' if IsExtern else ' | t.CExport' Edits.append((InsAt[0], InsAt[1], InsAt[1], Marker, '')) i = TypeEnd continue i += 1 if not Edits: return Code return _ApplySameLineEdits(Code, Edits) # --------------------------------------------------------------------------- # 注解位置的 C 风格 `__attribute__(...)` 描述符(与 TPV # includes/ast/parser.vp 的 _IsMagicAttributeCall / _MakeTAttrMarker 同源书写法, # TPC 侧在本层文本脱糖;与 const/volatile、`[N]T` 数组前缀同属一套语法糖): # V: __attribute__(section(".PRVERSN")) []char = "0.0.3-dev.428" # → V: t.attr.section(".PRVERSN") | t.CArray[char, None] = "0.0.3-dev.428" # x: __attribute__(aligned(16)) int = 0 # → x: t.attr.aligned(16) | int = 0 # 脱糖目标与手写 `T | t.attr.X(...)`(HandlesAssign 的模块级全局 t.attr.* 分支 # 所认的形态)逐字同形,故下游 AnnotationLoader / HandlesAssign / # DeclarationGenerator 零新增判据。 # `t.CArray[T, None]` = TPC 的"长度自动推导数组"(HandlesAssign.py:1606), # 恰是 `[]char` 的语义。 # --------------------------------------------------------------------------- _ATTRIBUTE_CALLEE: str = '__attribute__' _ATTR_MARKER_PREFIX: str = 't.attr.' _ATTRIBUTE_DESCRIPTOR_HINT: str = ( '`名字: __attribute__(section(".text")) 类型`(形参 / 返回值同样成立,' '可与 const/volatile 混写,其后可接 C 风格数组前缀 `[N]T` / `[]T`)' ) def _AttributeSyntaxError(Detail: str, Lineno: int, Col: int, Snippet: str) -> SyntaxError: """构造注解位置 `__attribute__(...)` 非法形态的 fail-fast 报文。""" return SyntaxError( f"[TPC] __attribute__ 描述符非法({Detail}):行 {Lineno} 列 {Col}," f"源码片段 {Snippet.strip()!r}。" f"`__attribute__(...)` 只在**注解位置**生效(AnnAssign 的 ':' 之后 / 形参 / " f"'->' 之后 / 类字段),期望形态:{_ATTRIBUTE_DESCRIPTOR_HINT};" f"正确示例:`V_TPV_VERSION: __attribute__(section('.PRVERSN')) []char = \"x\"` / " f"`g: __attribute__(aligned(16)) int = 0`。" f"(注解位置的 __attribute__ 无法脱糖时一律终止编译,绝不静默留作普通调用)" ) def _MatchOpenBracket(Tokens: list, CloseIdx: int) -> int: """回溯找与 CloseIdx 处闭括号配对的 `'[' / '(' / '{'` 下标(-1=未配对)。""" Depth: int = 0 j: int = CloseIdx while j >= 0: Tok = Tokens[j] if Tok.type == tokenize.OP: if Tok.string in _TYPE_BRACKET_CLOSERS: Depth += 1 elif Tok.string in _TYPE_BRACKET_OPENERS: Depth -= 1 if Depth == 0: return j j -= 1 return -1 def _AttributeMarkerText(ArgText: str) -> str: """`__attribute__` 单个实参文本 → 描述符文本(与 TPV _MakeTAttrMarker 同口径)。 - 裸名 `always_inline` → `t.attr.always_inline` - 调用 `section(".text")` → `t.attr.section(".text")` - 其余(已含点的 `t.attr.X` / `c.Attribute(...)`)→ 原样保留(允许混写) """ T: str = (ArgText or '').strip() if not T: return T if _RE_ATTR_BARE_IDENT.fullmatch(T) is not None: return f'{_ATTR_MARKER_PREFIX}{T}' if _RE_ATTR_CALL_IDENT.match(T) is not None: return f'{_ATTR_MARKER_PREFIX}{T}' return T def _InAttributeAnnotationPosition(Tokens: list, Index: int, Stack: list) -> bool: """`Index` 处的 `__attribute__` 是否处于注解位置的描述符序列中。 与 TPV `_IsMagicAttributeCall` + `_parse_type_ann` 的描述符循环同口径:允许 `x: __attribute__(...) T`、`def f(p: __attribute__(...) T)`、 `-> __attribute__(...) T`,以及 `const` / `volatile` / 前序 `__attribute__(...)` 之后(顺序自由、可混写)。表达式位置的普通调用 (`y = __attribute__(x)` / `f(__attribute__(x))`)不命中 ⇒ 原样保留。 """ j: int = _PrevSignificantTokenIndex(Tokens, Index) Guard: int = 0 while j >= 0 and Guard < 64: Tok = Tokens[j] if Tok.string == '|': j = _PrevSignificantTokenIndex(Tokens, j) Guard += 1 continue if Tok.type == tokenize.NAME and Tok.string in _TYPE_DESCRIPTOR_MARKERS: j = _PrevSignificantTokenIndex(Tokens, j) Guard += 1 continue if Tok.type == tokenize.OP and Tok.string == ')': # 前序 `__attribute__(...)`:回溯到配对的 '(' 并确认被调用者就是它 OpenIdx: int = _MatchOpenBracket(Tokens, j) if OpenIdx < 0: return False CalleeIdx: int = _PrevSignificantTokenIndex(Tokens, OpenIdx) if CalleeIdx < 0 or Tokens[CalleeIdx].string != _ATTRIBUTE_CALLEE: return False j = _PrevSignificantTokenIndex(Tokens, CalleeIdx) Guard += 1 continue break if j < 0: return False Tok = Tokens[j] if Tok.string == '->': return True if Tok.string != ':': return False return _AtAnnotationColon(Tokens, j, Stack) def _ExpandAttributeDescriptors(Code: str) -> str: """把注解位置的 `__attribute__(...)` 脱糖为 `t.attr.<名>` 描述符(同行替换)。 规则与示例见上方常量块注释。紧跟的 C 风格前置数组前缀 `[N]T` / `[]T` 在**同一次 编辑**里一并脱糖(`_DesugarArrayPrefix`,已支持空长度 ⇒ `None`)——否则 `[]char` 会被 vast 当成 `__attribute__(...)` 调用的下标,报出与根因脱节的 `unexpected token OP(']')`(本轮实测现象)。 跨行 / 未闭合 / 实参为空 / 后随非类型且非数组前缀 ⇒ 响亮失败。 非注解位置与 `@__attribute__(...)`(装饰器通道,另有 desugar)不受影响。 """ if _ATTRIBUTE_CALLEE not in Code: return Code try: Tokens: list = list(tokenize.generate_tokens(io.StringIO(Code).readline)) except Exception: return Code n: int = len(Tokens) Lines0: list[str] = Code.splitlines() Edits: list[tuple[int, int, int, str, str]] = [] Stack: list = [] i: int = 0 while i < n: Tok = Tokens[i] if Tok.type == tokenize.OP: if Tok.string in _TYPE_BRACKET_OPENERS: Stack.append((Tok.string, i)) elif Tok.string in _TYPE_BRACKET_CLOSERS: if Stack: Stack.pop() i += 1 continue if Tok.type != tokenize.NAME or Tok.string != _ATTRIBUTE_CALLEE \ or not _InAttributeAnnotationPosition(Tokens, i, Stack): i += 1 continue LineNo: int = Tok.start[0] Line0: str = Lines0[LineNo - 1] if 0 <= LineNo - 1 < len(Lines0) else '' OpenIdx: int = i + 1 while OpenIdx < n and Tokens[OpenIdx].type in _DESCRIPTOR_SKIP_TYPES: OpenIdx += 1 if OpenIdx >= n or Tokens[OpenIdx].string != '(': raise _AttributeSyntaxError('`__attribute__` 后缺实参列表 `(...)`', Tok.start[0], Tok.start[1], Line0) Depth: int = 0 k: int = OpenIdx while k < n: T2 = Tokens[k] if T2.type == tokenize.OP: if T2.string in _TYPE_BRACKET_OPENERS: Depth += 1 elif T2.string in _TYPE_BRACKET_CLOSERS: Depth -= 1 if Depth == 0: break k += 1 if k >= n or Depth != 0: raise _AttributeSyntaxError('`__attribute__(...)` 括号未闭合', Tok.start[0], Tok.start[1], Line0) for q in range(OpenIdx, k + 1): if Tokens[q].type not in _DESCRIPTOR_SKIP_TYPES and Tokens[q].start[0] != LineNo: raise _AttributeSyntaxError('`__attribute__(...)` 跨行(本层只做同行替换)', Tok.start[0], Tok.start[1], Line0) # 顶层逗号切分实参(跳过注释/NL;嵌套括号内的逗号不算) ArgSpans: list[tuple[int, int]] = [] ArgFirst: int = -1 Nest: int = 0 m: int = OpenIdx + 1 while m < k: T3 = Tokens[m] if T3.type in _DESCRIPTOR_SKIP_TYPES: m += 1 continue if T3.type == tokenize.OP and T3.string in _TYPE_BRACKET_OPENERS: Nest += 1 elif T3.type == tokenize.OP and T3.string in _TYPE_BRACKET_CLOSERS: Nest -= 1 elif T3.type == tokenize.OP and T3.string == ',' and Nest == 0: if ArgFirst >= 0: ArgSpans.append((ArgFirst, m - 1)) ArgFirst = -1 m += 1 continue if ArgFirst < 0: ArgFirst = m m += 1 if ArgFirst >= 0: ArgSpans.append((ArgFirst, k - 1)) Markers: list[str] = [] for (ArgA, ArgB) in ArgSpans: ArgText: str = Line0[Tokens[ArgA].start[1]:Tokens[ArgB].end[1]] Marker: str = _AttributeMarkerText(ArgText) if Marker: Markers.append(Marker) if not Markers: raise _AttributeSyntaxError('`__attribute__(...)` 实参为空', Tok.start[0], Tok.start[1], Line0) Nxt: int = k + 1 while Nxt < n and Tokens[Nxt].type in _DESCRIPTOR_SKIP_TYPES: Nxt += 1 LastCol: int = Tokens[k].end[1] if Nxt < n and Tokens[Nxt].type == tokenize.OP and Tokens[Nxt].string == '[': Desugared = _DesugarArrayPrefix(Tokens, Nxt, n, Code) if Desugared is None: raise _AttributeSyntaxError( '`__attribute__(...)` 后的数组前缀无法脱糖(缺元素类型 / 跨行 / 不完整)', Tok.start[0], Tok.start[1], Line0) End: int = Desugared[0] ArrayText: str = Desugared[1] LastCol = Tokens[End - 1].end[1] Text: str = ' | '.join(Markers) + f' | {ArrayText}' i = End elif Nxt < n and Tokens[Nxt].type == tokenize.NAME: Text = ' | '.join(Markers) + ' |' i = k + 1 else: raise _AttributeSyntaxError('`__attribute__(...)` 后缺少类型', Tok.start[0], Tok.start[1], Line0) Edits.append((LineNo, Tok.start[1], LastCol, Text, Line0[Tok.start[1]:LastCol])) continue if not Edits: return Code return _ApplySameLineEdits(Code, Edits) def _ExpandTypeDescriptors(Code: str) -> str: """把 `const T` / `volatile T`(含组合、任意顺序)展开为 `t.CConst | T` 等形态(行号不变)。 - 词法级切分(`tokenize`):注释/字符串内容天然不参与,不会被误改写。 - 只在"注解位置"(前一个 token 是 `:` 且再前一个是 Name,或前一个 token 是 `->`) 识别描述符串;非类型位置原样保留为普通标识符(软关键字边界)。 - **`static` 不再属于本展开器**(已迁出注解位置):注解位置出现 `static` (如 `memory_regions: static memory_region* = None`)一律**响亮失败** (_StaticPositionSyntaxError);前置形式 `static 名字: 类型 = 值` 由 _ExpandExportExternVar 处理,`static def` 由 vast.Parser._ParseStaticDef 处理。 入口的 `'static' not in Code` 判据保留,仅为让上述旧位置能被扫描到并报错。 - 只做**同行**替换(不增删换行 ⇒ 行号映射保持有效,与 fn/enum/define/typedef 同约定)。 - 词法失败(未闭合字符串等)时原样返回:该输入在后续 ast.parse 处必然响亮失败, 本层不新增第二套错误通道(不静默丢描述符:词法都没过,描述符根本没被识别为描述符)。 - `const [N]T` (C-style leading array prefix) desugars to `t.CConst | t.CArray[T, N]`; see _DesugarArrayPrefix. """ if 'const' not in Code and 'volatile' not in Code and 'static' not in Code: return Code try: Tokens: list = list(tokenize.generate_tokens(io.StringIO(Code).readline)) except Exception: return Code n: int = len(Tokens) # (行号, 起列, 止列, 替换文本, 原文片段) Edits: list[tuple[int, int, int, str, str]] = [] i: int = 0 while i < n: Tok = Tokens[i] # 旧的注解位置 static(`名字: static 类型`):已迁移为**前置**形式,此处 # 响亮失败(不脱糖、不静默)。注意 `static 名字: 类型` / `static def 名字` # 的 static 位于**语句起始**、不在注解位置,故不会被本判据命中(交由 # _ExpandExportExternVar / vast.Parser._ParseStaticDef 处理)。 if (Tok.type == tokenize.NAME and Tok.string == 'static' and _InStaticOldAnnotationPosition(Tokens, i)): raise _StaticPositionSyntaxError( Tok.start[0], Tok.start[1], Code.splitlines()[Tok.start[0] - 1]) if Tok.type == tokenize.NAME and Tok.string in _TYPE_DESCRIPTOR_MARKERS: Run: list = [] j: int = i while (j < n and Tokens[j].type == tokenize.NAME and Tokens[j].string in _TYPE_DESCRIPTOR_MARKERS): Run.append(Tokens[j]) j += 1 PrevIdx: int = _PrevSignificantTokenIndex(Tokens, i) Prev = Tokens[PrevIdx] if PrevIdx >= 0 else None Next = Tokens[j] if j < n else None InAnnotationPosition: bool = False if Prev is not None: if Prev.string == '->': InAnnotationPosition = True elif Prev.string == ':': BeforePrevIdx: int = _PrevSignificantTokenIndex(Tokens, PrevIdx) if BeforePrevIdx >= 0 and Tokens[BeforePrevIdx].type == tokenize.NAME: InAnnotationPosition = True if InAnnotationPosition: if Run[0].start[0] != Run[-1].end[0]: # 描述符串跨行:本层只支持同行(不增删换行的前提),响亮失败不静默丢 First = Run[0] raise _TypeDescriptorSyntaxError( '描述符串跨行', First.start[0], First.start[1], Code.splitlines()[First.start[0] - 1]) if Next is not None and Next.type == tokenize.OP and Next.string == '[': # `const [N]T` / `volatile [N]T` / `const volatile [N]T`: TPC vast has # no `[N]T`; desugar the array prefix together with the descriptor into # `t.CConst | t.CArray[T, N]` (same shape as TPV). Desugared = _DesugarArrayPrefix(Tokens, j, n, Code) if Desugared is not None: End, ArrayText = Desugared First = Run[0] Line0: str = Code.splitlines()[First.start[0] - 1] Text: str = ' | '.join( f't.{_TYPE_DESCRIPTOR_MARKERS[m.string]}' for m in Run ) + f' | {ArrayText}' Edits.append((First.start[0], First.start[1], Tokens[End - 1].end[1], Text, Line0[First.start[1]:Tokens[End - 1].end[1]])) i = End continue # Not a valid array prefix: fall through to the fail-fast below. if Next is not None and Next.type == tokenize.NAME: Text: str = ' | '.join( f't.{_TYPE_DESCRIPTOR_MARKERS[m.string]}' for m in Run) + ' |' Edits.append((Run[0].start[0], Run[0].start[1], Run[-1].end[1], Text, Code.splitlines()[Run[0].start[0] - 1] [Run[0].start[1]:Run[-1].end[1]])) else: First = Run[0] NextText: str = '(行尾)' if Next is None else repr(Next.string) Detail: str = ( f'描述符后缺少类型,后随 {NextText}' if (Next is None or Next.type in (tokenize.NEWLINE, tokenize.ENDMARKER) or Next.string in ('=', ';')) else f'描述符后随非类型 {NextText}' ) raise _TypeDescriptorSyntaxError( Detail, First.start[0], First.start[1], Code.splitlines()[First.start[0] - 1]) else: # 非注解位置:仅"整条语句恰好是一个描述符串"(裸用)才响亮失败, # 其余(t.attr.const() / const = 5 / x = const …)按普通标识符原样保留。 AtStmtStart: bool = Prev is None or Prev.type in ( tokenize.NEWLINE, tokenize.INDENT, tokenize.DEDENT) AtStmtEnd: bool = Next is None or Next.type in ( tokenize.NEWLINE, tokenize.ENDMARKER) or Next.string == ';' if AtStmtStart and AtStmtEnd: First = Run[0] raise _TypeDescriptorSyntaxError( '裸用描述符(整条语句只有描述符,缺类型)', First.start[0], First.start[1], Code.splitlines()[First.start[0] - 1]) i = j continue i += 1 if not Edits: return Code return _ApplySameLineEdits(Code, Edits) # --------------------------------------------------------------------------- # 枚举存储宽度("最宽成员类型")——两侧(TPV ClassGenEnum / TPC HandlesClassDef) # 的唯一同口径规则源,对照表见 Debugger/enum-semantics.md。 # --------------------------------------------------------------------------- # 规则: # 无注解 / `t.State` 标记注解 → "无整数成员"口径(枚举宽度回落默认 32) # 整数类型注解 → 按该类型位宽参与"取最宽"(见 EnumClassIntWidth) # `bool` / `t.CBool` → **8**(枚举侧口径):枚举存储必须是可按字节寻址的 # 整数类型,`i1` 不能作为独立枚举宽度;本口径只作用于"枚举成员注解" # 这一处,**不改** TPC 全局 `bool`→`i8`(LLVMTypeMapper.py:174)与 # TPV 全局 `bool`→`i1`(TypeGenResolve.vp:2201-2203)的各自的全局语义。 # TPC 侧 `bool` 经 BuiltinTypeMap → `t.CBool.Size == 8` 天然成立 # (t.CBool 同 8),故本文件不另加硬编码分支。 # 指针注解 → 在成员注册处明确报错(本文件与 ClassGenEnum 的 ptr 拦截) # 其余(无法解析 / 非整数:float/void/struct…)→ 视为"无整数成员" # (不引入新的硬失败;TPV 侧更严,对"有注解但解析失败"是 fatal,差异已记录) _ENUM_DEFAULT_INT_WIDTH: int = 32 # State 标记名(与 TPV ClassGenEnum._is_state_annotation 同集合):该注解只是 # `t.CEnum` 成员的存在性标记,不携带宽度语义。 _ENUM_STATE_MARKER_NAMES: frozenset[str] = frozenset({'State'}) def EnumIntWidth(Info: object | None, Default: int = _ENUM_DEFAULT_INT_WIDTH) -> int: """取枚举条目(枚举类或枚举成员)已登记的整数位宽;未登记 → Default。 宽度在枚举定义处按"最宽成员类型"推导(EnumClassIntWidth)并登记到条目上; 跨模块场景由**两条读侧路径**各自补齐同一属性: - `.vpi`/`.pyi` 声明(HandlesImports._EmitExternalClassDeclLlvm)→ 从成员 注解重新推导(`.vpi` 保留 `Name: T` 注解形态); - 共享符号表(SymbolExtractor._extract_class → SymbolTable pickle, temp/_shared_sym.json)→ 同样在提取期推导后随 `_sm._extra` 序列化。 两者都不带宽度信息时(旧产物 / 老 pickle 缺 `enum_width` 字段)→ Default(32), 与"改前恒 i32"逐字一致 ⇒ 向后兼容、不崩。 """ if Info is None: return Default Getter = getattr(Info, 'get', None) if Getter is None: return Default try: Width = Getter('enum_width') except Exception: return Default if isinstance(Width, int) and Width > 0: return Width return Default def IsEnumStateMarker(Annotation: object | None) -> bool: """注解节点是否为 `t.State` / `State`(枚举默认成员的存在性标记)。""" if Annotation is None: return False if isinstance(Annotation, ast.Attribute): return Annotation.attr in _ENUM_STATE_MARKER_NAMES if isinstance(Annotation, ast.Name): return Annotation.id in _ENUM_STATE_MARKER_NAMES return False def _EnumAnnotationSimpleName(Annotation: object | None) -> str | None: """注解节点的"顶名":`t.CInt64T` → 'CInt64T'、`int64` → 'int64';其余 → None。""" if isinstance(Annotation, ast.Attribute): return Annotation.attr if isinstance(Annotation, ast.Name): return Annotation.id return None def _IntSizeOf(BaseType: object | None) -> int | None: """CType 实例 → 整数位宽(bit);非整数(浮点/void/结构体/数组)→ None。""" Size = getattr(BaseType, 'Size', None) IsSigned = getattr(BaseType, 'IsSigned', None) if not isinstance(Size, int) or Size <= 0 or IsSigned is None: return None return int(Size) def _EnumTypeStrToClass(Text: str | None) -> type | None: """builtins typedef 值(`t.CInt64T` / `CInt64T`)→ t 模块类型类;其余 → None。 供"无 SymbolTable 的声明端"把 int8/int64/… 归一到 t 类型类取位宽。 """ if not Text: return None Name: str = Text.strip() if Name.startswith('t.') or Name.startswith('c.'): Name = Name[2:] if not Name or not Name.isidentifier(): return None Cls = getattr(t, Name, None) if isinstance(Cls, type) and issubclass(Cls, t.CType) and Cls is not t.CType: return Cls return None def EnumMemberAnnotationIntWidth(Annotation: object | None, SymbolTable: object | None = None) -> int | None: """枚举成员注解 → 整数位宽(bit);None = "默认口径"(走 _ENUM_DEFAULT_INT_WIDTH)。 `t.State` 标记或无注解 → None;整数类型(`int`/`char`/`C*` 家族 / builtins 与库 typedef)→ 其 `Size`;无法解析或非整数(float/void/struct…)→ None (默认 32,与改前行为一致,不引入新的硬失败)。指针形态由调用方在解析前 拦截(HandlesClassDef._EnumMemberAnnotationIsPtr / TPV 的 _enum_member_annotation_is_ptr),此处不重复判定。 解析顺序(同一规则源,供"有 SymbolTable 的定义端"与"无 SymbolTable 的 声明端(DeclarationGenerator)"共用): 1) t 模块类型类名(CInt64T/CUInt8T/CChar/CBool/CInt…)与其 C 速记名 2) includes/builtins.vp 的 CTypedef(int8/int16/…/uint64/size_t,仅扫描) 3) 符号表 typedef(需注入 SymbolTable;覆盖库内自定义 typedef) 4) AST 解析器兜底(联合/下标等复合形态,需注入 SymbolTable) """ if Annotation is None or IsEnumStateMarker(Annotation): return None Name = _EnumAnnotationSimpleName(Annotation) if Name is not None: Cls = getattr(t, Name, None) if isinstance(Cls, type) and issubclass(Cls, t.CType) and Cls is not t.CType: try: Width = _IntSizeOf(Cls()) except Exception: Width = None if Width is not None: return Width from lib.core.Handles.HandlesBase import BuiltinTypeMap BuiltinEntry = BuiltinTypeMap.Get(Name) if BuiltinEntry is not None and BuiltinEntry[1] == 0: Width3 = _IntSizeOf(BuiltinEntry[0]()) if Width3 is not None: return Width3 from lib.core.TypeAnnotationResolver import TypeAnnotationResolver try: BVal: str | None = TypeAnnotationResolver.builtins_typedef_value(Name) except Exception: BVal = None if BVal: BCls = _EnumTypeStrToClass(BVal) if BCls is not None: Width4 = _IntSizeOf(BCls()) if Width4 is not None: return Width4 try: BInfo = TypeAnnotationResolver._lookup_builtins_typedef(Name, SymbolTable) except Exception: BInfo = None if BInfo is not None and getattr(BInfo, 'PtrCount', 0) == 0: Width2 = _IntSizeOf(getattr(BInfo, 'BaseType', None)) if Width2 is not None: return Width2 if SymbolTable is None: return None if SymbolTable is None: return None # 局部导入:SymbolUtils 被 HandlesBase 导入,模块级引入 CTypeInfo 会成环。 from lib.core.Handles.HandlesBase import CTypeInfo try: Info = CTypeInfo.FromNode(Annotation, SymbolTable) except Exception: return None if Info is None or getattr(Info, 'PtrCount', 0) > 0: return None return _IntSizeOf(getattr(Info, 'BaseType', None)) def EnumClassIntWidth(Body: list | None, SymbolTable: object | None = None, Default: int = _ENUM_DEFAULT_INT_WIDTH) -> int: """枚举存储宽度 = **最宽成员类型**(bit);无"可解析为整数的成员" → Default(32)。 与 TPV ClassGenEnum._translate_enum_def 的 base_ty 同口径(`base_ty` 从 i32 起步,被任何 `get_llvm_type_bits > 0` 的成员替换 ⇒ 等价于"成员位宽最大值, 无成员时 i32")。**不是** `max(默认 32, 成员位宽)`:该写法会把 `A: uint8` 单成员的枚举钉在 i32,与 TPV 的 i8 分裂(本轮已统一为前者)。 调用点(同一规则源,禁止各写一套): - 定义端:HandlesClassDef._RegisterEnumMembers → _EnumClassIntWidth - 跨模块读取端:HandlesImports._EmitExternalClassDeclLlvm(.vpi/.pyi 读侧重推导) - 跨模块符号表构建:SymbolExtractor._extract_class(→ SymbolTable → pickle) - 声明端(无 SymbolTable):DeclarationGenerator 的 `@__config_*` 全局宽度 """ Width: int = 0 for item in Body or (): if not isinstance(item, ast.AnnAssign): continue if not isinstance(item.target, ast.Name): continue MemberWidth: int | None = EnumMemberAnnotationIntWidth( item.annotation, SymbolTable) if MemberWidth is not None and MemberWidth > Width: Width = MemberWidth return Width if Width > 0 else Default # --------------------------------------------------------------------------- # 枚举成员"可见名 vs 登记名"兜底判据——唯一规则源 # (TPC HandlesClassDef._RegisterEnumMembers / TPV # ClassGenEnum._translate_enum_def 在**注册处**共用同一口径) # # 背景:`enum` 成员行一旦出现"注册器不认的写法",成员会被**静默丢弃** # (ASTKind 71→3、OpKind 32→2;历史事故见 Debugger/enum-semantics.md §15、 # rawptr-migration-and-fastfail.md §27)。这是同族问题**第三次**发生,故不再 # 逐个白名单化"已知可展开形态",而在注册处比对 # **源码里可见的成员名字数** 与 **实际登记成员数**:不一致即 fail-fast。 # # 取全口径(为什么能兜住未知形态):枚举成员必定是**有名字的**,且成员名必定 # 落在成员语句的三类"名字位置"之一——Assign 的 targets、AnnAssign 的 target、 # Expr 语句的表达式(裸名 / 逗号组 `A, B, C` → Tuple/List)。本函数把这三类位置 # 里的标识符**全部**收进来(Tuple/List 逐项、Starred 解引用递归),因此任何 # 注册器漏掉的成员写法(`A, B = 1`、`A, B: int`、单名尾随逗号 `A,`,以及未来 # 出现的新形态)都会表现为"可见名数 > 登记数" ⇒ 一律 fail-fast,不再静默。 # 不含成员名的语句(docstring / pass / 方法定义 / 嵌套类)计 0,不误报。 # --------------------------------------------------------------------------- def _EnumMemberNameNodes(Node: object) -> list[object]: """成员名位置上的名字节点清单(Tuple/List 逐项、Starred 解引用)。""" if Node is None: return [] if isinstance(Node, ast.Name): return [Node] if isinstance(Node, (ast.Tuple, ast.List)): Out: list[object] = [] for Elt in (getattr(Node, 'elts', None) or []): Out.extend(_EnumMemberNameNodes(Elt)) return Out if isinstance(Node, ast.Starred): return _EnumMemberNameNodes(getattr(Node, 'value', None)) return [] def EnumBodyMemberNames(Body: list | None) -> list[tuple[str, object]]: """枚举类体内"源码可见的成员名"清单 `[(名字, 语句节点), …]`(按出现顺序)。 与 TPV `ClassGenEnum._enum_body_visible_names` / `_enum_target_visible_names` 同口径(同一判据的两侧实现)。调用点(注册处,登记完成后立即比对): - TPC:HandlesClassDef._RegisterEnumMembers - TPV:ClassGenEnum._translate_enum_def """ Names: list[tuple[str, object]] = [] for Stmt in (Body or ()): if isinstance(Stmt, ast.Assign): for Tgt in (getattr(Stmt, 'targets', None) or []): for Nd in _EnumMemberNameNodes(Tgt): Names.append((Nd.id, Stmt)) elif isinstance(Stmt, ast.AnnAssign): for Nd in _EnumMemberNameNodes(getattr(Stmt, 'target', None)): Names.append((Nd.id, Stmt)) elif isinstance(Stmt, ast.Expr): for Nd in _EnumMemberNameNodes(getattr(Stmt, 'value', None)): Names.append((Nd.id, Stmt)) return Names def EnumMemberCountMismatchError(ClassName: str, Visible: list[tuple[str, object]], RegisteredNames: list[str], Loc: str = '', LineOffset: int = 0) -> str: """"可见成员名数 ≠ 登记成员数"的 fail-fast 报文(含全部要求要素)。 要素:枚举名 + 可数(可见/登记/漏掉)+ 未识别的成员写法(首个漏掉的成员名 + 其语句形态与行号)+ 期望形态 + 修法 + 站点(文件:行:列 + 源码片段)。 `LineOffset` = `_inject_auto_imports` 注入行数(Phase2Translator 计算并挂到 translator): 成员语句行号须按它还原成磁盘真实行号(0/缺省时不改,逐字不变)。 """ Rest: list[str] = list(RegisteredNames) Missing: list[tuple[str, object]] = [] for Nm, Nd in Visible: if Nm in Rest: Rest.remove(Nm) else: Missing.append((Nm, Nd)) if Missing: BadName, BadNode = Missing[0] Shape: str = type(BadNode).__name__ BadLine: int = int(getattr(BadNode, 'lineno', 0) or 0) # 注入行偏移还原:AST 行号含 _inject_auto_imports 注入的 N 行 if LineOffset > 0 and BadLine > LineOffset: BadLine -= LineOffset BadCol: int = int(getattr(BadNode, 'col_offset', -1)) BadText: str = f'成员名 {BadName!r}(语句形态 {Shape},行 {BadLine},列 {BadCol})' else: BadText = '(可见名与登记名的差集为空:登记数多于可见数,属 AST 损坏)' return ( f"[fast-fail] 枚举 '{ClassName}' 成员登记数不一致:源码里可见的成员名 " f"{len(Visible)} 个,实际只登记 {len(RegisteredNames)} 个" f"(差额 {len(Visible) - len(RegisteredNames)} 个会被静默丢弃)。" f"未识别的成员写法:{BadText}。" f"期望形态:每行一个 `Name` / `Name: T` / `Name = 值`," f"或多个简单标识符用逗号分隔(`A, B, C`,可带尾随逗号)。" f"修法:把该成员改写为逐个成员一行,或改写为纯标识符逗号组(去掉 `= 值` / `: T`)。" f"(本判据在注册处比对『可见名数 vs 登记数』,任何未识别形态都会命中,不再静默丢成员)" + (f"。位置 {Loc}" if Loc else '') ) # ExpandSoftKeywords 是 Code 的纯函数(全程无全局可变状态,见其"九种展开"实现): # 同一文件内容在一次编译里会被多处重复展开(Phase1 / Phase2 / 依赖扫描), # 缓存结果避免重复正则替换与 tokenize(cProfile:_ExpandTypeDescriptors 里的 # tokenize.generate_tokens 全程 ~590 万次 TokenInfo._make)。纯函数缓存不改输出;有界防膨胀。 _EXPAND_CACHE: dict[str, str] = {} _EXPAND_CACHE_LIMIT: int = 256 def ExpandSoftKeywords(Code: str) -> str: """将 Viper 软关键字/语法糖展开为等价的 Python 形态(行号不变)。 1) fn/fun/func/function → def(函数定义行):仅在"行首缩进 + 关键字 + 空白 + 标识符"的函数定义形态匹配,变量名(fn = 5)、调用(fn(x))、注解(x: fn) 与字符串内容不受影响。 2) `enum Name:` 块 → `class Name(t.CEnum):` 块(裸成员补 `: t.State`): 与手写继承式枚举产出**同一 AST**,故语义(默认编号 0..N、自定义成员类型、 宽度取最宽成员)完全一致。 3) `define NAME[: T] = 值` → `NAME: [T |] t.CDefine = 值`:编译期常量声明, 与手写 `NAME: t.CDefine = 值` 产出**同一 AST/同一注册路径**(见 _ExpandDefineConsts)。`define NAME(...)`(宏函数)不受影响。 4) `typedef T1 = T2` → `T1: t.CTypedef = T2`:类型别名声明,与手写 `T1: t.CTypedef = T2` 产出**同一 AST/同一注册路径**(见 _ExpandTypedefs)。 行首 `typedef` 的非声明形态**响亮失败**(该词不在任何既有源码中作标识符)。 5) 类型描述符 `const T` / `volatile T` → `t.CConst | T` / `t.CVolatile | T`: 与手写 `t.CConst | T` 产出**同一 AST/同一解析路径**(见 _ExpandTypeDescriptors)。 只在注解位置生效的软关键字(`t.attr.const()` 等非类型位置原样保留); 缺类型 / 后随非类型 / 裸用**响亮失败**(SyntaxError)。`static` 已从注解位置 迁出——注解位置出现 `static`(`名字: static 类型`)**响亮失败**。 6) `union NAME:` → `class NAME(t.CUnion):`:与手写 `class NAME(t.CUnion):` 产出 **同一 AST/同一注册路径**(见 _ExpandUnionBlocks)。成员为普通注解字段。 行首 `union` 的非 `union 名字:` 形态**响亮失败**(该词不在任何源码中作标识符)。 7) 裸数组前缀 `[N]T` / `[]T` → `t.CArray[T, N]` / `t.CArray[T, None]`(见 _ExpandArrayPrefixAnnotations):只在注解位置生效(AnnAssign 的 ':' 之后 / 形参 / '->' 之后 / 类字段),右值列表字面量**绝不改动**;无法脱糖的注解位置 数组前缀**响亮失败**(SyntaxError,绝不静默产出 List 注解)。 8) `renum NAME:` → `class NAME(t.REnum):`:与手写 `class NAME(t.REnum):` 产出 **同一 AST/同一注册路径**(见 _ExpandRenumBlocks)。成员为普通类体(嵌套 `class 变体:` + 无载荷赋值)。行首 `renum` 的非 `renum 名字:` 形态**响亮失败** (该词不在任何源码中作标识符)。 9) `export 名字: T [= 初值]` → `名字: T | t.CExport [= 初值]`; `extern 名字: T` → `名字: T | t.State`;`static 名字: T [= 初值]` → `名字: t.CStatic | T [= 初值]`(见 _ExpandExportExternVar)。 与 TPV 同一标记、同一消费路径;export/extern 在类型右侧追加标记,static 在 类型左侧前置标记(与 TPV `_ParseStaticModified` 的 `pending_static` 同源); 函数形态(`export def f()` / `export f()` / `extern def f()` / `static def f()`, 后者由 vast.Parser._ParseStaticDef 处理)不受影响; `extern` 带初值**响亮失败**(外部定义不写初值)。 九种展开都同行替换(长度可增,不增删换行),AST 行号映射保持有效。 """ if not Code: return Code Cached: str | None = _EXPAND_CACHE.get(Code) if Cached is not None: return Cached Result: str = _ExpandSoftKeywordsImpl(Code) if len(_EXPAND_CACHE) >= _EXPAND_CACHE_LIMIT: _EXPAND_CACHE.clear() _EXPAND_CACHE[Code] = Result return Result def _ExpandSoftKeywordsImpl(Code: str) -> str: """ExpandSoftKeywords 的实现体(无缓存,供缓存未命中时调用)。""" Code = _SOFT_KEYWORD_RE.sub(lambda m: f'{m.group(1)}def{m.group(3)}', Code) if 'enum' in Code: 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: Code = _ExpandTypedefs(Code) # 描述符放在最后:先让语句级语法糖(enum/union/define/typedef)落到规范形态, # 本层再看规范注解(如 `define X: const int = 1` → `X: const int | t.CDefine = 1` # → `X: t.CConst | int | t.CDefine = 1`,两种顺序结果相同)。 # `__attribute__(...)` 排在 const/volatile 之前:其注解位置判据需能回溯到**原始** # 描述符串(`const __attribute__(x) int` 的 `const` 尚未被改写为 `t.CConst |`), # 且它自身产出的 `t.attr.X |` 不会干扰紧随的 const/volatile 展开。 if _ATTRIBUTE_CALLEE in Code: Code = _ExpandAttributeDescriptors(Code) # const/volatile 是注解位置的存储类描述符;`static` 已从注解位置迁出为**前置** # 修饰符(`static 名字: 类型 = 值`),本层进入 _ExpandTypeDescriptors 只为让旧位置 # `名字: static 类型` 能被扫描到并**响亮失败**(见 _StaticPositionSyntaxError)。 if 'const' in Code or 'volatile' in Code or 'static' in Code: Code = _ExpandTypeDescriptors(Code) # 裸 `[N]T` 数组前缀在描述符之后:`const [N]T` 已由 _ExpandTypeDescriptors 消化为 # `t.CConst | t.CArray[T, N]`(此处不再有注解位置的裸 '['),避免两条路径重复改写。 if '[' in Code: Code = _ExpandArrayPrefixAnnotations(Code) # export / extern / static 变量声明(语句级软关键字):必须排在**最后**——此时注解 # 已由数组前缀 / 描述符两层脱糖成规范形态(`t.CArray[T, N]` / `t.CConst | ...`), # 本层只删修饰符 + 插入存储类标记(export/extern 右追加 `| t.CExport` / `| t.State`; # static 左前置 `t.CStatic | `)。顺序若反过来,`|` 会被数组前缀展开并进元素类型 # (见 _ExpandExportExternVar)。 if 'export' in Code or 'extern' in Code or 'static' in Code: Code = _ExpandExportExternVar(Code) return Code # --------------------------------------------------------------------------- # t.CDefine 编译期整数常量的"可存储范围 + 宽度规则"判定——唯一规则源 # (`define` 与手写 `X: t.CDefine = 值` 共用同一判据;对照表见 # Debugger/define-constants.md) # --------------------------------------------------------------------------- # 规则: # ① 本语言**没有 64 位以上的整数类型**(t.py 最大为 CInt64T/CUInt64T,不存在 # CInt128T/CUInt128T)⇒ 常量的实际存储上限即 64 位,按"有符号/无符号两种 # 解释"合并为 [-2^63, 2^64-1]。这与既有位模式惯用法一致(0xFFFFFFFF 落 i32 # 的 -1、0xA5A5A5A5A5A5A5A5 落 i64 负数,见 includes/memhub.vp:123 与 # LLVMCG/TypeConvert.py:181-186、HandlesExprAttr.py:163-173)。 # ② 显式类型注解(`define NAME: T = 值` ⇒ `NAME: T | t.CDefine = 值`)另行要求 # 值能落进 T 的**位宽** w:接受 [-(2^(w-1)), 2^w - 1](同样按"两种解释" # 惯例,`define X: int = 0xFFFFFFFF` 与 `= -1` 均合法);超出位宽必须报错。 # ③ 判据基于**精确值**(任意精度整数:字面量原值或 ConstEvaluator 的求值结果), # 绝不做定宽截断/回绕后再比较。 # ④ 非整数常量(浮点/字符串/bool)不进入本判据,走既有 register_cdefine_double # /register_cdefine_string 路径。 # # 宽度规则("W_min + 使用处宽度",用户 2026-09-21 确认): # W_min = 8/16/32/64 中**能容纳该值的最小位宽**(DefineMinWidth)。 # 常量是立即数,故**每个使用点按上下文决定实际物化宽度**: # 使用处宽度更宽 ⇒ 就地按该宽度物化(升宽,不损失信息); # 使用处宽度更窄 ⇒ fail-fast(RequireDefineUseWidth,报文点明 W_min 与所需宽度); # 取不到使用处宽度时(裸语句 / 变参实参 / 当前架构未下传类型的调用实参)⇒ # 沿用既有 32/64 二值口径(语言默认整型宽度;见 §8 论证),**不**降到 W_min: # 降到 W_min 会把 i8/i16 立即数喂进 i32 上下文与 C 默认实参提升, # 属于"静默降级/ABI 破坏",与本轮口径冲突。 DEFINE_STORE_MIN: int = -(1 << 63) DEFINE_STORE_MAX: int = (1 << 64) - 1 # W_min 候选位宽(升序):取"最小可容纳者" DEFINE_WIDTH_LADDER: tuple[int, ...] = (8, 16, 32, 64) # 报文用:可用整数类型清单(与 EnumMemberAnnotationIntWidth 的类型解析口径同源) _DEFINE_INT_TYPE_HINT: str = ( 't.CInt8T/t.CUInt8T、t.CInt16T/t.CUInt16T、t.CInt32T/t.CUInt32T、t.CInt64T/t.CUInt64T' '(或 builtins 的 int8/uint8/int16/uint16/int32/uint32/int64/uint64、char、sizet)' ) def DefineWidthLoHi(Width: int) -> tuple[int, int]: """位宽 w → 可接受值闭区间(两解释口径):`[-(2^(w-1)), 2^w - 1]`。 负值恰为有符号可表示范围,非负值放宽到无符号位模式范围(与既有 `0xFFFFFFFF` → i32 `-1`、`0xA5A5A5A5A5A5A5A5` → i64 负数的惯用法一致)。 """ return (-(1 << (Width - 1)), (1 << Width) - 1) def DefineMinWidth(Value: object) -> int | None: """值 → W_min(8/16/32/64 中最小可容纳者);None = 超出 64 位(不可存储)。 判定表(与 Debugger/define-constants.md 的"判定表"逐行对应,**闭区间,边界值 归属右侧档**:`2^16 = 65536` 属 32 位档): [0, 2^8-1] → 8 [2^8, 2^16-1] → 16 [2^16, 2^32-1] → 32 [2^32, 2^64-1] → 64 [-2^7, -1] → 8 [-2^15, -2^7-1] → 16 [-2^31, -2^15-1] → 32 [-2^63, -2^31-1] → 64 > 2^64-1 / < -2^63 → None(需要扩容/更大的类型) 符号性:负数按**有符号**解释(必须留出符号位);非负数按**无符号位模式** 解释(同时兼容有符号解释下的可表示范围,取更宽的 2^w-1 上界)。 bool / 非 int 不进入本判据(返回 None,由调用方按非整数路径处理)。 本实现按位宽**计算**上下界(`1 << (w-1)` / `(1 << w) - 1`),不含十进制大字面量, Python 侧因此天然精确。**TPV 侧的孪生实现不得照抄十进制阈值**:TPC 编译 `[0x80000000, 0xFFFFFFFF]` 的无类型字面量会落到 i32 并在提升时 sext 成负数 (HandlesExpr.py:263-265 + HandlesExprOps.py:401-413),曾把 `<= 4294967295` 的 32 位档阈值退化成 `<= -1`(现场 `includes/subprocess.vp:141`, 详见 Debugger/define-constants.md §14)。 """ if isinstance(Value, bool) or not isinstance(Value, int): return None for Width in DEFINE_WIDTH_LADDER: Lo, Hi = DefineWidthLoHi(Width) if Lo <= Value <= Hi: return Width return None def IsCDefineMarker(BaseType: object | None) -> bool: """BaseType 是否为 t.CDefine 标记本身("未声明具体类型"的自动推导形态)。 兼容 HandlesTypeMerge 把**类型类**直接放进 BaseType 的形态 (`Info.BaseType = TypeClass`,HandlesTypeMerge.py:228-234)。 """ if BaseType is None: return False if isinstance(BaseType, type): return issubclass(BaseType, t.CDefine) return isinstance(BaseType, t.CDefine) def DefineIntWidthOf(BaseType: object | None) -> int | None: """声明类型 → 整数位宽(bit);非整数/无法判定 → None。 取宽口径与 EnumMemberAnnotationIntWidth 同源(复用 _IntSizeOf), 并兼容"类形态"(未实例化的 CType 子类)与"实例形态"两种 BaseType。 """ if BaseType is None: return None Width: int | None = _IntSizeOf(BaseType) if Width is not None: return Width if (isinstance(BaseType, type) and issubclass(BaseType, t.CType) and BaseType is not t.CType): try: return _IntSizeOf(BaseType()) except Exception: return None return None def RequireDefineIntStorable(Value: object, BaseType: object | None, Name: str, AnnotationText: str, Lineno: int, Col: int, FilePath: str, CodeLine: str) -> None: """t.CDefine 整数常量的可存储性校验(fail-fast;唯一判据,两侧同口径)。 通过则原样返回(零副作用);不可存储则抛 TypeError,报文含五要素: **文件:行[:列] / 源码片段 / 常量名 / 声明类型或自动推导上限 / 实际值 / 可存储范围与可用类型清单 / 修法建议(点明"需要更大的类型/扩容")**。 调用点(均为 CDefine 常量的**注册处**,即值与已解析注解同时可得之处): - 模块级 / 类体内:`Translator/LlvmGenerator._register_cdefine_constant` - 函数内:`Handles/HandlesAnnAssign._HandleAnnAssignLlvmInner` 的 IsDefine 分支 **不触碰**非 CDefine 声明路径(调用点全部在 IsDefine 分支内)。 """ if isinstance(Value, bool) or not isinstance(Value, int): return Lo: int = DEFINE_STORE_MIN Hi: int = DEFINE_STORE_MAX Width: int | None = DefineIntWidthOf(BaseType) WMin: int | None = DefineMinWidth(Value) WMinText: str = f'{WMin} 位' if WMin is not None else '无(超出 64 位)' if IsCDefineMarker(BaseType) or Width is None: Deduced: int = 32 if 0 <= Value <= 0xFFFFFFFF else 64 TypeDesc: str = f'未声明具体类型(自动推导宽度 {Deduced} 位)' else: TypeDesc = f'{AnnotationText or "?"}({Width} 位)' if Width > 0: Lo = max(Lo, -(1 << (Width - 1))) Hi = min(Hi, (1 << Width) - 1) if Lo <= Value <= Hi: return Loc: str = f'{FilePath or "unknown"}:{Lineno}' if Col >= 0: Loc += f':{Col}' raise TypeError( f'[TransPyC] define 常量数值不可存储: 常量 {Name!r} 的初始值 {Value} 不可存储。' f'最小可容纳宽度 W_min: {WMinText}。' f'声明类型: {TypeDesc};该声明可存储范围: {Lo}..{Hi}。' f'本语言最宽的整数类型为 64 位({DEFINE_STORE_MIN}..{DEFINE_STORE_MAX},' f'覆盖有符号/无符号两种解释),不存在更宽的整数类型 ⇒ 需要扩容。' f'禁止静默截断/降级,已终止编译。可用整数类型: {_DEFINE_INT_TYPE_HINT}。' f'修法: 把声明类型改为更宽的类型(如 t.CInt64T / uint64);若仍放不下,' f'说明该数字超出本语言可表示的整数范围,需要更大的整数类型(扩容);' f'或修正该字面量。位置 {Loc}' + (f',源码: {CodeLine}' if CodeLine else '') ) def RequireDefineUseWidth(Value: object, ContextWidth: int | None, Name: str, Lineno: int, Col: int, FilePath: str, CodeLine: str, ContextDesc: str = '') -> int | None: """**使用处宽度**校验(fail-fast;唯一判据,两侧同口径)。 规则(`define` 常量被物化成立即数的使用点): 上下文宽度已知且 `>= W_min` ⇒ 返回该宽度(调用方按此宽度就地升宽物化); 上下文宽度已知且 `< W_min` ⇒ 抛 TypeError(含 W_min、使用处要求的宽度、 实际值、文件:行:列、源码片段、修法); 上下文宽度无法判定(None) ⇒ 返回 None(调用方沿用既有 32/64 二值口径)。 非整数常量(float/str/bool)或值超出 64 位(W_min=None)不在此判据内:后者由 RequireDefineIntStorable 在**注册处**拦住(更早、更准确的站点)。 """ if isinstance(Value, bool) or not isinstance(Value, int): return None WMin: int | None = DefineMinWidth(Value) if WMin is None or ContextWidth is None: return None if ContextWidth >= WMin: return ContextWidth Loc: str = f'{FilePath or "unknown"}:{Lineno}' if Col >= 0: Loc += f':{Col}' raise TypeError( f'[TransPyC] define 常量使用处宽度不足: 常量 {Name!r} 的实际值 {Value} ' f'需要至少 {WMin} 位(最小可容纳宽度 W_min),' f'但使用处{("(" + ContextDesc + ")") if ContextDesc else ""}只提供 {ContextWidth} 位' f'(可表示范围: {DefineWidthLoHi(ContextWidth)[0]}..{DefineWidthLoHi(ContextWidth)[1]})。' f'禁止静默截断/降级,已终止编译。' f'修法: 把使用处(变量声明 / 形参 / 运算对象 / 返回注解)的类型加宽到至少 {WMin} 位' f'(如 t.CInt{WMin}T / CUInt{WMin}T 及更宽类型),或改用更小的常量值。' f'位置 {Loc}' + (f',源码: {CodeLine}' if CodeLine else '') ) def _DefineSiteInfo(Gen: object, Trans: object, Node: object) -> tuple[int, int, str, str]: """站点四要素:行号(含 `_line_offset` 还原)/ 列号 / 源文件路径 / 源码片段。 口径与 `HandlesExprCall._RequireArgValue`(rawptr-migration-and-fastfail.md §27.3) 逐条一致,供 RequireDefineConstStorable / RequireDefineUseWidthAt 共用。 """ Lineno: int = int(getattr(Node, 'lineno', 0) or 0) Offset: int = int(getattr(Trans, '_line_offset', 0) or 0) if Offset > 0 and Lineno > Offset: Lineno -= Offset Col: int = int(getattr(Node, 'col_offset', -1)) FilePath: str = getattr(Gen, '_current_source_file', '') or '' CodeLine: str = '' if Lineno > 0 and hasattr(Gen, '_get_source_line'): try: CodeLine = (Gen._get_source_line(Lineno) or '').strip() except Exception: CodeLine = '' return Lineno, Col, FilePath, CodeLine # 整数字面量的可存储范围(两种解释的并集):[-2^63, 2^64-1]。 # 与 TPV 侧(includes/ast/lexer.vp 的 _lex_int_exceeds_u64 / _lex_int_neg_exceeds_i64、 # includes/ast/parser.vp 的一元减联合判定)**同一区间、同一口径**。 INT_LITERAL_MIN: int = -(1 << 63) INT_LITERAL_MAX: int = (1 << 64) - 1 def RequireIntLiteralStorable(Gen: object, Trans: object, Node: object, Value: int) -> None: """整数字面量(含"一元减作用于整数字面量"后的**结果值**)的存储范围 fail-fast。 `-` 是一元运算符、不属于字面量:字面量本身无符号(0..2^64-1),负数侧由一元减 表达,取负后必须 >= -2^63。任何越界值(如 18446744073709551616、-18446744073709551615、 -9223372036854775809)在此 **fail-fast**(不静默回绕/截断)。 只在 `Value` 为 int(非 bool)时生效;报文含 `文件:行[:列] + 源码片段 + 字面量原文 (含负号)+ 允许范围 [-2^63, 2^64-1] + 修法`(站点口径与 _DefineSiteInfo 一致)。 """ if isinstance(Value, bool) or not isinstance(Value, int): return if INT_LITERAL_MIN <= Value <= INT_LITERAL_MAX: return Lineno, Col, FilePath, CodeLine = _DefineSiteInfo(Gen, Trans, Node) Loc: str = f'{FilePath}:{Lineno}:{Col}' if FilePath else f'line {Lineno}:{Col}' raise TypeError( f'[TransPyC] 整数字面量越界: 字面量 {str(Value)!r} 超出 64 位可表示范围' f'(允许范围 -9223372036854775808..18446744073709551615 = [-2^63, 2^64-1];' f'负数结果必须 >= -2^63)。本语言不存在更宽的整数类型;' f'禁止静默回绕/截断,已终止编译。' f'修法: 把该字面量改到上述范围内(如需更大数值请拆分为多个 64 位字表达)。' f'位置 {Loc}' + (f',源码: {CodeLine}' if CodeLine else '') ) def RequireDefineUseWidthAt(Gen: object, Trans: object, Node: object, Value: object, ContextWidth: int | None, Name: str, ContextDesc: str = '') -> int | None: """使用处宽度校验的站点包装(取"文件:行[:列] + 源码片段"后转调 RequireDefineUseWidth)。""" Lineno, Col, FilePath, CodeLine = _DefineSiteInfo(Gen, Trans, Node) return RequireDefineUseWidth(Value, ContextWidth, Name, Lineno, Col, FilePath, CodeLine, ContextDesc) def RequireDefineConstStorable(Gen: object, Trans: object, Node: object, BaseType: object | None, Annotation: object | None, Value: object, Name: str) -> None: """编译期常量注册处的站点包装:取"文件:行[:列] + 源码片段"后调 RequireDefineIntStorable。 站点信息口径与 `HandlesExprCall._RequireArgValue`(rawptr-migration-and-fastfail.md §27) 逐条一致:`Gen._current_source_file` / `Gen._get_source_line(行号)` / `Trans._line_offset`(`_inject_auto_imports` 注入行导致的行号偏移还原)。 """ AnnText: str = '' if Annotation is not None: try: AnnText = ast.unparse(Annotation) except Exception: AnnText = '' Lineno, Col, FilePath, CodeLine = _DefineSiteInfo(Gen, Trans, Node) RequireDefineIntStorable(Value, BaseType, Name, AnnText, Lineno, Col, FilePath, CodeLine) def RequireNameDefined(Gen: object, Trans: object, Node: object, Name: str) -> None: """未定义名称的 fail-fast(NameError)。 调用点:`HandlesExpr._HandleNameLlvm` 的所有查找路径(局部/全局变量、函数、 枚举成员、CDefine、from-import 别名、t/c 别名)都已失败之后。原实现返回 `ir.Constant(IntType(32), 0)`,会把"常量名写错/define 丢失/变量漏声明"这类 上游缺陷静默变成运行期的 0(实测:`8 * RENUM_CLASS_MAX` 因常量名少一个 `_ENUM_` 被折成 `mul i32 8, 0` → `alloc(0)` → REnum 变体表注册静默跳过 → match 分派丢失 全部 case)。 """ Lineno, Col, FilePath, CodeLine = _DefineSiteInfo(Gen, Trans, Node) Loc: str = f'{FilePath}:{Lineno}:{Col}' if FilePath else f'line {Lineno}:{Col}' raise NameError( f'[TransPyC] 未定义名称: {Name!r} —— 该处既不是局部/全局变量、函数、枚举成员,' f'也不是 CDefine 常量 / from-import 别名 / t.c 导入名。' f'禁止静默物化为 i32 0,已终止编译。' f'修法: 核对该名拼写;补上声明(模块级 `name: 类型 = 值`、`define NAME = 值`、' f'函数形参或局部注解);跨模块使用需显式 import / from … import。' f'位置 {Loc}' + (f',源码: {CodeLine}' if CodeLine else '') ) def IsTModule(module_id: str, symtable: object | None = None) -> bool: """检查模块标识符是否指向 t 类型模块(支持别名如 import t as tt)""" if module_id in _T_MODULE_NAMES: return True if symtable is not None and hasattr(symtable, 'import_aliases'): resolved: str = symtable.import_aliases.get(module_id, module_id) if resolved in _T_MODULE_NAMES: return True return False # --------------------------------------------------------------------------- # virtual / novirtual 修饰符(软关键字)的判据 —— TPC 侧**唯一规则源** # --------------------------------------------------------------------------- # 源码形态 等价既有写法 语义 # virtual class C: @t.CVTable class C: 类级:强制有 vtable # novirtual class C: @t.NoVTable class C: 类级:强制无 vtable # virtual def m(...) @t.CVTable def m(...) 方法级:占 vtable 槽 # novirtual def m(...) @t.NoVTable def m(...) 方法级:不占 vtable 槽 # # 解析层(lib/vast/Parser.py:_ParseVirtualModified)只合成同名装饰器,**不新造第二套 # 表示**;语义全部落在既有点位,本节函数是那些点位共用的同一判据(避免多处各写一份 # 而漂移),逐条对齐 TPV(Viper 自举编译器)的既有实现: # - ClassHasFuncLevelCVTable ←→ TPV ClassGenBase._has_any_func_cvtable # - MethodVirtualMarker ←→ TPV ClassGenBase._should_method_be_virtual 第 1/2 条 # - IsVtableSlotMethod ←→ TPV ClassGenBase._should_method_be_virtual 全 5 条 # # 回归性:判据只在"方法带 CVTable/NoVTable 标记"时偏离原行为;全仓既有 .vp 无任何 # 方法级标记(普查结论见报告),故对既有类/既有代码零影响。 def _DecoratorListHasMarker(DecoratorList: object, MarkerName: str) -> bool: """装饰器表中是否存在指定标记名(接受 @t.X / @X / @t.X() / @X() 四种既有写法)。""" if not DecoratorList: return False for Dec in DecoratorList: Target: object = Dec if isinstance(Dec, ast.Call): Target = Dec.func if isinstance(Target, ast.Attribute): if Target.attr == MarkerName: return True elif isinstance(Target, ast.Name): if Target.id == MarkerName: return True return False def MethodVirtualMarker(MethodNode: ast.AST) -> str | None: """方法级 virtual/novirtual 标记 → 'CVTable' / 'NoVTable' / None。 NoVTable 优先(与 TPV _should_method_be_virtual 第 1 条同序):同一方法同时带 两种标记时按"排除"处理。 """ DecList: object = getattr(MethodNode, 'decorator_list', None) if _DecoratorListHasMarker(DecList, 'NoVTable'): return 'NoVTable' if _DecoratorListHasMarker(DecList, 'CVTable'): return 'CVTable' return None def ClassHasFuncLevelCVTable(ClassNode: ast.AST) -> bool: """类体内是否有方法带方法级 @t.CVTable(对齐 TPV _has_any_func_cvtable)。 跳过构造函数(__new__/__init__/__before_init__)——它们不进虚表。 用于"类的 vtable 是否由方法级标记触发"这一支(类级 NoVTable / 父类 NoVTable / 无真实父类时,TPV _detect_vtable_status 返回 _has_any_func_cvtable)。 """ for Item in getattr(ClassNode, 'body', None) or []: if not isinstance(Item, ast.FunctionDef): continue if Item.name in ('__new__', '__init__', '__before_init__'): continue if _DecoratorListHasMarker(getattr(Item, 'decorator_list', None), 'CVTable'): return True return False def IsVtableSlotMethod(MethodNode: ast.AST, VtableByClassLevel: bool) -> bool: """方法是否占 vtable 槽——与 TPV ClassGenBase._should_method_be_virtual 同判据: 1) 方法级 NoVTable ⇒ 不占(最高优先级) 2) 方法级 CVTable ⇒ 占 3) vtable 来自类级 CVTable / 继承链(VtableByClassLevel,即**不看**方法级 标记算出来的 vtable 状态)⇒ 占 4) 其余(含"类的 vtable 仅由方法级标记触发":如普通类里只写了一个 `virtual def`)⇒ 不占 VtableByClassLevel 必须由调用方按"忽略方法级标记"的口径计算(TPC: HandlesClassDef._resolve_class_flags 返回的 VtableByClassLevel)。 """ Marker: str | None = MethodVirtualMarker(MethodNode) if Marker == 'NoVTable': return False if Marker == 'CVTable': return True return bool(VtableByClassLevel) def IsTModulePath(module_path: str) -> bool: """检查模块路径是否指向 t 类型模块""" if module_path in _T_MODULE_NAMES: return True if module_path: t_name: str for t_name in _T_MODULE_NAMES: if module_path == t_name or module_path.startswith(t_name + '.'): return True return False def AnnotationContainsTType(node: ast.AST, TypeName: str) -> bool: """检测 AST 节点中是否包含指定名称的 t 模块类型""" if isinstance(node, ast.Attribute): if (hasattr(node.value, 'id') and IsTModule(node.value.id) and node.attr == TypeName): return True elif isinstance(node, ast.BinOp): return AnnotationContainsTType(node.left, TypeName) or AnnotationContainsTType(node.right, TypeName) elif isinstance(node, ast.Call): if AnnotationContainsTType(node.func, TypeName): return True for arg in node.args: if AnnotationContainsTType(arg, TypeName): return True elif isinstance(node, ast.Subscript): if AnnotationContainsTType(node.value, TypeName): return True if AnnotationContainsTType(node.slice, TypeName): return True return False # 内建定长数组构造子的裸名(与 t.CArray 等价的写法): # CArray —— from t import * 后的裸名形式 # rawarray —— CArray 的内建复刻名(hashlib 等裸名化重构使用,免写 t. 前缀) # array —— 本轮新增的内建语法保留字(软关键字,仅在类型/注解位置生效, # 等价 t.CArray[E, N];非类型位置仍可作普通标识符——见 # Debugger/array-builtin-spec.md 与 FixedArrayKeywordAnnotationError) _FIXED_ARRAY_BARE_NAMES: tuple[str, ...] = ('CArray', 'rawarray', 'array') _ARRAY_KEYWORD: str = 'array' def IsFixedArrayBareName(Name: str) -> bool: """裸名是否为内建定长数组构造子(CArray / rawarray / array)""" return Name in _FIXED_ARRAY_BARE_NAMES def IsListAnnotation(annotation: ast.expr) -> bool: """检查注解是否为栈上固定数组类型(t.CArray[...] / 内建 array[...] / list[type, count])""" if not isinstance(annotation, ast.Subscript): return False value = annotation.value # t.CArray[...] / c.CArray[...] if (isinstance(value, ast.Attribute) and isinstance(value.value, ast.Name) and value.value.id in ('t', 'c') and value.attr == 'CArray'): return True # 裸名 CArray[...](from t import *)/ 内建 rawarray[...] / 内建 array[...]: # 三者与 t.CArray 等价(array 为本轮新增的内建软关键字,见上方常量注释) if (isinstance(value, ast.Name) and value.id in _FIXED_ARRAY_BARE_NAMES): return True # list[type, count] — 仅匹配双参数 Tuple 切片(固定数组), # 单参数 list[type] 不匹配(走泛型类 list[T] 路径) if (isinstance(value, ast.Name) and value.id == 'list' and isinstance(annotation.slice, ast.Tuple) and len(annotation.slice.elts) == 2): return True return False def ExtractListFromBinOp(annotation: ast.expr) -> ast.Subscript | None: """从 BinOp(BitOr) 注解中提取 list[...] 部分,如 list[i32] | t.CPtr""" if isinstance(annotation, ast.BinOp) and isinstance(annotation.op, ast.BitOr): for side in (annotation.left, annotation.right): if IsListAnnotation(side): return side return None class ListAnnotationParseResult: """list[...] 注解解析结果。 is_pointer=True 表示单参数 list[type] → 指针模式; is_pointer=False 表示 Tuple(2) 模式 list[type, count]。 """ __slots__ = ('elem_type_node', 'count_node', 'is_pointer') def __init__(self, elem_type_node: ast.expr, count_node: ast.expr | None, is_pointer: bool) -> None: self.elem_type_node: ast.expr = elem_type_node self.count_node: ast.expr | None = count_node self.is_pointer: bool = is_pointer def ParseListAnnotation(annotation: ast.expr) -> ListAnnotationParseResult | None: """解析 list[...] 注解,统一处理 list[type, count] / list[type] / BinOp(BitOr) 形式。 自动处理 BinOp(BitOr) 包裹(如 list[i32, N] | t.CPtr)。 返回 None 表示不是 list 注解。 """ list_node: ast.expr = annotation if isinstance(annotation, ast.BinOp) and isinstance(annotation.op, ast.BitOr): extracted: ast.Subscript | None = ExtractListFromBinOp(annotation) if extracted is None: return None list_node = extracted if not IsListAnnotation(list_node): return None slice_node: ast.AST = list_node.slice if isinstance(slice_node, ast.Tuple) and len(slice_node.elts) == 2: return ListAnnotationParseResult(slice_node.elts[0], slice_node.elts[1], is_pointer=False) # 单参数 list[type] → 指针模式 return ListAnnotationParseResult(slice_node, None, is_pointer=True) # --------------------------------------------------------------------------- # 内建定长数组构造子 array 的注解校验(唯一规则源) # # array 是**软关键字**:仅在"类型/注解位置"作为内建定长数组构造子生效, # 语义与 t.CArray 逐点等价(元素类型在前、长度在后)。非类型位置仍是普通 # 标识符(如 includes/json 的 def array(...)、numpy 的 def array(...), # 因此**不能**做成词法保留字)。 # # 与内建 rawptr 的机制对应:都"不可重定义"(在类型位置优先于任何用户声明), # 但 array 因与既有标识符重名,只在类型位置生效(软关键字),而 rawptr 在 # 各解析入口直接短路(硬内置名)。 # # 新写法要求**恰好两个参数** `array[E, N]`:缺参 / 多参 / 长度非正整型常量 # 表达式均为 fail-fast(不静默降级)。t.CArray[E] 的单参/长度自动推导写法 # 仍然可用(t.CArray 不受影响)。 # --------------------------------------------------------------------------- def _IsConstExprShape(Node: ast.AST) -> bool: """节点是否为"常量表达式形态"(字面量 / 名字 / 属性 / 常量算术/位运算/一元)。 用于 array 长度参数的**语法级**拒绝:调用/列表/比较/布尔/推导式等明显不是 编译期整数常量,直接判为误用;Name/Attribute(可能是 CDefine 常量或 跨模块常量)与算子组合保留,交由既有常量求值路径判定(避免前向引用误报)。 """ if isinstance(Node, (ast.Constant, ast.Name, ast.Attribute)): return True if isinstance(Node, ast.BinOp): return _IsConstExprShape(Node.left) and _IsConstExprShape(Node.right) if isinstance(Node, ast.UnaryOp): return _IsConstExprShape(Node.operand) return False def FixedArrayKeywordAnnotationError(Annotation: ast.AST, SymbolTable: object = None) -> str | None: """校验内建定长数组构造子 array 的注解写法。 返回 None 表示"合法"或"不是 array 注解";否则返回中文错误消息(不含 位置与源码片段——那两项由调用方按 @p Annotation 的 lineno/col_offset 与源码行补齐)。SymbolTable 非空时对常量表达式长度做一次求值校验; 求值不到(前向引用/声明期依赖未就绪)则**延迟**给下游常量求值路径, 避免误报。 """ if Annotation is None: return None if isinstance(Annotation, ast.Name): if Annotation.id == _ARRAY_KEYWORD: return ("裸用内建定长数组构造子 'array'(缺少下标与参数)" "——'array' 只在类型/注解位置生效,必须写成 array[E, N]" "(元素类型在前、长度在后)。" "期望形态:array[E, N];正确示例:state: array[uint32, 4]") return None if not isinstance(Annotation, ast.Subscript): return None base: ast.AST = Annotation.value if not (isinstance(base, ast.Name) and base.id == _ARRAY_KEYWORD): return None SliceNode: ast.AST = Annotation.slice if not isinstance(SliceNode, ast.Tuple): return ("内建定长数组构造子 'array' 缺少长度参数(当前仅 1 个参数)" "——必须同时给出元素类型与长度。" "期望形态:array[E, N];正确示例:state: array[uint32, 4]") if len(SliceNode.elts) != 2: return (f"内建定长数组构造子 'array' 参数个数错误(当前 {len(SliceNode.elts)} 个,期望 2 个)" "——只接受 array[E, N](E=元素类型,N=长度)。" "期望形态:array[E, N];正确示例:state: array[uint32, 4]") CountNode: ast.AST = SliceNode.elts[1] if isinstance(CountNode, ast.Constant): Value = CountNode.value if not (isinstance(Value, int) and not isinstance(Value, bool) and Value > 0): return ("内建定长数组构造子 'array' 的长度必须是正整型常量(>0)" "——None/0/负数/非整数都不允许(长度自动推导请改用 t.CArray[E])。" "期望形态:array[E, N];正确示例:state: array[uint32, 4]") return None if not _IsConstExprShape(CountNode): return ("内建定长数组构造子 'array' 的长度必须是整型常量表达式" "(字面量 / CDefine 常量名 / 常量算术或位运算表达式)" "——函数调用/列表/比较等运行期表达式不允许。" "期望形态:array[E, N];正确示例:state: array[uint8, MAX_LEN]") if SymbolTable is not None: try: from lib.core.ConstEvaluator import ConstEvaluator Value = ConstEvaluator.eval_with_symtab(CountNode, SymbolTable) except Exception: Value = None if Value is not None and not (isinstance(Value, int) and not isinstance(Value, bool) and Value > 0): return ("内建定长数组构造子 'array' 的长度必须求值为正整型常量(>0)。" "期望形态:array[E, N];正确示例:state: array[uint32, 4]") return None def CheckAnnotationHasCInline(annotation_node: ast.AST | None) -> bool: """检测注解节点中是否包含 CInline""" if annotation_node is None: return False if isinstance(annotation_node, ast.Attribute): if hasattr(annotation_node.value, 'id') and IsTModule(annotation_node.value.id) and annotation_node.attr == 'CInline': return True if isinstance(annotation_node, ast.Name): if annotation_node.id == 'CInline': return True if isinstance(annotation_node, ast.BinOp) and isinstance(annotation_node.op, ast.BitOr): return CheckAnnotationHasCInline(annotation_node.left) or CheckAnnotationHasCInline(annotation_node.right) return False def ExtractTypeNameFromBinOp(annotation: ast.expr) -> str | None: """从 BinOp(BitOr) 注解左侧提取类型名(如 TypeA | TypeB → TypeA)。 返回 Name.id 或 Attribute.attr,非 BinOp 返回 None。 """ if isinstance(annotation, ast.BinOp) and isinstance(annotation.op, ast.BitOr): left: ast.expr = annotation.left if isinstance(left, ast.Name): return left.id if isinstance(left, ast.Attribute): return left.attr return None def FindStructNameInAnnotation(node: ast.AST, struct_names: dict | set, TCAliasMap: dict[str, tuple[str, str]] | None = None, StructSha1Map: dict[str, str] | None = None) -> str | None: """递归在注解中查找结构体名(支持 BinOp(BitOr) 递归)。 检查 Name.id、Constant.str、Attribute.attr 是否在 struct_names 中。 支持泛型特化类型(如 GSList[Param]):特化名按**同一规则源** (ResolveTypeArgStr:sha1 前缀 + rawptr/CPtr 的 '*' 逐层)构造后在 struct_names 中查找——查表键与 _mangle_generic_class_name 产物逐字一致, 否则同一注解在"注解查找"与"特化注册"两处解析出不同名字(opaque 副本)。 """ if isinstance(node, ast.Name) and node.id in struct_names: return node.id if isinstance(node, ast.Constant) and isinstance(node.value, str) and node.value in struct_names: return node.value if isinstance(node, ast.Attribute) and node.attr in struct_names: return node.attr if isinstance(node, ast.Subscript): base_name: str | None = None if isinstance(node.value, ast.Name): base_name = node.value.id elif isinstance(node.value, ast.Attribute): base_name = node.value.attr if base_name: slice_node: ast.AST = node.slice if hasattr(ast, 'Index') and isinstance(slice_node, ast.Index): slice_node = slice_node.value elem_nodes: list[ast.AST] = ( list(slice_node.elts) if isinstance(slice_node, ast.Tuple) else [slice_node]) type_args: list[str] = [ ResolveTypeArgStr(E, TCAliasMap, StructSha1Map) for E in elem_nodes] spec_name: str = base_name + '[' + ']['.join(type_args) + ']' if spec_name in struct_names: return spec_name if base_name in struct_names: return base_name if isinstance(node, ast.BinOp) and isinstance(node.op, ast.BitOr): result: str | None = FindStructNameInAnnotation( node.left, struct_names, TCAliasMap, StructSha1Map) if result: return result return FindStructNameInAnnotation( node.right, struct_names, TCAliasMap, StructSha1Map) return None def FindStructNameInAnnotationWithContext(Node: ast.AST, Trans: object, Gen: object, StructNames: dict | set | None = None) -> str | None: """按 Gen/Trans 上下文注入规则表后查找结构体名。 规则本体唯一(FindStructNameInAnnotation),此处只做与 HandlesExprCall._resolve_type_arg_str 同口径的上下文注入,避免各调用点 各写一套拼名(查表键必须与 _mangle_generic_class_name 产物逐字一致)。 """ Names: dict | set = StructNames if StructNames is not None else Gen.structs return FindStructNameInAnnotation( Node, Names, TCAliasMap=getattr(Trans, '_t_c_imported_names', {}), StructSha1Map=getattr(Gen, '_struct_sha1_map', {})) def AnnotationContainsName(annotation: ast.AST, name: str) -> bool: """检查注解中是否包含指定名称(支持 BinOp(BitOr) 递归)。 同时检查 Attribute.attr 和 Name.id,比 ast.dump 字符串匹配更精确。 """ if isinstance(annotation, ast.Attribute): return annotation.attr == name if isinstance(annotation, ast.Name): return annotation.id == name if isinstance(annotation, ast.BinOp) and isinstance(annotation.op, ast.BitOr): return AnnotationContainsName(annotation.left, name) or AnnotationContainsName(annotation.right, name) return False def IsCPtrNode(node: ast.AST) -> bool: """检查节点是否为 t.CPtr 属性引用。""" return (isinstance(node, ast.Attribute) and isinstance(node.value, ast.Name) and node.value.id == 't' and node.attr == 'CPtr') def IsCPtrSubscriptNode(node: ast.AST) -> bool: """检查节点是否为 t.CPtr[T] 下标指针类型(非裸标记,裸标记见 IsCPtrNode)。 与 TPV TypeGenResolve._is_cptr_subscript(TypeGenResolve.vp:1485-1505)同口径: value 必须是 t.CPtr 属性引用(只认 t 前缀,c.CPtr[...] 不认); 切片内的指针层级不限(t.CPtr[t.CPtr]、t.CPtr[t.CPtr[t.CInt]] 均命中)。 链式写法 t.CPtr[T][T](value 是 Subscript)两套编译器均不认作该标记。 """ return (isinstance(node, ast.Subscript) and IsCPtrNode(node.value)) # 指针下标的 base 名:rawptr 是编译器内置语法(TPC/TPV 双端保留字,见 # includes/builtins.vp 语义注释与 TypeAnnotationResolver._BUILTIN_ROWPTR_NAME), # 单独 rawptr 等价 t.CPtr(void*)。 _RAWPTR_SUBSCRIPT_NAME: str = 'rawptr' # 指针标记类型名(t.CPtr):裸名书写 `CPtr[T]` / 导入别名 `X`(from t import CPtr as X) # 都归一到本名。 _CPTR_TYPE_NAME: str = 'CPtr' def ResolveTCImportAliasName(NameId: str, symtable: object | None = None, TCAliasMap: dict[str, tuple[str, str]] | None = None) -> str: """t/c 导入别名 → 原始类型名('X' → 'CPtr'、'myint' → 'CInt');未命中原样返回。 两张同源别名表(注册点唯一:HandlesImports._EmitImportFromDeclarationsLlvm / _RegisterFromImportAliases,与 SymbolTable 序列化),调用方按手头有的表注入: - TCAliasMap:{别名: (模块, 原名)}(Trans._t_c_imported_names / DeclarationGenerator._t_c_imported_names 同结构) - symtable._t_type_symbols:{别名: t.CType 子类}(from t import CPtr as X → t.CPtr) 非 t/c 模块的 from 导入别名(from ast import AST as A)不参与归一。 """ if not NameId: return NameId if TCAliasMap: Entry: tuple[str, str] | None = TCAliasMap.get(NameId) if Entry: SrcModule, SrcName = Entry if IsTModule(SrcModule): return SrcName if symtable is not None: Cls: object | None = getattr(symtable, '_t_type_symbols', {}).get(NameId) if Cls is not None: return getattr(Cls, '__name__', NameId) return NameId def IsRawPtrSubscriptNode(node: ast.AST) -> bool: """检查节点是否为内置裸指针下标 `rawptr[T]`(非裸标记 rawptr)。 `rawptr[T]` 语义 = `t.CPtr[T]` = 源码联合写法 `T | t.CPtr`(Ptr(T)),三者 必须走同一解析与拼名路径,否则同一类型的不同书写会分裂出不同特化名 (结构体/方法声明/调用点三处不一致)。 注意:本判据**只**认 `rawptr[T]`;`t.CPtr[T]` / 裸名 `CPtr[T]` 由 IsPtrSubscriptNode 判定(见下),二者共用同一条脱糖规则 (DesugarPtrSubscriptToUnion),此处保持 rawptr 专用以便仍需区分书写时使用。 """ return (isinstance(node, ast.Subscript) and isinstance(node.value, ast.Name) and node.value.id == _RAWPTR_SUBSCRIPT_NAME) def IsI8DoublePtrType(T: object) -> bool: """是否为 **i8 系两层指针**(`i8**`)—— 即 `T | t.CPtr` 在 T 的类型丢失后**退化**成的形状。 与 "T 是有类型的上层指针" 无关:只认 `i8**`(`Ptr(Ptr(i8))`)。用于识别 「类引用指针被解析成通用两层指针」这类**类型信息丢失**。 """ import llvmlite.ir as ir return (isinstance(T, ir.PointerType) and isinstance(T.pointee, ir.PointerType) and isinstance(T.pointee.pointee, ir.IntType) and T.pointee.pointee.width == 8) def IsClassRefPtrAnnotation(Ann: ast.AST, symtable: object | None = None) -> bool: """注解是否为「**指针包装的类引用**」:`rawptr[Mod.Class]` / `Mod.Class | t.CPtr`。 只回答"这个注解**本应**解析出结构体指针",不做任何类型解析,判据保守: - 必须是指针包装:`rawptr[T]` / `t.CPtr[T]` / 裸名 `CPtr[T]`,或含 `t.CPtr` 标记的联合写法 `T | t.CPtr`; - 内层须是类引用形态:`Attribute` 且**根模块不是 `t` / `c`** (如 `ExprGenFstr.ExprGenFstr`、`llvmlite.LLVMType`),或首字母大写的裸名 (类名书写惯例)。 模块名、`rawptr` / `CPtr` 标记本身、泛型特化实参(内层是 Subscript)、 `str` / `int` 这类小写内建名一律**不算**。 """ Inner: ast.AST | None = None if IsRawPtrSubscriptNode(Ann) or IsCPtrSubscriptNode(Ann): Inner = Ann.slice elif isinstance(Ann, ast.BinOp) and isinstance(Ann.op, ast.BitOr): for Side in (Ann.left, Ann.right): if IsCPtrMarkerNode(Side, symtable): continue if Inner is not None: return False Inner = Side else: return False if isinstance(Inner, ast.Attribute): Cur: ast.AST = Inner while isinstance(Cur, ast.Attribute): Cur = Cur.value Root: str = Cur.id if isinstance(Cur, ast.Name) else '' return Root not in ('t', 'c') if isinstance(Inner, ast.Name): return bool(Inner.id) and Inner.id[0].isupper() return False def IsPtrSubscriptNode(node: ast.AST, symtable: object | None = None, TCAliasMap: dict[str, tuple[str, str]] | None = None) -> bool: """检查节点是否为"指针下标类型":`rawptr[T]` / `t.CPtr[T]` / 裸名 `CPtr[T]` / 导入别名 `X[T]`(`from t import CPtr as X`)。 三者语义完全相同(= 源码联合写法 `T | t.CPtr` = Ptr(T),见 builtins.vp 与 t.py 的 CPtr 语义注释):内层 T 按**参数注解 `a: T` 的规则** 解析(跨模块类 → '.X'),再包一层指针——T 为类引用时该层被结构体 自身吸收('.X*'),其余(标量 / 已是指针的 typedef / 泛型容器特化 / 嵌套指针下标)逐层外包。 - 裸名 `CPtr[T]`(`from t import CPtr` / `from t import *`)与声明端 DeclarationGenerator._get_type_str 的 ValueIsCPtr 判据一致(那里同样接受 Attribute 'CPtr' 与 Name 'CPtr' 两种书写)。 - 传入 symtable 时,`import t as tt` 的 `tt.CPtr[T]` 一并命中(与声明端 "只看 attr 名"的 ValueIsCPtr 判据对齐,避免同一书写两端分裂); 不传(名称域 ResolveTypeArgStr 的字面名场景)时只认字面 't'。 - 导入名别名(`from t import CPtr as X` 的 `X[T]`)经别名表解析 X→CPtr (TCAliasMap 或 symtable._t_type_symbols,见 ResolveTCImportAliasName), 定义端与声明端各自注入手头的同一批表,判据只有这一处。 """ if IsRawPtrSubscriptNode(node): return True if not isinstance(node, ast.Subscript): return False value: ast.AST = node.value if isinstance(value, ast.Name) and value.id == _CPTR_TYPE_NAME: return True return IsCPtrMarkerNode(value, symtable, TCAliasMap) def DesugarPtrSubscriptToUnion(node: ast.AST, symtable: object | None = None, TCAliasMap: dict[str, tuple[str, str]] | None = None) -> ast.AST: """把指针下标(`rawptr[T]` / `t.CPtr[T]` / 裸名 `CPtr[T]` / 别名 `X[T]`) 折叠成等价的联合写法 `T | t.CPtr`。 内层 T 递归折叠(`rawptr[rawptr[X]]` → `(X | t.CPtr) | t.CPtr`、 `t.CPtr[rawptr[X]]` → `(X | t.CPtr) | t.CPtr`), 与 TPV TypeGenResolve.resolve_annotation_type 的 Subscript 分支同语义 (T → Ptr(T),层数逐层外包);折叠后交给 HandlesTypeMerge 既有的 联合解析(_CollectAllOperandTypes 会把嵌套 BitOr 拉平成 [T, t.CPtr, ...], struct 基类型吸收一层、其余逐层累加),从而: - 内层 T 走通用类型解析(含跨模块类经 Gen.structs 反查 SHA1 前缀); - `rawptr[X]` / `t.CPtr[X]` / `X[T]` / `X | t.CPtr` 得到同一 CTypeInfo (PtrCount/BaseType 一致)与同一泛型实参串(逐层 '*')。 非指针下标节点原样返回(调用方无需先判类型);symtable/TCAliasMap 透传给 判据(识别 `import t as tt` 的 `tt.CPtr[T]` 与 `from t import CPtr as X` 的 `X[T]` 内层嵌套)。折叠产物统一用字面 `t.CPtr` 标记,与源码联合写法同形。 """ if not IsPtrSubscriptNode(node, symtable, TCAliasMap): return node inner: ast.AST = DesugarPtrSubscriptToUnion(node.slice, symtable, TCAliasMap) cptr_marker: ast.AST = ast.Attribute( value=ast.Name(id='t', ctx=ast.Load()), attr='CPtr', ctx=ast.Load()) union_node: ast.AST = ast.BinOp(left=inner, op=ast.BitOr(), right=cptr_marker) try: ast.copy_location(union_node, node) except Exception: pass return union_node # --------------------------------------------------------------------------- # 泛型实参串(mangling):定义端与声明端唯一规则源 # --------------------------------------------------------------------------- # 同一泛型特化(如 list[rawptr[ast.AST]])在 define 端发射的结构体名 # (HandlesClassDef._mangle_generic_class_name → %".list[ast.ASTptr]")、 # 各端方法名、以及 declare/stub 端引用的类型名必须逐字一致,否则会出现 # Undefined method / 签名错位。此前 DeclarationGenerator._get_type_str 的泛型 # 分支自带第二套(只认 Name / Name-Tuple 实参的)规则,其余形态(Attribute / # Subscript / BinOp / 混合 Tuple)退回未特化的 %".list"*,与 define 端 # 不一致。故此处收拢唯一规则,两端皆调用本组函数。 # 定义端 _resolve_generic_slice_type 的 ast.Name 规范化表: # 源码里的 C 定长类型名 → 喂给 _mangle_generic_class_name 的 type_arg。 _GENERIC_ARG_INT_NAMES: frozenset[str] = frozenset({ 'CInt', 'CUInt', 'CInt32T', 'CUInt32T', 'CInt64T', 'CUInt64T', 'CInt16T', 'CUInt16T', 'CInt8T', 'CUInt8T', 'CChar', 'CShort', 'CLong', }) _GENERIC_ARG_FLOAT_NAMES: frozenset[str] = frozenset({ 'CDouble', 'CFloat', 'CFloat64T', 'CFloat32T', }) # 源码名 → 泛型 T 替换用的原始类型名(_resolve_generic_slice_type 的第二返回值) _GENERIC_ARG_RAW_NAMES: dict[str, str] = { 'int': 'CInt', 'double': 'CDouble', 'float': 'CFloat', 'char': 'CChar', 'bool': 'CBool', } # _mangle_generic_class_name 的浮点归一集合(LLVM 浮点串 → 'f' + Size) _LLVM_FLOAT_STRINGS: frozenset[str] = frozenset({'float', 'double', 'half', 'fp128'}) def NormalizeGenericArgName(NameId: str) -> str: """ast.Name 泛型实参 → 定义端 type_arg(HandlesExprCall._resolve_generic_slice_type 的 ast.Name 分支,逐分支同构): int/double/float 保留源码名('list[int]' 而非 'list[i32]')、 char/bool → 'i8'、C 整数族 → 'int'、C 浮点族 → 'double'、其余原名。 """ if NameId in ('int', 'double', 'float'): return NameId if NameId in ('char', 'bool'): return 'i8' if NameId in _GENERIC_ARG_INT_NAMES: return 'int' if NameId in _GENERIC_ARG_FLOAT_NAMES: return 'double' return NameId def GenericArgRawName(NameId: str) -> str: """ast.Name 泛型实参 → 替换模板 T 用的原始类型名(_resolve_generic_slice_type 的第二返回值):'int' → 'CInt'(内建类型须用源码名替换 T,见 HandlesClassDef._specialize_generic_class 的 _BUILTIN_TYPE_NAMES 判定), 其余原名。仅定义端 T 替换用;mangling 只用 NormalizeGenericArgName。 """ return _GENERIC_ARG_RAW_NAMES.get(NameId, NameId) def MangleTypeArgByRegistry(ArgStr: str) -> str: """实参串 → 最终 mangled 实参(HandlesClassDef._mangle_generic_class_name 的单项逻辑,逐分支同构):t 类型名经 NameToLLVM 归一(CInt32T → 'i32', 't.CInt'/'c.CInt' 先去模块前缀再归一),浮点按 Size 归一为 f{n} (CDouble → 'f8'),未注册名原样保留(含指针层数与已解析的内层名)。 """ LookupStr: str = ArgStr if ArgStr.startswith('t.') or ArgStr.startswith('c.'): LookupStr = ArgStr[2:] LlvmStr: str | None = CTypeRegistry.NameToLLVM(LookupStr) if not LlvmStr: return ArgStr if LlvmStr in _LLVM_FLOAT_STRINGS: Cls: type | None = CTypeRegistry.GetClassByName(LookupStr) Size: int = Cls().Size if Cls is not None else 0 return 'f' + str(Size) return LlvmStr # 指针层数的首层归一化(与 LLVMTypeMapper._add_pointer_level 同口径) _POINTER_PLACEHOLDER_BASES: frozenset[str] = frozenset({'', 'void', 'void*'}) def AddPointerLayers(TypeStr: str, Layers: int) -> str: """给已解析的类型串追加 Layers 层指针(空/void/void* 的首层归一化为 i8*)。 与 LLVMTypeMapper._add_pointer_level 同口径;名称域与类型域因此对 rawptr[t.CVoid] 一致地给出 i8*(唯一允许的 void 兜底)。 """ Result: str = TypeStr for _ in range(Layers): if Result in _POINTER_PLACEHOLDER_BASES: Result = 'i8*' else: Result = Result + '*' return Result def IsCPtrMarkerNode(Node: ast.AST, symtable: object | None = None, TCAliasMap: dict[str, tuple[str, str]] | None = None) -> bool: """`t.CPtr` 裸指针标记(含 `import t as tt` 与 `from t import CPtr as X` 别名): 联合写法里贡献一层 '*'。 与 IsCPtrNode 的区别:仅按 attr 名 + t 模块前缀判定,不看切片 (`t.CPtr[T]` 下标是**基类型**而非层数标记,见 ResolveTypeArgStr 的 Subscript 分支)。 - Attribute:`t.CPtr` / `tt.CPtr`(t 模块别名经 symtable.import_aliases 解析)。 - Name:经别名表归一为 'CPtr' 的书写名(`from t import CPtr as X` 的 `X`); 字面裸名 `CPtr` 是否算标记取决于该模块是否 `from t import CPtr` (别名表命中一致:两端都由同一批表判定,不另设白名单)。 """ if isinstance(Node, ast.Attribute): return (Node.attr == _CPTR_TYPE_NAME and isinstance(Node.value, ast.Name) and IsTModule(Node.value.id, symtable)) if isinstance(Node, ast.Name): return ResolveTCImportAliasName(Node.id, symtable, TCAliasMap) == _CPTR_TYPE_NAME return False def FlattenUnionOperands(Node: ast.AST) -> list[ast.AST]: """拉平嵌套 BitOr,按书写顺序返回操作数(`A | t.CPtr | t.CPtr` → [A, CPtr, CPtr])。""" if isinstance(Node, ast.BinOp) and isinstance(Node.op, ast.BitOr): return FlattenUnionOperands(Node.left) + FlattenUnionOperands(Node.right) return [Node] def ResolveTypeArgStr(Node: ast.AST, TCAliasMap: dict[str, tuple[str, str]] | None = None, StructSha1Map: dict[str, str] | None = None) -> str: """将类型参数 AST 节点解析为规范化的类型字符串(泛型 mangling 用)。 规则(**与参数注解 `a: T` 完全同一套解析**,定义端/声明端唯一规则源): - ast.Name: t/c 导入别名 → '{module}.{name}';已注册类 → '{sha1}.{名}' (与参数注解的 %"{sha1}.X" 同前缀);其余按 NormalizeGenericArgName 归一('int'/'i32'/'str' 原名、'char'/'bool' → 'i8'、C 整数/浮点族 → 'int'/'double') - ast.Attribute: t 模块类型按 CTypeRegistry.NameToLLVM 解析(t.CPtr → i8*、 t.CInt → i32);跨模块类引用 module.X → '{sha1}.X';其余 '{mod}.{attr}' - ast.Subscript: 指针下标(`rawptr[T]` / `t.CPtr[T]` / 裸名 `CPtr[T]` / 导入别名 `X[T]`)已在入口脱糖为上面的联合写法(走同一规则),故此处只剩泛型容器: '<容器名>[<已解析实参>]'(多实参 '][' 分隔),容器名的 模块前缀由调用方(_mangle_name / '%"{模sha1}.{特化名}"')统一加 - ast.BinOp(BitOr)(含指针下标脱糖而来): 取非 CPtr 侧为基类型, 每个 `t.CPtr` 标记追加一层 '*'(`X | t.CPtr` ≡ `rawptr[X]` ≡ `t.CPtr[X]` → '.X*';`rawptr[rawptr[X]]` → '.X**'; 标量 `int | t.CPtr` → 'int*') - 其余形态: ast.unparse 注意:名称域**不做**"裸类引用吸收一层 CPtr"(那是注解→LLVM 类型域的规则, 见 DeclarationGenerator._is_bare_class_ref_annotation):名称域描述的是类型 身份('X' = X 本身、'X*' = 指向 X 的指针),逐层累加,否则 `rawptr[X]` 与 `X | t.CPtr` 会分裂成两个名字。 """ if IsPtrSubscriptNode(Node, TCAliasMap=TCAliasMap): Node = DesugarPtrSubscriptToUnion(Node, TCAliasMap=TCAliasMap) if isinstance(Node, ast.Name): tn: str = Node.id if TCAliasMap and tn in TCAliasMap: # t 模块别名(from t import CInt as myint)按内建归一表解析到同一名字 # ('myint' 与 'CInt' 都 → 'int'),避免同义写法分裂特化名 SrcModule, SrcName = TCAliasMap[tn] if IsTModule(SrcModule): return NormalizeGenericArgName(SrcName) return f'{SrcModule}.{SrcName}' if StructSha1Map and tn in StructSha1Map: return f'{StructSha1Map[tn]}.{tn}' return NormalizeGenericArgName(tn) if isinstance(Node, ast.Attribute): return _ResolveAttributeArgStr(Node, TCAliasMap, StructSha1Map) if isinstance(Node, ast.Subscript): return _ResolveSubscriptArgStr(Node, TCAliasMap, StructSha1Map) if isinstance(Node, ast.BinOp) and isinstance(Node.op, ast.BitOr): return _ResolveUnionArgStr(Node, TCAliasMap, StructSha1Map) return ast.unparse(Node) def _ResolveAttributeArgStr(Node: ast.Attribute, TCAliasMap: dict[str, tuple[str, str]] | None, StructSha1Map: dict[str, str] | None) -> str: """Attribute 形态(t.CInt / ast.AST / .AST)→ 实参串。""" if not isinstance(Node.value, ast.Name): return ast.unparse(Node) ModName: str = Node.value.id AttrName: str = Node.attr if IsTModule(ModName) or (TCAliasMap and ModName in TCAliasMap): LlvmStr: str | None = CTypeRegistry.NameToLLVM(AttrName) if LlvmStr is not None: return LlvmStr return f'{ModName}.{AttrName}' # 跨模块类引用(ast.AST / .AST):与参数注解同一条 sha1 解析 if StructSha1Map and AttrName in StructSha1Map: return f'{StructSha1Map[AttrName]}.{AttrName}' return f'{ModName}.{AttrName}' def _ResolveSubscriptArgStr(Node: ast.Subscript, TCAliasMap: dict[str, tuple[str, str]] | None, StructSha1Map: dict[str, str] | None) -> str: """下标形态(t.CPtr[T] / 泛型容器 list[X] / dict[K,V])→ 实参串。 容器名('list' / 'dict' ...)**不带**模块前缀:它只是 _mangle_generic_class_name 的基名,特化名的模块前缀由调用方统一加(定义端 _mangle_name / 声明端 '%"{模sha1}.{特化名}"'),并保证 '_make_replacement_node' 能把实参串 重新解析成可用的注解 AST('.list[...]' 会因数字开头而解析失败)。 """ if IsCPtrSubscriptNode(Node): Inner: str = ResolveTypeArgStr(Node.slice, TCAliasMap, StructSha1Map) return AddPointerLayers(Inner, 1) BaseName: str = '' if isinstance(Node.value, ast.Name): BaseName = Node.value.id elif isinstance(Node.value, ast.Attribute): BaseName = Node.value.attr if not BaseName: return ast.unparse(Node) SliceNode: ast.AST = Node.slice ElemNodes: list[ast.AST] = list(SliceNode.elts) if isinstance(SliceNode, ast.Tuple) else [SliceNode] Resolved: list[str] = [ResolveTypeArgStr(E, TCAliasMap, StructSha1Map) for E in ElemNodes] return f'{BaseName}[' + ']['.join(Resolved) + ']' def _ResolveUnionArgStr(Node: ast.BinOp, TCAliasMap: dict[str, tuple[str, str]] | None, StructSha1Map: dict[str, str] | None) -> str: """联合形态(X | t.CPtr | ...)实参串:非 CPtr 侧为基类型,CPtr 标记逐层加 '*'。 标记判据是同一处 IsCPtrMarkerNode(含 `from t import CPtr as X` 的裸标记别名), 别名经 TCAliasMap 解析;没带别名表的 `import t as tt` 的 `tt.CPtr` 在此域 不做模块别名解析(两端口径一致的既有缺口)。 """ BaseType: str = '' Layers: int = 0 for Operand in FlattenUnionOperands(Node): if IsCPtrMarkerNode(Operand, TCAliasMap=TCAliasMap): Layers += 1 elif not BaseType: BaseType = ResolveTypeArgStr(Operand, TCAliasMap, StructSha1Map) if not BaseType: # 只有 CPtr 标记(如 `t.CPtr | t.CPtr`):等价 void* 逐层——首层 i8* return AddPointerLayers('void', Layers) return AddPointerLayers(BaseType, Layers) def GetStructSha1MapFromSymtab(Symtab: object | None) -> dict[str, str]: """从符号表取「类名 → 定义模块 sha1」表(Phase2 在 Convert 前挂到 translator)。""" _Tr: Any = getattr(Symtab, 'translator', None) _Map: Any = getattr(_Tr, '_struct_sha1_map', None) if _Tr is not None else None return _Map if isinstance(_Map, dict) else {} def GetTCAliasMapFromSymtab(Symtab: object | None) -> dict | None: """从符号表取 t/c 导入别名表(`from t import CInt as myint`)。缺则 None。""" _Tr: Any = getattr(Symtab, 'translator', None) _Map: Any = getattr(_Tr, '_t_c_imported_names', None) if _Tr is not None else None return _Map if isinstance(_Map, dict) and _Map else None def IsGenericClassSubscriptBase(Node: ast.AST, Symtab: object | None = None, StructSha1Map: dict[str, str] | None = None) -> bool: """下标基名是否为「有类定义的泛型类」(内建 list/dict/set… 或用户泛型类)。 判据与 DeclarationGenerator._get_type_str 的泛型分支(L1393-1423)同一条规则源: **该名有类定义**即按泛型特化处理。内建容器本身在 includes/_list.vp / _dict.vp 等 就是类(实测符号表 `list` 条目 IsStruct=True),故本判据等价覆盖声明端的 `non_struct_types ∪ _generic_class_names`;用户泛型类(Box[T])同样命中。 只认 Name 形态:`t.CPtr[...]` / `t.CArray[...]` / 裸 `array[...]` / 定长 `list[T, N]` / `rawptr[...]` 等由调用方各自分支先行处理,不走这里。 """ if not isinstance(Node, ast.Name) or not Node.id: return False if StructSha1Map and Node.id in StructSha1Map: return True if Symtab is not None: _Entry: Any = None try: if hasattr(Symtab, 'lookup'): _Entry = Symtab.lookup(Node.id) elif hasattr(Symtab, 'get'): _Entry = Symtab.get(Node.id) except Exception: _Entry = None if _Entry is not None and getattr(_Entry, 'IsStruct', False): return True for _Ns in (getattr(Symtab, '_namespaces', {}) or {}).values(): if not isinstance(_Ns, dict): continue _Cand: Any = _Ns.get(Node.id) if _Cand is not None and getattr(_Cand, 'IsStruct', False): return True return False def ResolveGenericClassSpecName(Node: ast.Subscript, Symtab: object | None = None, StructSha1Map: dict[str, str] | None = None, TCAliasMap: dict | None = None) -> str | None: """泛型类下标注解 → 声明端同口径的**特化名**('list[str]' / 'dict[str][int]')。 与 DeclarationGenerator._get_type_str 的泛型分支(L1393-1423)逐字同口径: spec = 基名 + '[' + ']['.join(实参串) + ']' 实参串走 ResolveGenericTypeArgStr(与定义端 _mangle_generic_class_name 同一规则源)。 非泛型类下标(指针下标 / 定长数组 / t 模块类型 / 未知名)返回 None,由调用方各自 既有分支处理(调用点必须放在那些分支**之后**)。 必要性:单参泛型下标此前在两个注解解析器里都 miss(TypeAnnotationResolver 只认 双参定长 list[T, N];LLVMTypeMapper 亦然)⇒ 落到"未知类型"兜底 i32 / 空 CTypeInfo, 形参与成员类型整体错位(现场:ClassGenGeneric._specialize_generic_class 的 形参 `type_args: list[str]*` 在符号表里成 i32;字段 `TypeGenBase.CTypedefNames: list[str] | t.CPtr` 成 i8*)。 """ if not isinstance(Node, ast.Subscript): return None Base: ast.AST = Node.value if not IsGenericClassSubscriptBase(Base, Symtab, StructSha1Map): return None SliceNode: ast.AST = Node.slice Args: list[ast.AST] = list(SliceNode.elts) if isinstance(SliceNode, ast.Tuple) else [SliceNode] if not Args: return None Mangled: list[str] = [ ResolveGenericTypeArgStr(A, TCAliasMap, StructSha1Map) for A in Args] if not any(Mangled): return None return Base.id + '[' + ']['.join(Mangled) + ']' def ResolveGenericTypeArgStr(Node: ast.AST, TCAliasMap: dict[str, tuple[str, str]] | None = None, StructSha1Map: dict[str, str] | None = None) -> str: """泛型实参的最终 mangled 串(喂给 _mangle_generic_class_name 的单参数)。 与定义端(HandlesExprCall._resolve_generic_slice_type + _mangle_generic_class_name) 对单个实参的实际结果同一套规则: - ast.Name: NormalizeGenericArgName →(命中注册类则 '{sha1}.{名}')→ MangleTypeArgByRegistry ('int' → 'int'、'i32' → 'i32'、'str' → 'str'、'CInt32T' → 'int'、 'CFloat64T' → 'double'、'CChar' → 'i8') - 其余形态(Attribute / Subscript(含 rawptr[...])/ BinOp / 混合 Tuple 元素): ResolveTypeArgStr(rawptr[T] 脱糖 → 逐层 '*'、t.CPtr → 'i8*'、跨模块类 → sha1 前缀) → MangleTypeArgByRegistry """ return MangleTypeArgByRegistry(ResolveTypeArgStr(Node, TCAliasMap, StructSha1Map))