Files
ViperC/App/lib/core/Handles/HandlesAnnAssign.vp
T

549 lines
24 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import t, c
from stdint import *
import ast
import llvmlite
import memhub
import string
import stdio
import lib.core.Handles.HandlesBase as HandlesBase
import lib.core.Handles.HandlesTranslator as HT
import lib.core.Handles.HandlesVar as HandlesVar
import lib.core.Handles.HandlesExpr as HandlesExpr
import lib.core.Handles.HandlesType as HandlesType
import lib.core.Handles.HandlesNonlocal as HandlesNonlocal
import lib.core.Handles.HandlesClassDef as HandlesClassDef
import lib.core.Handles.HandlesExprCall as HandlesExprCall
import lib.core.Handles.HandlesStruct as HandlesStruct
import lib.core.VLogger as VLogger
# ============================================================
# HandlesAnnAssign - AnnAssign 语句处理(Mixin 继承模式)
# ============================================================
# ============================================================
# is_cdefine_annotation - 检测注解是否为 t.CDefine(模块级函数)
#
# 支持两种形式:
# 1. Attribute(Name('t'), 'CDefine')
# 2. BinOp(... | Attribute(Name('t'), 'CDefine'))(联合注解)
# ============================================================
def is_cdefine_annotation(annot: ast.AST | t.CPtr) -> int:
"""检测注解是否为 t.CDefine,返回 1 表示是,0 表示否"""
if annot is None:
return 0
k: int = annot.kind()
if k == ast.ASTKind.Attribute:
at: ast.Attribute | t.CPtr = (ast.Attribute | t.CPtr)(annot)
if at.attr is not None and string.strcmp(at.attr, "CDefine") == 0:
return 1
return 0
if k == ast.ASTKind.BinOp:
bop: ast.BinOp | t.CPtr = (ast.BinOp | t.CPtr)(annot)
if is_cdefine_annotation(bop.left) != 0:
return 1
if is_cdefine_annotation(bop.right) != 0:
return 1
return 0
# ============================================================
# extract_cdefine_int_value - 从 AnnAssign.value 提取整数常量(模块级函数)
#
# 支持的表达式形式:
# 1. Constant(INT) — 如 0x0002, 42
# 2. BinOp(BitOr/BitAnd) — 如 FOREGROUND_RED | FOREGROUND_GREEN
# 3. Name — 引用已注册的 CDefine 常量
# 4. Call — 如 t.CUnsignedLong(-11) → 取第一个参数
# 5. UnaryOp(USub/UAdd/Invert) — 如 -11
# ============================================================
def extract_cdefine_int_value(val_node: ast.AST | t.CPtr) -> int:
"""从值节点提取整数常量(支持 Constant/BinOp/Name/Call/UnaryOp)"""
if val_node is None:
return 0
k: int = val_node.kind()
# Case 1: Constant(INT) — 如 0x0002
if k == ast.ASTKind.Constant:
cn: ast.Constant | t.CPtr = (ast.Constant | t.CPtr)(val_node)
if cn.const_kind != ast.CONST_INT:
return 0
return cn.int_val
# Case 2: BinOp — 如 FOREGROUND_RED | FOREGROUND_GREEN
if k == ast.ASTKind.BinOp:
bop: ast.BinOp | t.CPtr = (ast.BinOp | t.CPtr)(val_node)
left_val: int = extract_cdefine_int_value(bop.left)
right_val: int = extract_cdefine_int_value(bop.right)
if bop.op == ast.OpKind.BitOr:
return left_val | right_val
if bop.op == ast.OpKind.BitAnd:
return left_val & right_val
if bop.op == ast.OpKind.Add:
return left_val + right_val
if bop.op == ast.OpKind.Sub:
return left_val - right_val
return 0
# Case 3: Name — 引用已注册的 CDefine 常量
if k == ast.ASTKind.Name:
nm: ast.Name | t.CPtr = (ast.Name | t.CPtr)(val_node)
if nm.id is not None:
looked_up: int = HandlesType.lookup_cdefine_constant(nm.id)
if HandlesType.is_cdefine_found() != 0:
return looked_up
return 0
# Case 4: Call — 如 t.CUnsignedLong(-11)
if k == ast.ASTKind.Call:
cl: ast.Call | t.CPtr = (ast.Call | t.CPtr)(val_node)
# 必须先把属性赋给显式类型为 list[...] | t.CPtr 的局部变量再调用 __len__(),
# 否则编译器无法识别属性返回的 list 类型,GEP base 会变成 i32 0 导致 llc 报错
cl_args_list: list[ast.AST | t.CPtr] | t.CPtr = cl.args
if cl_args_list is not None and cl_args_list.__len__() > 0:
first_arg: ast.AST | t.CPtr = cl_args_list.get(0)
arg_val: int = extract_cdefine_int_value(first_arg)
return arg_val
return 0
# Case 5: UnaryOp — 如 -11
if k == ast.ASTKind.UnaryOp:
uop: ast.UnaryOp | t.CPtr = (ast.UnaryOp | t.CPtr)(val_node)
operand_val: int = extract_cdefine_int_value(uop.operand)
if uop.op == ast.OpKind.USub:
return -operand_val
if uop.op == ast.OpKind.UAdd:
return operand_val
if uop.op == ast.OpKind.Invert:
return ~operand_val
return 0
return 0
# ============================================================
# _annotation_contains_name - 递归检查注解中是否包含指定名称
#
# 支持 BinOp(BitOr) 递归,同时检查 Attribute.attr 和 Name.id。
# ============================================================
def _annotation_contains_name(annot: ast.AST | t.CPtr, name: str) -> int:
"""递归检查注解中是否包含指定名称,返回 1=包含 / 0=不包含"""
if annot is None or name is None:
return 0
k: int = annot.kind()
if k == ast.ASTKind.Attribute:
at: ast.Attribute | t.CPtr = (ast.Attribute | t.CPtr)(annot)
if at.attr is not None and string.strcmp(at.attr, name) == 0:
return 1
return 0
if k == ast.ASTKind.Name:
nm: ast.Name | t.CPtr = (ast.Name | t.CPtr)(annot)
if nm.id is not None and string.strcmp(nm.id, name) == 0:
return 1
return 0
if k == ast.ASTKind.BinOp:
bop: ast.BinOp | t.CPtr = (ast.BinOp | t.CPtr)(annot)
if _annotation_contains_name(bop.left, name) != 0:
return 1
if _annotation_contains_name(bop.right, name) != 0:
return 1
return 0
# ============================================================
# _is_ptr_element_annotation - 检查注解是否为 bytes|t.CPtr 或 str|t.CPtr
#
# 返回 1=是(下标按 8 字节步长, i8** 语义), 0=否(下标按 1 字节步长, i8* 语义)
#
# 规则:
# bytes|t.CPtr / str|t.CPtr → i8**(8 字节步长)→ 返回 1
# t.CChar|t.CPtr / t.CInt8T|t.CPtr → i8*(1 字节步长)→ 返回 0
# 纯 bytes / str → i8*(1 字节步长)→ 返回 0
#
# 注: 注解中的 "|" 是"和"的意思(组合类型),非右值中的位或运算
# ============================================================
def _is_ptr_element_annotation(annot: ast.AST | t.CPtr) -> int:
"""检查注解是否为 bytes|t.CPtr 或 str|t.CPtr 形式"""
if annot is None:
return 0
k: int = annot.kind()
if k != ast.ASTKind.BinOp:
return 0
bop: ast.BinOp | t.CPtr = (ast.BinOp | t.CPtr)(annot)
if bop.op != ast.OpKind.BitOr:
return 0
if _annotation_contains_name(annot, "str") != 0:
return 1
if _annotation_contains_name(annot, "bytes") != 0:
return 1
return 0
# ============================================================
# _init_global_array_from_list — 从列表字面量初始化全局数组
#
# 为每个元素生成 GEP + store 指令,将值存入全局数组的对应位置。
# 适用于模块级 t.CArray[elem_ty, count] = [v0, v1, ...] 的初始化。
#
# Args:
# builder: IRBuilder
# pool: 编译器内存池
# mod: LLVMModule
# array_alloca: 全局数组变量的 Value 引用(类型为 [N x elem_ty]*)
# list_node: ast.List 节点
# trans: Translator 对象
#
# Returns:
# 0 成功,1 失败
# ============================================================
def _init_global_array_from_list(builder: llvmlite.IRBuilder | t.CPtr,
pool: memhub.MemBuddy | t.CPtr,
mod: llvmlite.LLVMModule | t.CPtr,
array_alloca: llvmlite.Value | t.CPtr,
list_node: ast.AST | t.CPtr,
trans: HT.Translator | t.CPtr) -> int:
"""从列表字面量初始化全局数组,为每个元素生成 GEP + store"""
if builder is None or array_alloca is None or list_node is None:
return 1
# 获取数组类型(Pointee of [N x elem_ty]*)
arr_ty: llvmlite.LLVMType | t.CPtr = None
if array_alloca.Ty is not None:
arr_ty = array_alloca.Ty.Pointee
if arr_ty is None:
return 1
# 匹配数组类型获取元素类型
elem_ty: llvmlite.LLVMType | t.CPtr = None
match arr_ty:
case llvmlite.LLVMType.Array(et, _):
elem_ty = et
if elem_ty is None:
return 1
# 获取列表元素
lst: ast.List | t.CPtr = (ast.List | t.CPtr)(list_node)
if lst is None or lst.elts is None:
return 1
elts: list[ast.AST | t.CPtr] | t.CPtr = lst.elts
elts_count: t.CSizeT = elts.__len__()
# 为每个元素生成 GEP + store
i: t.CSizeT = 0
while i < elts_count:
elem_node: ast.AST | t.CPtr = elts.get(i)
elem_val: llvmlite.Value | t.CPtr = HandlesExpr.translate_value(
builder, pool, mod, elem_node, None, 0, trans)
if elem_val is not None:
idx_val: llvmlite.Value | t.CPtr = llvmlite.const_int32(pool, i)
elem_ptr: llvmlite.Value | t.CPtr = llvmlite.build_gep_array(
builder, arr_ty, elem_ty, array_alloca, idx_val)
if elem_ptr is not None:
# 类型转换(如 i32 → i8 截断)
store_val: llvmlite.Value | t.CPtr = elem_val
if elem_ptr.Ty is not None and elem_ptr.Ty.Pointee is not None:
store_val = HandlesExpr.coerce_to_type(
builder, elem_val, elem_ptr.Ty.Pointee, elem_node)
llvmlite.build_store(builder, store_val, elem_ptr)
i += 1
return 0
@t.NoVTable
class AnnAssignHandle(HandlesBase.Mixin):
"""AnnAssign 语句处理器:继承 Mixin 获得 Trans 回指针"""
def __init__(self, trans: HT.Translator | t.CPtr):
self.Trans = trans
# ============================================================
# PreScan - 预扫描 AnnAssign,为有类型注解的变量提前创建 alloca
#
# 返回新增的变量数
# ============================================================
def PreScan(self, node: ast.AST | t.CPtr) -> int:
"""预扫描 AnnAssign,提前创建 alloca 到 entry block 顶部"""
if node is None:
return 0
k: int = node.kind()
if k != ast.ASTKind.AnnAssign:
return 0
aa: ast.AnnAssign | t.CPtr = (ast.AnnAssign | t.CPtr)(node)
if aa is None or aa.target is None:
return 0
target: ast.AST | t.CPtr = aa.target
if target.kind() != ast.ASTKind.Name:
return 0
nm: ast.Name | t.CPtr = (ast.Name | t.CPtr)(target)
if nm.id is None:
return 0
# CDefine 注解: 不创建 alloca,但注册常量到全局表
# 供后续 t.CArray[elem_ty, NAME] 编译期解析使用
if is_cdefine_annotation(aa.annotation) != 0:
pool_ps: memhub.MemBuddy | t.CPtr = self.Trans.Pool
val_ps: int = extract_cdefine_int_value(aa.value)
HandlesType.register_cdefine_constant(pool_ps, nm.id, val_ps)
return 0
# global/nonlocal 变量不需要局部 alloca
if HT.is_global_name(self.Trans, nm.id) != 0:
return 0
if HT.is_nonlocal_name(self.Trans, nm.id) != 0:
return 0
# 模块级全局变量已由 handle_module_level_var 注册到模块作用域。
# Python 语义:仅模块级代码(Current == Root)时跳过局部 alloca;
# 函数内无 global 声明时应创建局部变量(不遮蔽全局)。
if HandlesVar.is_module_scope(self.Trans.SymTab) != 0:
if HandlesVar.lookup_module_var(self.Trans.SymTab, nm.id) is not None:
return 0
# 检查是否已存在
existing: llvmlite.Value | t.CPtr = HandlesVar.lookup_current(
self.Trans.SymTab, nm.id)
if existing is not None:
return 0
pool: memhub.MemBuddy | t.CPtr = self.Trans.Pool
builder: llvmlite.IRBuilder | t.CPtr = self.Trans._cur_builder
# 从 annotation 推断类型(修复:原硬编码 i32 导致 str 等类型错误)
var_ty: llvmlite.LLVMType | t.CPtr = None
annot: ast.AST | t.CPtr = aa.annotation
if annot is not None:
resolved: llvmlite.LLVMType | t.CPtr = HandlesType.resolve_annotation_type(
pool, annot, self.Trans._imported_modules, self.Trans._from_imports, self.Trans)
if resolved is None:
# 尝试特化泛型类注解(如 list[str])
# resolve_annotation_type 返回 None 可能是因为泛型类未特化
if annot.kind() == ast.ASTKind.Subscript:
sub_annot: ast.Subscript | t.CPtr = (ast.Subscript | t.CPtr)(annot)
if sub_annot.value is not None and sub_annot.value.kind() == ast.ASTKind.Name:
sub_nm: ast.Name | t.CPtr = (ast.Name | t.CPtr)(sub_annot.value)
if sub_nm.id is not None:
template_cd: ast.ClassDef | t.CPtr = HandlesClassDef._find_generic_template(sub_nm.id)
if template_cd is not None:
type_args_ps: list[str] | t.CPtr = HandlesExprCall._extract_type_args_from_slice(pool, sub_annot.slice)
if type_args_ps is not None and type_args_ps.__len__() > 0:
spec_name_ps: str = HandlesClassDef._specialize_generic_class(
self.Trans, sub_nm.id, type_args_ps)
if spec_name_ps is not None:
resolved = HandlesType.resolve_annotation_type(
pool, annot, self.Trans._imported_modules, self.Trans._from_imports, self.Trans)
if resolved is not None:
var_ty = resolved
else:
# fast-fail: 注解类型解析失败,不再静默回退 i32(会导致变量类型错误)
VLogger.error(
f"变量类型注解解析失败,变量名: {nm.id}",
"HandlesAnnAssign")
var_ty = llvmlite.Int32(pool)
else:
# fast-fail: AnnAssign 必须有 annotation
VLogger.error(
f"AnnAssign 缺少类型注解,变量名: {nm.id}",
"HandlesAnnAssign")
var_ty = llvmlite.Int32(pool)
# 确保跨模块结构体的完整定义在当前模块中可用(供 alloca 分配空间)
HandlesStruct.ensure_struct_def_in_module(pool, self.Trans.Module, var_ty)
alloca: llvmlite.Value | t.CPtr = HandlesVar._alloca_at_entry(builder, var_ty)
if alloca is not None:
if HandlesVar.define_var(
self.Trans.SymTab, nm.id, alloca) == 0:
# 存储原始类型注解的类名(方法调用检测时,Ptr(i8) 回退到类名查找结构体)
if annot is not None:
cls_nm_ps: str = HandlesType.extract_class_name_from_annotation(
annot, self.Trans._imported_modules)
if cls_nm_ps is not None:
HandlesVar.set_var_annot_class_name(
self.Trans.SymTab, nm.id, cls_nm_ps)
# 检查是否为 bytes|t.CPtr 或 str|t.CPtr(i8** 语义,8 字节步长)
if _is_ptr_element_annotation(annot) != 0:
HandlesVar.set_var_ptr_element(
self.Trans.SymTab, nm.id)
return 0
return 0
# ============================================================
# Handle - 翻译 AnnAssign 语句
#
# 返回新增的变量数
# ============================================================
def Handle(self, node: ast.AST | t.CPtr) -> int:
"""翻译 AnnAssign(target=Name, annotation=..., value=expr)"""
aa: ast.AnnAssign | t.CPtr = (ast.AnnAssign | t.CPtr)(node)
if aa is None:
return 0
target: ast.AST | t.CPtr = aa.target
if target is None or target.kind() != ast.ASTKind.Name:
return 0
nm: ast.Name | t.CPtr = (ast.Name | t.CPtr)(target)
if nm.id is None:
return 0
# CDefine 注解: 编译期常量,不生成运行时代码
# 注册到全局 CDefine 表供 t.CArray[elem_ty, NAME] 解析
# 注: PreScan 已注册过,此处保证即使跳过 PreScan 也能正确注册
if is_cdefine_annotation(aa.annotation) != 0:
pool_cd: memhub.MemBuddy | t.CPtr = self.Trans.Pool
val_cd: int = extract_cdefine_int_value(aa.value)
HandlesType.register_cdefine_constant(pool_cd, nm.id, val_cd)
return 0
pool: memhub.MemBuddy | t.CPtr = self.Trans.Pool
builder: llvmlite.IRBuilder | t.CPtr = self.Trans._cur_builder
mod: llvmlite.LLVMModule | t.CPtr = self.Trans.Module
# 确定类型(从 annotation 推断)
var_ty: llvmlite.LLVMType | t.CPtr = None
annot: ast.AST | t.CPtr = aa.annotation
if annot is not None:
resolved: llvmlite.LLVMType | t.CPtr = HandlesType.resolve_annotation_type(
pool, annot, self.Trans._imported_modules, self.Trans._from_imports, self.Trans)
if resolved is not None:
var_ty = resolved
else:
# fast-fail: 注解类型解析失败,不再静默回退 i32
VLogger.error(
f"变量类型注解解析失败,变量名: {nm.id}",
"HandlesAnnAssign")
var_ty = llvmlite.Int32(pool)
else:
# fast-fail: AnnAssign 必须有 annotation
VLogger.error(
f"AnnAssign 缺少类型注解,变量名: {nm.id}",
"HandlesAnnAssign")
var_ty = llvmlite.Int32(pool)
# global 变量:写入模块作用域中的全局变量
if HT.is_global_name(self.Trans, nm.id) != 0:
mod_alloca: llvmlite.Value | t.CPtr = HandlesVar.lookup_module_var(
self.Trans.SymTab, nm.id)
if mod_alloca is not None and aa.value is not None:
rhs_val: llvmlite.Value | t.CPtr = HandlesExpr.translate_value(
builder, pool, mod, aa.value,
None, 0, self.Trans)
if rhs_val is not None:
target_ty: llvmlite.LLVMType | t.CPtr = None
if mod_alloca.Ty is not None:
target_ty = mod_alloca.Ty.Pointee
if target_ty is not None:
rhs_val = HandlesExpr.coerce_to_type(builder, rhs_val, target_ty, node)
llvmlite.build_store(builder, rhs_val, mod_alloca)
return 0
# nonlocal 变量:通过闭包 env 写入
if HT.is_nonlocal_name(self.Trans, nm.id) != 0:
if aa.value is not None:
rhs_val: llvmlite.Value | t.CPtr = HandlesExpr.translate_value(
builder, pool, mod, aa.value,
None, 0, self.Trans)
if rhs_val is not None:
nl_ptr: llvmlite.Value | t.CPtr = HandlesNonlocal.get_nonlocal_var_ptr(
self.Trans, nm.id)
if nl_ptr is not None:
# 从 nl_ptr 的类型推断 nonlocal 变量实际类型,
# 不再强制 i32(会截断指针/结构体/i64 导致闭包数据损坏)
nl_target_ty: llvmlite.LLVMType | t.CPtr = None
if nl_ptr.Ty is not None:
nl_target_ty = nl_ptr.Ty.Pointee
if nl_target_ty is None:
# fast-fail: nl_ptr 类型信息丢失,无法安全 store
VLogger.error(
f"nonlocal 变量类型信息丢失,无法写入: {nm.id}",
"HandlesAnnAssign")
return 0
rhs_coerced: llvmlite.Value | t.CPtr = HandlesExpr.coerce_to_type(
builder, rhs_val, nl_target_ty, node)
llvmlite.build_store(builder, rhs_coerced, nl_ptr)
return 0
# 模块级全局变量(由 handle_module_level_var 注册到模块作用域)
# Python 语义:仅模块级代码(Current == Root)时修改全局变量;
# 函数内无 global 声明时应创建局部变量(走后面的 get_or_create_sym 路径)。
if HandlesVar.is_module_scope(self.Trans.SymTab) != 0:
mod_glob: llvmlite.Value | t.CPtr = HandlesVar.lookup_module_var(
self.Trans.SymTab, nm.id)
else:
mod_glob = None
if mod_glob is not None:
if aa.value is not None:
# 列表字面量 → 逐元素 GEP + store 初始化数组
if aa.value.kind() == ast.ASTKind.List:
_init_global_array_from_list(
builder, pool, mod, mod_glob, aa.value, self.Trans)
else:
rhs_val_mg: llvmlite.Value | t.CPtr = HandlesExpr.translate_value(
builder, pool, mod, aa.value,
None, 0, self.Trans)
if rhs_val_mg is not None:
target_ty_mg: llvmlite.LLVMType | t.CPtr = None
if mod_glob.Ty is not None:
target_ty_mg = mod_glob.Ty.Pointee
if target_ty_mg is not None:
rhs_val_mg = HandlesExpr.coerce_to_type(builder, rhs_val_mg, target_ty_mg, node)
llvmlite.build_store(builder, rhs_val_mg, mod_glob)
return 0
# 普通局部变量
# 创建 alloca
alloca: llvmlite.Value | t.CPtr = HandlesVar.get_or_create_sym(
self.Trans.SymTab, pool, builder, nm.id, var_ty)
if alloca is None:
return 0
# 存储原始类型注解的类名(方法调用检测时,Ptr(i8) 回退到类名查找结构体)
if annot is not None:
cls_nm_hd: str = HandlesType.extract_class_name_from_annotation(
annot, self.Trans._imported_modules)
if cls_nm_hd is not None:
HandlesVar.set_var_annot_class_name(
self.Trans.SymTab, nm.id, cls_nm_hd)
# 检查是否为 bytes|t.CPtr 或 str|t.CPtr(i8** 语义,8 字节步长)
if _is_ptr_element_annotation(annot) != 0:
HandlesVar.set_var_ptr_element(
self.Trans.SymTab, nm.id)
new_vars: int = 0
existing: llvmlite.Value | t.CPtr = HandlesVar.lookup_current(
self.Trans.SymTab, nm.id)
if existing is None:
new_vars = 1
# 如果有初始值,store
if aa.value is not None:
rhs_val: llvmlite.Value | t.CPtr = HandlesExpr.translate_value(
builder, pool, mod, aa.value,
None, 0, self.Trans)
if rhs_val is not None:
# 类型转换:将 rhs_val 转换为 alloca 的 pointee 类型
# 修复:整数字面量是 i32,但变量可能是 i8/i16/i64,需 trunc/sext
target_ty: llvmlite.LLVMType | t.CPtr = None
if alloca.Ty is not None:
target_ty = alloca.Ty.Pointee
if target_ty is not None:
rhs_val = HandlesExpr.coerce_to_type(builder, rhs_val, target_ty, node)
llvmlite.build_store(builder, rhs_val, alloca)
return 0
# ============================================================
# NewAnnAssignHandle - 工厂函数
# ============================================================
def NewAnnAssignHandle(pool: memhub.MemBuddy | t.CPtr,
trans: HT.Translator | t.CPtr) -> AnnAssignHandle | t.CPtr:
h: AnnAssignHandle | t.CPtr = pool.alloc(AnnAssignHandle.__sizeof__())
if h is None:
return None
string.memset(h, 0, AnnAssignHandle.__sizeof__())
h.Trans = trans
return h