feat(for): list 容器迭代与 enumerate/zip 多变量迭代

- HandlesFor: for v in lst 按 __count__+__data__ 动态长度迭代; 统一序列循环(RANGE/STR/LIST); cast 链属性访问改中间变量(避免字段偏移推断错误)
- ExprGenMixin: 跨模块 CDefine 完整修复——vpi 仅声明无值回退源文件、includes 双前缀路径、浮点值 atof 解析+double 缓存+ConstFloat 返回
This commit is contained in:
TermiNexus committed 2026-08-11 22:10:01 +08:00
1 parent 882c78c67a
commit 244c8efc96
2 files changed
+650 -12

No files matched your search

+73 -3
View File
@@ -1663,7 +1663,9 @@ class ExprGenMixin:
rel_path: str = rel_arr_ptr + ridx
if rel_path[0] == '\0':
return None
# 选择基础目录:rel_path 以 "lib/" 开头 → App 源文件(SourceDir),否则 includes 文件
# 选择基础目录:rel_path 以 "lib/" 开头 → App 源文件(SourceDir);
# 以 "includes/" 开头 → includes 文件(去掉 "includes/" 前缀,
# 因 IncludesDir 已包含 includes 目录本身,避免双前缀导致文件不存在)
base_dir: str = Config.IncludesDir
is_app: int = 0
if string.strlen(rel_path) >= 4:
@@ -1672,6 +1674,11 @@ class ExprGenMixin:
is_app = 1
if is_app != 0 and Config.SourceDir is not None:
base_dir = Config.SourceDir
elif string.strlen(rel_path) >= 9:
if (rel_path[0] == 'i' and rel_path[1] == 'n' and rel_path[2] == 'c' and
rel_path[3] == 'l' and rel_path[4] == 'u' and rel_path[5] == 'd' and
rel_path[6] == 'e' and rel_path[7] == 's' and rel_path[8] == '/'):
rel_path = rel_path + 9
# 构建完整路径: {base_dir}/{rel_path}
base_len: t.CSizeT = string.strlen(base_dir)
rel_len: t.CSizeT = string.strlen(rel_path)
@@ -1803,6 +1810,33 @@ class ExprGenMixin:
pyi_buf[bytes_read] = 0
else:
pyi_buf[PYI_READ_BUF_SIZE - 1] = 0
# vpi(存根签名文件)中的 CDefine 只声明名字不带值
# (如 "U_M_PI: t.CDefine",无 '='),真正的值定义在源
# .vp/.py 文件中。若 vpi 中所有 "t.CDefine" 声明后均无
# '='(即 vpi 不含 CDefine 值),必须回退读源文件才能找到。
cd_sig: int = 0
cs_total: t.CSizeT = string.strlen(pyi_buf)
cs_i: t.CSizeT = 0
while cs_i + 9 < cs_total:
if (pyi_buf[cs_i] == 't' and pyi_buf[cs_i + 1] == '.' and
pyi_buf[cs_i + 2] == 'C' and pyi_buf[cs_i + 3] == 'D' and
pyi_buf[cs_i + 4] == 'e' and pyi_buf[cs_i + 5] == 'f' and
pyi_buf[cs_i + 6] == 'i' and pyi_buf[cs_i + 7] == 'n' and
pyi_buf[cs_i + 8] == 'e'):
# 检查该行剩余部分是否有 '='(带值声明才在 vpi 中匹配)
cd_eq: int = 0
cs_j: t.CSizeT = cs_i + 9
while cs_j < cs_total and pyi_buf[cs_j] != '\n':
if pyi_buf[cs_j] == '=':
cd_eq = 1
break
cs_j += 1
if cd_eq != 0:
cd_sig = 1
break
cs_i += 1
if cd_sig == 0:
need_py_fallback = 1
stdlib.free(pyi_path)
if need_py_fallback != 0:
@@ -1842,6 +1876,7 @@ class ExprGenMixin:
pos: t.CSizeT = 0
result_val: int = 0
result_found: int = 0
res_is_double: int = 0
while pos < total_len:
# 找到行首
line_start: t.CSizeT = pos
@@ -1910,7 +1945,7 @@ class ExprGenMixin:
while eq_pos < line_start + line_len and pyi_buf[eq_pos] == ' ':
eq_pos += 1
# 解析整数值(支持十六进制 0x 前缀和负号)
# 解析整数值(支持十六进制 0x 前缀、负号、浮点小数点)
# 负号标记
is_neg: int = 0
if eq_pos < line_start + line_len and pyi_buf[eq_pos] == '-':
@@ -1919,6 +1954,7 @@ class ExprGenMixin:
val_str_start: t.CSizeT = eq_pos
val_str_len: t.CSizeT = 0
is_hex: int = 0
is_float_val: int = 0
if eq_pos + 1 < line_start + line_len:
if pyi_buf[eq_pos] == '0' and (pyi_buf[eq_pos + 1] == 'x' or pyi_buf[eq_pos + 1] == 'X'):
is_hex = 1
@@ -1938,6 +1974,11 @@ class ExprGenMixin:
if ch >= '0' and ch <= '9':
val_str_len += 1
eq_pos += 1
elif ch == '.' and is_float_val == 0:
# 浮点值:'3.14159'('.' 只允许一次)
is_float_val = 1
val_str_len += 1
eq_pos += 1
else:
break
@@ -1971,6 +2012,19 @@ class ExprGenMixin:
result_val = hex_result
# 十六进制负值(如 0xFFFFFFFF)保持原样,由 32 位截断处理
result_found = 1
elif is_float_val != 0:
# 浮点 CDefine(如 U_M_PI = 3.14159...):
# 值缓存为 double(register_cdefine_double),供调用处生成
# double 常量;整数部分供 int 路径使用(is_cdefine_double 标志)。
dval_f: double = string.atof(val_buf)
if is_neg != 0:
dval_f = -dval_f
HandlesType.register_cdefine_double(pool, name, dval_f)
result_val = string.atoi(val_buf)
if is_neg != 0:
result_val = -result_val
result_found = 1
res_is_double = 1
else:
result_val = string.atoi(val_buf)
# 应用负号(如 t.CUnsignedLong(-11) → -11)
@@ -2173,8 +2227,11 @@ class ExprGenMixin:
result_found = 1
# 10. 缓存到当前模块的 CDefine 表中
# 浮点 CDefine 已在匹配循环中 register_cdefine_double 缓存
# (res_is_double=1),此处只缓存整数 CDefine,避免覆盖 double。
if result_found != 0:
HandlesType.register_cdefine_constant(pool, name, result_val)
if res_is_double == 0:
HandlesType.register_cdefine_constant(pool, name, result_val)
HandlesType.set_cdefine_found(1)
else:
HandlesType.set_cdefine_found(0)
@@ -2252,6 +2309,11 @@ class ExprGenMixin:
cdef_val = self._lookup_cross_module_cdefine(
pool, nm_id, trans._from_imports, trans._imported_modules)
if HandlesType.is_cdefine_found() != 0:
# 跨模块浮点 CDefine 已缓存为 double(is_cdefine_double),
# 需返回浮点常量而非截断的整数
dchk_nm: double = HandlesType.lookup_cdefine_double(nm_id)
if HandlesType.is_cdefine_double() != 0:
return llvmlite.ConstFloat(pool, llvmlite.Double(pool), dchk_nm)
return llvmlite.const_int32(pool, cdef_val)
# global 变量:从模块作用域查找(global 声明优先级最高)
@@ -3590,6 +3652,10 @@ class ExprGenMixin:
viperlib.snprintf(ma_fi, ma_fi_len, "%s:%s", at.attr, real_mod_name)
cdef_ma_val = self._lookup_cross_module_cdefine(pool, at.attr, ma_fi)
if HandlesType.is_cdefine_found() != 0:
# 跨模块浮点 CDefine(如 vipermath.U_M_PI)→ 浮点常量
dchk_ma: double = HandlesType.lookup_cdefine_double(at.attr)
if HandlesType.is_cdefine_double() != 0:
return llvmlite.ConstFloat(pool, llvmlite.Double(pool), dchk_ma)
return llvmlite.const_int32(pool, cdef_ma_val)
# 检查 nm_ma.id.at.attr 是否是已导入子模块 (如 w32.win32file)
# 如果是子模块,不应创建全局变量,让嵌套属性访问路径处理
@@ -3690,6 +3756,10 @@ class ExprGenMixin:
viperlib.snprintf(tmp_fi, tmp_fi_len, "%s:%s", at.attr, qual_buf)
cdef_val_nested = self._lookup_cross_module_cdefine(pool, at.attr, tmp_fi)
if HandlesType.is_cdefine_found() != 0:
# 跨模块浮点 CDefine → 浮点常量
dchk_nst: double = HandlesType.lookup_cdefine_double(at.attr)
if HandlesType.is_cdefine_double() != 0:
return llvmlite.ConstFloat(pool, llvmlite.Double(pool), dchk_nst)
return llvmlite.const_int32(pool, cdef_val_nested)
# CDefine 未找到: 报 FATAL 错误(不再创建未定义的全局变量引用)
# 之前此处创建 @INVALID_HANDLE_VALUE 等前向引用,但全局未定义,
+577 -9
View File
@@ -51,6 +51,17 @@ import lib.core.Handles.HandlesType as HandlesType
_g_gen: t.CVoid | t.CPtr = None
# ============================================================
# 序列迭代源类型码(for x in ... / enumerate / zip 迭代源分类)
# RANGE: range(start, stop, step)
# STR: 字符串(常量或 str 变量)
# LIST: list[T] 容器(按 __count__ + __data__ 迭代)
# ============================================================
SEQ_KIND_RANGE: t.CDefine = 1
SEQ_KIND_STR: t.CDefine = 2
SEQ_KIND_LIST: t.CDefine = 3
def set_global_gen(gen: t.CVoid | t.CPtr) -> None:
"""注入全局 gen 单例(BuildPipeline/main 调用)"""
global _g_gen
@@ -82,16 +93,19 @@ class ForHandle(HandlesBase.Mixin):
for_node: ast.For | t.CPtr = (ast.For | t.CPtr)(node)
# 1. 获取循环变量名(仅支持 for i in range(...))
# 1. 获取循环变量(Name 单变量 / Tuple 多变量)
target: ast.AST | t.CPtr = for_node.target
if target is None:
return 0
if target.kind() != ast.ASTKind.Name:
return 0
target_nm: ast.Name | t.CPtr = (ast.Name | t.CPtr)(target)
var_name: str = target_nm.id
if var_name is None:
tk: int = target.kind()
if tk != ast.ASTKind.Name and tk != ast.ASTKind.Tuple:
return 0
var_name: str = None
if tk == ast.ASTKind.Name:
target_nm: ast.Name | t.CPtr = (ast.Name | t.CPtr)(target)
var_name = target_nm.id
if var_name is None:
return 0
# 2. 解析迭代器:支持 range() 和指针迭代
iter_node: ast.AST | t.CPtr = for_node.iter
@@ -106,18 +120,64 @@ class ForHandle(HandlesBase.Mixin):
if fn_pre is not None:
if string.strcmp(fn_pre, "range") == 0:
is_range_iter = 1
elif string.strcmp(fn_pre, "enumerate") == 0 or string.strcmp(fn_pre, "zip") == 0:
pass # 多变量迭代,在下方非 range 分支处理
else:
err_msg: t.CChar | t.CPtr = pool.alloc(256)
if err_msg is not None:
viperlib.snprintf(err_msg, 256, "仅支持 range() 或指针迭代,got call '%s'", fn_pre)
viperlib.snprintf(err_msg, 256, "仅支持 range()/enumerate()/zip() 或指针迭代,got call '%s'", fn_pre)
HandlesType.fatal_error(iter_node, err_msg)
HandlesType.fatal_error(iter_node, "仅支持 range() 或指针迭代")
HandlesType.fatal_error(iter_node, "仅支持 range()/enumerate()/zip() 或指针迭代")
# 非范围迭代:走指针/数组/列表字面量迭代路径
# 非范围迭代:走指针/数组/列表字面量/容器/多变量迭代路径
if is_range_iter == 0:
# 列表字面量:按元素顺序迭代(for x in [1, 2, 3])
if iter_node.kind() == ast.ASTKind.List:
return self._handle_list_literal_iter(for_node, var_name, iter_node)
# enumerate(x):for i, v in enumerate(x)(多变量迭代)
if iter_node.kind() == ast.ASTKind.Call:
call_mi: ast.Call | t.CPtr = (ast.Call | t.CPtr)(iter_node)
fn_mi: str = HandlesExpr.get_func_name(call_mi.func)
if fn_mi is not None and string.strcmp(fn_mi, "enumerate") == 0:
if tk != ast.ASTKind.Tuple:
HandlesType.fatal_error(iter_node, "enumerate 迭代需要两个循环变量 (for i, v in enumerate(x))")
tup_ie: ast.Tuple | t.CPtr = (ast.Tuple | t.CPtr)(target)
t1_buf_ie: bytes = pool.alloc(128)
t2_buf_ie: bytes = pool.alloc(128)
if t1_buf_ie is None or t2_buf_ie is None:
return 0
t1_buf_ie[0] = '\0'
t2_buf_ie[0] = '\0'
if self._extract_tuple_names(tup_ie, t1_buf_ie, t2_buf_ie) == 0:
HandlesType.fatal_error(iter_node, "enumerate 迭代变量必须是简单名称")
return self._handle_enumerate_iter(for_node, t1_buf_ie, t2_buf_ie, call_mi)
if fn_mi is not None and string.strcmp(fn_mi, "zip") == 0:
if tk != ast.ASTKind.Tuple:
HandlesType.fatal_error(iter_node, "zip 迭代需要两个循环变量 (for a, b in zip(...))")
tup_iz: ast.Tuple | t.CPtr = (ast.Tuple | t.CPtr)(target)
t1_buf_iz: bytes = pool.alloc(128)
t2_buf_iz: bytes = pool.alloc(128)
if t1_buf_iz is None or t2_buf_iz is None:
return 0
t1_buf_iz[0] = '\0'
t2_buf_iz[0] = '\0'
if self._extract_tuple_names(tup_iz, t1_buf_iz, t2_buf_iz) == 0:
HandlesType.fatal_error(iter_node, "zip 迭代变量必须是简单名称")
return self._handle_zip_iter(for_node, t1_buf_iz, t2_buf_iz, call_mi)
# list[T] 容器变量:按 __count__ 动态长度迭代
if iter_node.kind() == ast.ASTKind.Name and trans is not None:
nm_lst: ast.Name | t.CPtr = (ast.Name | t.CPtr)(iter_node)
if nm_lst.id is not None:
ve_lst: HandlesVar.VarEntry | t.CPtr = HandlesVar.lookup_var_entry(
trans.SymTab, nm_lst.id)
if ve_lst is not None and ve_lst.AnnotClassName is not None:
if string.strstr(ve_lst.AnnotClassName, "list") is not None:
lst_val: llvmlite.Value | t.CPtr = HandlesExpr.translate_value(
builder, pool, trans.Module, iter_node,
trans._funcs, trans._func_count, trans)
if lst_val is not None and lst_val.Ty is not None \
and HandlesExpr.is_ptr_type(lst_val.Ty) != 0:
return self._handle_list_iter(for_node, var_name, lst_val)
# 其它不支持的字面量:fast-fail 显式报错,
# 避免 translate_value 触发 list[T] 特化崩溃或语义歧义。
ik: int = iter_node.kind()
@@ -617,6 +677,514 @@ class ForHandle(HandlesBase.Mixin):
HandlesType.fatal_error(for_node, "列表字面量迭代: 数组加载失败")
return self._handle_array_iter(for_node, var_name, arr_val)
# ============================================================
# _handle_list_iter - list[T] 容器迭代: for v in lst:
# 按 __count__ 动态长度迭代(元素任意类型,不做 null 终止符检查)
#
# list 结构布局 (6 字段 × 8 字节):
# __data__(0) __count__(8) __capacity__(16) __pool__(24)
# __elem_size__(32) __iter_index__(40)
#
# 生成 IR 结构:
# %count = load __count__
# %idx = alloca i64 ; store 0
# br %cond
# cond:
# %i = load i64, %idx
# %c = icmp slt i64 %i, %count
# br i1 %c, %body, %end
# body:
# %data = load __data__ (i8*)
# %es = load __elem_size__ (i64)
# %off = mul i64 %i, %es
# %addr = getelementptr i8, i8* %data, i64 %off
# %el = load i8*, i8** %addr
# store i8* %el, i8** %var
# ... 循环体 ...
# br %incr
# incr:
# %n = add i64 %i, 1 ; store ; br %cond
# end:
# ============================================================
def _handle_list_iter(self, for_node: ast.For | t.CPtr,
var_name: str,
list_val: llvmlite.Value | t.CPtr) -> int:
"""list[T] 容器迭代: for v in lst: 按 __count__ 动态长度迭代"""
trans: HT.Translator | t.CPtr = self.Trans
pool: memhub.MemBuddy | t.CPtr = trans.Pool
builder: llvmlite.IRBuilder | t.CPtr = trans._cur_builder
func: llvmlite.Function | t.CPtr = trans._cur_func
i64_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Int64(pool)
i8_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Int8(pool)
i8_ptr_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr(pool, i8_ty)
# 读取 __count__ (偏移 8, GEP index 1)
cnt_ptr: llvmlite.Value | t.CPtr = llvmlite.build_gep(
builder, i64_ty, list_val, llvmlite.const_int64(pool, 1))
if cnt_ptr is None:
HandlesType.fatal_error(for_node, "list 迭代: 无法定位 __count__")
count_val: llvmlite.Value | t.CPtr = llvmlite.build_load(builder, i64_ty, cnt_ptr)
# 循环变量 alloca(元素类型 i8*,与 list.get 内联一致)
var_alloca: llvmlite.Value | t.CPtr = HandlesVar.lookup_var(
trans.SymTab, var_name)
new_vars: int = 0
if var_alloca is None:
var_alloca = HandlesVar._alloca_at_entry(builder, i8_ptr_ty)
if HandlesVar.define_var(
trans.SymTab, var_name, var_alloca) == 0:
new_vars = 1
# 隐式 index 变量 (i64,与 __count__ 类型对齐)
idx_alloca: llvmlite.Value | t.CPtr = HandlesVar._alloca_at_entry(builder, i64_ty)
llvmlite.build_store(builder, llvmlite.const_int64(pool, 0), idx_alloca)
# 基本块: cond / body / incr / end
cnt: int = trans._label_counter
trans._label_counter = cnt + 1
name_buf: t.CChar | t.CPtr = pool.alloc(32)
viperlib.snprintf(name_buf, 32, "lst.cond.%d", cnt)
cond_bb: llvmlite.BasicBlock | t.CPtr = llvmlite.create_block(pool, func, name_buf)
viperlib.snprintf(name_buf, 32, "lst.body.%d", cnt)
body_bb: llvmlite.BasicBlock | t.CPtr = llvmlite.create_block(pool, func, name_buf)
viperlib.snprintf(name_buf, 32, "lst.incr.%d", cnt)
incr_bb: llvmlite.BasicBlock | t.CPtr = llvmlite.create_block(pool, func, name_buf)
viperlib.snprintf(name_buf, 32, "lst.end.%d", cnt)
end_bb: llvmlite.BasicBlock | t.CPtr = llvmlite.create_block(pool, func, name_buf)
llvmlite.build_br(builder, cond_bb)
# cond 块: load index, icmp slt idx, count
llvmlite.position_at_end(builder, cond_bb)
cur_idx: llvmlite.Value | t.CPtr = llvmlite.build_load(builder, i64_ty, idx_alloca)
in_bounds: llvmlite.Value | t.CPtr = llvmlite.build_icmp(
builder, llvmlite.ICMP_SLT, cur_idx, count_val)
llvmlite.build_cond_br(builder, in_bounds, body_bb, end_bb)
# body 块: __data__ + idx*__elem_size__ → load 元素 → store to var
llvmlite.position_at_end(builder, body_bb)
dpp: llvmlite.Value | t.CPtr = llvmlite.build_gep(
builder, i64_ty, list_val, llvmlite.const_int64(pool, 0))
data_ptr: llvmlite.Value | t.CPtr = llvmlite.build_load(builder, i8_ptr_ty, dpp)
epp: llvmlite.Value | t.CPtr = llvmlite.build_gep(
builder, i64_ty, list_val, llvmlite.const_int64(pool, 4))
esize: llvmlite.Value | t.CPtr = llvmlite.build_load(builder, i64_ty, epp)
offset: llvmlite.Value | t.CPtr = llvmlite.build_mul(builder, cur_idx, esize)
elem_addr: llvmlite.Value | t.CPtr = llvmlite.build_gep(
builder, i8_ty, data_ptr, offset)
cur_elem: llvmlite.Value | t.CPtr = llvmlite.build_load(builder, i8_ptr_ty, elem_addr)
llvmlite.build_store(builder, cur_elem, var_alloca)
# 保存旧循环上下文,设置 break/continue 目标
old_break: llvmlite.BasicBlock | t.CPtr = trans._break_bb
old_continue: llvmlite.BasicBlock | t.CPtr = trans._continue_bb
trans._break_bb = end_bb
trans._continue_bb = incr_bb
body: list[ast.AST | t.CPtr] | t.CPtr = for_node.children
if body is not None:
body_count: t.CSizeT = body.__len__()
for bi in range(body_count):
stmt: ast.AST | t.CPtr = body.get(bi)
if stmt is not None:
HandlesBody.translate_stmt(trans, stmt)
trans._break_bb = old_break
trans._continue_bb = old_continue
if llvmlite.builder_cur_block_is_terminated(builder) == 0:
llvmlite.build_br(builder, incr_bb)
# incr 块: index++
llvmlite.position_at_end(builder, incr_bb)
one_val: llvmlite.Value | t.CPtr = llvmlite.const_int64(pool, 1)
next_idx: llvmlite.Value | t.CPtr = llvmlite.build_add(builder, cur_idx, one_val)
llvmlite.build_store(builder, next_idx, idx_alloca)
llvmlite.build_br(builder, cond_bb)
# 定位到 end 块
llvmlite.position_at_end(builder, end_bb)
return new_vars
# ============================================================
# 多变量迭代(enumerate / zip)
#
# 支持迭代源: range(start, stop, step) / 字符串(常量|变量) / list[T]
# enumerate(x) → for i, v in enumerate(x):i 为序号,v 为元素
# zip(a, b) → for x, y in zip(a, b):任一源耗尽即终止
# ============================================================
def _extract_tuple_names(self, tup: ast.Tuple | t.CPtr,
out1: str | t.CPtr, out2: str | t.CPtr) -> int:
"""从 Tuple target 提取前两个 Name 变量名,写入 out1/out2,成功返回 1"""
if tup is None or tup.elts is None or out1 is None or out2 is None:
return 0
elts: list[ast.AST | t.CPtr] | t.CPtr = tup.elts
n: t.CSizeT = elts.__len__()
if n < 2:
return 0
e0: ast.AST | t.CPtr = elts.get(0)
e1: ast.AST | t.CPtr = elts.get(1)
if e0 is None or e1 is None:
return 0
if e0.kind() != ast.ASTKind.Name or e1.kind() != ast.ASTKind.Name:
return 0
n0: ast.Name | t.CPtr = (ast.Name | t.CPtr)(e0)
n1: ast.Name | t.CPtr = (ast.Name | t.CPtr)(e1)
if n0.id is None or n1.id is None:
return 0
string.strcpy(out1, n0.id)
string.strcpy(out2, n1.id)
return 1
def _seq_kind(self, src: ast.AST | t.CPtr) -> int:
"""判断迭代源类型: SEQ_KIND_RANGE / STR / LIST,不支持返回 0"""
if src is None:
return 0
k: int = src.kind()
if k == ast.ASTKind.Call:
cl_s: ast.Call | t.CPtr = (ast.Call | t.CPtr)(src)
fn_s: str = HandlesExpr.get_func_name(cl_s.func)
if fn_s is not None and string.strcmp(fn_s, "range") == 0:
return SEQ_KIND_RANGE
return 0
if k == ast.ASTKind.Constant:
cn_s: ast.Constant | t.CPtr = (ast.Constant | t.CPtr)(src)
if cn_s is not None and cn_s.const_kind == ast.CONST_STR:
return SEQ_KIND_STR
return 0
if k == ast.ASTKind.Name:
nm_s: ast.Name | t.CPtr = (ast.Name | t.CPtr)(src)
if nm_s.id is not None and self.Trans is not None:
ve_s: HandlesVar.VarEntry | t.CPtr = HandlesVar.lookup_var_entry(
self.Trans.SymTab, nm_s.id)
if ve_s is not None and ve_s.AnnotClassName is not None:
if string.strstr(ve_s.AnnotClassName, "list") is not None:
return SEQ_KIND_LIST
return SEQ_KIND_STR
return 0
def _handle_enumerate_iter(self, for_node: ast.For | t.CPtr,
t1: str, t2: str,
call: ast.Call | t.CPtr) -> int:
"""enumerate 迭代: for i, v in enumerate(x)"""
if call is None or call.args is None:
return 0
cargs: list[ast.AST | t.CPtr] | t.CPtr = call.args
if cargs.__len__() < 1:
HandlesType.fatal_error(for_node, "enumerate 需要至少一个参数")
return self._build_seq_loop(for_node, t1, t2, cargs.get(0), None, 1)
def _handle_zip_iter(self, for_node: ast.For | t.CPtr,
t1: str, t2: str,
call: ast.Call | t.CPtr) -> int:
"""zip 迭代: for a, b in zip(x, y)"""
if call is None or call.args is None:
return 0
cargs: list[ast.AST | t.CPtr] | t.CPtr = call.args
if cargs.__len__() < 2:
HandlesType.fatal_error(for_node, "zip 需要至少两个参数")
return self._build_seq_loop(for_node, t1, t2, cargs.get(0), cargs.get(1), 2)
def _build_seq_loop(self, for_node: ast.For | t.CPtr,
t1: str, t2: str,
src1: ast.AST | t.CPtr,
src2: ast.AST | t.CPtr,
n_srcs: int) -> int:
"""生成序列迭代循环(enumerate: n_srcs=1 / zip: n_srcs=2)
循环变量 alloca 自动创建或复用;元素类型由源类型决定
(RANGE→i32, STR→i8, LIST→i8*)。索引内部统一 i64。
"""
trans: HT.Translator | t.CPtr = self.Trans
pool: memhub.MemBuddy | t.CPtr = trans.Pool
builder: llvmlite.IRBuilder | t.CPtr = trans._cur_builder
func: llvmlite.Function | t.CPtr = trans._cur_func
i32_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Int32(pool)
i64_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Int64(pool)
i8_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Int8(pool)
i8_ptr_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr(pool, i8_ty)
# ---- 源 1 准备 ----
kind1: int = self._seq_kind(src1)
if kind1 == 0:
HandlesType.fatal_error(for_node, "序列迭代: 源 1 类型不支持")
r1_start: llvmlite.Value | t.CPtr = None
r1_stop: llvmlite.Value | t.CPtr = None
r1_step: llvmlite.Value | t.CPtr = None
s1_ptr: llvmlite.Value | t.CPtr = None
l1_count: llvmlite.Value | t.CPtr = None
l1_data: llvmlite.Value | t.CPtr = None
l1_esize: llvmlite.Value | t.CPtr = None
elem1_ty: llvmlite.LLVMType | t.CPtr = i8_ptr_ty
if kind1 == SEQ_KIND_RANGE:
elem1_ty = i32_ty
r1_start = llvmlite.const_int64(pool, 0)
r1_step = llvmlite.const_int64(pool, 1)
r1_stop = llvmlite.const_int64(pool, 0)
# 先赋中间变量再取 args(cast 链式属性访问会推断错字段偏移)
call_src1: ast.Call | t.CPtr = (ast.Call | t.CPtr)(src1)
cargs1: list[ast.AST | t.CPtr] | t.CPtr = call_src1.args
if cargs1 is not None:
n1: t.CSizeT = cargs1.__len__()
if n1 >= 1:
sv: llvmlite.Value | t.CPtr = HandlesExpr.translate_value(
builder, pool, trans.Module, cargs1.get(0),
trans._funcs, trans._func_count, trans)
if sv is not None:
r1_stop = HandlesExpr.coerce_to_type(builder, sv, i64_ty, cargs1.get(0))
if n1 >= 2:
sv2: llvmlite.Value | t.CPtr = HandlesExpr.translate_value(
builder, pool, trans.Module, cargs1.get(1),
trans._funcs, trans._func_count, trans)
if sv2 is not None:
r1_start = HandlesExpr.coerce_to_type(builder, sv2, i64_ty, cargs1.get(1))
if n1 >= 3:
sv3: llvmlite.Value | t.CPtr = HandlesExpr.translate_value(
builder, pool, trans.Module, cargs1.get(2),
trans._funcs, trans._func_count, trans)
if sv3 is not None:
r1_step = HandlesExpr.coerce_to_type(builder, sv3, i64_ty, cargs1.get(2))
elif kind1 == SEQ_KIND_STR:
elem1_ty = i8_ty
s1_ptr = HandlesExpr.translate_value(
builder, pool, trans.Module, src1,
trans._funcs, trans._func_count, trans)
if s1_ptr is None:
HandlesType.fatal_error(for_node, "序列迭代: 字符串源翻译失败")
elif kind1 == SEQ_KIND_LIST:
elem1_ty = i8_ptr_ty
l1_obj: llvmlite.Value | t.CPtr = HandlesExpr.translate_value(
builder, pool, trans.Module, src1,
trans._funcs, trans._func_count, trans)
if l1_obj is None or l1_obj.Ty is None \
or HandlesExpr.is_ptr_type(l1_obj.Ty) == 0:
HandlesType.fatal_error(for_node, "序列迭代: list 源不是指针类型")
l1_count = llvmlite.build_load(builder, i64_ty,
llvmlite.build_gep(builder, i64_ty, l1_obj, llvmlite.const_int64(pool, 1)))
l1_data = llvmlite.build_load(builder, i8_ptr_ty,
llvmlite.build_gep(builder, i64_ty, l1_obj, llvmlite.const_int64(pool, 0)))
l1_esize = llvmlite.build_load(builder, i64_ty,
llvmlite.build_gep(builder, i64_ty, l1_obj, llvmlite.const_int64(pool, 4)))
# ---- 源 2 准备(zip)----
kind2: int = 0
elem2_ty: llvmlite.LLVMType | t.CPtr = elem1_ty
s2_ptr: llvmlite.Value | t.CPtr = None
l2_count: llvmlite.Value | t.CPtr = None
l2_data: llvmlite.Value | t.CPtr = None
l2_esize: llvmlite.Value | t.CPtr = None
r2_start: llvmlite.Value | t.CPtr = None
r2_stop: llvmlite.Value | t.CPtr = None
r2_step: llvmlite.Value | t.CPtr = None
if n_srcs >= 2:
kind2 = self._seq_kind(src2)
if kind2 == 0:
HandlesType.fatal_error(for_node, "序列迭代: 源 2 类型不支持")
if kind2 == SEQ_KIND_RANGE:
elem2_ty = i32_ty
r2_start = llvmlite.const_int64(pool, 0)
r2_step = llvmlite.const_int64(pool, 1)
r2_stop = llvmlite.const_int64(pool, 0)
# 先赋中间变量再取 args(cast 链式属性访问会推断错字段偏移)
call_src2: ast.Call | t.CPtr = (ast.Call | t.CPtr)(src2)
cargs2: list[ast.AST | t.CPtr] | t.CPtr = call_src2.args
if cargs2 is not None:
n2: t.CSizeT = cargs2.__len__()
if n2 >= 1:
svz: llvmlite.Value | t.CPtr = HandlesExpr.translate_value(
builder, pool, trans.Module, cargs2.get(0),
trans._funcs, trans._func_count, trans)
if svz is not None:
r2_stop = HandlesExpr.coerce_to_type(builder, svz, i64_ty, cargs2.get(0))
if n2 >= 2:
svz2: llvmlite.Value | t.CPtr = HandlesExpr.translate_value(
builder, pool, trans.Module, cargs2.get(1),
trans._funcs, trans._func_count, trans)
if svz2 is not None:
r2_start = HandlesExpr.coerce_to_type(builder, svz2, i64_ty, cargs2.get(1))
if n2 >= 3:
svz3: llvmlite.Value | t.CPtr = HandlesExpr.translate_value(
builder, pool, trans.Module, cargs2.get(2),
trans._funcs, trans._func_count, trans)
if svz3 is not None:
r2_step = HandlesExpr.coerce_to_type(builder, svz3, i64_ty, cargs2.get(2))
elif kind2 == SEQ_KIND_STR:
elem2_ty = i8_ty
s2_ptr = HandlesExpr.translate_value(
builder, pool, trans.Module, src2,
trans._funcs, trans._func_count, trans)
if s2_ptr is None:
HandlesType.fatal_error(for_node, "序列迭代: 字符串源 2 翻译失败")
elif kind2 == SEQ_KIND_LIST:
elem2_ty = i8_ptr_ty
l2_obj: llvmlite.Value | t.CPtr = HandlesExpr.translate_value(
builder, pool, trans.Module, src2,
trans._funcs, trans._func_count, trans)
if l2_obj is None or l2_obj.Ty is None \
or HandlesExpr.is_ptr_type(l2_obj.Ty) == 0:
HandlesType.fatal_error(for_node, "序列迭代: list 源 2 不是指针类型")
l2_count = llvmlite.build_load(builder, i64_ty,
llvmlite.build_gep(builder, i64_ty, l2_obj, llvmlite.const_int64(pool, 1)))
l2_data = llvmlite.build_load(builder, i8_ptr_ty,
llvmlite.build_gep(builder, i64_ty, l2_obj, llvmlite.const_int64(pool, 0)))
l2_esize = llvmlite.build_load(builder, i64_ty,
llvmlite.build_gep(builder, i64_ty, l2_obj, llvmlite.const_int64(pool, 4)))
# ---- 循环变量 alloca ----
# t1: enumerate → 序号 (i32);zip → 源 1 元素
t1_alloca: llvmlite.Value | t.CPtr = HandlesVar.lookup_var(trans.SymTab, t1)
new_vars: int = 0
if t1_alloca is None:
if n_srcs == 1:
t1_alloca = HandlesVar._alloca_at_entry(builder, i32_ty)
else:
t1_alloca = HandlesVar._alloca_at_entry(builder, elem1_ty)
if t1_alloca is not None:
if HandlesVar.define_var(trans.SymTab, t1, t1_alloca) == 0:
new_vars += 1
# t2: 源 2 元素(enumerate 时 = 源 1 元素)
t2_alloca: llvmlite.Value | t.CPtr = HandlesVar.lookup_var(trans.SymTab, t2)
if t2_alloca is None:
t2_alloca = HandlesVar._alloca_at_entry(builder, elem2_ty)
if t2_alloca is not None:
if HandlesVar.define_var(trans.SymTab, t2, t2_alloca) == 0:
new_vars += 1
# 隐式 index 变量 (i64)
idx_alloca: llvmlite.Value | t.CPtr = HandlesVar._alloca_at_entry(builder, i64_ty)
llvmlite.build_store(builder, llvmlite.const_int64(pool, 0), idx_alloca)
# 基本块: cond / body / incr / end
cnt: int = trans._label_counter
trans._label_counter = cnt + 1
name_buf: t.CChar | t.CPtr = pool.alloc(32)
viperlib.snprintf(name_buf, 32, "seq.cond.%d", cnt)
cond_bb: llvmlite.BasicBlock | t.CPtr = llvmlite.create_block(pool, func, name_buf)
viperlib.snprintf(name_buf, 32, "seq.body.%d", cnt)
body_bb: llvmlite.BasicBlock | t.CPtr = llvmlite.create_block(pool, func, name_buf)
viperlib.snprintf(name_buf, 32, "seq.incr.%d", cnt)
incr_bb: llvmlite.BasicBlock | t.CPtr = llvmlite.create_block(pool, func, name_buf)
viperlib.snprintf(name_buf, 32, "seq.end.%d", cnt)
end_bb: llvmlite.BasicBlock | t.CPtr = llvmlite.create_block(pool, func, name_buf)
llvmlite.build_br(builder, cond_bb)
# cond 块: 源 1 done 检查(zip 需源 2 也 done)
llvmlite.position_at_end(builder, cond_bb)
cur_idx: llvmlite.Value | t.CPtr = llvmlite.build_load(builder, i64_ty, idx_alloca)
done1: llvmlite.Value | t.CPtr = None
if kind1 == SEQ_KIND_RANGE:
done1 = llvmlite.build_icmp(builder, llvmlite.ICMP_SLT, cur_idx, r1_stop)
elif kind1 == SEQ_KIND_STR:
ch_ptr1: llvmlite.Value | t.CPtr = llvmlite.build_gep(builder, i8_ty, s1_ptr, cur_idx)
ch1: llvmlite.Value | t.CPtr = llvmlite.build_load(builder, i8_ty, ch_ptr1)
zero1: llvmlite.Value | t.CPtr = llvmlite.const_int(pool, 8, 0)
done1 = llvmlite.build_icmp(builder, llvmlite.ICMP_NE, ch1, zero1)
elif kind1 == SEQ_KIND_LIST:
done1 = llvmlite.build_icmp(builder, llvmlite.ICMP_SLT, cur_idx, l1_count)
cont: llvmlite.Value | t.CPtr = done1
if n_srcs >= 2:
done2: llvmlite.Value | t.CPtr = None
if kind2 == SEQ_KIND_RANGE:
done2 = llvmlite.build_icmp(builder, llvmlite.ICMP_SLT, cur_idx, r2_stop)
elif kind2 == SEQ_KIND_STR:
ch_ptr2: llvmlite.Value | t.CPtr = llvmlite.build_gep(builder, i8_ty, s2_ptr, cur_idx)
ch2: llvmlite.Value | t.CPtr = llvmlite.build_load(builder, i8_ty, ch_ptr2)
zero2: llvmlite.Value | t.CPtr = llvmlite.const_int(pool, 8, 0)
done2 = llvmlite.build_icmp(builder, llvmlite.ICMP_NE, ch2, zero2)
elif kind2 == SEQ_KIND_LIST:
done2 = llvmlite.build_icmp(builder, llvmlite.ICMP_SLT, cur_idx, l2_count)
cont = llvmlite.build_and(builder, done1, done2)
llvmlite.build_cond_br(builder, cont, body_bb, end_bb)
# body 块: 获取元素 → store → 循环体
llvmlite.position_at_end(builder, body_bb)
# 源 1 元素
v1: llvmlite.Value | t.CPtr = None
if kind1 == SEQ_KIND_RANGE:
step1: llvmlite.Value | t.CPtr = llvmlite.build_mul(builder, cur_idx, r1_step)
v1 = llvmlite.build_add(builder, r1_start, step1)
elif kind1 == SEQ_KIND_STR:
ch_ptr1b: llvmlite.Value | t.CPtr = llvmlite.build_gep(builder, i8_ty, s1_ptr, cur_idx)
v1 = llvmlite.build_load(builder, i8_ty, ch_ptr1b)
elif kind1 == SEQ_KIND_LIST:
off1: llvmlite.Value | t.CPtr = llvmlite.build_mul(builder, cur_idx, l1_esize)
addr1: llvmlite.Value | t.CPtr = llvmlite.build_gep(builder, i8_ty, l1_data, off1)
v1 = llvmlite.build_load(builder, i8_ptr_ty, addr1)
# store t1 / t2
if t1_alloca is not None and t1_alloca.Ty is not None:
t1_ty: llvmlite.LLVMType | t.CPtr = t1_alloca.Ty.Pointee
if n_srcs == 1:
# enumerate: t1 = 序号(trunc i64 → i32)
idx32: llvmlite.Value | t.CPtr = llvmlite.build_trunc(builder, cur_idx, i32_ty)
llvmlite.build_store(builder, idx32, t1_alloca)
if t2_alloca is not None and t2_alloca.Ty is not None and v1 is not None:
t2_ty: llvmlite.LLVMType | t.CPtr = t2_alloca.Ty.Pointee
v1c: llvmlite.Value | t.CPtr = HandlesExpr.coerce_to_type(builder, v1, t2_ty, src1)
llvmlite.build_store(builder, v1c, t2_alloca)
else:
if v1 is not None:
v1c: llvmlite.Value | t.CPtr = HandlesExpr.coerce_to_type(builder, v1, t1_ty, src1)
llvmlite.build_store(builder, v1c, t1_alloca)
# 源 2 元素 → t2
v2: llvmlite.Value | t.CPtr = None
if kind2 == SEQ_KIND_RANGE:
step2: llvmlite.Value | t.CPtr = llvmlite.build_mul(builder, cur_idx, r2_step)
v2 = llvmlite.build_add(builder, r2_start, step2)
elif kind2 == SEQ_KIND_STR:
ch_ptr2b: llvmlite.Value | t.CPtr = llvmlite.build_gep(builder, i8_ty, s2_ptr, cur_idx)
v2 = llvmlite.build_load(builder, i8_ty, ch_ptr2b)
elif kind2 == SEQ_KIND_LIST:
off2: llvmlite.Value | t.CPtr = llvmlite.build_mul(builder, cur_idx, l2_esize)
addr2: llvmlite.Value | t.CPtr = llvmlite.build_gep(builder, i8_ty, l2_data, off2)
v2 = llvmlite.build_load(builder, i8_ptr_ty, addr2)
if t2_alloca is not None and t2_alloca.Ty is not None and v2 is not None:
t2_ty2: llvmlite.LLVMType | t.CPtr = t2_alloca.Ty.Pointee
v2c: llvmlite.Value | t.CPtr = HandlesExpr.coerce_to_type(builder, v2, t2_ty2, src2)
llvmlite.build_store(builder, v2c, t2_alloca)
# 保存旧循环上下文,设置 break/continue 目标
old_break: llvmlite.BasicBlock | t.CPtr = trans._break_bb
old_continue: llvmlite.BasicBlock | t.CPtr = trans._continue_bb
trans._break_bb = end_bb
trans._continue_bb = incr_bb
body: list[ast.AST | t.CPtr] | t.CPtr = for_node.children
if body is not None:
body_count: t.CSizeT = body.__len__()
for bi in range(body_count):
stmt: ast.AST | t.CPtr = body.get(bi)
if stmt is not None:
HandlesBody.translate_stmt(trans, stmt)
trans._break_bb = old_break
trans._continue_bb = old_continue
if llvmlite.builder_cur_block_is_terminated(builder) == 0:
llvmlite.build_br(builder, incr_bb)
# incr 块: index++
llvmlite.position_at_end(builder, incr_bb)
one_val: llvmlite.Value | t.CPtr = llvmlite.const_int64(pool, 1)
next_idx: llvmlite.Value | t.CPtr = llvmlite.build_add(builder, cur_idx, one_val)
llvmlite.build_store(builder, next_idx, idx_alloca)
llvmlite.build_br(builder, cond_bb)
# 定位到 end 块
llvmlite.position_at_end(builder, end_bb)
return new_vars
# ============================================================
# NewForHandle - 工厂函数(委托到 gen.NewForHandle)