feat(viperbuild): 落地 --src 工程模式配置面,并补齐循环 else 与整型 None 比较
- 新增 lib/core/builtin/ViperBuild.vp:--src Pass 0 依赖闭包扫描 + 宏配置提取 + 默认值填充 + 写回 Config;main.vp 路由改为工程模式并删除单文件分支 - Translator.vp:写入 triple / datalayout,翻译期擦除 viperbuild 宏语句; ImportsGenMixin.vp:对 viperbuild 豁免 SHA1 校验 - StmtGenCifMixin.vp:for / while 的 else 子句在 11 条循环生成路径全部接通 - ExprGenOps.vp:None 比较按 None 所在侧分派,整型 None 隐式为 == 0 / != 0
This commit is contained in:
1 parent
15b416b527
commit
8d01c01afd
8 files changed
+1845
-277
No files matched your search
@@ -647,35 +647,51 @@ novirtual class ExprGenOps(ExprGenBase.ExprGenBase):
|
||||
# 不同级指针(1 vs 2 等)→ 禁止(fast-fail)
|
||||
self.Gen.TypeGenErr.fatal_error(cmp, f"比较类型不匹配: 不同级指针比较 (lhs 层级={lhs_ptr_level}, rhs 层级={rhs_ptr_level})", "TypeError")
|
||||
else:
|
||||
# None 比较: None 被翻译为 i8* null(一级指针)
|
||||
# 如果 lhs 是指针,将 rhs (i8* null) bitcast 为 lhs 的指针类型
|
||||
# 如果 lhs 是数值,将 rhs 转为数值 0(走正常整数比较)
|
||||
# 如果 lhs 是结构体值类型,结构体永远不等于 None,返回常量 i1
|
||||
# None 比较:把 None 一侧隐式当作「0 / null」,形态由**另一侧**的类型决定
|
||||
# 另一侧是指针 → None 转成同类型 null 指针,走指针相等比较(icmp eq/ne)
|
||||
# 另一侧是整数 → None 转成整数 0,等价 `x == 0` / `x != 0`
|
||||
# (整数上的 None 隐式为 0,与 TPC 统一)
|
||||
# 另一侧是结构体值 → 结构体值永不为 None:==/is 恒 false,!=/is not 恒 true
|
||||
lhs_is_none_lit: int = 0
|
||||
if lhs_node is not None and lhs_node.kind() == ast.ASTKind.Constant:
|
||||
lhs_none_cn: ast.Constant* = ast.Constant*(lhs_node)
|
||||
if lhs_none_cn is not None and lhs_none_cn.const_kind == ast.CONST_NONE:
|
||||
lhs_is_none_lit = 1
|
||||
lhs_ptr_level_none: int = self.get_ptr_level(lhs.Ty)
|
||||
rhs_ptr_level_none: int = self.get_ptr_level(rhs.Ty)
|
||||
if lhs_ptr_level_none != 0 and rhs_ptr_level_none != 0:
|
||||
# 两边都是指针,类型可能不同(如 lhs=Struct*, rhs=i8* null)
|
||||
# 由下方 bitcast 对齐处理,此处不处理
|
||||
pass
|
||||
elif lhs_ptr_level_none != 0 and rhs_ptr_level_none == 0:
|
||||
# lhs 是指针, rhs 是数值 → 将 rhs 转为同层级 null 指针
|
||||
rhs = llvmlite.ConstNull(pool, lhs.Ty, "null")
|
||||
elif rhs_ptr_level_none != 0 and lhs_ptr_level_none == 0:
|
||||
# 检查 lhs 是否是结构体值类型(非指针的 Struct)
|
||||
lhs_is_struct_none: int = 0
|
||||
match lhs.Ty:
|
||||
case llvmlite.LLVMType.Struct(_, _, _):
|
||||
lhs_is_struct_none = 1
|
||||
case _:
|
||||
lhs_is_struct_none = 0
|
||||
if lhs_is_struct_none != 0:
|
||||
# 结构体值永远不等于 None:==/is 返回 false,!=/is not 返回 true
|
||||
if op == ast.OpKind.Eq or op == ast.OpKind.Is:
|
||||
return llvmlite.const_int(pool, 1, 0)
|
||||
else: # Ne, IsNot
|
||||
if lhs_is_none_lit != 0:
|
||||
# None 在左(`None is x` / `None == x`):按右值类型决定 None 的形态
|
||||
if rhs_ptr_level_none == 0:
|
||||
rhs_is_struct_none: int = 0
|
||||
match rhs.Ty:
|
||||
case llvmlite.LLVMType.Struct(_, _, _):
|
||||
rhs_is_struct_none = 1
|
||||
case _:
|
||||
rhs_is_struct_none = 0
|
||||
if rhs_is_struct_none != 0:
|
||||
if op == ast.OpKind.Eq or op == ast.OpKind.Is:
|
||||
return llvmlite.const_int(pool, 1, 0)
|
||||
return llvmlite.const_int(pool, 1, 1)
|
||||
# lhs 是数值, rhs 是 i8* null → 将 rhs 转为数值 0(保持 lhs 不变)
|
||||
rhs = llvmlite.const_int32(pool, 0)
|
||||
# 右值是数值 → 把 None 侧转为整数 0(保持 rhs 不变)
|
||||
lhs = llvmlite.const_int32(pool, 0)
|
||||
else:
|
||||
# None 在右(`x is None` / `x == None`):按左值类型决定 None 的形态
|
||||
if lhs_ptr_level_none == 0:
|
||||
# 检查 lhs 是否是结构体值类型(非指针的 Struct)
|
||||
lhs_is_struct_none: int = 0
|
||||
match lhs.Ty:
|
||||
case llvmlite.LLVMType.Struct(_, _, _):
|
||||
lhs_is_struct_none = 1
|
||||
case _:
|
||||
lhs_is_struct_none = 0
|
||||
if lhs_is_struct_none != 0:
|
||||
# 结构体值永远不等于 None:==/is 返回 false,!=/is not 返回 true
|
||||
if op == ast.OpKind.Eq or op == ast.OpKind.Is:
|
||||
return llvmlite.const_int(pool, 1, 0)
|
||||
else: # Ne, IsNot
|
||||
return llvmlite.const_int(pool, 1, 1)
|
||||
# lhs 是数值, rhs 是 i8* null → 将 rhs 转为数值 0(保持 lhs 不变)
|
||||
rhs = llvmlite.const_int32(pool, 0)
|
||||
|
||||
# 类型对齐:icmp 要求两边类型相同
|
||||
# 仅对数值类型做对齐(指针类型无需对齐,LLVM 允许同层级指针直接 icmp)
|
||||
|
||||
@@ -778,8 +778,12 @@ novirtual class ImportsGenMixin:
|
||||
return 1
|
||||
# t/c 是内部库,不参与 includes 扫描,豁免检查
|
||||
# ctraits 是纯编译期宏库(.vpi 后缀,IncludesScanner 不扫描 .vpi),豁免检查
|
||||
# viperbuild 是编译器内建宏库(无磁盘模块、无 .vpi;配置由 Pass 0 的
|
||||
# lib.core.builtin.ViperBuild 从依赖闭包提取),同样豁免检查
|
||||
if string.strcmp(mod_name, "t") == 0 or string.strcmp(mod_name, "c") == 0 or string.strcmp(mod_name, "ctraits") == 0:
|
||||
return 1
|
||||
if string.strcmp(mod_name, "viperbuild") == 0:
|
||||
return 1
|
||||
mod_sha1: str = StubMergerStore.LookupSha1ByModName(mod_name)
|
||||
if mod_sha1 is None:
|
||||
# App 兄弟模块豁免:当前文件同目录存在 {mod_name}.vp/.py
|
||||
|
||||
@@ -848,6 +848,61 @@ novirtual class StmtGenCifMixin:
|
||||
llvmlite.position_at_end(builder, merge_bb)
|
||||
return 0
|
||||
|
||||
# ============================================================
|
||||
# 循环 else 子句支持(for-else / while-else)
|
||||
#
|
||||
# Python 语义:循环因「条件变假 / 迭代耗尽」正常结束时执行 else;
|
||||
# 因 break 退出时跳过 else。实现:自然退出边指向 else_bb(无 else 时
|
||||
# 仍指向 end_bb),break 恒指向 end_bb;循环收尾在 else_bb 里翻译 orelse
|
||||
# 后 br end_bb。
|
||||
# ============================================================
|
||||
def _LoopElseCount(self, orelse_list: list[ast.AST*]*) -> int:
|
||||
"""orelse 非空返回 1(有 else 子句)"""
|
||||
if orelse_list is None:
|
||||
return 0
|
||||
if len(orelse_list) > 0:
|
||||
return 1
|
||||
return 0
|
||||
|
||||
def _MakeLoopElseBlock(self, pool: memhub.MemBuddy*,
|
||||
func: llvmlite.Function*,
|
||||
name_buf: str, kind: str, cnt: int,
|
||||
has_else: int) -> rawptr[llvmlite.BasicBlock]:
|
||||
"""has_else=0 返回 None;否则 snprintf(name_buf,32,"%s.else.%d",kind,cnt) 并 create_block"""
|
||||
if has_else == 0:
|
||||
return None
|
||||
if func is None:
|
||||
return None
|
||||
viperlib.snprintf(name_buf, 32, "%s.else.%d", kind, cnt)
|
||||
return llvmlite.create_block(pool, func, name_buf)
|
||||
|
||||
def _EmitLoopElse(self, trans: HT.Translator*,
|
||||
orelse_list: list[ast.AST*]*,
|
||||
else_bb: rawptr[llvmlite.BasicBlock],
|
||||
end_bb: rawptr[llvmlite.BasicBlock]) -> int:
|
||||
"""else_bb 非空:position_at_end(else_bb) → 逐条 translate_stmt(orelse) →
|
||||
若当前块未终止则 build_br(end_bb) 并返回 1;否则返回 0。
|
||||
else_bb 为空:直接返回 0(调用方自己 position_at_end(end_bb))。
|
||||
翻译 orelse 前须置 trans._ctrl_merge_reachable = 1(对齐 translate_if 的做法)。"""
|
||||
if else_bb is None:
|
||||
return 0
|
||||
builder: llvmlite.IRBuilder* = trans._cur_builder
|
||||
if builder is None:
|
||||
return 0
|
||||
# 对齐 translate_if:翻译分支前重置 merge 可达标志,防上层残留值污染
|
||||
trans._ctrl_merge_reachable = 1
|
||||
llvmlite.position_at_end(builder, else_bb)
|
||||
if orelse_list is not None:
|
||||
n: sizet = len(orelse_list)
|
||||
for i in range(n):
|
||||
stmt: ast.AST* = orelse_list.get(i)
|
||||
if stmt is not None:
|
||||
self.Gen.StmtGen.translate_stmt(trans, stmt)
|
||||
if llvmlite.builder_cur_block_is_terminated(builder) == 0:
|
||||
llvmlite.build_br(builder, end_bb)
|
||||
return 1
|
||||
return 0
|
||||
|
||||
# ============================================================
|
||||
# translate_while - while 循环语句翻译(迁移自 HandlesWhile.WhileHandle.Handle)
|
||||
#
|
||||
@@ -891,6 +946,10 @@ novirtual class StmtGenCifMixin:
|
||||
viperlib.snprintf(name_buf, 32, "while.end.%d", cnt)
|
||||
end_bb: rawptr[llvmlite.BasicBlock] = llvmlite.create_block(pool, func, name_buf)
|
||||
|
||||
# 循环 else:自然退出(条件变假)边指向 else_bb(无 else 时仍指向 end_bb)
|
||||
has_else_while: int = self._LoopElseCount(while_node.orelse)
|
||||
else_bb_while: rawptr[llvmlite.BasicBlock] = self._MakeLoopElseBlock(pool, func, name_buf, "while", cnt, has_else_while)
|
||||
|
||||
# 2. 跳转到 cond 块
|
||||
llvmlite.build_br(builder, cond_bb)
|
||||
|
||||
@@ -913,7 +972,7 @@ novirtual class StmtGenCifMixin:
|
||||
else:
|
||||
zero: llvmlite.Value* = llvmlite.const_int32(pool, 0)
|
||||
cond_i1 = llvmlite.build_icmp(builder, llvmlite.ICMP_NE, cond_val, zero)
|
||||
llvmlite.build_cond_br(builder, cond_i1, body_bb, end_bb)
|
||||
llvmlite.build_cond_br(builder, cond_i1, body_bb, else_bb_while if else_bb_while is not None else end_bb)
|
||||
|
||||
# 4. body 块: 翻译循环体,跳回 cond
|
||||
llvmlite.position_at_end(builder, body_bb)
|
||||
@@ -939,16 +998,20 @@ novirtual class StmtGenCifMixin:
|
||||
if llvmlite.builder_cur_block_is_terminated(builder) == 0:
|
||||
llvmlite.build_br(builder, cond_bb)
|
||||
|
||||
# 5. 定位到 end 块
|
||||
# 5. 循环 else 收尾(自然退出时先执行 orelse),再定位到 end 块
|
||||
ret_while: int = self._EmitLoopElse(trans, while_node.orelse, else_bb_while, end_bb)
|
||||
llvmlite.position_at_end(builder, end_bb)
|
||||
# 追加90:while 的 cond_br 恒引用 end_bb → merge 恒可达(需正常收尾)
|
||||
# 追加90:有 else 时 endif;无 else 时 while 的 cond_br 恒引用 end_bb
|
||||
# → merge 恒可达(需正常收尾)
|
||||
trans._ctrl_merge_reachable = 1
|
||||
# 无限循环(条件常量真 1/True)且循环体无本层 break → 运行时无任何边
|
||||
# 到达 end 块(merge 结构性不可达)→ 置 0,函数收尾对末句 While 生成
|
||||
# unreachable(追加90:无 br 到达 merge 时才安全;否则 llc O2 把
|
||||
# br-to-unreachable 优化为 fall-through → 双分支执行崩溃)。
|
||||
# 修复 vrandom.gammavariate 的 while True 全 return 被误判"流程掉出末尾"。
|
||||
if self._IsConstTrue(while_node.test) != 0 and self._StmtHasBreak(body) == 0:
|
||||
# [else 子句] 有 else 时 else_bb 会 br 到 end_bb ⇒ merge 结构性可达,
|
||||
# 不能再置 0(否则 end 块被 unreachable 收尾,与 else 的 br 冲突)。
|
||||
if has_else_while == 0 and self._IsConstTrue(while_node.test) != 0 and self._StmtHasBreak(body) == 0:
|
||||
trans._ctrl_merge_reachable = 0
|
||||
return 0
|
||||
|
||||
@@ -1358,6 +1421,10 @@ novirtual class StmtGenCifMixin:
|
||||
viperlib.snprintf(name_buf, 32, "for.end.%d", cnt)
|
||||
end_bb: rawptr[llvmlite.BasicBlock] = llvmlite.create_block(pool, func, name_buf)
|
||||
|
||||
# 循环 else:迭代耗尽(i 越界)边指向 else_bb(无 else 时仍指向 end_bb)
|
||||
has_else_for: int = self._LoopElseCount(for_node.orelse)
|
||||
else_bb_for: rawptr[llvmlite.BasicBlock] = self._MakeLoopElseBlock(pool, func, name_buf, "for", cnt, has_else_for)
|
||||
|
||||
# 7. 跳转到 cond 块
|
||||
llvmlite.build_br(builder, cond_bb)
|
||||
|
||||
@@ -1389,7 +1456,7 @@ novirtual class StmtGenCifMixin:
|
||||
else:
|
||||
range_cmp_op = llvmlite.ICMP_ULT
|
||||
cond_i1: llvmlite.Value* = llvmlite.build_icmp(builder, range_cmp_op, cmp_lhs, cmp_rhs)
|
||||
llvmlite.build_cond_br(builder, cond_i1, body_bb, end_bb)
|
||||
llvmlite.build_cond_br(builder, cond_i1, body_bb, else_bb_for if else_bb_for is not None else end_bb)
|
||||
|
||||
# 9. body 块: 翻译循环体,跳到 incr
|
||||
llvmlite.position_at_end(builder, body_bb)
|
||||
@@ -1432,7 +1499,8 @@ novirtual class StmtGenCifMixin:
|
||||
llvmlite.build_store(builder, next_i, var_alloca)
|
||||
llvmlite.build_br(builder, cond_bb)
|
||||
|
||||
# 11. 定位到 end 块
|
||||
# 11. 循环 else 收尾(迭代耗尽时先执行 orelse),再定位到 end 块
|
||||
ret_for: int = self._EmitLoopElse(trans, for_node.orelse, else_bb_for, end_bb)
|
||||
llvmlite.position_at_end(builder, end_bb)
|
||||
# 追加90:for 循环正常结束路径 br 到 end_bb → merge 恒可达(需正常收尾)
|
||||
trans._ctrl_merge_reachable = 1
|
||||
@@ -1669,6 +1737,10 @@ novirtual class StmtGenCifMixin:
|
||||
viperlib.snprintf(name_buf, 32, "seq.end.%d", cnt)
|
||||
end_bb: rawptr[llvmlite.BasicBlock] = llvmlite.create_block(pool, func, name_buf)
|
||||
|
||||
# 循环 else:迭代耗尽(任一源越界/到终止符)边指向 else_bb(无 else 时仍指向 end_bb)
|
||||
has_else_seq: int = self._LoopElseCount(for_node.orelse)
|
||||
else_bb_seq: rawptr[llvmlite.BasicBlock] = self._MakeLoopElseBlock(pool, func, name_buf, "seq", cnt, has_else_seq)
|
||||
|
||||
llvmlite.build_br(builder, cond_bb)
|
||||
|
||||
# cond 块: 源 1 done 检查(zip 需源 2 也 done)
|
||||
@@ -1697,7 +1769,7 @@ novirtual class StmtGenCifMixin:
|
||||
elif kind2 == GenTypes.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)
|
||||
llvmlite.build_cond_br(builder, cont, body_bb, else_bb_seq if else_bb_seq is not None else end_bb)
|
||||
|
||||
# body 块: 获取元素 → store → 循环体
|
||||
llvmlite.position_at_end(builder, body_bb)
|
||||
@@ -1772,7 +1844,8 @@ novirtual class StmtGenCifMixin:
|
||||
llvmlite.build_store(builder, next_idx, idx_alloca)
|
||||
llvmlite.build_br(builder, cond_bb)
|
||||
|
||||
# 定位到 end 块
|
||||
# 循环 else 收尾(迭代耗尽时先执行 orelse),再定位到 end 块
|
||||
ret_seq: int = self._EmitLoopElse(trans, for_node.orelse, else_bb_seq, end_bb)
|
||||
llvmlite.position_at_end(builder, end_bb)
|
||||
# 追加90:for 循环正常结束路径 br 到 end_bb → merge 恒可达(需正常收尾)
|
||||
trans._ctrl_merge_reachable = 1
|
||||
@@ -1873,6 +1946,10 @@ novirtual class StmtGenCifMixin:
|
||||
viperlib.snprintf(name_buf, 32, "ptr.end.%d", cnt)
|
||||
end_bb: rawptr[llvmlite.BasicBlock] = llvmlite.create_block(pool, func, name_buf)
|
||||
|
||||
# 循环 else:迭代到终止元素(解引用为空)边指向 else_bb(无 else 时仍指向 end_bb)
|
||||
has_else_ptr: int = self._LoopElseCount(for_node.orelse)
|
||||
else_bb_ptr: rawptr[llvmlite.BasicBlock] = self._MakeLoopElseBlock(pool, func, name_buf, "ptr", cnt, has_else_ptr)
|
||||
|
||||
# 跳转到 cond
|
||||
llvmlite.build_br(builder, cond_bb)
|
||||
|
||||
@@ -1895,7 +1972,7 @@ novirtual class StmtGenCifMixin:
|
||||
is_null = llvmlite.build_icmp(builder, llvmlite.ICMP_EQ, cur_elem, null_val)
|
||||
if is_null is None:
|
||||
is_null = llvmlite.const_int(pool, 1, 0)
|
||||
llvmlite.build_cond_br(builder, is_null, end_bb, body_bb)
|
||||
llvmlite.build_cond_br(builder, is_null, else_bb_ptr if else_bb_ptr is not None else end_bb, body_bb)
|
||||
|
||||
# body 块: store elem to var, 翻译循环体
|
||||
llvmlite.position_at_end(builder, body_bb)
|
||||
@@ -1929,7 +2006,8 @@ novirtual class StmtGenCifMixin:
|
||||
llvmlite.build_store(builder, next_idx, idx_alloca)
|
||||
llvmlite.build_br(builder, cond_bb)
|
||||
|
||||
# 定位到 end 块
|
||||
# 循环 else 收尾(迭代耗尽时先执行 orelse),再定位到 end 块
|
||||
ret_ptr: int = self._EmitLoopElse(trans, for_node.orelse, else_bb_ptr, end_bb)
|
||||
llvmlite.position_at_end(builder, end_bb)
|
||||
# 追加90:for 循环正常结束路径 br 到 end_bb → merge 恒可达(需正常收尾)
|
||||
trans._ctrl_merge_reachable = 1
|
||||
@@ -2023,6 +2101,10 @@ novirtual class StmtGenCifMixin:
|
||||
viperlib.snprintf(name_buf, 32, "arr.end.%d", cnt)
|
||||
end_bb: rawptr[llvmlite.BasicBlock] = llvmlite.create_block(pool, func, name_buf)
|
||||
|
||||
# 循环 else:越界或到终止元素(自然结束)边指向 else_bb(无 else 时仍指向 end_bb)
|
||||
has_else_arr: int = self._LoopElseCount(for_node.orelse)
|
||||
else_bb_arr: rawptr[llvmlite.BasicBlock] = self._MakeLoopElseBlock(pool, func, name_buf, "arr", cnt, has_else_arr)
|
||||
|
||||
llvmlite.build_br(builder, cond_bb)
|
||||
|
||||
# cond 块: 先查越界(idx < count,越界时不能 GEP+load 判空)
|
||||
@@ -2030,7 +2112,7 @@ novirtual class StmtGenCifMixin:
|
||||
cur_idx: llvmlite.Value* = llvmlite.build_load(builder, i32_ty, idx_alloca)
|
||||
count_val: llvmlite.Value* = llvmlite.const_int32(pool, elem_count)
|
||||
in_bounds: llvmlite.Value* = llvmlite.build_icmp(builder, llvmlite.ICMP_SLT, cur_idx, count_val)
|
||||
llvmlite.build_cond_br(builder, in_bounds, elem_check_bb, end_bb)
|
||||
llvmlite.build_cond_br(builder, in_bounds, elem_check_bb, else_bb_arr if else_bb_arr is not None else end_bb)
|
||||
|
||||
# elem_check 块: GEP(0, idx) → load elem → 空值判定(NULL 终止)
|
||||
# 用户语义:整数/指针/浮点元素按"解引用为空"提前终止(如 split 尾哨兵
|
||||
@@ -2055,7 +2137,7 @@ novirtual class StmtGenCifMixin:
|
||||
zero_ai: llvmlite.Value* = llvmlite.const_int(pool, elem_bits_a, 0)
|
||||
is_empty_a = llvmlite.build_icmp(builder, llvmlite.ICMP_EQ, cur_elem, zero_ai)
|
||||
if is_empty_a is not None:
|
||||
llvmlite.build_cond_br(builder, is_empty_a, end_bb, body_bb)
|
||||
llvmlite.build_cond_br(builder, is_empty_a, else_bb_arr if else_bb_arr is not None else end_bb, body_bb)
|
||||
else:
|
||||
llvmlite.build_br(builder, body_bb)
|
||||
else:
|
||||
@@ -2091,7 +2173,8 @@ novirtual class StmtGenCifMixin:
|
||||
llvmlite.build_store(builder, next_idx, idx_alloca)
|
||||
llvmlite.build_br(builder, cond_bb)
|
||||
|
||||
# 定位到 end 块
|
||||
# 循环 else 收尾(迭代耗尽时先执行 orelse),再定位到 end 块
|
||||
ret_arr: int = self._EmitLoopElse(trans, for_node.orelse, else_bb_arr, end_bb)
|
||||
llvmlite.position_at_end(builder, end_bb)
|
||||
# 追加90:for 循环正常结束路径 br 到 end_bb → merge 恒可达(需正常收尾)
|
||||
trans._ctrl_merge_reachable = 1
|
||||
@@ -2262,13 +2345,17 @@ novirtual class StmtGenCifMixin:
|
||||
viperlib.snprintf(name_buf, 32, "lst.end.%d", cnt)
|
||||
end_bb: rawptr[llvmlite.BasicBlock] = llvmlite.create_block(pool, func, name_buf)
|
||||
|
||||
# 循环 else:迭代耗尽(idx 越界)边指向 else_bb(无 else 时仍指向 end_bb)
|
||||
has_else_lst: int = self._LoopElseCount(for_node.orelse)
|
||||
else_bb_lst: rawptr[llvmlite.BasicBlock] = self._MakeLoopElseBlock(pool, func, name_buf, "lst", cnt, has_else_lst)
|
||||
|
||||
llvmlite.build_br(builder, cond_bb)
|
||||
|
||||
# cond 块: load index, icmp slt idx, count
|
||||
llvmlite.position_at_end(builder, cond_bb)
|
||||
cur_idx: llvmlite.Value* = llvmlite.build_load(builder, i64_ty, idx_alloca)
|
||||
in_bounds: llvmlite.Value* = llvmlite.build_icmp(builder, llvmlite.ICMP_SLT, cur_idx, count_val)
|
||||
llvmlite.build_cond_br(builder, in_bounds, body_bb, end_bb)
|
||||
llvmlite.build_cond_br(builder, in_bounds, body_bb, else_bb_lst if else_bb_lst is not None else end_bb)
|
||||
|
||||
# body 块: __data__ + idx*__elem_size__ → load 元素 → store to var
|
||||
llvmlite.position_at_end(builder, body_bb)
|
||||
@@ -2308,7 +2395,8 @@ novirtual class StmtGenCifMixin:
|
||||
llvmlite.build_store(builder, next_idx, idx_alloca)
|
||||
llvmlite.build_br(builder, cond_bb)
|
||||
|
||||
# 定位到 end 块
|
||||
# 循环 else 收尾(迭代耗尽时先执行 orelse),再定位到 end 块
|
||||
ret_lst: int = self._EmitLoopElse(trans, for_node.orelse, else_bb_lst, end_bb)
|
||||
llvmlite.position_at_end(builder, end_bb)
|
||||
# 追加90:for 循环正常结束路径 br 到 end_bb → merge 恒可达(需正常收尾)
|
||||
trans._ctrl_merge_reachable = 1
|
||||
@@ -2368,13 +2456,17 @@ novirtual class StmtGenCifMixin:
|
||||
viperlib.snprintf(name_buf, 32, "dict.end.%d", cnt)
|
||||
end_bb: rawptr[llvmlite.BasicBlock] = llvmlite.create_block(pool, func, name_buf)
|
||||
|
||||
# 循环 else:迭代耗尽(idx 越界)边指向 else_bb(无 else 时仍指向 end_bb)
|
||||
has_else_dict: int = self._LoopElseCount(for_node.orelse)
|
||||
else_bb_dict: rawptr[llvmlite.BasicBlock] = self._MakeLoopElseBlock(pool, func, name_buf, "dict", cnt, has_else_dict)
|
||||
|
||||
llvmlite.build_br(builder, cond_bb)
|
||||
|
||||
# cond 块: load index, icmp slt idx, count
|
||||
llvmlite.position_at_end(builder, cond_bb)
|
||||
cur_idx: llvmlite.Value* = llvmlite.build_load(builder, i64_ty, idx_alloca)
|
||||
in_bounds: llvmlite.Value* = llvmlite.build_icmp(builder, llvmlite.ICMP_SLT, cur_idx, count_val)
|
||||
llvmlite.build_cond_br(builder, in_bounds, body_bb, end_bb)
|
||||
llvmlite.build_cond_br(builder, in_bounds, body_bb, else_bb_dict if else_bb_dict is not None else end_bb)
|
||||
|
||||
# body 块: __keys__ + idx*8 → load key (i8*) → store to var
|
||||
llvmlite.position_at_end(builder, body_bb)
|
||||
@@ -2413,7 +2505,8 @@ novirtual class StmtGenCifMixin:
|
||||
llvmlite.build_store(builder, next_idx, idx_alloca)
|
||||
llvmlite.build_br(builder, cond_bb)
|
||||
|
||||
# 定位到 end 块
|
||||
# 循环 else 收尾(迭代耗尽时先执行 orelse),再定位到 end 块
|
||||
ret_dict: int = self._EmitLoopElse(trans, for_node.orelse, else_bb_dict, end_bb)
|
||||
llvmlite.position_at_end(builder, end_bb)
|
||||
# 追加90:for 循环正常结束路径 br 到 end_bb → merge 恒可达(需正常收尾)
|
||||
trans._ctrl_merge_reachable = 1
|
||||
@@ -2535,6 +2628,10 @@ novirtual class StmtGenCifMixin:
|
||||
if cond_bb is None or body_bb is None or step_bb is None or end_bb is None:
|
||||
return 0
|
||||
|
||||
# 循环 else:__next__ 返回 stop(迭代耗尽)边指向 else_bb(无 else 时仍指向 end_bb)
|
||||
has_else_oop: int = self._LoopElseCount(for_node.orelse)
|
||||
else_bb_oop: rawptr[llvmlite.BasicBlock] = self._MakeLoopElseBlock(pool, func, name_buf, "oop", cnt, has_else_oop)
|
||||
|
||||
llvmlite.build_br(builder, cond_bb)
|
||||
|
||||
# 6. cond 块: 重置 flag → 调 __next__(iter, flag) → 检查 flag 判结束
|
||||
@@ -2565,7 +2662,7 @@ novirtual class StmtGenCifMixin:
|
||||
continue_i1 = llvmlite.build_icmp(builder, llvmlite.ICMP_EQ, stop_val, flag_zero2)
|
||||
if continue_i1 is None:
|
||||
return 0
|
||||
llvmlite.build_cond_br(builder, continue_i1, body_bb, end_bb)
|
||||
llvmlite.build_cond_br(builder, continue_i1, body_bb, else_bb_oop if else_bb_oop is not None else end_bb)
|
||||
|
||||
# 7. body 块: store 元素 → 翻译循环体 → br step
|
||||
llvmlite.position_at_end(builder, body_bb)
|
||||
@@ -2587,9 +2684,10 @@ novirtual class StmtGenCifMixin:
|
||||
if llvmlite.builder_cur_block_is_terminated(builder) == 0:
|
||||
llvmlite.build_br(builder, step_bb)
|
||||
|
||||
# 8. step 块 → cond;定位到 end 块
|
||||
# 8. step 块 → cond;循环 else 收尾(迭代耗尽时先执行 orelse),再定位到 end 块
|
||||
llvmlite.position_at_end(builder, step_bb)
|
||||
llvmlite.build_br(builder, cond_bb)
|
||||
ret_oop: int = self._EmitLoopElse(trans, for_node.orelse, else_bb_oop, end_bb)
|
||||
llvmlite.position_at_end(builder, end_bb)
|
||||
# 追加90:for 循环正常结束路径 br 到 end_bb → merge 恒可达(需正常收尾)
|
||||
trans._ctrl_merge_reachable = 1
|
||||
|
||||
@@ -473,11 +473,15 @@ novirtual class Translator:
|
||||
# --traceback: reset per-module DWARF state (no-op when DI is disabled).
|
||||
DebugInfoGen.BeginModule(mod, GenApi.get_current_file())
|
||||
|
||||
# 设置目标(优先使用 project.vpj 中的配置)
|
||||
# 设置目标(优先使用 project.vpj / viperbuild 中的配置)
|
||||
triple: str = Config.TargetTriple
|
||||
if triple is None:
|
||||
triple = "x86_64-pc-windows-msvc"
|
||||
llvmlite.module_set_target(mod, triple)
|
||||
# 数据布局:project.vpj / viperbuild 的 target.datalayout(None 时不输出该行)。
|
||||
# 写入 .ll 模块头,使生成的 IR 自带 LLVM 布局信息,与目标 triple 配套。
|
||||
if Config.TargetDataLayout is not None:
|
||||
llvmlite.module_set_datalayout(mod, Config.TargetDataLayout)
|
||||
|
||||
# 类型
|
||||
i32_ty: llvmlite.LLVMType* = llvmlite.Int32(pool)
|
||||
@@ -575,7 +579,13 @@ novirtual class Translator:
|
||||
# 依赖签名 coerce)。现场:includes/socket.vp 的 extern def connect 被
|
||||
# w32.winsock2 的 connect 顶掉 → socket.vp:236 报
|
||||
# 「指针隐式转指针已禁止:SocketAddr* → WSASocketAddr*」。
|
||||
GenApi.register_file_funcs(pool, tree)
|
||||
GenApi.register_file_funcs(pool, tree)
|
||||
# viperbuild 宏语句擦除:把模块级 `import viperbuild` 配套的宏语句
|
||||
# proj = vb.Project(...) / proj.<字段> = ... / proj.config(...) / proj.build(...)
|
||||
# 从 tree.children 里摘掉(Pass 0 已在配置面消费;翻译期若留着重放会被当真实
|
||||
# 调用 codegen ⇒ `vb.Project` 未定义类)。位置必须在下面 import 处理/变量预注册/
|
||||
# translate_children 之前,使三者看到同一份已擦除的 children。
|
||||
self._erase_viperbuild_macros(pool, tree)
|
||||
# 先处理导入语句,再创建前向声明,解决前向引用问题。
|
||||
# 两道门分开:
|
||||
# ① 导入处理 + 自动容器可见性 + 依赖 vpi 注入 —— 全量翻译**与 App 预注册**
|
||||
@@ -651,6 +661,208 @@ novirtual class Translator:
|
||||
GenApi.forward_declare_functions(self, tree)
|
||||
added: int = GenApi.translate_children(self, tree)
|
||||
|
||||
# ============================================================
|
||||
# _erase_viperbuild_macros - 擦除模块级 viperbuild 宏语句
|
||||
# ------------------------------------------------------------
|
||||
# viperbuild 是**编译器内建宏库**(编译期配置面),其用法形如:
|
||||
# import viperbuild as vb
|
||||
# proj = vb.Project("Test", "1.0.0")
|
||||
# proj.build(selfmachine=True)
|
||||
# 这些是**宏语句**:Pass 0(lib.core.builtin.ViperBuild)已从依赖闭包把它们消费成
|
||||
# Config 配置;翻译期若原样放行,`vb.Project`/`proj.build` 会被当真实调用
|
||||
# codegen(未定义类/函数)⇒ 报错。故此处把整组语句从 tree.children 摘除。
|
||||
# 只处理**模块级顶层语句**(配置不允许写在函数体里)。
|
||||
# ============================================================
|
||||
def _vb_in_proj_vars(self, name: str, proj_vars: list[str]*) -> int:
|
||||
"""name 是否为已登记的 Project 宏变量;1=是"""
|
||||
if name is None or proj_vars is None:
|
||||
return 0
|
||||
n: sizet = len(proj_vars)
|
||||
i: sizet = 0
|
||||
while i < n:
|
||||
s: str = proj_vars.get(i)
|
||||
if s is not None and string.strcmp(s, name) == 0:
|
||||
return 1
|
||||
i += 1
|
||||
return 0
|
||||
|
||||
def _vb_is_project_ctor(self, value: ast.AST*, alias: str, ctor: str) -> int:
|
||||
"""value 是否为 `vb.Project(...)` / `Project(...)`;1=是"""
|
||||
if value is None or value.kind() != ast.ASTKind.Call:
|
||||
return 0
|
||||
cl: ast.Call* = ast.Call*(value)
|
||||
if cl is None or cl.func is None:
|
||||
return 0
|
||||
fk: int = cl.func.kind()
|
||||
if fk == ast.ASTKind.Attribute:
|
||||
if alias is None:
|
||||
return 0
|
||||
at: ast.Attribute* = ast.Attribute*(cl.func)
|
||||
if at is None or at.attr is None or at.value is None:
|
||||
return 0
|
||||
if at.value.kind() != ast.ASTKind.Name:
|
||||
return 0
|
||||
bn: ast.Name* = ast.Name*(at.value)
|
||||
if bn is None or bn.id is None:
|
||||
return 0
|
||||
if string.strcmp(bn.id, alias) != 0:
|
||||
return 0
|
||||
if string.strcmp(at.attr, "Project") == 0:
|
||||
return 1
|
||||
return 0
|
||||
if fk == ast.ASTKind.Name:
|
||||
if ctor is None:
|
||||
return 0
|
||||
nm: ast.Name* = ast.Name*(cl.func)
|
||||
if nm is None or nm.id is None:
|
||||
return 0
|
||||
if string.strcmp(nm.id, ctor) == 0:
|
||||
return 1
|
||||
return 0
|
||||
|
||||
def _vb_is_macro_assign(self, target: ast.AST*, value: ast.AST*,
|
||||
alias: str, ctor: str,
|
||||
proj_vars: list[str]*) -> int:
|
||||
"""赋值是否为 viperbuild 宏语句(Project 绑定 / 字段赋值);1=是且已登记"""
|
||||
if target is None or value is None:
|
||||
return 0
|
||||
tk: int = target.kind()
|
||||
if tk == ast.ASTKind.Name:
|
||||
nm: ast.Name* = ast.Name*(target)
|
||||
if nm is None or nm.id is None:
|
||||
return 0
|
||||
if self._vb_is_project_ctor(value, alias, ctor) != 0:
|
||||
proj_vars.append(nm.id)
|
||||
return 1
|
||||
return 0
|
||||
if tk == ast.ASTKind.Attribute:
|
||||
at: ast.Attribute* = ast.Attribute*(target)
|
||||
if at is None or at.value is None:
|
||||
return 0
|
||||
if at.value.kind() != ast.ASTKind.Name:
|
||||
return 0
|
||||
bn: ast.Name* = ast.Name*(at.value)
|
||||
if bn is None or bn.id is None:
|
||||
return 0
|
||||
if self._vb_in_proj_vars(bn.id, proj_vars) != 0:
|
||||
return 1
|
||||
return 0
|
||||
|
||||
def _vb_is_macro_call(self, value: ast.AST*, proj_vars: list[str]*) -> int:
|
||||
"""表达式是否为 `proj.config(...)` / `proj.build(...)`;1=是"""
|
||||
if value is None or value.kind() != ast.ASTKind.Call:
|
||||
return 0
|
||||
cl: ast.Call* = ast.Call*(value)
|
||||
if cl is None or cl.func is None:
|
||||
return 0
|
||||
if cl.func.kind() != ast.ASTKind.Attribute:
|
||||
return 0
|
||||
at: ast.Attribute* = ast.Attribute*(cl.func)
|
||||
if at is None or at.attr is None or at.value is None:
|
||||
return 0
|
||||
if at.value.kind() != ast.ASTKind.Name:
|
||||
return 0
|
||||
bn: ast.Name* = ast.Name*(at.value)
|
||||
if bn is None or bn.id is None:
|
||||
return 0
|
||||
if self._vb_in_proj_vars(bn.id, proj_vars) == 0:
|
||||
return 0
|
||||
if string.strcmp(at.attr, "config") == 0:
|
||||
return 1
|
||||
if string.strcmp(at.attr, "build") == 0:
|
||||
return 1
|
||||
return 0
|
||||
|
||||
def _vb_is_macro_stmt(self, child: ast.AST*, alias: str, ctor: str,
|
||||
proj_vars: list[str]*) -> int:
|
||||
"""单条模块级语句是否为 viperbuild 宏语句;1=是(应擦除)"""
|
||||
if child is None:
|
||||
return 0
|
||||
kd: int = child.kind()
|
||||
if kd == ast.ASTKind.Assign:
|
||||
a: ast.Assign* = ast.Assign*(child)
|
||||
if a is None or a.targets is None or a.value is None:
|
||||
return 0
|
||||
if len(a.targets) != 1:
|
||||
return 0
|
||||
return self._vb_is_macro_assign(a.targets.get(0), a.value, alias, ctor, proj_vars)
|
||||
if kd == ast.ASTKind.AnnAssign:
|
||||
an: ast.AnnAssign* = ast.AnnAssign*(child)
|
||||
if an is None or an.target is None or an.value is None:
|
||||
return 0
|
||||
return self._vb_is_macro_assign(an.target, an.value, alias, ctor, proj_vars)
|
||||
if kd == ast.ASTKind.Expr:
|
||||
ex: ast.Expr* = ast.Expr*(child)
|
||||
if ex is None or ex.value is None:
|
||||
return 0
|
||||
return self._vb_is_macro_call(ex.value, proj_vars)
|
||||
return 0
|
||||
|
||||
def _erase_viperbuild_macros(self, pool: memhub.MemBuddy*, tree: ast.AST*) -> int:
|
||||
"""擦除模块级 viperbuild 宏语句(就地替换 tree.children);1=发生擦除"""
|
||||
if tree is None or tree.children is None:
|
||||
return 0
|
||||
ch: list[ast.AST*]* = tree.children
|
||||
cn: sizet = len(ch)
|
||||
if cn == 0:
|
||||
return 0
|
||||
# ① 找 viperbuild 的本地名 / Project 构造名
|
||||
alias: str = None
|
||||
ctor: str = None
|
||||
ci: sizet = 0
|
||||
while ci < cn:
|
||||
child: ast.AST* = ch.get(ci)
|
||||
if child is not None:
|
||||
kd: int = child.kind()
|
||||
if kd == ast.ASTKind.Import:
|
||||
names: list[ast.AST*]* = ast.Import*(child).names
|
||||
if names is not None:
|
||||
ni: sizet = 0
|
||||
while ni < len(names):
|
||||
al: ast.Alias* = ast.Alias*(names.get(ni))
|
||||
if al is not None and al.name is not None:
|
||||
if string.strcmp(al.name, "viperbuild") == 0:
|
||||
if al.asname is not None:
|
||||
alias = al.asname
|
||||
else:
|
||||
alias = "viperbuild"
|
||||
ni += 1
|
||||
elif kd == ast.ASTKind.ImportFrom:
|
||||
ifr: ast.ImportFrom* = ast.ImportFrom*(child)
|
||||
if ifr is not None and ifr.level == 0 and ifr.names is not None:
|
||||
if ifr.module is not None and string.strcmp(ifr.module, "viperbuild") == 0:
|
||||
ni: sizet = 0
|
||||
while ni < len(ifr.names):
|
||||
al: ast.Alias* = ast.Alias*(ifr.names.get(ni))
|
||||
if al is not None and al.name is not None:
|
||||
if string.strcmp(al.name, "Project") == 0:
|
||||
if al.asname is not None:
|
||||
ctor = al.asname
|
||||
else:
|
||||
ctor = "Project"
|
||||
ni += 1
|
||||
ci += 1
|
||||
if alias is None and ctor is None:
|
||||
return 0
|
||||
# ② 过滤:先登记 Project 宏变量,再摘掉相关语句
|
||||
proj_vars: list[str]* = list[str](pool, 8)
|
||||
kept: list[ast.AST*]* = list[ast.AST*](pool, 8)
|
||||
if proj_vars is None or kept is None:
|
||||
return 0
|
||||
erased: int = 0
|
||||
ci = 0
|
||||
while ci < cn:
|
||||
child = ch.get(ci)
|
||||
if child is not None:
|
||||
if self._vb_is_macro_stmt(child, alias, ctor, proj_vars) != 0:
|
||||
erased = 1
|
||||
else:
|
||||
kept.append(child)
|
||||
ci += 1
|
||||
if erased != 0:
|
||||
tree.children = kept
|
||||
return erased
|
||||
|
||||
# 容器模块自动可见性注入(对齐 TPC _inject_auto_imports)
|
||||
# TPC 在翻译前自动注入 `import _list / import _dict (+json)`,使 list/dict 内建容器类
|
||||
# 在符号表中可见;TPV 用可见性机制等效替代:_list.vp/_dict.vp 已由 Phase1 注册,
|
||||
|
||||
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,3 @@
|
||||
# ============================================================
|
||||
# builtin 包入口 —— 编译器内建库(如 viperbuild)的编译器侧实现
|
||||
# ============================================================
|
||||
+38
-228
@@ -11,6 +11,7 @@ import lib.core.CompilerFactory as CompilerFactory
|
||||
import lib.core.Gen.Generator as GenGenerator
|
||||
import lib.Projectrans.Config as Config
|
||||
import lib.Projectrans.Utils as Utils
|
||||
import lib.core.builtin.ViperBuild as ViperBuild
|
||||
import lib
|
||||
from stdint import *
|
||||
import stdio
|
||||
@@ -241,6 +242,17 @@ Target: %s
|
||||
phase: str = args.get_str("phase")
|
||||
cc: str = args.get_str("cc")
|
||||
build_dir: str = args.get_str("build-dir")
|
||||
|
||||
# === --project 与 --src 互斥 ===
|
||||
# --project:传统 project.vpj 工程模式;--src:viperbuild 内建配置模式(入口文件)。
|
||||
# 两者的配置来源互斥,同时给无法确定以谁为准 ⇒ fail-fast。
|
||||
if project is not None and src is not None:
|
||||
stdio.printf("FAIL: --project 与 --src 互斥\n")
|
||||
stdio.printf(" 原因: --project 走 project.vpj 工程模式,--src 走入口文件的 viperbuild 内建配置\n")
|
||||
stdio.printf(" 处理: 二选一;--src 模式下构建配置写在入口文件依赖闭包的 import viperbuild 里\n")
|
||||
argparse.release(args)
|
||||
return 2
|
||||
|
||||
_gb_clean: bool = args.get_bool("clean")
|
||||
show_args: bool = args.get_bool("show-args")
|
||||
if show_args:
|
||||
@@ -348,13 +360,15 @@ Target: %s
|
||||
log.warning(f"无法加载 project.vpj: {proj_path}", "config")
|
||||
else:
|
||||
# 尝试默认路径 project.vpj(当前目录和上级目录)
|
||||
proj_path = "project.vpj"
|
||||
if Config.Load_project_config(proj_path) == 0:
|
||||
proj_loaded = 1
|
||||
else:
|
||||
proj_path = "../project.vpj"
|
||||
# --src 模式走 viperbuild 内建配置,不得再隐式加载 project.vpj(否则污染配置来源)
|
||||
if src is None:
|
||||
proj_path = "project.vpj"
|
||||
if Config.Load_project_config(proj_path) == 0:
|
||||
proj_loaded = 1
|
||||
else:
|
||||
proj_path = "../project.vpj"
|
||||
if Config.Load_project_config(proj_path) == 0:
|
||||
proj_loaded = 1
|
||||
if proj_loaded == 1:
|
||||
# 提取 project.vpj 所在目录,将相对路径解析为基于该目录的路径
|
||||
proj_len: sizet = string.strlen(proj_path)
|
||||
@@ -377,6 +391,17 @@ Target: %s
|
||||
Config.resolve_paths(proj_dir)
|
||||
Config.print_config()
|
||||
|
||||
# === --src: viperbuild 内建配置发现与应用(Pass 0)===
|
||||
# 位置:project.vpj 加载之后、--asan/--clean/--phase 之前 —— 后续这些开关都要读
|
||||
# Config.TempDir/BuildDir/static_libs,必须先在 Pass 0 里从入口文件的依赖闭包
|
||||
# 提取 `import viperbuild` 配置并写回 Config。没找到 / 找到但坏一律 fail-fast。
|
||||
if src is not None:
|
||||
log.banner("viperbuild")
|
||||
if ViperBuild.DiscoverAndApply(mb, src) != 0:
|
||||
argparse.release(args)
|
||||
return 1
|
||||
Config.print_config()
|
||||
|
||||
# === --repl: M1 交互式循环(表达式片段 → IR → llc → clang++ -shared → DLL → 调用回显)===
|
||||
# 位置:工程配置加载之后(Repl 经 Config 取 compiler/linker 命令与 target triple;
|
||||
# 未加载 project.vpj 时 Repl 退回内置默认 llc/clang++),仍在 Phase1/Phase2 之前。
|
||||
@@ -464,9 +489,11 @@ Target: %s
|
||||
argparse.release(args)
|
||||
return 1
|
||||
|
||||
# === 多文件项目模式 ===
|
||||
# 当 --project 指定但 --src 未指定时,扫描 source_dir 下所有 .vp/.py 文件编译
|
||||
if src_path is None and project is not None and Config.SourceDir is not None:
|
||||
# === 工程模式(多文件) ===
|
||||
# --project:project.vpj 工程模式(扫 source_dir 下所有 .vp 编译);
|
||||
# --src + viperbuild:Pass 0 已把配置写回 Config,同样按工程模式构建(SourceDir 为根)。
|
||||
# 二者在参数解析处已判定互斥,不会同时成立。
|
||||
if Config.SourceDir is not None and (project is not None or ViperBuild.IsApplied() != 0):
|
||||
mf_temp: str = Config.TempDir if Config.TempDir is not None else "."
|
||||
mf_output: str = Config.OutputDir if Config.OutputDir is not None else "."
|
||||
mf_cc: str = Config.CompilerCmd if Config.CompilerCmd is not None else "llc"
|
||||
@@ -487,224 +514,7 @@ Target: %s
|
||||
log.success("TransPyV 完成")
|
||||
return mf_ret
|
||||
|
||||
# === 单文件模式:--src 指定时使用指定文件;--project 但无 SourceDir 时回退 ===
|
||||
if src_path is None and project is not None:
|
||||
if Config.SourceDir is not None:
|
||||
# 构造入口路径: SourceDir/main.vp(不再做 vp -> py 后缀识别回退)
|
||||
sd_len: sizet = string.strlen(Config.SourceDir)
|
||||
path_buf: bytes = stdlib.malloc(sd_len + 16)
|
||||
if path_buf is not None:
|
||||
viperlib.snprintf(path_buf, sd_len + 16, "%s/main.vp", Config.SourceDir)
|
||||
src_path = path_buf
|
||||
log.info(f"入口文件: {src_path}", "project")
|
||||
|
||||
if src_path is not None:
|
||||
log.banner("AST 解析")
|
||||
# 打开文件
|
||||
f: fileio.File = fileio.File(src_path, fileio.MODE.R)
|
||||
if f.closed:
|
||||
log.error(f"无法打开文件: {src_path}", "fileio")
|
||||
argparse.release(args)
|
||||
return 1
|
||||
# read_all 的容量参数含终止符(最多读 cap-1 字节), 文件更长会被**静默截断**;
|
||||
# 这里先按文件大小拒绝, 否则拿截断后的源码去 parse 只会得到莫名其妙的语法错误
|
||||
if f.size() >= SRC_BUF_SIZE:
|
||||
log.error(f"源文件超过 {SRC_BUF_SIZE} 字节上限, 拒绝读取: {src_path}", "fileio")
|
||||
f.close()
|
||||
argparse.release(args)
|
||||
return 1
|
||||
# 分配源代码缓冲区
|
||||
src_buf: bytes = stdlib.malloc(SRC_BUF_SIZE)
|
||||
if src_buf is None:
|
||||
log.error("malloc for src_buf failed", "memhub")
|
||||
f.close()
|
||||
argparse.release(args)
|
||||
return 1
|
||||
# 读取文件内容
|
||||
bytes_read: LONG = f.read_all(src_buf, SRC_BUF_SIZE)
|
||||
f.close()
|
||||
if bytes_read < 0:
|
||||
log.error(f"读取文件失败 (错误码: {bytes_read})", "fileio")
|
||||
argparse.release(args)
|
||||
return 1
|
||||
# 添加 null 终止符,确保 strlen 和 SHA1 计算正确
|
||||
if bytes_read < SRC_BUF_SIZE:
|
||||
src_buf[bytes_read] = 0
|
||||
# 解析 AST(拆分为词法+语法两阶段,便于调试)
|
||||
ast._init_tables(mb)
|
||||
lx: ast.Lexer* = ast.new_lexer(mb)
|
||||
ast._lexer_init(lx, src_buf, mb)
|
||||
tokens: ast.Token* = ast.tokenize(lx)
|
||||
tree: ast.AST* = ast.parse_tokens(mb, tokens)
|
||||
if tree is None:
|
||||
# 解析失败:行/列 + 原因一并进日志行(原因由 parser.vp 的 _record_* /
|
||||
# parse_tokens 兜底补记提供)
|
||||
log.error(f"AST 解析失败(第 {ast.get_parse_err_line()} 行第 {ast.get_parse_err_col()} 列: {ast.get_parse_err_msg()})", "ast")
|
||||
argparse.release(args)
|
||||
return 1
|
||||
|
||||
# === 翻译 AST → LLVM IR ===
|
||||
log.banner("LLVM IR 翻译")
|
||||
tr: rawptr[HandlesTranslator.Translator] = HandlesTranslator.Translator()
|
||||
result: int = tr.translate(tree)
|
||||
if result == 0:
|
||||
IR_BUF_SIZE: sizet = 262144
|
||||
ir_buf: bytes = stdlib.malloc(IR_BUF_SIZE)
|
||||
if ir_buf is not None:
|
||||
tr.dump_ir(ir_buf, IR_BUF_SIZE, llvmlite.OUTPUT_FULL)
|
||||
|
||||
# === 编译管线: .ll → .obj → .exe ===
|
||||
log.banner("编译管线")
|
||||
|
||||
# module_name 用源代码内容的 SHA1(前 16 字符),与 Projectrans.py 一致
|
||||
# output_name 从文件名推导(如 test.vp -> test.exe),保持可读性
|
||||
module_name: str = Utils.compute_sha1(mb, src_buf)
|
||||
if module_name is None:
|
||||
VLogger.error("SHA1 计算失败", "main")
|
||||
output_name: str = "test_prog.exe"
|
||||
# 从文件名提取 output_name(去掉路径和 .vp/.py 扩展名)
|
||||
src_len: sizet = string.strlen(src_path)
|
||||
if src_len > 0:
|
||||
# 找到最后一个 / 或 \ 之后的部分
|
||||
base_start: sizet = src_len
|
||||
i: sizet = src_len
|
||||
while i > 0:
|
||||
i -= 1
|
||||
ch: char = src_path[i]
|
||||
if ch == '/' or ch == '\\':
|
||||
base_start = i + 1
|
||||
break
|
||||
if i == 0:
|
||||
base_start = 0
|
||||
# 找到 .vp/.py 扩展名
|
||||
base_end: sizet = src_len
|
||||
j: sizet = base_start
|
||||
while j < src_len:
|
||||
if src_path[j] == '.':
|
||||
base_end = j
|
||||
break
|
||||
j += 1
|
||||
# 输出名 = 文件名 + ".exe"
|
||||
name_len: sizet = base_end - base_start
|
||||
if name_len > 0:
|
||||
out_len: sizet = name_len + 5
|
||||
output_name = stdlib.malloc(out_len)
|
||||
if output_name is not None:
|
||||
# 手动复制文件名并追加 .exe(viperlib.snprintf 不支持 %.*s,string 无 strcat)
|
||||
string.strncpy(output_name, src_path + base_start, name_len)
|
||||
output_name[name_len] = '.'
|
||||
output_name[name_len + 1] = 'e'
|
||||
output_name[name_len + 2] = 'x'
|
||||
output_name[name_len + 3] = 'e'
|
||||
output_name[name_len + 4] = '\0'
|
||||
# 使用 project.vpj 中的配置
|
||||
temp_dir: str = Config.TempDir if Config.TempDir is not None else "."
|
||||
output_dir: str = Config.OutputDir if Config.OutputDir is not None else "."
|
||||
# 设置全局 temp_dir(供跨模块 CDefine 查找使用)
|
||||
GenApi.set_temp_dir(temp_dir)
|
||||
cc_cmd: str = Config.CompilerCmd if Config.CompilerCmd is not None else "llc"
|
||||
cc_flags: str = "-filetype=obj -relocation-model=pic"
|
||||
linker_cmd: str = Config.LinkerCmd if Config.LinkerCmd is not None else "clang++"
|
||||
linker_flags: str = "-lmsvcrt -lucrt -lpthread -lmingwex -lkernel32 -lgcc -Wl,--allow-multiple-definition"
|
||||
linker_static_libs: str = Config.LinkerStaticLibs
|
||||
linker_dynamic_libs: str = Config.LinkerDynamicLibs
|
||||
# --project 模式且未显式指定 --src 时:使用 config 中的 linker_output(如 TransPyV.exe)
|
||||
# 显式指定 --src 时:使用从文件名推导的 output_name(如 test.exe),避免覆盖正在运行的 exe
|
||||
if project is not None and Config.LinkerOutput is not None and args.get_str("src") is None:
|
||||
linker_output: str = Config.LinkerOutput
|
||||
else:
|
||||
linker_output: str = output_name
|
||||
|
||||
# 计算 includes.binary 目录路径(链接时附加预编译 .obj)
|
||||
includes_binary_dir: str = Config.get_includes_binary_dir()
|
||||
|
||||
ir_len: sizet = string.strlen(ir_buf)
|
||||
|
||||
# (--phase 参数已在配置加载后处理,此处直接使用 do_phase1/do_phase2)
|
||||
|
||||
# === Phase1 stub 分离:使用 OUTPUT_STUB/TEXT 模式分别生成 stub.ll 和 text.ll ===
|
||||
if do_phase1 != 0 and temp_dir is not None and module_name is not None:
|
||||
log.banner("stub 分离")
|
||||
SF_IR_SIZE: sizet = 262144
|
||||
td_len_sf: sizet = string.strlen(temp_dir)
|
||||
# 保存 stub.ll
|
||||
sf_stub_buf: bytes = stdlib.malloc(SF_IR_SIZE)
|
||||
if sf_stub_buf is not None:
|
||||
tr.dump_ir(sf_stub_buf, SF_IR_SIZE, llvmlite.OUTPUT_STUB)
|
||||
sf_stub_len: sizet = string.strlen(sf_stub_buf)
|
||||
sf_stub_path: str = StubMergerCollect._sliced_path(temp_dir, td_len_sf, module_name, "stub.ll")
|
||||
if sf_stub_path is not None:
|
||||
sf_f: rawptr[fileio.File] = fileio.File(sf_stub_path, fileio.MODE.W)
|
||||
if not sf_f.closed:
|
||||
sf_f.write(sf_stub_buf, sf_stub_len)
|
||||
sf_f.close()
|
||||
stdlib.free(sf_stub_path)
|
||||
stdlib.free(sf_stub_buf)
|
||||
# 保存 text.ll
|
||||
sf_text_buf: bytes = stdlib.malloc(SF_IR_SIZE)
|
||||
if sf_text_buf is not None:
|
||||
tr.dump_ir(sf_text_buf, SF_IR_SIZE, llvmlite.OUTPUT_TEXT)
|
||||
sf_text_len: sizet = string.strlen(sf_text_buf)
|
||||
sf_text_path: str = StubMergerCollect._sliced_path(temp_dir, td_len_sf, module_name, "text.ll")
|
||||
if sf_text_path is not None:
|
||||
sf_tf: rawptr[fileio.File] = fileio.File(sf_text_path, fileio.MODE.W)
|
||||
if not sf_tf.closed:
|
||||
sf_tf.write(sf_text_buf, sf_text_len)
|
||||
sf_tf.close()
|
||||
stdlib.free(sf_text_path)
|
||||
stdlib.free(sf_text_buf)
|
||||
|
||||
# phase=1 模式:仅生成 stub,不执行编译
|
||||
if do_phase2 == 0:
|
||||
log.success("Phase1 完成(仅 stub 分离,跳过编译)")
|
||||
argparse.release(args)
|
||||
return 0
|
||||
|
||||
# === Phase2: 组合本地 stub + 依赖 stubs + 本地 text → 完整 IR ===
|
||||
final_ir: bytes = ir_buf
|
||||
final_ir_len: sizet = ir_len
|
||||
if temp_dir is not None and module_name is not None:
|
||||
log.banner("IR 组合")
|
||||
SF_COMBINED_SIZE: sizet = 4194304 # 4MB
|
||||
combined_buf: bytes = stdlib.malloc(SF_COMBINED_SIZE)
|
||||
if combined_buf is not None:
|
||||
combined_len: sizet = StubMergerBuild.BuildCombinedIR(temp_dir, module_name, combined_buf, SF_COMBINED_SIZE)
|
||||
if combined_len > 0:
|
||||
final_ir = combined_buf
|
||||
final_ir_len = combined_len
|
||||
else:
|
||||
stdlib.free(combined_buf)
|
||||
VLogger.error("BuildCombinedIR 失败,终止编译", "stub")
|
||||
|
||||
br: rawptr[BuildPipeline.BuildResult] = BuildPipeline.run_pipeline(
|
||||
final_ir, final_ir_len, temp_dir, output_dir, module_name,
|
||||
cc_cmd, cc_flags, linker_cmd, linker_flags, linker_output,
|
||||
includes_binary_dir, linker_static_libs, linker_dynamic_libs
|
||||
)
|
||||
if br is not None and br.Success == 1:
|
||||
log.success("编译管线完成")
|
||||
log.info(f"输出: {output_dir}/{linker_output}")
|
||||
# --run 模式:执行生成的可执行文件
|
||||
# phase2command 模式:exe 尚未链接(命令在 bat 中),跳过 --run
|
||||
if args.get_bool("run") and BuildPipeline._phase2_cmd_path is None:
|
||||
exe_path: bytes = stdlib.malloc(string.strlen(output_dir) + string.strlen(linker_output) + 2)
|
||||
if exe_path is not None:
|
||||
viperlib.snprintf(exe_path, 256, "%s/%s", output_dir, linker_output)
|
||||
log.info(f"执行: {exe_path}")
|
||||
r: rawptr[subprocess.CompletedProcess] = subprocess.run(exe_path, False, False)
|
||||
if r is not None:
|
||||
log.info(f"退出码: {r.returncode}")
|
||||
else:
|
||||
log.error("编译管线失败", "pipeline")
|
||||
if br is not None and br.ErrorMsg is not None:
|
||||
log.error(f"编译管线失败: {br.ErrorMsg}", "FATAL")
|
||||
sys.exit(1)
|
||||
else:
|
||||
log.error("malloc for IR buffer failed", "memhub")
|
||||
else:
|
||||
log.error("翻译失败", "translator")
|
||||
|
||||
# 走到这里说明没有任何可构建的工程根(SourceDir 缺失)—— fail-fast,不再静默"完成"
|
||||
log.error("无可构建的工程根:project.vpj 缺 source_dir,或 viperbuild 未提供 source", "config")
|
||||
argparse.release(args)
|
||||
log.success("TransPyV 完成")
|
||||
|
||||
return 0
|
||||
return 1
|
||||
@@ -216,7 +216,7 @@ source_dir 下全部 .vp/.py(多文件模式:递归扫描所有源文件)
|
||||
- [TransPyC 编译器仓库(种子)](https://git.gvsds.com/GVSDS/TransPyC)
|
||||
- [Viper 语言文档(wiki 目录)](https://git.gvsds.com/GVSDS/TransPyC/tree/main/wiki)
|
||||
- [自举可行性分析与进展](https://git.gvsds.com/GVSDS/TransPyC/blob/main/wiki/13-bootstrapping.md)
|
||||
- [工程配置 `project.vpj` 说明](https://git.gvsds.com/GVSDS/TransPyC/blob/main/wiki/12-project.md)
|
||||
- [编译系统(工程配置 `project.vpj` / `viperbuild` 元编程)](https://git.gvsds.com/GVSDS/TransPyC/blob/main/wiki/12-compilation-system.md)
|
||||
- [ViperOS 操作系统(用 Viper 写的 OS)](https://git.gvsds.com/GVSDS/ViperOS)
|
||||
|
||||
---
|
||||
@@ -430,5 +430,5 @@ out-of-bounds writes.
|
||||
- [TransPyC compiler repo (the seed)](https://git.gvsds.com/GVSDS/TransPyC)
|
||||
- [Viper language docs (wiki)](https://git.gvsds.com/GVSDS/TransPyC/tree/main/wiki)
|
||||
- [Bootstrapping analysis and progress](https://git.gvsds.com/GVSDS/TransPyC/blob/main/wiki/13-bootstrapping.md)
|
||||
- [Project config `project.vpj`](https://git.gvsds.com/GVSDS/TransPyC/blob/main/wiki/12-project.md)
|
||||
- [Compilation system (`project.vpj` / `viperbuild` metaprogramming)](https://git.gvsds.com/GVSDS/TransPyC/blob/main/wiki/12-compilation-system.md)
|
||||
- [ViperOS (an OS written in Viper)](https://git.gvsds.com/GVSDS/ViperOS)
|
||||
Reference in new issue
Block a user