Files
ViperC/App/main.vp
T
TermiNexus 8d5dbeab89 feat: 引入 CompilerFactory 永久内存池,1a 每文件 reset 解 NewAstTest OOM
main 创建 512MB perm_pool(MemBuddy 第二实例,永不 reset)并注册
CompilerFactory 工厂(对齐 TPC Translator 装配模式,memhub 保持通用)。
各 Gen Mixin 跨 mark 持久表(struct/枚举/Renum/spec/vtable/全局函数/
闭包/函数指针/Cexport/Reexport/TraceFrames/Doc)全部迁移到 perm_pool,
替代 stdlib.malloc 深拷贝提升速度。

Phase1 1a 主循环(98 includes 全量 parse AST 驻留主 pool → 1GB 耗尽)
改为:StdlibAlloc=1 注册数据进 perm_pool + 每文件翻译完 reset_to(文件级
mark) 回收 AST;1b-pre vpi 预生成循环同样 reset_to(mark1)。同步修 7 处
reset 后悬空隐患:StructTable 表体、add_field DefaultVal、Renum 变体/
别名表内容、vtable full_names 内容、FuncptrRets 深拷贝。

CompilerFactory 类名与模块名同名(合法场景,TPC 已支持模块别名优先)。
2026-08-14 04:02:36 +08:00

