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:
TermiNexus committed 2026-10-02 22:07:01 +08:00
1 parent 15b416b527
commit 8d01c01afd
8 files changed
+1845 -277

No files matched your search

+42 -26
View File
@@ -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)
+4
View File
@@ -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
+117 -19
View File
@@ -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
+214 -2
View File
@@ -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
+3
View File
@@ -0,0 +1,3 @@
# ============================================================
# builtin 包入口 —— 编译器内建库(如 viperbuild)的编译器侧实现
# ============================================================
+38 -228
View File
@@ -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
+2 -2
View File
@@ -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)