feat(core): for 支持定长数组与列表字面量迭代,其余字面量 fast-fail
This commit is contained in:
1 parent
762d535a86
commit
d6690c6e39
1 file changed
+215
-2
@@ -93,8 +93,25 @@ class ForHandle(HandlesBase.Mixin):
|
|||||||
HandlesType.fatal_error(iter_node, err_msg)
|
HandlesType.fatal_error(iter_node, err_msg)
|
||||||
HandlesType.fatal_error(iter_node, "仅支持 range() 或指针迭代")
|
HandlesType.fatal_error(iter_node, "仅支持 range() 或指针迭代")
|
||||||
|
|
||||||
# 非范围迭代:走指针迭代路径
|
# 非范围迭代:走指针/数组/列表字面量迭代路径
|
||||||
if is_range_iter == 0:
|
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)
|
||||||
|
# 其它不支持的字面量:fast-fail 显式报错,
|
||||||
|
# 避免 translate_value 触发 list[T] 特化崩溃或语义歧义。
|
||||||
|
ik: int = iter_node.kind()
|
||||||
|
if ik == ast.ASTKind.Constant:
|
||||||
|
cn_ik: ast.Constant | t.CPtr = (ast.Constant | t.CPtr)(iter_node)
|
||||||
|
if cn_ik.const_kind != ast.CONST_STR:
|
||||||
|
HandlesType.fatal_error(
|
||||||
|
iter_node,
|
||||||
|
"for 迭代不支持该常量(仅支持字符串、数组、列表字面量、range())")
|
||||||
|
elif ik == ast.ASTKind.Dict or ik == ast.ASTKind.Set or ik == ast.ASTKind.Tuple \
|
||||||
|
or ik == ast.ASTKind.SetComp or ik == ast.ASTKind.DictComp:
|
||||||
|
HandlesType.fatal_error(
|
||||||
|
iter_node,
|
||||||
|
"for 迭代不支持字典/集合/元组字面量,请使用 range()、t.CArray 数组或列表字面量")
|
||||||
return self._handle_ptr_iter(for_node, var_name)
|
return self._handle_ptr_iter(for_node, var_name)
|
||||||
|
|
||||||
call: ast.Call | t.CPtr = (ast.Call | t.CPtr)(iter_node)
|
call: ast.Call | t.CPtr = (ast.Call | t.CPtr)(iter_node)
|
||||||
@@ -287,8 +304,11 @@ class ForHandle(HandlesBase.Mixin):
|
|||||||
if ptr_val is None:
|
if ptr_val is None:
|
||||||
HandlesType.fatal_error(for_node, "指针迭代: 无法翻译迭代器表达式")
|
HandlesType.fatal_error(for_node, "指针迭代: 无法翻译迭代器表达式")
|
||||||
|
|
||||||
# 检查是否是指针类型
|
# 检查迭代器类型
|
||||||
if HandlesExpr.is_ptr_type(ptr_val.Ty) == 0:
|
if HandlesExpr.is_ptr_type(ptr_val.Ty) == 0:
|
||||||
|
# 数组迭代:for k in arr(定长数组按长度迭代,元素可为任意类型)
|
||||||
|
if HandlesExpr.is_array_type(ptr_val.Ty) != 0:
|
||||||
|
return self._handle_array_iter(for_node, var_name, ptr_val)
|
||||||
HandlesType.fatal_error(for_node, "指针迭代: 迭代器不是指针类型")
|
HandlesType.fatal_error(for_node, "指针迭代: 迭代器不是指针类型")
|
||||||
|
|
||||||
# 获取元素类型
|
# 获取元素类型
|
||||||
@@ -384,6 +404,199 @@ class ForHandle(HandlesBase.Mixin):
|
|||||||
llvmlite.position_at_end(builder, end_bb)
|
llvmlite.position_at_end(builder, end_bb)
|
||||||
return new_vars
|
return new_vars
|
||||||
|
|
||||||
|
# ============================================================
|
||||||
|
# _handle_array_iter - 数组迭代: for x in arr:
|
||||||
|
# 定长数组按长度迭代(元素可为任意类型,不做 null 终止符检查)
|
||||||
|
#
|
||||||
|
# 数组值是数组类型而非指针(如局部 t.CArray 修复后),
|
||||||
|
# 先拷到临时 alloca 获得地址,再按 [0, N) 迭代。
|
||||||
|
#
|
||||||
|
# 生成 IR 结构:
|
||||||
|
# %tmp = alloca [N x elem_ty]
|
||||||
|
# store [N x elem_ty] %arr_val, [N x elem_ty]* %tmp
|
||||||
|
# %idx = alloca i32 ; store 0
|
||||||
|
# br %cond
|
||||||
|
# cond:
|
||||||
|
# %i = load i32, i32* %idx
|
||||||
|
# %c = icmp slt i32 %i, N
|
||||||
|
# br i1 %c, %body, %end
|
||||||
|
# body:
|
||||||
|
# %ep = getelementptr [N x elem_ty], [N x elem_ty]* %tmp, i32 0, i32 %i
|
||||||
|
# %ev = load elem_ty, elem_ty* %ep
|
||||||
|
# store elem_ty %ev, elem_ty* %var
|
||||||
|
# ... 循环体 ...
|
||||||
|
# br %incr
|
||||||
|
# incr:
|
||||||
|
# %n = add i32 %i, 1 ; store %n ; br %cond
|
||||||
|
# end:
|
||||||
|
# ============================================================
|
||||||
|
def _handle_array_iter(self, for_node: ast.For | t.CPtr,
|
||||||
|
var_name: str,
|
||||||
|
array_val: llvmlite.Value | t.CPtr) -> int:
|
||||||
|
"""数组迭代: for x in arr: 按数组长度迭代"""
|
||||||
|
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
|
||||||
|
|
||||||
|
# 解析数组类型 [N x elem_ty]
|
||||||
|
arr_ty: llvmlite.LLVMType | t.CPtr = array_val.Ty
|
||||||
|
elem_ty: llvmlite.LLVMType | t.CPtr = None
|
||||||
|
elem_count: int = 0
|
||||||
|
match arr_ty:
|
||||||
|
case llvmlite.LLVMType.Array(et, n):
|
||||||
|
elem_ty = et
|
||||||
|
elem_count = n
|
||||||
|
if elem_ty is None or elem_count <= 0:
|
||||||
|
HandlesType.fatal_error(for_node, "数组迭代: 无法解析数组类型")
|
||||||
|
|
||||||
|
# 数组值 → 临时 alloca(数组值不是指针,需拷贝后 GEP 迭代)
|
||||||
|
arr_tmp: llvmlite.Value | t.CPtr = llvmlite.build_alloca(builder, arr_ty)
|
||||||
|
if arr_tmp is None:
|
||||||
|
HandlesType.fatal_error(for_node, "数组迭代: 临时存储分配失败")
|
||||||
|
llvmlite.build_store(builder, array_val, arr_tmp)
|
||||||
|
|
||||||
|
i32_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Int32(pool)
|
||||||
|
|
||||||
|
# 循环变量 alloca(存储元素值)
|
||||||
|
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, elem_ty)
|
||||||
|
if HandlesVar.define_var(
|
||||||
|
trans.SymTab, var_name, var_alloca) == 0:
|
||||||
|
new_vars = 1
|
||||||
|
|
||||||
|
# 隐式 index 变量,初始为 0
|
||||||
|
idx_alloca: llvmlite.Value | t.CPtr = HandlesVar._alloca_at_entry(builder, i32_ty)
|
||||||
|
llvmlite.build_store(builder, llvmlite.const_int32(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, "arr.cond.%d", cnt)
|
||||||
|
cond_bb: llvmlite.BasicBlock | t.CPtr = llvmlite.create_block(pool, func, name_buf)
|
||||||
|
|
||||||
|
viperlib.snprintf(name_buf, 32, "arr.body.%d", cnt)
|
||||||
|
body_bb: llvmlite.BasicBlock | t.CPtr = llvmlite.create_block(pool, func, name_buf)
|
||||||
|
|
||||||
|
viperlib.snprintf(name_buf, 32, "arr.incr.%d", cnt)
|
||||||
|
incr_bb: llvmlite.BasicBlock | t.CPtr = llvmlite.create_block(pool, func, name_buf)
|
||||||
|
|
||||||
|
viperlib.snprintf(name_buf, 32, "arr.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, elem_count
|
||||||
|
llvmlite.position_at_end(builder, cond_bb)
|
||||||
|
cur_idx: llvmlite.Value | t.CPtr = llvmlite.build_load(builder, i32_ty, idx_alloca)
|
||||||
|
count_val: llvmlite.Value | t.CPtr = llvmlite.const_int32(pool, elem_count)
|
||||||
|
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 块: GEP(0, idx) → load elem → store to var → 循环体
|
||||||
|
llvmlite.position_at_end(builder, body_bb)
|
||||||
|
elem_ptr: llvmlite.Value | t.CPtr = llvmlite.build_gep_array(
|
||||||
|
builder, arr_ty, elem_ty, arr_tmp, cur_idx)
|
||||||
|
cur_elem: llvmlite.Value | t.CPtr = llvmlite.build_load(builder, elem_ty, elem_ptr)
|
||||||
|
llvmlite.build_store(builder, cur_elem, var_alloca)
|
||||||
|
|
||||||
|
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_int32(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
|
||||||
|
|
||||||
|
# ============================================================
|
||||||
|
# _handle_list_literal_iter - 列表字面量迭代: for x in [a, b, c]:
|
||||||
|
# 把列表字面量转成定长数组值,再复用数组迭代(按长度迭代)。
|
||||||
|
#
|
||||||
|
# 元素类型取第一个元素的值类型,其余元素 coerce 到该类型。
|
||||||
|
# ============================================================
|
||||||
|
def _handle_list_literal_iter(self, for_node: ast.For | t.CPtr,
|
||||||
|
var_name: str,
|
||||||
|
list_node: ast.AST | t.CPtr) -> int:
|
||||||
|
"""列表字面量迭代: for x in [a, b, c]: 按元素顺序迭代"""
|
||||||
|
trans: HT.Translator | t.CPtr = self.Trans
|
||||||
|
pool: memhub.MemBuddy | t.CPtr = trans.Pool
|
||||||
|
builder: llvmlite.IRBuilder | t.CPtr = trans._cur_builder
|
||||||
|
|
||||||
|
lst: ast.List | t.CPtr = (ast.List | t.CPtr)(list_node)
|
||||||
|
if lst is None or lst.elts is None:
|
||||||
|
HandlesType.fatal_error(for_node, "列表字面量迭代: 空列表")
|
||||||
|
elts: list[ast.AST | t.CPtr] | t.CPtr = lst.elts
|
||||||
|
elts_count: t.CSizeT = elts.__len__()
|
||||||
|
if elts_count <= 0:
|
||||||
|
HandlesType.fatal_error(for_node, "列表字面量迭代: 空列表")
|
||||||
|
|
||||||
|
# 翻译第一个元素,确定元素类型
|
||||||
|
first_el: ast.AST | t.CPtr = elts.get(0)
|
||||||
|
first_val: llvmlite.Value | t.CPtr = HandlesExpr.translate_value(
|
||||||
|
builder, pool, trans.Module, first_el,
|
||||||
|
trans._funcs, trans._func_count, trans)
|
||||||
|
if first_val is None or first_val.Ty is None:
|
||||||
|
HandlesType.fatal_error(first_el, "列表字面量迭代: 无法确定元素类型")
|
||||||
|
elem_ty: llvmlite.LLVMType | t.CPtr = first_val.Ty
|
||||||
|
|
||||||
|
# 构建 [N x elem_ty] 数组值:临时 alloca 逐元素写入后 load
|
||||||
|
arr_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Array(pool, elem_ty, elts_count)
|
||||||
|
tmp_alloca: llvmlite.Value | t.CPtr = llvmlite.build_alloca(builder, arr_ty)
|
||||||
|
if tmp_alloca is None:
|
||||||
|
HandlesType.fatal_error(for_node, "列表字面量迭代: 临时数组分配失败")
|
||||||
|
first_ptr: llvmlite.Value | t.CPtr = llvmlite.build_gep_array(
|
||||||
|
builder, arr_ty, elem_ty, tmp_alloca, llvmlite.const_int32(pool, 0))
|
||||||
|
if first_ptr is not None:
|
||||||
|
llvmlite.build_store(builder, first_val, first_ptr)
|
||||||
|
# 写其余元素(coerce 到 elem_ty)
|
||||||
|
i: t.CSizeT = 1
|
||||||
|
while i < elts_count:
|
||||||
|
el_node: ast.AST | t.CPtr = elts.get(i)
|
||||||
|
el_val: llvmlite.Value | t.CPtr = HandlesExpr.translate_value(
|
||||||
|
builder, pool, trans.Module, el_node,
|
||||||
|
trans._funcs, trans._func_count, trans)
|
||||||
|
if el_val is not None:
|
||||||
|
el_val = HandlesExpr.coerce_to_type(builder, el_val, elem_ty, el_node)
|
||||||
|
el_ptr: llvmlite.Value | t.CPtr = llvmlite.build_gep_array(
|
||||||
|
builder, arr_ty, elem_ty, tmp_alloca,
|
||||||
|
llvmlite.const_int32(pool, i))
|
||||||
|
if el_ptr is not None:
|
||||||
|
llvmlite.build_store(builder, el_val, el_ptr)
|
||||||
|
i += 1
|
||||||
|
# load 回数组值,复用数组迭代
|
||||||
|
arr_val: llvmlite.Value | t.CPtr = llvmlite.build_load(builder, arr_ty, tmp_alloca)
|
||||||
|
if arr_val is None:
|
||||||
|
HandlesType.fatal_error(for_node, "列表字面量迭代: 数组加载失败")
|
||||||
|
return self._handle_array_iter(for_node, var_name, arr_val)
|
||||||
|
|
||||||
|
|
||||||
# ============================================================
|
# ============================================================
|
||||||
# NewForHandle - 工厂函数
|
# NewForHandle - 工厂函数
|
||||||
|
|||||||
Reference in new issue
Block a user