712 lines
34 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import t, c
from stdint import *
import stdio
import string
import stdlib
import memhub
import argparse
import w32.win32console as w32cmd
import w32.fileio as fileio
import w32.win32file
import w32.win32base
import sys
import ast
import lib
import lib.core.VLogger as VLogger
import lib.core.Handles.HandlesTranslator as HandlesTranslator
import lib.core.Handles.HandlesStruct as HandlesStruct
import lib.core.Handles.HandlesExpr as HandlesExpr
import lib.core.Handles.HandlesExprOps as HandlesExprOps
import lib.core.Handles.HandlesExprCall as HandlesExprCall
import lib.core.Handles.HandlesFunctions as HandlesFunctions
import lib.core.Handles.HandlesClassDef as HandlesClassDef
import lib.core.Handles.HandlesType as HandlesType
import lib.core.Handles.HandlesImports as HandlesImports
import lib.core.Handles.HandlesBody as HandlesBody
import lib.core.Handles.HandlesIf as HandlesIf
import lib.core.Handles.HandlesWhile as HandlesWhile
import lib.core.Handles.HandlesFor as HandlesFor
import lib.core.Handles.HandlesReturn as HandlesReturn
import lib.core.Handles.HandlesNonlocal as HandlesNonlocal
import lib.core.Handles.HandlesMatch as HandlesMatch
import lib.core.Handles.HandlesEnum as HandlesEnum
import lib.core.Handles.HandlesAnnAssign as HandlesAnnAssign
import lib.core.Handles.HandlesAugAssign as HandlesAugAssign
import lib.core.Handles.HandlesVar as HandlesVar
import lib.core.Handles.HandlesMain as HandlesMain
import lib.core.BuildPipeline as BuildPipeline
import lib.core.IncludesScanner as IncludesScanner
import lib.core.StubMerger as StubMerger
import lib.core.Phase1 as Phase1
import lib.core.Phase2 as Phase2
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 llvmlite
import subprocess
import viperlib
import hashlib
# 内存大小: fl_bytes=264 (33*8), POOL_SIZE-fl_bytes 必须是 2 的幂以避免浪费
# 1073742088 - 264 = 1073741824 (1024MB usable = 2^30)
# 注意: POOL_SIZE 必须是 2^N+264,否则 _largest_pow2_le 会取低一级 2 的幂
# Phase1 翻译 87 个 includes 文件 + Phase2 翻译 30 个测试文件,512MB 不足导致 Phase2 崩溃
# 2026-08-13: NewAstTest 依赖 ast 模块(98 includes),Phase2 的 Phase A-pre-inc/Phase A
# 无 mark/reset 回收,98 个 includes 的 AST/IR 全量累积 pool 超 1GB → 曾增大到 2GB。
# → 已在 Phase2 对齐 Phase1 的 mark1/reset_to 清理机制(每文件翻译后回收瞬时数据),
# 峰值回落至单文件水平,恢复 1GB(1073742088 - 264 = 1073741824 = 2^30)
POOL_SIZE: t.CDefine = 1073742088
# 永久内存池大小:2^29 + 264 = 512MB usable(存跨 mark 持久数据:泛型模板
# 表/节点、struct/union/REnum 类型树、枚举 BaseTy、spec 缓存等)。
# 与主 pool 独立(MemBuddy 第二实例),永不 reset_to,不占主 pool 配额。
PERM_POOL_SIZE: t.CDefine = 536871176
# 语言编码页
CODE_PAGE: t.CDefine = 65001
# 源代码缓冲区大小(1MB)
SRC_BUF_SIZE: t.CDefine = 1048576
# ============================================================
# main: 命令行入口
#
# 解析参数 → 加载配置 → AST 解析 → LLVM IR 翻译 → 编译管线 → 可选执行
# ============================================================
def main() -> int:
w32cmd.SetConsoleOutputCP(CODE_PAGE)
w32cmd.SetConsoleCP(CODE_PAGE)
# 初始化 mbuddy 内存池
arena: bytes = stdlib.malloc(POOL_SIZE)
if arena is None:
stdio.printf("FAIL: malloc for arena failed\n")
return 1
mb: memhub.MemBuddy | t.CPtr = memhub.MemBuddy(arena, POOL_SIZE)
if mb is None:
stdio.printf("FAIL: MemBuddy init failed\n")
return 1
# 创建全局永久内存池(perm_pool,512MB):存放跨 mark/reset 持久数据
# (泛型模板表/节点、struct/union/REnum 类型树、枚举 BaseTy、spec 缓存
# 等),替代 stdlib.malloc 深拷贝(buddy 分配更快),永不 reset,不占
# 主 pool 配额。经 CompilerFactory 单例分发(不改通用库 memhub)。
perm_arena: bytes = stdlib.malloc(PERM_POOL_SIZE)
if perm_arena is None:
stdio.printf("FAIL: malloc for perm_pool arena failed\n")
return 1
perm_pool: memhub.MemBuddy | t.CPtr = memhub.MemBuddy(perm_arena, PERM_POOL_SIZE)
if perm_pool is None:
stdio.printf("FAIL: perm_pool init failed\n")
return 1
CompilerFactory.set_factory(CompilerFactory.CompilerFactory(mb, perm_pool))
# 设置全局 mbuddy 指针(sys 和 argparse 都需要)
sys._mbuddy = mb
argparse._mbuddy = mb
ast._mbuddy = (memhub.MemBuddy | t.CPtr)(mb)
lib._mbuddy = (memhub.MemBuddy | t.CPtr)(mb)
Config._mbuddy = (memhub.MemBuddy | t.CPtr)(mb)
Utils._mbuddy = (memhub.MemBuddy | t.CPtr)(mb)
HandlesTranslator._mbuddy = mb
BuildPipeline._mbuddy = (memhub.MemBuddy | t.CPtr)(mb)
IncludesScanner._mbuddy = (memhub.MemBuddy | t.CPtr)(mb)
StubMerger._mbuddy = (memhub.MemBuddy | t.CPtr)(mb)
Phase1._mbuddy = (memhub.MemBuddy | t.CPtr)(mb)
Phase2._mbuddy = (memhub.MemBuddy | t.CPtr)(mb)
subprocess._mbuddy = mb
# hashlib._mbuddy = (memhub.MemBuddy | t.CPtr)(mb)
VLogger._mbuddy = (memhub.MemBuddy | t.CPtr)(mb)
lib.InitLib((memhub.MemBuddy | t.CPtr)(mb))
# P2: 创建项目级 Generator 单例(Phase1/Phase2/BuildPipeline 全程复用;
# HandlesType 等旧模块函数经延迟 import 委托到 gen 方法,实现对象化过渡)
gen: GenGenerator.Generator | t.CPtr = mb.alloc(GenGenerator.Generator.__sizeof__())
if gen is None:
stdio.printf("FAIL: Generator alloc failed\n")
return 1
gen.__before_init__()
gen.__init__(mb)
GenGenerator.set_global_gen(gen)
# P2-P5: 注入各 Handles 模块级单例(Phase1 阶段即调用其状态函数)
HandlesType.set_global_gen(gen)
HandlesStruct.set_global_gen(gen)
HandlesExpr.set_global_gen(gen)
HandlesExprOps.set_global_gen(gen)
HandlesExprCall.set_global_gen(gen)
HandlesFunctions.set_global_gen(gen)
HandlesClassDef.set_global_gen(gen)
# P6: 注入语句/赋值/变量/导入/翻译驱动模块的单例
HandlesImports.set_global_gen(gen)
HandlesBody.set_global_gen(gen)
HandlesIf.set_global_gen(gen)
HandlesWhile.set_global_gen(gen)
HandlesFor.set_global_gen(gen)
HandlesReturn.set_global_gen(gen)
HandlesNonlocal.set_global_gen(gen)
HandlesMatch.set_global_gen(gen)
HandlesEnum.set_global_gen(gen)
HandlesAnnAssign.set_global_gen(gen)
HandlesAugAssign.set_global_gen(gen)
HandlesVar.set_global_gen(gen)
HandlesTranslator.set_global_gen(gen)
HandlesMain.set_global_gen(gen)
# 初始化 VLogger 并打印启动日志
log: VLogger.Logger | t.CPtr = VLogger.get_logger()
if log is not None:
log.info("TransPyV 启动")
# 初始化命令行参数(Windows: GetCommandLineA, POSIX: /proc/self/cmdline)
sys._init_argv()
# 创建参数解析器
parser: argparse.ArgumentParser | t.CPtr = argparse.ArgumentParser(
"TransPyV", "TransPyV 命令行参数解析", pool=mb)
# 注册参数(与 Projectrans.py main() 一致)
parser.add_argument("--project", None, argparse.STRING, 0, None, False,
argparse.STORE, "project.vpj 路径(默认查找当前目录)")
parser.add_argument("--src", None, argparse.STRING, 0, None, False,
argparse.STORE, "源文件目录(覆盖 project.vpj)")
parser.add_argument("--temp", None, argparse.STRING, 0, None, False,
argparse.STORE, "声明接口临时目录(覆盖 project.vpj)")
parser.add_argument("--output", None, argparse.STRING, 0, None, False,
argparse.STORE, "输出目录(覆盖 project.vpj)")
parser.add_argument("--phase", None, argparse.STRING, 0, None, False,
argparse.STORE, "阶段: 1=生成声明, 2=翻译+编译, all=全部")
parser.add_argument("--cc", None, argparse.STRING, 0, None, False,
argparse.STORE, "LLVM 编译器命令(覆盖 project.vpj)")
parser.add_argument("--clean", None, argparse.BOOL, 0, None, False,
argparse.STORE_TRUE, "清理 output 和 temp 目录")
parser.add_argument("--run", None, argparse.BOOL, 0, None, False,
argparse.STORE_TRUE, "编译成功后立即执行生成的可执行文件")
parser.add_argument("--rebuild-includes", None, argparse.BOOL, 0, None, False,
argparse.STORE_TRUE, "删除 includes.binary 预编译缓存并重新编译所有 includes")
parser.add_argument("--clear-cache", None, argparse.BOOL, 0, None, False,
argparse.STORE_TRUE, "清除 .transpyc_cache 全局缓存")
parser.add_argument("--asan", None, argparse.BOOL, 0, None, False,
argparse.STORE_TRUE, "ASan 模式:.ll 先 opt -passes=asan 插桩,链接加 -fsanitize=address")
# 解析命令行参数
args: argparse.ParsedArgs | t.CPtr = parser.parse_args(sys._argc, sys._argv)
if args is None:
if log is not None:
log.error("参数解析失败", "argparse")
return 1
# 打印解析结果
if log is not None:
log.banner("TransPyV 参数解析结果")
# 字符串参数
project: str = args.get_str("project")
if project is not None:
stdio.printf(" --project: %s\n", project)
else:
stdio.printf(" --project: (未指定)\n")
src: str = args.get_str("src")
if src is not None:
stdio.printf(" --src: %s\n", src)
else:
stdio.printf(" --src: (未指定)\n")
temp: str = args.get_str("temp")
if temp is not None:
stdio.printf(" --temp: %s\n", temp)
else:
stdio.printf(" --temp: (未指定)\n")
output: str = args.get_str("output")
if output is not None:
stdio.printf(" --output: %s\n", output)
else:
stdio.printf(" --output: (未指定)\n")
phase: str = args.get_str("phase")
if phase is not None:
stdio.printf(" --phase: %s\n", phase)
else:
stdio.printf(" --phase: (未指定,默认 all)\n")
cc: str = args.get_str("cc")
if cc is not None:
stdio.printf(" --cc: %s\n", cc)
else:
stdio.printf(" --cc: (未指定)\n")
# 布尔参数
_gb_clean: INT = args.get_bool("clean")
if _gb_clean:
stdio.printf(" --clean: True\n")
else:
stdio.printf(" --clean: False\n")
if args.get_bool("run"):
stdio.printf(" --run: True\n")
else:
stdio.printf(" --run: False\n")
if args.get_bool("rebuild-includes"):
stdio.printf(" --rebuild-includes: True\n")
else:
stdio.printf(" --rebuild-includes: False\n")
if args.get_bool("clear-cache"):
stdio.printf(" --clear-cache: True\n")
else:
stdio.printf(" --clear-cache: False\n")
stdio.printf("\n参数解析完成。\n")
# ASan 模式:设置 BuildPipeline 全局标志(compile_ll_to_obj 先 opt 插桩,链接 asan 库)
# includes 缓存用独立目录 .binary_asan 隔离 asan 插桩 obj,避免与非 asan obj 混链
# (asan obj 引用 __asan_init 等符号,非 asan 链接不提供 asan 库 → undefined reference)
if args.get_bool("asan"):
BuildPipeline.set_asan_mode(1)
Config.set_inc_bin_suffix(".binary_asan")
if log is not None:
log.info("ASan 模式已启用(opt -passes=asan + libclang_rt)", "asan")
# ASan 模式清理 asan includes 缓存(强制重建 asan 插桩 obj)
mf_inc_bin_asan: str = Config.get_includes_binary_dir()
if mf_inc_bin_asan is not None:
n_asan: int = Utils.CleanDir(mf_inc_bin_asan)
if log is not None:
fb_asan: str = VLogger.fmt_buf()
if fb_asan is not None:
viperlib.snprintf(fb_asan, 1024, "ASan 模式清理 includes 缓存: %d 个文件(将重建 asan obj)", n_asan)
log.info(fb_asan, "asan")
else:
BuildPipeline.set_asan_mode(0)
Config.set_inc_bin_suffix(".binary")
# 加载 project.vpj 配置
proj_loaded: int = 0
proj_path: str = project
if proj_path is not None:
if log is not None:
log.banner("工程配置")
cfg_ret: int = Config.Load_project_config(proj_path)
if cfg_ret == 0:
proj_loaded = 1
else:
if log is not None:
warn_buf: str = stdlib.malloc(256)
if warn_buf is not None:
viperlib.snprintf(warn_buf, 256, "无法加载 project.vpj: %s", proj_path)
log.warning(warn_buf, "config")
stdlib.free(warn_buf)
else:
# 尝试默认路径 project.vpj(当前目录和上级目录)
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: t.CSizeT = string.strlen(proj_path)
slash_pos: t.CSizeT = proj_len
for i in range(proj_len):
idx: t.CSizeT = proj_len - 1 - i
ch: t.CChar = proj_path[idx]
if ch == '/' or ch == '\\':
slash_pos = idx
break
proj_dir: str = ""
if slash_pos > 0 and slash_pos < proj_len:
proj_dir = stdlib.malloc(slash_pos + 1)
if proj_dir is not None:
string.memcpy(proj_dir, proj_path, slash_pos)
proj_dir[slash_pos] = '\0'
Config.resolve_paths(proj_dir)
Config.print_config()
# === --clean: 清理 temp、output 和 includes.binary 目录 ===
if _gb_clean:
if log is not None:
log.info("清理临时目录...", "clean")
if Config.TempDir is not None:
n: int = Utils.CleanDir(Config.TempDir)
if log is not None:
fb: str = VLogger.fmt_buf()
if fb is not None:
viperlib.snprintf(fb, 1024, "%s: 删除 %d 个文件", Config.TempDir, n)
log.info(fb, "clean")
if Config.OutputDir is not None:
n2: int = Utils.CleanDir(Config.OutputDir)
if log is not None:
fb2: str = VLogger.fmt_buf()
if fb2 is not None:
viperlib.snprintf(fb2, 1024, "%s: 删除 %d 个文件", Config.OutputDir, n2)
log.info(fb2, "clean")
# 清理 includes.binary 目录(旧 .obj 文件会导致跳过重新编译)
mf_inc_bin_clean: str = Config.get_includes_binary_dir()
if mf_inc_bin_clean is not None:
n3: int = Utils.CleanDir(mf_inc_bin_clean)
if log is not None:
fb3: str = VLogger.fmt_buf()
if fb3 is not None:
viperlib.snprintf(fb3, 1024, "%s: 删除 %d 个文件", mf_inc_bin_clean, n3)
log.info(fb3, "clean")
# === --rebuild-includes: 删除 includes.binary 预编译缓存 ===
# 强制下次编译时重新编译所有 includes(不清 temp/output)
if args.get_bool("rebuild-includes"):
if log is not None:
log.info("重建 includes 缓存...", "rebuild-includes")
mf_inc_bin_rb: str = Config.get_includes_binary_dir()
if mf_inc_bin_rb is not None:
n_rb: int = Utils.CleanDir(mf_inc_bin_rb)
if log is not None:
fb_rb: str = VLogger.fmt_buf()
if fb_rb is not None:
viperlib.snprintf(fb_rb, 1024, "%s: 删除 %d 个文件(includes 将重新编译)", mf_inc_bin_rb, n_rb)
log.info(fb_rb, "rebuild-includes")
# === --clear-cache: 清除 temp 目录缓存(.vpi/.stub.ll/.text.ll) ===
# 强制下次 Phase1 重新生成 stub(不清 output/includes.binary)
if args.get_bool("clear-cache"):
if log is not None:
log.info("清除 temp 缓存...", "clear-cache")
if Config.TempDir is not None:
n_cc: int = Utils.CleanDir(Config.TempDir)
if log is not None:
fb_cc: str = VLogger.fmt_buf()
if fb_cc is not None:
viperlib.snprintf(fb_cc, 1024, "%s: 删除 %d 个文件(.vpi/.stub.ll 缓存)", Config.TempDir, n_cc)
log.info(fb_cc, "clear-cache")
# === --phase 参数控制 ===
# phase=1: 仅 stub 分离(生成 .stub.ll/.text.ll),不编译
# phase=2: 仅 stub 合并 + 编译(跳过 stub 分离)
# phase=all: 两者都执行(默认)
phase_mode: str = phase
if phase_mode is None:
phase_mode = "all"
do_phase1: int = 0
do_phase2: int = 0
# 注意: 使用 string.strcmp 而非 ==,避免编译器 == 语义 bug
if string.strcmp(phase_mode, "1") == 0 or string.strcmp(phase_mode, "all") == 0:
do_phase1 = 1
if string.strcmp(phase_mode, "2") == 0 or string.strcmp(phase_mode, "all") == 0:
do_phase2 = 1
if log is not None:
fb3: str = VLogger.fmt_buf()
if fb3 is not None:
viperlib.snprintf(fb3, 1024, "模式: %s (phase1=%d phase2=%d)", phase_mode, do_phase1, do_phase2)
log.info(fb3, "phase")
# === Phase1: 扫描 includes(按需翻译)===
# 对 includes 目录中每个 .vp/.py 文件,若 stub 不存在则翻译并分离 stub
if do_phase1 != 0 and Config.IncludesDirs is not None:
ph1_temp: str = Config.TempDir
if ph1_temp is None:
ph1_temp = "."
if Phase1.RunPhase1(mb, Config.IncludesDirs, ph1_temp, log) != 0:
VLogger.error("Phase1 失败,终止编译", "Phase1")
return 1
# 如果仅 Phase1(不执行 Phase2),直接退出
if do_phase2 == 0:
if log is not None:
log.info("Phase1 完成,退出", "phase")
return 0
# AST 解析:如果指定了 --src 或 --project,读取文件并解析为 AST 树
src_path: str = args.get_str("src")
# 无 project 配置且未指定 --src:无法构建,fail-fast 退出。
# (非工作区执行时 CWD 无 project.vpj,proj_loaded=0;若继续执行,
# 未初始化的 Config 字段可能为垃圾指针 → is not None 检查通过 →
# 用非法路径 → 崩溃且 ASan 堆栈来不及输出)
if proj_loaded == 0 and src_path is None:
if log is not None:
log.error("未找到 project.vpj(当前目录/上级目录)且未指定 --src,无法构建", "config")
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:
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"
mf_cc_flags: str = Config.CompilerFlags if Config.CompilerFlags is not None else "-filetype=obj -relocation-model=pic"
mf_linker: str = Config.LinkerCmd if Config.LinkerCmd is not None else "clang++"
mf_linker_flags: str = Config.LinkerFlags if Config.LinkerFlags is not None else "-lmsvcrt -lucrt -lpthread -lmingwex -lkernel32 -lgcc -Wl,--allow-multiple-definition"
mf_linker_out: str = Config.LinkerOutput if Config.LinkerOutput is not None else "app.exe"
mf_includes_bin: str = Config.get_includes_binary_dir()
mf_ret: int = Phase2.RunMultiFileProject(
mb, Config.SourceDir, mf_temp, mf_output,
mf_cc, mf_cc_flags, mf_linker, mf_linker_flags, mf_linker_out,
mf_includes_bin, Config.IncludesDirs, do_phase1, do_phase2, log, args)
argparse.release(args)
if log is not None:
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,回退 SourceDir/main.py
sd_len: t.CSizeT = string.strlen(Config.SourceDir)
path_buf: bytes = stdlib.malloc(sd_len + 16)
if path_buf is not None:
# 优先尝试 main.vp
viperlib.snprintf(path_buf, sd_len + 16, "%s/main.vp", Config.SourceDir)
ef: fileio.File | t.CPtr = fileio.File(path_buf, fileio.MODE.R)
if ef.closed:
# .vp 不存在,回退 main.py
viperlib.snprintf(path_buf, sd_len + 16, "%s/main.py", Config.SourceDir)
else:
ef.close()
src_path = path_buf
if log is not None:
fb4: str = VLogger.fmt_buf()
if fb4 is not None:
viperlib.snprintf(fb4, 1024, "入口文件: %s", src_path)
log.info(fb4, "project")
if src_path is not None:
if log is not None:
log.banner("AST 解析")
# 打开文件
f: fileio.File | t.CPtr = fileio.File(src_path, fileio.MODE.R)
if f.closed:
if log is not None:
fb5: str = VLogger.fmt_buf()
if fb5 is not None:
viperlib.snprintf(fb5, 1024, "无法打开文件: %s", src_path)
log.error(fb5, "fileio")
argparse.release(args)
return 1
# 分配源代码缓冲区
src_buf: bytes = stdlib.malloc(SRC_BUF_SIZE)
if src_buf is None:
if log is not 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:
if log is not None:
fb6: str = VLogger.fmt_buf()
if fb6 is not None:
viperlib.snprintf(fb6, 1024, "读取文件失败 (错误码: %d)", bytes_read)
log.error(fb6, "fileio")
argparse.release(args)
return 1
# 添加 null 终止符,确保 strlen 和 SHA1 计算正确
if bytes_read < SRC_BUF_SIZE:
src_buf[bytes_read] = 0
else:
src_buf[SRC_BUF_SIZE - 1] = 0
if log is not None:
log.info("文件读取完成", "fileio")
fb7: str = VLogger.fmt_buf()
if fb7 is not None:
viperlib.snprintf(fb7, 1024, "读取 %d 字节", bytes_read)
log.info(fb7, "fileio")
# 解析 AST(拆分为词法+语法两阶段,便于调试)
ast._init_tables(mb)
lx: ast.Lexer | t.CPtr = ast.new_lexer(mb)
ast._lexer_init(lx, src_buf, mb)
tokens: ast.Token | t.CPtr = ast.tokenize(lx)
tree: ast.AST | t.CPtr = ast.parse_tokens(mb, tokens)
if tree is None:
if log is not None:
log.error("AST 解析失败", "ast")
argparse.release(args)
return 1
# === 翻译 AST → LLVM IR ===
if log is not None:
log.banner("LLVM IR 翻译")
tr: HandlesTranslator.Translator | t.CPtr = HandlesTranslator.Translator()
result: int = tr.translate(tree)
if result == 0:
IR_BUF_SIZE: t.CSizeT = 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 ===
if log is not None:
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: t.CSizeT = string.strlen(src_path)
if src_len > 0:
# 找到最后一个 / 或 \ 之后的部分
base_start: t.CSizeT = src_len
i: t.CSizeT = src_len
while i > 0:
i -= 1
ch: t.CChar = src_path[i]
if ch == '/' or ch == '\\':
base_start = i + 1
break
if i == 0:
base_start = 0
# 找到 .vp/.py 扩展名
base_end: t.CSizeT = src_len
j: t.CSizeT = base_start
while j < src_len:
if src_path[j] == '.':
base_end = j
break
j += 1
# 输出名 = 文件名 + ".exe"
name_len: t.CSizeT = base_end - base_start
if name_len > 0:
out_len: t.CSizeT = 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 查找使用)
HandlesType.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"
# --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: t.CSizeT = 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:
if log is not None:
log.banner("stub 分离")
SF_IR_SIZE: t.CSizeT = 262144
td_len_sf: t.CSizeT = 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: t.CSizeT = string.strlen(sf_stub_buf)
sf_stub_path: str = StubMerger._sliced_path(temp_dir, td_len_sf, module_name, "stub.ll")
if sf_stub_path is not None:
sf_f: fileio.File | t.CPtr = 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: t.CSizeT = string.strlen(sf_text_buf)
sf_text_path: str = StubMerger._sliced_path(temp_dir, td_len_sf, module_name, "text.ll")
if sf_text_path is not None:
sf_tf: fileio.File | t.CPtr = 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:
if log is not None:
log.success("Phase1 完成(仅 stub 分离,跳过编译)")
argparse.release(args)
return 0
# === Phase2: 组合本地 stub + 依赖 stubs + 本地 text → 完整 IR ===
final_ir: bytes = ir_buf
final_ir_len: t.CSizeT = ir_len
if temp_dir is not None and module_name is not None:
if log is not None:
log.banner("IR 组合")
SF_COMBINED_SIZE: t.CSizeT = 4194304 # 4MB
combined_buf: bytes = stdlib.malloc(SF_COMBINED_SIZE)
if combined_buf is not None:
combined_len: t.CSizeT = StubMerger.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: BuildPipeline.BuildResult | t.CPtr = 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
)
if br is not None and br.Success == 1:
if log is not None:
log.success("编译管线完成")
fb8: str = VLogger.fmt_buf()
if fb8 is not None:
viperlib.snprintf(fb8, 1024, "输出: %s/%s", output_dir, linker_output)
log.info(fb8)
# --run 模式:执行生成的可执行文件
if args.get_bool("run"):
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)
if log is not None:
fb9: str = VLogger.fmt_buf()
if fb9 is not None:
viperlib.snprintf(fb9, 1024, "执行: %s", exe_path)
log.info(fb9, "run")
r: subprocess.CompletedProcess | t.CPtr = subprocess.run(exe_path, False, False)
if r is not None:
if log is not None:
fb10: str = VLogger.fmt_buf()
if fb10 is not None:
viperlib.snprintf(fb10, 1024, "退出码: %d", r.returncode)
log.info(fb10, "run")
else:
if log is not None:
log.error("编译管线失败", "pipeline")
if br is not None and br.ErrorMsg is not None:
fb11: str = VLogger.fmt_buf()
if fb11 is not None:
viperlib.snprintf(fb11, 1024, "编译管线失败: %s", br.ErrorMsg)
log.error(fb11, "FATAL")
sys.exit(1)
else:
if log is not None:
log.error("malloc for IR buffer failed", "memhub")
else:
if log is not None:
log.error("翻译失败", "translator")
argparse.release(args)
if log is not None:
log.success("TransPyV 完成")
return 0