Files
ViperC/App/main.vp
T
TermiNexus f5c4be3995 refactor(core): Phase1 拆 3 组(Scan/Topo/Main,1801→46 行)
直接移动+改调用方:7 个函数按功能移至 3 个子模块,主文件仅保留
常量/_mbuddy。
- 子模块函数体内裸调用映射子模块前缀,全局符号经 Phase1._xxx 访问
- 调用方 main.vp 的 Phase1.func( → 子模块前缀并补 import
- 全 App 语法/引用/import 验证通过
2026-08-17 08:14:45 +08:00

700 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 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 lib
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 llvmlite
import subprocess
import viperlib
import hashlib
import t, c
import lib.core.StubMergerBuild as StubMergerBuild
import lib.core.StubMergerCollect as StubMergerCollect
import lib.core.Phase1Main as Phase1Main
# Main Pool 1 GiB
POOL_SIZE: t.CDefine = 1073741824
# Permanent memory pool size: 2^29 + 264 = 512MB usable
PERM_POOL_SIZE: t.CDefine = 536871176
# Code page: UTF-8 (Windows 65001)
CODE_PAGE: t.CDefine = 65001
# Source code buffer size (1MB)
SRC_BUF_SIZE: t.CDefine = 1048576
# Version (dev/beta/rc)
VIPER_VERSION: t.CDefine = "1.0.3-dev.29 2026-07-27 Non-LTS"
TPV_VERSION: t.CDefine = "0.0.2-dev.173"
RUNTIME_VERSION: t.CDefine = "0.2.2+3-Non-LTS"
TERMINEXUS_AGE: t.CDefine = "15.3"
# ============================================================
# main: Command-line entry
# ============================================================
def main() -> int:
w32cmd.SetConsoleOutputCP(CODE_PAGE)
w32cmd.SetConsoleCP(CODE_PAGE)
# Initialize the mbuddy memory pool
arena: bytes = stdlib.malloc(POOL_SIZE)
if arena is None:
stdio.printf("FAIL: malloc for arena failed\n")
return 1
# Create a global permanent memory pool (perm_pool, 512MB)
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
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: 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 None:
log.error("TransPyV Failed to parse parameters")
# 初始化命令行参数(Windows: GetCommandLineA, POSIX: /proc/self/cmdline)
sys._init_argv()
# 创建参数解析器
parser: argparse.ArgumentParser | t.CPtr = argparse.ArgumentParser("TransPyV", "TransPyV command-line parameter parser", pool=mb)
# 注册参数(与 Projectrans.py main() 一致)
parser.add_argument("--project", None, argparse.STRING, 0, None, False, argparse.STORE, "Project VPJ path, by default it looks in the current directory")
parser.add_argument("--src", None, argparse.STRING, 0, None, False, argparse.STORE, "Source directory")
parser.add_argument("--temp", None, argparse.STRING, 0, None, False, argparse.STORE, "Declare a temporary directory for the interface")
parser.add_argument("--output", None, argparse.STRING, 0, None, False, argparse.STORE, "Output directory")
parser.add_argument("--phase", None, argparse.STRING, 0, None, False, argparse.STORE, "Phase: 1 = Generate statement, 2 = Translate and compile, all = Everything")
parser.add_argument("--cc", None, argparse.STRING, 0, None, False, argparse.STORE, "LLVM compiler command")
parser.add_argument("--clean", None, argparse.BOOL, 0, None, False, argparse.STORE_TRUE, "Clean output and temp directories")
parser.add_argument("--run", None, argparse.BOOL, 0, None, False, argparse.STORE_TRUE, "Run the generated executable after compilation")
parser.add_argument("--rebuild-includes", None, argparse.BOOL, 0, None, False, argparse.STORE_TRUE, "Delete the includes.binary precompiled cache and rebuild all the includes")
parser.add_argument("--clear-cache", None, argparse.BOOL, 0, None, False, argparse.STORE_TRUE, "Clear .transpyc_cache global cache")
parser.add_argument("--asan", None, argparse.BOOL, 0, None, False, argparse.STORE_TRUE, "ASan mode: .ll first opt -passes=asan then link -fsanitize=address")
parser.add_argument("--nowarn-register", None, argparse.BOOL, 0, None, False, argparse.STORE_TRUE, "Suppress Phase A dependency WARN")
parser.add_argument("--version", "-v", argparse.BOOL, 0, None, False, argparse.STORE_TRUE, "Show version and exit")
parser.add_argument("--help", "-h", argparse.BOOL, 0, None, False, argparse.STORE_TRUE, "Show help and exit")
# 解析命令行参数
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 args.get_bool("help"):
parser.print_help()
return 0
if args.get_bool("version"):
stdio.printf("""Viper %s
GVSDS(TM) Viper SE Runtime Environment and Library (VRTE) %s
Viper Frontend Compiler TransPyV %s
TermiNexus Age %sY\n\n""", VIPER_VERSION, RUNTIME_VERSION, TPV_VERSION, TERMINEXUS_AGE)
return 0
# 打印解析结果
#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")
# --nowarn-register:抑制"外部函数返回类型未登记"类 WARN 输出
# (自举编译中大量出现刷屏,仅抑制打印,默认 i32 推断/标记重试逻辑保留)
# 注意:argparse 的 dest 保留短横线("--nowarn-register" → "nowarn-register"),
# 读取时必须用破折号 key,否则 get_bool 查不到 → 开关永不置位 → WARN 照打。
if args.get_bool("nowarn-register"):
VLogger.set_nowarn_register(1)
if log is not None:
log.info("--nowarn-register 已启用:抑制注册类 WARN 输出", "vlogger")
# 加载 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 Phase1Main.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 = StubMergerCollect._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 = StubMergerCollect._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 = 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: 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