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