Files
TransPyC/lib/core/SymbolUtils.py
T
TermiNexus 0bded58999 refactor(type): 全面清理旧联合与 t/c 标记判断
审计并修复 23 个文件:类型注解中的 T1 | T2 一律 fail-fast;typedef/define/export/extern/static/const/volatile/attribute 优先读取 Markers/Attributes/ByteOrder/BitWidth;指针统一 CPtr/rawptr/CRef/CTempRef/CArrayPtr/CAutoPtr Subscript;stub 输出统一新语法。
2026-10-18 18:01:20 +08:00

2996 lines
149 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""共享工具函数:符号表处理中使用的 AST 分析工具"""
from __future__ import annotations
import io
import tokenize
import vast as ast
import re
from lib.core import TypeSystem
from lib.core.TypeSystem import CTypeRegistry
# 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(CEnum):` **完全等价**(同一实现路径——展开后再解析,
# 下游 EnumTable/符号表注册只看 ClassDef + CEnum 基类,不存在第二套枚举实现)。
# 头行 enum Name: → class Name(CEnum):
# 裸成员 Name → Name: State(默认 i32;必须落为 AnnAssign,
# 否则 Enum/REnum 成员收集按 Assign/AnnAssign 过滤会漏掉裸名语句)
# 带注解 Name: T → 原样保留(自定义具体类型)
# 带值 Name = 0 → 原样保留(Assign 形态,两侧枚举收集均支持)
# 多名行 A, B, C → A: State; B: State; C: State(同行分号分隔,见下;
# 可带尾随逗号 A, B,,与 VPC 的 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,不再静默丢成员)。
# 尾随逗号必须支持:VPC 侧走 AST(`class X(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]*((?:#.*)?)$'
)
# 裸名语句保留集:这些行是语句而非枚举成员,展开时不得追加 `: 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(CEnum):` 块(行号不变)。
只在"行首缩进 + enum + 空白 + 标识符 + 冒号(行尾)"的枚举头形态触发:
`enum = 5` / `import enum` / `x.enum` / 注解 `x: enum` 均不匹配。
成员行只对块内"更深缩进"的行做展开,块内空行与注释行原样保留;
缩进回到枚举头同级(或更浅)即块结束。全程不增删换行,行号映射保持有效。
成员行两种形态:单名(`Name`)追加 `: State`;逗号分组多名(`Add, Sub`)
在同一行内展开为分号分隔的多个 `Name: 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)}(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)}: State{member.group(3)}{term2}')
else:
# 逗号分组成员行:同一行展开为"分号分隔的多个 AnnAssign"。
# `Name: 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}: 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(CUnion):` (same desugar target as VPC 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(CUnion):` (same-line, line numbers kept).
Mirrors VPC _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)}(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 名字(CUnion):`)。union 是语句级保留字,成员必须写成 "
f"`名字: 类型`;非法形态一律终止编译(非静默)。"
)
out.append(raw)
return ''.join(out)
# Viper soft keyword `renum` (statement-level reserved word): `renum X:` is exactly
# `class X(REnum):` (same desugar target as VPC 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(REnum):` (same-line, line numbers kept).
Mirrors VPC _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)}(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 名字(REnum):`)。renum 是语句级保留字,成员写成嵌套 "
f"`class 变体:` 或无载荷赋值(如 `None_ = 0`);非法形态一律终止编译(非静默)。"
)
out.append(raw)
return ''.join(out)
# ---------------------------------------------------------------------------
# define 常量声明(Viper 语法糖)——与 `NAME: CDefine = 值` **完全等价**
# ---------------------------------------------------------------------------
# 带类型 define NAME: T = <表达式> → NAME: T | CDefine = <表达式>
# 不带类型 define NAME = <表达式> → NAME: CDefine = <表达式>
#
# 语义:编译期常量声明,落到**既有的** CDefine 注册路径(TPC:
# LlvmGenerator._register_cdefine_constant / HandlesAnnAssign 的 IsDefine 分支;
# 跨模块:SymbolExtractor → SymbolTable → .vpi/.pyi → HandlesImports),
# 不新增第二套常量实现。可见性与 `CDefine` 常量完全一致:
# 模块级 / 类体内 / 函数内 三种位置均可(各自走既有注册点)。
#
# 为什么带类型写成 `T | CDefine`(**类型在前**)而不是既有手写惯用法的
# `CDefine | T`:HandlesTypeMerge._MergeCTypeInfoInto 的 BaseType 采用
# "首个非 void 基类型胜出"(`if Target.BaseType is None: Target.BaseType = Source.BaseType`),
# 而 _CollectAllOperandTypes 按**书写顺序**左→右展开 ⇒ `CDefine | T` 的
# BaseType 会停在 CDefine,显式类型 T 被**丢弃**(既有手写形态的既有事实,
# 见 Test/DefineTest/App/main.vp:30-32);把 T 写在前面才能让 BaseType = T,
# 从而让"值必须能放进 T"这条判据有据可依(同时也是显式类型的真实含义)。
#
# 只在"行首缩进 + define + 标识符 + (`=` 或 `: … =`)"形态触发:
# - `define NAME(...)`(既有宏函数软关键字,返回注解自动追加 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 |] 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()} | 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)}: CDefine ={plain.group(3)}{term}')
continue
out.append(raw)
return ''.join(out)
# ---------------------------------------------------------------------------
# typedef 类型别名声明(Viper 语法糖)——与 `T1: CTypedef = T2` **完全等价**
# ---------------------------------------------------------------------------
# typedef T1 = T2 → T1: CTypedef = T2
#
# 语义:与手写 `T1: CTypedef = T2` 落到**同一条** typedef 注册/查找路径,不新增
# 第二套实现(TPC:符号表 SymbolTypeResolver._resolve_ctypedef_typedef →
# _resolve_typedef_from_value;翻译期 HandlesAnnAssign / DeclarationGenerator 的
# typedef 表。VPC:AssignGenMixin.translate_annassign 的 CTypedef 分支 →
# TypeGenCType.register_ctypedef)。RHS 即"类型表达式"(书写在值位):
# 裸名 `int32` / `X` / 并集 `CChar | CPtr` / 泛型 `array[E, N]` / 链式 `A = B`。
#
# `typedef` 是**语句级保留字**:行首出现即必须是 `typedef 名字 = 类型` 形态。
# - 合法形态:同行替换为 `名字: 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: 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)}: CTypedef ={decl.group(3)}{term}')
continue
if _TYPEDEF_HEAD_RE.match(body) is not None:
raise SyntaxError(
f"typedef 语法错误(原文: {body.strip()!r}):应为 "
f"`typedef 名字 = 类型`(等价于 `名字: CTypedef = 类型`)。"
f"合法的类型写法:裸名 / X / a | CPtr / array[E, N] / 另一 typedef 名。"
)
out.append(raw)
return ''.join(out)
# ---------------------------------------------------------------------------
# 类型描述符 const / volatile(Viper 语法糖:描述符写在**类型前**)
# ---------------------------------------------------------------------------
# const int → CConst | int
# volatile int → CVolatile | int
# const volatile int → CConst | CVolatile | int
# volatile const int → CVolatile | CConst | int(顺序任意,两个限定符都累积)
# const CInt32T → CConst | CInt32T
# const rawptr[Foo] → CConst | rawptr[Foo] (描述符只管注解最前面)
# a: const int → a: CConst | int (局部 / 类字段)
# def f(p: const int) → def f(p: CConst | int) (形参)
# def f() -> const int → ... -> CConst | int (返回)
#
# 语义:与**手写** `CConst | T` / `CVolatile | T` 逐点等价——**同一表示**,
# 不新造第二套(描述符不是新类型,只是 `|` 联合里的既有 flag 载体 CConst /
# CVolatile):
# - TPC:展开文本落到既有识别路径(HandlesTypeMerge.GetCTypeInfo 的
# 内建限定符 → Info.DataConst/DataVolatile;
# 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),故
# `CConst | int` 与 `int | CConst` 等价,本层统一产出**描述符在前**
# (与全仓既有真实写法一致:includes/stdio.vp:4 `fmt: CConst | str`、
# includes/spinlock.vp:6 `locked: CVolatile | int`、
# ViperApps/ViperOS/VKernel/Kernel/main.vp:301 `CConst | int | CPtr`)。
# - VPC: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);
# 旧注解位置一律**响亮失败**(见下),不再脱糖为 `CStatic | T`。
# 非类型位置**原样保留**为普通标识符(不做词法保留字):
# - `attr.const()`(ViperC/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 / VPC 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 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"(等价 `名字: CStatic | 类型 = 值`);"
f"正确示例:`static memory_regions: memory_region* = None` / "
f"`static pack_head: uint32 = 0`。"
f"(旧写法 `名字: static 类型` 已删除,绝不静默改写)"
)
_TYPEDEF_RHS_SCAN_LIMIT: int = 256
def _IsTypedefAnnotationRhs(Tokens: list, EqIdx: int) -> bool:
"""`EqIdx` 处的 `=` 是否是 `名字: <含 CTypedef 的注解> = <类型>` 的赋值号。
用途:把 `CTypedef` 注解的**右值**也纳入类型位置,使
`LPCSTR: CTypedef = const str` / `= const uint16*` 里的描述符被展开
(与注解位同一展开规则;右值的 `T*` 后缀由 vast 的 _ParseTypeAnnotation 处理)。
判据:从 `=` 反向扫描到**深度 0 的 `:`**(跳过注释/NL,遇换行/`;` 即视为非声明行),
再确认 `:` 与 `=` 之间出现 NAME `CTypedef`。故 `x: int = const`(值恰为标识符 const)、
`d = {"k": const}`、`f = lambda x: CTypedef` 等一律不受影响。
"""
if EqIdx < 0 or EqIdx >= len(Tokens):
return False
depth: int = 0
colon_idx: int = -1
j: int = EqIdx - 1
steps: int = 0
while j >= 0 and steps < _TYPEDEF_RHS_SCAN_LIMIT:
steps += 1
tok = Tokens[j]
if tok.type in (tokenize.NL, tokenize.COMMENT):
j -= 1
continue
if tok.type in (tokenize.NEWLINE, tokenize.INDENT, tokenize.DEDENT) or tok.string == ';':
return False
if tok.type == tokenize.OP:
if tok.string in (')', ']', '}'):
depth += 1
elif tok.string in ('(', '[', '{'):
if depth == 0:
return False
depth -= 1
elif tok.string == ':' and depth == 0:
colon_idx = j
break
j -= 1
if colon_idx < 0:
return False
for k in range(colon_idx + 1, EqIdx):
tk = Tokens[k]
if tk.type == tokenize.NAME and tk.string == 'CTypedef':
return True
return False
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
# `CArray[T, N]` (same shape as VPC 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 会落到注释上,
# 使数组前缀脱糖把注释并进 `CArray[...]` 内部(`data: [512]uint8 # 注释`
# 会变成 `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 `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'CArray[{Result}, {GroupText}]'
return ElemEnd, Result
# Bare C-style leading array declarator `[N]T` / `[]T` at annotation positions (no
# descriptor), desugared to `CArray[T, N]` / `CArray[T, None]` — same shape as
# VPC _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
)
# 注解位置的前置修饰词(由 vast 解析器原生解析为 AST 字段):数组前缀
# `[N]T` 的注解位置判据需要跳过它们,例如 `const [80]uint64` /
# `__attribute__(...) [N]T` / `BigEndian [2]uint16`。
_ANNOTATION_PREFIX_WORDS: frozenset[str] = frozenset({
'const', 'volatile', 'static', 'export', 'extern', 'inline',
'BigEndian', 'LittleEndian',
})
_ANNOTATION_PREFIX_CALLS: frozenset[str] = frozenset({'__attribute__', 'Bit'})
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`(存储类修饰符,
# 由 vast.Parser 原生解析):修饰符与名字之间已构成语句起始,同样算注解位置。
# 本判据必须在数组前缀脱糖时成立——靠它把
# `export gdt: [7]gdt_entry = []` / `static g: [7]int = []` 正常脱糖成
# `CArray[...]` 形态。
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 '['.
Annotation prefix modifiers (const/volatile/... / __attribute__(...) / BigEndian /
Bit(...)) between the delimiter and '[' are skipped. Dict / slice / lambda colons
are excluded so right-hand `[...]` literals stay put.
"""
j: int = _PrevSignificantTokenIndex(Tokens, Index)
while j >= 0:
Prev = Tokens[j]
if Prev.type == tokenize.NAME and Prev.string in _ANNOTATION_PREFIX_WORDS:
j = _PrevSignificantTokenIndex(Tokens, j)
continue
if Prev.type == tokenize.OP and Prev.string in (')', ']'):
OpenIdx: int = _MatchOpenBracket(Tokens, j)
if OpenIdx < 0:
return False
CalleeIdx: int = _PrevSignificantTokenIndex(Tokens, OpenIdx)
if (CalleeIdx >= 0 and Tokens[CalleeIdx].type == tokenize.NAME
and Tokens[CalleeIdx].string in _ANNOTATION_PREFIX_CALLS):
j = _PrevSignificantTokenIndex(Tokens, CalleeIdx)
continue
return False
break
if j < 0:
return False
Prev = Tokens[j]
if Prev.string == '->':
return True
if Prev.string != ':':
return False
return _AtAnnotationColon(Tokens, j, 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 `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(语句级软关键字)——与 VPC
# includes/ast/parser.vp **同一标记、同一消费路径**:
# export 名字: T [= 初值] → 名字: T | CExport [= 初值]
# (本模块**定义**:定义端裸名,不加模块 SHA1 头;内联汇编 / C 符号对接必需。
# 无初值时按既有零初始化全局定义处理,如 `export tss_buf: [104]uint8`。)
# extern 名字: T → 名字: T | State
# (**仅声明**:State = CExtern | CExport ⇒ 定义端 `@裸名 = external global T`。
# extern 是外部定义,**不允许**初值 ⇒ 带 `= 初值` 一律终止编译。)
# static 名字: T [= 初值] → 名字: CStatic | T [= 初值]
# (存储类标记**前置**在类型联合最左,与 VPC `_ParseStaticModified` 的
# `pending_static` 通道同源——`static 名字: T` → `名字: CStatic | T`;允许初值。
# 产出与手写 `名字: CStatic | 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 名字: 类型 [= 初值]`(裸名导出,等价 `名字: 类型 | CExport`);'
'`extern 名字: 类型`(仅声明,等价 `名字: 类型 | State`,**不允许初值**);'
'`static 名字: 类型 [= 初值]`(等价 `名字: 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 [= 初值]`
展开为带存储类标记的注解。
与 VPC 同口径:export → 类型**右侧追加** `| CExport`;extern → 追加 `| State`
(extern 带初值 ⇒ **响亮失败** SyntaxError);static → 类型**左侧前置** `CStatic | `
(与 ViperC `_ParseStaticModified` / `pending_static` 通道同源——`static 名字: T`
→ `名字: CStatic | T`,亦与手写 `名字: CStatic | T` / `@CStatic` 逐字段同形;
static **允许**初值)。
- 只在"语句起始(可缩进)+ export/extern/static + 标识符 + `:`"形态触发;函数形态
(`export def f()` / `export f()` / `extern def f()` / `static def f()`)第三个
token 不是 `:` ⇒ 不命中,vast 的既有函数处理逐字不变。
- **必须排在 `_ExpandArrayPrefixAnnotations` 之后**:此时注解已是规范形态
(`CArray[T, N]` / `CConst | ...`),插入点由 `_ScanAnnotationTypeEnd`
确定;`|` 不在 `_TYPE_EXPR_STOP_TOKENS` 中,若在数组前缀展开前插入会被
并进数组元素类型(`CArray[gdt_entry | 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):存储类标记**前置**在类型左侧(联合最左)。
# 与 ViperC `_ParseStaticModified` / `pending_static` 通道
# 逐字同形:`static 名字: T` → `名字: CStatic | T`,产出与
# 手写 `名字: CStatic | T` / `@CStatic` 逐字段同形。
InsAt: tuple[int, int] = Tokens[TypeStart].start
Edits.append((InsAt[0], InsAt[1], InsAt[1], 'CStatic | ', ''))
else:
# 编辑 2(export/extern):存储类标记**追加**在注解类型终点
# (右侧):export → `| CExport`;extern → `| State`。
InsAt = Tokens[TypeEnd - 1].end
Marker: str = ' | State' if IsExtern else ' | 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__(...)` 描述符(与 VPC
# includes/ast/parser.vp 的 _IsMagicAttributeCall / _MakeTAttrMarker 同源书写法,
# TPC 侧在本层文本脱糖;与 const/volatile、`[N]T` 数组前缀同属一套语法糖):
# V: __attribute__(section(".PRVERSN")) []char = "0.0.3-dev.428"
# → V: attr.section(".PRVERSN") | CArray[char, None] = "0.0.3-dev.428"
# x: __attribute__(aligned(16)) int = 0
# → x: attr.aligned(16) | int = 0
# 脱糖目标与手写 `T | attr.X(...)`(HandlesAssign 的模块级全局 attr.* 分支
# 所认的形态)逐字同形,故下游 AnnotationLoader / HandlesAssign /
# DeclarationGenerator 零新增判据。
# `CArray[T, None]` = TPC 的"长度自动推导数组"(HandlesAssign.py:1606),
# 恰是 `[]char` 的语义。
# ---------------------------------------------------------------------------
_ATTRIBUTE_CALLEE: str = '__attribute__'
_ATTR_MARKER_PREFIX: str = '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_VPC_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__` 单个实参文本 → 描述符文本(与 VPC _MakeTAttrMarker 同口径)。
- 裸名 `always_inline` → `attr.always_inline`
- 调用 `section(".text")` → `attr.section(".text")`
- 其余(已含点的 `attr.X` / `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__` 是否处于注解位置的描述符序列中。
与 VPC `_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__(...)` 脱糖为 `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`(含组合、任意顺序)展开为 `CConst | T` 等形态(行号不变)。
- 词法级切分(`tokenize`):注释/字符串内容天然不参与,不会被误改写。
- 只在"注解位置"(前一个 token 是 `:` 且再前一个是 Name,或前一个 token 是 `->`,
或 `名字: CTypedef =` 的赋值号右侧,见 _IsTypedefAnnotationRhs)
识别描述符串;非类型位置原样保留为普通标识符(软关键字边界)。
- **`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
`CConst | 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
elif Prev.string == '=' and _IsTypedefAnnotationRhs(Tokens, PrevIdx):
# `LPCSTR: CTypedef = const str`:typedef 的右值也是类型位置
# (与 VPC 的 _parse_type_ann 右值分支同口径)。
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
# `CConst | CArray[T, N]` (same shape as VPC).
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'{_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'{_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:
# 非注解位置:仅"整条语句恰好是一个描述符串"(裸用)才响亮失败,
# 其余(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)
# ---------------------------------------------------------------------------
# 枚举存储宽度("最宽成员类型")——两侧(VPC ClassGenEnum / TPC HandlesClassDef)
# 的唯一同口径规则源,对照表见 Debugger/enum-semantics.md。
# ---------------------------------------------------------------------------
# 规则:
# 无注解 / `State` 标记注解 → "无整数成员"口径(枚举宽度回落默认 32)
# 整数类型注解 → 按该类型位宽参与"取最宽"(见 EnumClassIntWidth)
# `bool` / `CBool` → **8**(枚举侧口径):枚举存储必须是可按字节寻址的
# 整数类型,`i1` 不能作为独立枚举宽度;本口径只作用于"枚举成员注解"
# 这一处,**不改** TPC 全局 `bool`→`i8`(LLVMTypeMapper.py:174)与
# VPC 全局 `bool`→`i1`(TypeGenResolve.vp:2201-2203)的各自的全局语义。
# TPC 侧 `bool` 经 BuiltinTypeMap → `CBool.Size == 8` 天然成立
# (CBool 同 8),故本文件不另加硬编码分支。
# 指针注解 → 在成员注册处明确报错(本文件与 ClassGenEnum 的 ptr 拦截)
# 其余(无法解析 / 非整数:float/void/struct…)→ 视为"无整数成员"
# (不引入新的硬失败;VPC 侧更严,对"有注解但解析失败"是 fatal,差异已记录)
_ENUM_DEFAULT_INT_WIDTH: int = 32
# State 标记名(与 VPC ClassGenEnum._is_state_annotation 同集合):该注解只是
# `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:
"""注解节点是否为 `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:
"""注解节点的"顶名":`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 值(`CInt64T` / `CInt64T`)→ 内建类型类;其余 → None。
供"无 SymbolTable 的声明端"把 int8/int64/… 归一到内建类型类取位宽。
"""
if not Text:
return None
Name: str = Text.strip()
if not Name or not Name.isidentifier():
return None
Cls = getattr(TypeSystem, Name, None)
if Cls is None:
Cls = TypeSystem.BUILTIN_TYPE_ALIASES.get(Name)
if isinstance(Cls, type) and issubclass(Cls, TypeSystem.CType) and Cls is not TypeSystem.CType:
return Cls
return None
def EnumMemberAnnotationIntWidth(Annotation: object | None,
SymbolTable: object | None = None) -> int | None:
"""枚举成员注解 → 整数位宽(bit);None = "默认口径"(走 _ENUM_DEFAULT_INT_WIDTH)。
`State` 标记或无注解 → None;整数类型(`int`/`char`/`C*` 家族 / builtins
与库 typedef)→ 其 `Size`;无法解析或非整数(float/void/struct…)→ None
(默认 32,与改前行为一致,不引入新的硬失败)。指针形态由调用方在解析前
拦截(HandlesClassDef._EnumMemberAnnotationIsPtr / VPC 的
_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(TypeSystem, Name, None)
if isinstance(Cls, type) and issubclass(Cls, TypeSystem.CType) and Cls is not TypeSystem.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)。
与 VPC ClassGenEnum._translate_enum_def 的 base_ty 同口径(`base_ty` 从 i32
起步,被任何 `get_llvm_type_bits > 0` 的成员替换 ⇒ 等价于"成员位宽最大值,
无成员时 i32")。**不是** `max(默认 32, 成员位宽)`:该写法会把 `A: uint8`
单成员的枚举钉在 i32,与 VPC 的 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 / VPC
# 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]]:
"""枚举类体内"源码可见的成员名"清单 `[(名字, 语句节点), …]`(按出现顺序)。
与 VPC `ClassGenEnum._enum_body_visible_names` / `_enum_target_visible_names`
同口径(同一判据的两侧实现)。调用点(注册处,登记完成后立即比对):
- TPC:HandlesClassDef._RegisterEnumMembers
- VPC: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(数组前缀展开需要词法切分)。
# 纯函数缓存不改输出;有界防膨胀。
_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(CEnum):` 块(裸成员补 `: State`):
与手写继承式枚举产出**同一 AST**,故语义(默认编号 0..N、自定义成员类型、
宽度取最宽成员)完全一致。
3) `union NAME:` → `class NAME(CUnion):`:与手写 `class NAME(CUnion):` 产出
**同一 AST/同一注册路径**(见 _ExpandUnionBlocks)。成员为普通注解字段。
行首 `union` 的非 `union 名字:` 形态**响亮失败**(该词不在任何源码中作标识符)。
4) `renum NAME:` → `class NAME(REnum):`:与手写 `class NAME(REnum):` 产出
**同一 AST/同一注册路径**(见 _ExpandRenumBlocks)。成员为普通类体(嵌套
`class 变体:` + 无载荷赋值)。行首 `renum` 的非 `renum 名字:` 形态**响亮失败**
(该词不在任何源码中作标识符)。
5) 裸数组前缀 `[N]T` / `[]T` → `CArray[T, N]` / `CArray[T, None]`(见
_ExpandArrayPrefixAnnotations):只在注解位置生效(AnnAssign 的 ':' 之后 /
形参 / '->' 之后 / 类字段),右值列表字面量**绝不改动**;无法脱糖的注解位置
数组前缀**响亮失败**(SyntaxError,绝不静默产出 List 注解)。
`define` / `typedef` / `const` / `volatile` / `static` / `export` / `extern` /
`inline` / `__attribute__(...)` / 字节序与位宽修饰由 vast 解析器在注解位置原生
解析成 AST 字段(Markers / Attributes / ByteOrder / BitWidth),本层不再做文本展开。
以上展开都同行替换(长度可增,不增删换行),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)
# 裸 `[N]T` 数组前缀(只改注解位置):`const [N]T` 中的 `const` 由解析器
# 作为注解前缀消费,本层只需把数组前缀脱糖成 `CArray[T, N]`。
if '[' in Code:
Code = _ExpandArrayPrefixAnnotations(Code)
return Code
# ---------------------------------------------------------------------------
# CDefine 编译期整数常量的"可存储范围 + 宽度规则"判定——唯一规则源
# (`define` 与手写 `X: 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 | 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 = (
'CInt8T/CUInt8T、CInt16T/CUInt16T、CInt32T/CUInt32T、CInt64T/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 侧因此天然精确。**VPC 侧的孪生实现不得照抄十进制阈值**: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 是否为 CDefine 标记本身("未声明具体类型"的自动推导形态)。
兼容 HandlesTypeMerge 把**类型类**直接放进 BaseType 的形态
(`Info.BaseType = TypeClass`,HandlesTypeMerge.py:228-234)。
"""
if BaseType is None:
return False
if isinstance(BaseType, type):
return issubclass(BaseType, TypeSystem.CDefine)
return isinstance(BaseType, TypeSystem.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, TypeSystem.CType)
and BaseType is not TypeSystem.CType):
try:
return _IntSizeOf(BaseType())
except Exception:
return None
return None
def DefineIntSignednessOf(BaseType: object | None) -> bool | None:
"""声明类型 → 是否有符号;非整数/无法判定 → None(与 DefineIntWidthOf 同口径)。
兼容"类形态"(未实例化的 CType 子类)与"实例形态"两种 BaseType。
"""
if BaseType is None:
return None
v: object | None = getattr(BaseType, 'IsSigned', None)
if v is not None:
return bool(v)
if isinstance(BaseType, type) and issubclass(BaseType, TypeSystem.CType) and BaseType is not TypeSystem.CType:
try:
inst: object = BaseType()
except Exception:
return None
v2: object | None = getattr(inst, 'IsSigned', None)
return bool(v2) if v2 is not None else None
return None
# 无类型 define 的注解占位名(Parser 写入 `Name('CDefine').Markers=('define',)`):
# 它不是类型,取"声明类型"时跳过。修饰信息一律读 Markers 字段。
_DEFINE_PLACEHOLDER_NAME: str = 'CDefine'
def _FirstTypeNameInAnnotation(node: object) -> str | None:
"""从注解 AST 中取**第一个真正的类型名**(修饰词由 Markers 承载)。
`define X: int = 1` → 'int'(int 节点 Markers 含 'define');
无类型 `define X = 1` → None(占位名 CDefine)。
"""
if node is None:
return None
if isinstance(node, ast.Name):
return None if node.id == _DEFINE_PLACEHOLDER_NAME else node.id
if isinstance(node, ast.Attribute):
return node.attr
if isinstance(node, ast.BinOp) and isinstance(node.op, ast.BitOr):
RejectTypeUnionAnnotation(node)
if isinstance(node, ast.Tuple):
for elt in node.elts:
found: str | None = _FirstTypeNameInAnnotation(elt)
if found is not None:
return found
return None
if isinstance(node, ast.Subscript):
# rawptr[X] / CArray[X, N]:指针/聚合,非整数 typedef → 不参与"声明整数类型"判定
return None
return None
def DefineDeclIntTypeFromAnnotation(symtab: object, annotation: object) -> tuple[int, bool] | None:
"""由 define 的**注解**(`T | CDefine`)解析声明整数类型 → (位宽, 是否有符号)。
仅当 T 能经符号表的 typedef 链解析到整数 C 类型时返回;否则 None(指针/聚合/未知)。
供"带类型注册"用:跨模块 define 只能从 .vpi 注解恢复声明类型。
"""
name: str | None = _FirstTypeNameInAnnotation(annotation)
if name is None or symtab is None:
return None
lookup = getattr(symtab, 'lookup', None)
if lookup is None:
return None
entry: object | None = lookup(name)
if entry is None or not getattr(entry, 'IsTypedef', False):
return None
base: object | None = getattr(entry, 'BaseType', None)
# 别名链:`DWORD` 这类 typedef 的 BaseType 常为 None,底层类型记在 OriginalType 里:
# ① 字符串 t 类名(实测 `DWORD` → `'CUInt32T'`、`ULONG` → `'CUInt32T'`);
# ② 内层 CTypeInfo(再取它的 BaseType)。
# 带空格/指针的串(如 `'void *'`)不是整数别名 → 放弃。
orig: object | None = getattr(entry, 'OriginalType', None)
depth: int = 0
while base is None and orig is not None and depth < 8:
depth += 1
if isinstance(orig, str):
cand: object | None = getattr(TypeSystem, orig, None)
if cand is None:
break
base = cand
break
if hasattr(orig, 'BaseType'):
base = getattr(orig, 'BaseType', None)
orig = getattr(orig, 'OriginalType', None)
continue
break
width: int | None = DefineIntWidthOf(base)
if width is None:
return None
signed: bool | None = DefineIntSignednessOf(base)
return (width, bool(signed) if signed is not None else False)
def DefineDeclTypeValue(DeclTypes: object | None, Name: str | None, Value: object) -> tuple[int, int] | None:
"""按 define 的**声明类型**归一常量值 → (位宽, 位型值);无声明类型/非整数 → None。
位型值:无符号 ⇒ 掩码后原样;有符号 ⇒ 掩码后按补码解释(可能为负)。
调用方据此物化 `ir.Constant(IntType(位宽), 位型值)` —— 例如 `define X: DWORD = -11`
得到 (32, 0xFFFFFFF5),即 u32 位型,而不是按数值大小猜 i32/i64。
"""
if not DeclTypes or Name is None or isinstance(Value, bool) or not isinstance(Value, int):
return None
get = getattr(DeclTypes, 'get', None)
if get is None:
return None
ent: object | None = get(Name)
if ent is None:
return None
try:
width: int
signed: bool
width, signed = ent
except Exception:
return None
if not isinstance(width, int) or width <= 0 or width > 64:
return None
mask: int = (1 << width) - 1
v: int = int(Value) & mask
if signed and v >= (1 << (width - 1)):
v -= (1 << width)
return (width, v)
def GlobalDeclShadowsDefine(Gen: object, Name: str | None) -> bool:
"""该名字在本模块是否有**已定义**(带初值)的全局 → 视为遮蔽同名 CDefine 常量。
仅 `external global`(**无初值**,典型来自跨模块 stub 注入的 declare)**不算**遮蔽:
否则 stub 里一条错误的 `@X = external global` 会把 .vpi 里的 CDefine 常量顶掉,
读路径退化成"加载该全局" → 链接期 undefined symbol(现场:win32file 的 STD_*_HANDLE)。
真正的本模块全局在 IR 里带初值/存储,仍按既有优先级遮蔽常量。
"""
mod: object | None = getattr(Gen, 'module', None)
if mod is None or Name is None:
return False
g: object | None = getattr(mod, 'globals', {}).get(Name)
if g is None:
return False
return getattr(g, 'initializer', None) is not None
def RequireDefineIntStorable(Value: object, BaseType: object | None, Name: str,
AnnotationText: str, Lineno: int, Col: int,
FilePath: str, CodeLine: str) -> None:
"""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'修法: 把声明类型改为更宽的类型(如 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'(如 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]。
# 与 VPC 侧(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 RequireExplicitWidthLiteral(Gen: object, Trans: object, Node: object,
Value: int, Width: int, Unsigned: int) -> None:
"""显式宽度字面量(`123_int32` / `0xFFFFFFFF_uint32`)的范围 fail-fast(**不允许隐式截断**)。
有符号 `int<N>`:[-2^(N-1), 2^(N-1)-1];无符号 `uint<N>`:[0, 2^N-1];
N == 64 时 int64 全域都合法(十六进制高位型在解析时已按 64 位位型回绕)。
越界即 raise(报文含 文件:行[:列] + 源码片段 + 字面量 + 允许范围 + 修法)。
"""
if isinstance(Value, bool) or not isinstance(Value, int):
return
if Width <= 0 or Width > 64:
return
Ok: bool = True
if Unsigned:
if Width < 64 and Value < 0:
Ok = False
elif Width <= 62 and Value > (1 << Width) - 1:
Ok = False
else:
if Width < 64:
Lo: int = -(1 << (Width - 1))
Hi: int = (1 << (Width - 1)) - 1
if Value < Lo or Value > Hi:
Ok = False
if Ok:
return
Lineno, Col, FilePath, CodeLine = _DefineSiteInfo(Gen, Trans, Node)
Loc: str = f'{FilePath}:{Lineno}:{Col}' if FilePath else f'line {Lineno}:{Col}'
Kind: str = 'uint' if Unsigned else 'int'
if Unsigned:
RangeTxt: str = f'[0, 2^{Width}-1 = {((1 << Width) - 1) if Width < 64 else 2 ** 64 - 1}]'
else:
RangeTxt: str = f'[-2^{Width - 1}, 2^{Width - 1}-1]' if Width < 64 else '[-2^63, 2^63-1]'
raise TypeError(
f'[TransPyC] 显式宽度字面量越界: {str(Value)!r} 放不进 {Kind}{Width}'
f'(该后缀宽度的允许范围是 {RangeTxt})。显式宽度后缀不允许截断,已终止编译。'
f'修法: 放宽后缀宽度(如改为 {Kind}{Width * 2 if Width * 2 <= 64 else 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 '')
)
# 注解修饰标记的旧书写名 → AST 字段 Markers 中的值。类型解析入口只认
# Markers / ByteOrder / BitWidth 字段;任何 `T1 | T2` 联合一律 fail-fast。
_ANNOTATION_MARKER_ALIASES: dict[str, str] = {
'CConst': 'const', 'CVolatile': 'volatile', 'CStatic': 'static',
'CExport': 'export', 'CExtern': 'extern', 'CInline': 'inline',
'State': 'state', 'CDefine': 'define', 'CTypedef': 'typedef',
}
# 指针/引用下标的 base 名:注解里出现这些名字即表示"指针层"(等价旧 CPtr 标记)。
_POINTER_SUBSCRIPT_NAMES: frozenset[str] = frozenset({
'CPtr', 'rawptr', 'CRef', 'CTempRef', 'CArrayPtr', 'CAutoPtr',
})
def RejectTypeUnionAnnotation(node: ast.AST | None) -> None:
"""类型注解位置遇到 `T1 | T2` 立即 fail-fast(不再合并/解析)。"""
if isinstance(node, ast.BinOp) and isinstance(node.op, ast.BitOr):
Line: int = getattr(node, 'lineno', 0) or 0
raise TypeError(
f"[TPC] 不支持类型联合 'T1 | T2'(第 {Line} 行);"
f"类型注解只能是单一类型:指针请写 CPtr[T],修饰符请用关键字")
def AnnotationContainsMarker(node: ast.AST, MarkerName: str) -> bool:
"""注解 AST 中是否出现指定内建标记名(裸名,或属性形态的尾名)。"""
if isinstance(node, ast.Name):
return node.id == MarkerName
if isinstance(node, ast.Attribute):
return node.attr == MarkerName
if isinstance(node, ast.BinOp):
RejectTypeUnionAnnotation(node)
if isinstance(node, ast.Call):
if AnnotationContainsMarker(node.func, MarkerName):
return True
return any(AnnotationContainsMarker(arg, MarkerName) for arg in node.args)
if isinstance(node, ast.Subscript):
return (AnnotationContainsMarker(node.value, MarkerName)
or AnnotationContainsMarker(node.slice, MarkerName))
return False
# ---------------------------------------------------------------------------
# virtual / novirtual 修饰符(软关键字)的判据 —— TPC 侧**唯一规则源**
# ---------------------------------------------------------------------------
# 源码形态 等价既有写法 语义
# virtual class C: @CVTable class C: 类级:强制有 vtable
# novirtual class C: @NoVTable class C: 类级:强制无 vtable
# virtual def m(...) @CVTable def m(...) 方法级:占 vtable 槽
# novirtual def m(...) @NoVTable def m(...) 方法级:不占 vtable 槽
#
# 解析层(lib/vast/Parser.py:_ParseVirtualModified)只合成同名装饰器,**不新造第二套
# 表示**;语义全部落在既有点位,本节函数是那些点位共用的同一判据(避免多处各写一份
# 而漂移),逐条对齐 VPC(Viper 自举编译器)的既有实现:
# - ClassHasFuncLevelCVTable ←→ VPC ClassGenBase._has_any_func_cvtable
# - MethodVirtualMarker ←→ VPC ClassGenBase._should_method_be_virtual 第 1/2 条
# - IsVtableSlotMethod ←→ VPC ClassGenBase._should_method_be_virtual 全 5 条
#
# 回归性:判据只在"方法带 CVTable/NoVTable 标记"时偏离原行为;全仓既有 .vp 无任何
# 方法级标记(普查结论见报告),故对既有类/既有代码零影响。
def _DecoratorListHasMarker(DecoratorList: object, MarkerName: str) -> bool:
"""装饰器表中是否存在指定内建标记(接受 @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.Name):
if Target.id == MarkerName:
return True
return False
def MethodVirtualMarker(MethodNode: ast.AST) -> str | None:
"""方法级 virtual/novirtual 标记 → 'CVTable' / 'NoVTable' / None。
NoVTable 优先(与 VPC _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:
"""类体内是否有方法带方法级 @CVTable(对齐 VPC _has_any_func_cvtable)。
跳过构造函数(__new__/__init__/__before_init__)——它们不进虚表。
用于"类的 vtable 是否由方法级标记触发"这一支(类级 NoVTable / 父类 NoVTable /
无真实父类时,VPC _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 槽——与 VPC 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)
# 内建定长数组构造子的裸名(与 CArray 等价的写法):
# CArray —— from t import * 后的裸名形式
# rawarray —— CArray 的内建复刻名(hashlib 等裸名化重构使用,免写 t. 前缀)
# array —— 本轮新增的内建语法保留字(软关键字,仅在类型/注解位置生效,
# 等价 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:
"""检查注解是否为栈上固定数组类型(CArray[...] / 内建 array[...])
**不含 `list[...]`**:`list` 是 includes/_list.vp 定义的堆上 STL 容器泛型类
(`list[T]` / `list[T, N]` 都走泛型特化),与定长数组无关。
"""
if not isinstance(annotation, ast.Subscript):
return False
value = annotation.value
# 裸名 CArray[...] / rawarray[...] / array[...]
if (isinstance(value, ast.Name)
and value.id in _FIXED_ARRAY_BARE_NAMES):
return True
return False
class ListAnnotationParseResult:
"""定长数组注解(CArray / rawarray / array)解析结果。
is_pointer=True 表示单参数 CArray[type](等价 CArray[type, None])→ 指针模式;
is_pointer=False 表示 Tuple(2) 模式 CArray[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:
"""解析定长数组注解(CArray / rawarray / array)。
接受 `CArray[type, count]` / `CArray[type]`;`T1 | T2` 联合直接 fail-fast。
返回 None 表示不是定长数组注解(`list[...]` 也返回 None —— 它是堆上 STL 容器)。
"""
RejectTypeUnionAnnotation(annotation)
if not IsListAnnotation(annotation):
return None
slice_node: ast.AST = annotation.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 是**软关键字**:仅在"类型/注解位置"作为内建定长数组构造子生效,
# 语义与 CArray 逐点等价(元素类型在前、长度在后)。非类型位置仍是普通
# 标识符(如 includes/json 的 def array(...)、numpy 的 def array(...),
# 因此**不能**做成词法保留字)。
#
# 与内建 rawptr 的机制对应:都"不可重定义"(在类型位置优先于任何用户声明),
# 但 array 因与既有标识符重名,只在类型位置生效(软关键字),而 rawptr 在
# 各解析入口直接短路(硬内置名)。
#
# 新写法要求**恰好两个参数** `array[E, N]`:缺参 / 多参 / 长度非正整型常量
# 表达式均为 fail-fast(不静默降级)。CArray[E] 的单参/长度自动推导写法
# 仍然可用(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/负数/非整数都不允许(长度自动推导请改用 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:
"""注解节点的 Markers 是否含 'inline'(`CInline` 旧名不再按文本判断)。"""
if annotation_node is None:
return False
RejectTypeUnionAnnotation(annotation_node)
for node in ast.walk(annotation_node):
if 'inline' in (getattr(node, 'Markers', None) or ()):
return True
return False
def ExtractTypeNameFromBinOp(annotation: ast.expr) -> str | None:
"""旧联合写法的残留入口:`T1 | T2` 一律 fail-fast(返回 None 不再作为语义)。
保留函数名仅为兼容既有导入;合法调用不会命中 BinOp。
"""
RejectTypeUnionAnnotation(annotation)
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:
"""递归在注解中查找结构体名(`T1 | T2` 联合直接 fail-fast)。
检查 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])
if IsListAnnotation(node) and elem_nodes:
# 定长数组只有第一个元素是类型;其余是长度常量表达式
elem_nodes = elem_nodes[:1]
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):
RejectTypeUnionAnnotation(node)
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, '_from_import_aliases', {}),
StructSha1Map=getattr(Gen, '_struct_sha1_map', {}))
def AnnotationContainsName(annotation: ast.AST, name: str) -> bool:
"""注解中是否包含指定名称(同时读取 AST 修饰字段)。
- 修饰词(CConst/CVolatile/CStatic/CExport/CExtern/CInline/State/CDefine/
CTypedef)按节点 `Markers` 字段判断,不再依赖裸名/属性名;
- 指针/引用下标(CPtr/rawptr/CRef/CTempRef/CArrayPtr/CAutoPtr)递归到
Subscript 的 base/slice,故查询 'CPtr' 时 rawptr[X] 等指针注解同样命中;
- 任何 `T1 | T2` 联合直接 fail-fast。
"""
if annotation is None:
return False
RejectTypeUnionAnnotation(annotation)
marker: str | None = _ANNOTATION_MARKER_ALIASES.get(name)
if marker is not None and marker in (getattr(annotation, 'Markers', None) or ()):
return True
if isinstance(annotation, ast.Attribute):
return annotation.attr == name
if isinstance(annotation, ast.Name):
return annotation.id == name
if isinstance(annotation, ast.Subscript):
base: ast.AST = annotation.value
base_name: str = ''
if isinstance(base, ast.Name):
base_name = base.id
elif isinstance(base, ast.Attribute):
base_name = base.attr
if name == 'CPtr' and base_name in _POINTER_SUBSCRIPT_NAMES:
return True
slice_node: ast.AST = annotation.slice
if IsListAnnotation(annotation):
# 定长数组只有第一个元素是类型;其余是长度常量表达式(可能是 `A | B` 位运算)
if isinstance(slice_node, ast.Tuple) and slice_node.elts:
slice_node = slice_node.elts[0]
return (AnnotationContainsName(base, name)
or AnnotationContainsName(slice_node, name))
if isinstance(annotation, ast.Call):
if AnnotationContainsName(annotation.func, name):
return True
return any(AnnotationContainsName(arg, name) for arg in annotation.args)
if isinstance(annotation, (ast.Tuple, ast.List)):
return any(AnnotationContainsName(elt, name) for elt in annotation.elts)
return False
def IsCPtrNode(node: ast.AST) -> bool:
"""检查节点是否为 CPtr 标记(裸名 `CPtr`)。"""
return isinstance(node, ast.Name) and node.id == 'CPtr'
# 值引用参数标记名 → 自动取址模式(仅注解 AST 内按名字识别,非真实类):
# 'CRef' → 'ref' (&T :实参非指针左值取址,右值 fail-fast)
# 'CTempRef' → 'temp' (&temp T :实参非指针时在调用方建同型临时再取址)
# 'CAutoPtr' → 'always' (CAutoPtr:无条件取址,保持既有语义)
_AUTO_ADDR_MARKER_MODES: dict[str, str] = {
'CRef': 'ref', 'CTempRef': 'temp', 'CAutoPtr': 'always',
}
def FindAutoAddrMode(node: ast.AST | None) -> str | None:
"""注解 AST 内查找自动取址模式标记,返回 'ref' / 'temp' / 'always';无则 None。
只按名字识别(Attribute.attr 或 Name.id),递归嵌套注解;CRef/CTempRef
非真实类,不出现在类型模型模块中。`T1 | T2` 联合直接 fail-fast。
"""
if node is None:
return None
RejectTypeUnionAnnotation(node)
if isinstance(node, ast.Attribute):
mode = _AUTO_ADDR_MARKER_MODES.get(node.attr)
if mode is not None:
return mode
return FindAutoAddrMode(node.value)
if isinstance(node, ast.Name):
return _AUTO_ADDR_MARKER_MODES.get(node.id)
if isinstance(node, ast.Subscript):
mode = FindAutoAddrMode(node.value)
if mode is not None:
return mode
slice_node: ast.AST = node.slice
if IsListAnnotation(node) and isinstance(slice_node, ast.Tuple) and slice_node.elts:
# 定长数组长度是常量表达式,不参与取址标记查找
slice_node = slice_node.elts[0]
return FindAutoAddrMode(slice_node)
if isinstance(node, ast.Call):
mode = FindAutoAddrMode(node.func)
if mode is not None:
return mode
for arg in list(node.args) + [kw.value for kw in node.keywords]:
mode = FindAutoAddrMode(arg)
if mode is not None:
return mode
return None
if isinstance(node, (ast.Tuple, ast.List)):
for elt in node.elts:
mode = FindAutoAddrMode(elt)
if mode is not None:
return mode
return None
def IsCPtrSubscriptNode(node: ast.AST) -> bool:
"""检查节点是否为 `CPtr[T]` 下标指针类型(非裸标记,裸标记见 IsCPtrNode)。
与 VPC TypeGenResolve._is_cptr_subscript 同口径:value 必须是裸名 `CPtr`;
切片内的指针层级不限(`CPtr[CPtr]` / `CPtr[CPtr[CInt]]` 均命中)。
链式写法 CPtr[T][T](value 是 Subscript)不认作该标记。
"""
return (isinstance(node, ast.Subscript)
and IsCPtrNode(node.value))
# 指针下标的 base 名:rawptr 是编译器内置语法(TPC/VPC 双端保留字,见
# includes/builtins.vp 语义注释与 TypeAnnotationResolver._BUILTIN_ROWPTR_NAME),
# 单独 rawptr 等价 CPtr(void*)。
_RAWPTR_SUBSCRIPT_NAME: str = 'rawptr'
# 指针标记类型名(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:
"""from-import 别名 → 原始类型名('X' → 'CPtr'、'myint' → 'CInt');未命中原样返回。"""
if not NameId:
return NameId
if TCAliasMap:
Entry: tuple[str, str] | None = TCAliasMap.get(NameId)
if Entry:
return Entry[1]
if symtable is not None:
Cls: object | None = getattr(symtable, '_builtin_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]` 语义 = `CPtr[T]` = Ptr(T),二者必须走同一解析与拼名路径,
否则同一类型的不同书写会分裂出不同特化名(结构体/方法声明/调用点三处不一致)。
注意:本判据**只**认 `rawptr[T]`;`CPtr[T]` / 裸名 `CPtr[T]` 由
IsPtrSubscriptNode 判定(见下),此处保持 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 | 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]` / `CPtr[Mod.Class]`。
只回答"这个注解**本应**解析出结构体指针",不做任何类型解析,判据保守:
- 必须是指针包装:`rawptr[T]` / `CPtr[T]` / 裸名 `CPtr[T]`(`T1 | T2` 联合
已不再允许,命中即 fail-fast);
- 内层须是类引用形态:`Attribute` 且**根模块不是 `t` / `c`**
(如 `ExprGenFstr.ExprGenFstr`、`llvmlite.LLVMType`),或首字母大写的裸名
(类名书写惯例)。
模块名、`rawptr` / `CPtr` 标记本身、泛型特化实参(内层是 Subscript)、
`str` / `int` 这类小写内建名一律**不算**。
"""
RejectTypeUnionAnnotation(Ann)
Inner: ast.AST | None = None
if IsRawPtrSubscriptNode(Ann) or IsCPtrSubscriptNode(Ann):
Inner = Ann.slice
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 bool(Root)
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]` / `CPtr[T]` / 裸名 `CPtr[T]`
/ 导入别名 `X[T]`(`from t import CPtr as X`)。
二者语义完全相同(= Ptr(T)):内层 T 按**参数注解 `a: T` 的规则**
解析(跨模块类 → '<sha1>.X'),再包一层指针——T 为类引用时该层被结构体
自身吸收('<sha1>.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 判据对齐,避免同一书写两端分裂);
字面名场景直接按内建名归一。
- 导入名别名(`from t import CPtr as X` 的 `X[T]`)经别名表解析 X→CPtr
(TCAliasMap 或 symtable._builtin_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 NormalizeGenericArgName(NameId: str) -> str:
"""泛型实参名归一:内建短名 → C 类名(int → CInt、uint64 → CUInt64T、
sizet → CSizeT、char → CChar、bool → CBool…),C* 类名与其余名字原样返回。
归一结果再交给 `MangleTypeArgByRegistry` 变成 LLVM 名(CInt → i32、
CUInt64T → i64),因此 `list[int]` 与 `list[CInt]` 得到同一特化名。
"""
return GenericArgRawName(NameId)
_LLVM_FLOAT_STRINGS: frozenset[str] = frozenset({'half', 'float', 'double', 'fp128'})
_GENERIC_ARG_RAW_NAMES: dict[str, str] = {
'int': 'CInt', 'char': 'CChar', 'bool': 'CBool',
'float': 'CFloat', 'double': 'CDouble', 'void': 'CVoid',
}
for _generic_short_name, _generic_class in TypeSystem.BUILTIN_TYPE_ALIASES.items():
_GENERIC_ARG_RAW_NAMES.setdefault(_generic_short_name, _generic_class.__name__)
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/c 模块前缀先剥离;C 类型名经 NameToLLVM 归一
(CInt32T → 'i32'、CInt → 'i32'、CUInt64T → 'i64'),浮点按 Size 归一为
f{n}(CDouble → 'f64'),未注册名原样保留(含指针层数与已解析的内层名)。
"""
LookupStr: str = ArgStr
Stars: int = len(LookupStr) - len(LookupStr.rstrip('*'))
if Stars:
LookupStr = LookupStr[:-Stars]
if LookupStr.startswith(('t.', 'c.')):
LookupStr = LookupStr.split('.', 1)[1]
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) + ('*' * Stars)
return LlvmStr + ('*' * Stars)
# 指针层数的首层归一化(与 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[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:
"""`CPtr` 裸指针标记(含 `import t as tt` 与 `from t import CPtr as X` 别名)。
与 IsCPtrNode 的区别:仅按 attr 名 + t 模块前缀判定,不看切片
(`CPtr[T]` 下标是**基类型**而非层数标记,见 ResolveTypeArgStr 的
Subscript 分支)。注解联合 `T1 | T2` 已禁止,本判据仅用于类型实参解析。
- Attribute:`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)
)
if isinstance(Node, ast.Name):
return ResolveTCImportAliasName(Node.id, symtable, TCAliasMap) == _CPTR_TYPE_NAME
return False
def FlattenUnionOperands(Node: ast.AST) -> list[ast.AST]:
"""旧联合辅助函数:`T1 | T2` 已不再允许,命中即 fail-fast;其余返回 [Node]。"""
RejectTypeUnionAnnotation(Node)
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
归一(内建短名 → C 类名,再由 MangleTypeArgByRegistry 归一为 LLVM 名)
- ast.Attribute: t 模块类型按 CTypeRegistry.NameToLLVM 解析(CPtr → i8*、
CInt → i32);跨模块类引用 module.X → '{sha1}.X';其余 '{mod}.{attr}'
- ast.Subscript: 指针/引用下标(`rawptr[T]` / `CPtr[T]` / `CRef[T]` /
`CTempRef[T]` / `CArrayPtr[T]` / `CAutoPtr[T]`)→ 内层实参串 + 一层 '*'
(`_ResolveSubscriptArgStr`);其余为泛型容器:
'<容器名>[<已解析实参>]'(多实参 '][' 分隔),容器名的
模块前缀由调用方(_mangle_name / '%"{模sha1}.{特化名}"')统一加
- ast.BinOp(BitOr): `T1 | T2` 类型联合已禁止 ⇒ fail-fast
- 其余形态: ast.unparse
注意:名称域**不做**"裸类引用吸收一层 CPtr"(那是注解→LLVM 类型域的规则,
见 DeclarationGenerator._is_bare_class_ref_annotation):名称域描述的是类型
身份('X' = X 本身、'X*' = 指向 X 的指针),逐层累加。
"""
if isinstance(Node, ast.Name):
tn: str = Node.id
if TCAliasMap and tn in TCAliasMap:
SrcModule, SrcName = TCAliasMap[tn]
return NormalizeGenericArgName(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):
RejectTypeUnionAnnotation(Node)
return ast.unparse(Node)
def _ResolveAttributeArgStr(Node: ast.Attribute, TCAliasMap: dict[str, tuple[str, str]] | None,
StructSha1Map: dict[str, str] | None) -> str:
"""Attribute 形态(CInt / ast.AST / <sha1>.AST)→ 实参串。"""
if not isinstance(Node.value, ast.Name):
return ast.unparse(Node)
ModName: str = Node.value.id
AttrName: str = Node.attr
if TCAliasMap and ModName in TCAliasMap:
LlvmStr: str | None = CTypeRegistry.NameToLLVM(AttrName)
if LlvmStr is not None:
return LlvmStr
return f'{ModName}.{AttrName}'
# 跨模块类引用(ast.AST / <sha1>.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:
"""下标形态(CPtr[T] / CRef[T] / 泛型容器 list[X] / dict[K,V])→ 实参串。
指针/引用下标(CPtr/rawptr/CRef/CTempRef/CArrayPtr/CAutoPtr)统一表示为
"内层实参串 + 一层 '*'",与声明端逐层累加口径一致。
容器名('list' / 'dict' ...)**不带**模块前缀:它只是 _mangle_generic_class_name
的基名,特化名的模块前缀由调用方统一加(定义端 _mangle_name / 声明端
'%"{模sha1}.{特化名}"'),并保证 '_make_replacement_node' 能把实参串
重新解析成可用的注解 AST('<sha1>.list[...]' 会因数字开头而解析失败)。
"""
BaseName: str = ''
if isinstance(Node.value, ast.Name):
BaseName = Node.value.id
elif isinstance(Node.value, ast.Attribute):
BaseName = Node.value.attr
if IsPtrSubscriptNode(Node, TCAliasMap=TCAliasMap) \
or BaseName in ('CRef', 'CTempRef', 'CArrayPtr', 'CAutoPtr'):
Inner: str = ResolveTypeArgStr(Node.slice, TCAliasMap, StructSha1Map)
return AddPointerLayers(Inner, 1)
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]
if IsListAnnotation(Node) and ElemNodes:
# 定长数组只有元素类型参与类型身份;长度是常量表达式,不参与 mangling
ElemNodes = ElemNodes[:1]
Resolved: list[str] = [ResolveTypeArgStr(E, TCAliasMap, StructSha1Map) for E in ElemNodes]
return f'{BaseName}[' + ']['.join(Resolved) + ']'
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, '_from_import_aliases', 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 形态:`CPtr[...]` / `CArray[...]` / 裸 `array[...]` /
`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 只认
双参定长 CArray[T, N];LLVMTypeMapper 亦然)⇒ 落到"未知类型"兜底 i32 / 空 CTypeInfo,
形参与成员类型整体错位(现场:ClassGenGeneric._specialize_generic_class 的
形参 `type_args: list[str]*` 在符号表里成 i32;字段
`TypeGenBase.CTypedefNames: list[str] | 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(内建短名 → C 类名)→(命中注册类则
'{sha1}.{名}')→ MangleTypeArgByRegistry
('int' → 'i32'、'i64' → 'i64'、'str' → 'str'、'CInt32T' → 'i32'、
'CFloat64T' → 'f64'、'CChar' → 'i8')
- 其余形态(Attribute / Subscript / 混合 Tuple 元素):
ResolveTypeArgStr(指针/引用下标 → 逐层 '*'、跨模块类 → sha1 前缀)
→ MangleTypeArgByRegistry;`T1 | T2` 联合 fail-fast。
"""
return MangleTypeArgByRegistry(ResolveTypeArgStr(Node, TCAliasMap, StructSha1Map))