stage3: unify to cleanup; MIR return-defer; docs+smokes updated; LLVM(harness): finalize_phis ownership, ret.py simplified, uses-predeclare; cleanup return override green; method-postfix cleanup return WIP (PHI head)
This commit is contained in:
177
src/runner/modes/common_util/exec.rs
Normal file
177
src/runner/modes/common_util/exec.rs
Normal file
@ -0,0 +1,177 @@
|
||||
use std::path::Path;
|
||||
|
||||
use super::io::spawn_with_timeout;
|
||||
|
||||
/// Emit MIR JSON and invoke the Python llvmlite harness to produce an object file.
|
||||
/// - module: lib-side MIR module
|
||||
/// - out_path: destination object path
|
||||
/// - timeout_ms: process timeout
|
||||
#[allow(dead_code)]
|
||||
pub fn llvmlite_emit_object(
|
||||
module: &nyash_rust::mir::MirModule,
|
||||
out_path: &str,
|
||||
timeout_ms: u64,
|
||||
) -> Result<(), String> {
|
||||
// Ensure parent directory exists
|
||||
if let Some(parent) = Path::new(out_path).parent() {
|
||||
let _ = std::fs::create_dir_all(parent);
|
||||
}
|
||||
// Locate python3 and harness
|
||||
let py3 = which::which("python3").map_err(|e| format!("python3 not found: {}", e))?;
|
||||
let harness = Path::new("tools/llvmlite_harness.py");
|
||||
if !harness.exists() {
|
||||
return Err(format!("llvmlite harness not found: {}", harness.display()));
|
||||
}
|
||||
// Emit MIR(JSON) to tmp
|
||||
let tmp_dir = Path::new("tmp");
|
||||
let _ = std::fs::create_dir_all(tmp_dir);
|
||||
let mir_json_path = tmp_dir.join("nyash_harness_mir.json");
|
||||
crate::runner::mir_json_emit::emit_mir_json_for_harness(module, &mir_json_path)
|
||||
.map_err(|e| format!("MIR JSON emit error: {}", e))?;
|
||||
crate::cli_v!(
|
||||
"[Runner/LLVM] using llvmlite harness → {} (mir={})",
|
||||
out_path,
|
||||
mir_json_path.display()
|
||||
);
|
||||
// Spawn harness
|
||||
let mut cmd = std::process::Command::new(py3);
|
||||
cmd.args([
|
||||
harness.to_string_lossy().as_ref(),
|
||||
"--in",
|
||||
&mir_json_path.display().to_string(),
|
||||
"--out",
|
||||
out_path,
|
||||
]);
|
||||
let out = spawn_with_timeout(cmd, timeout_ms).map_err(|e| format!("spawn harness: {}", e))?;
|
||||
if out.timed_out || !out.status_ok {
|
||||
return Err(format!(
|
||||
"llvmlite harness failed (timeout={} code={:?})",
|
||||
out.timed_out, out.exit_code
|
||||
));
|
||||
}
|
||||
// Verify output
|
||||
match std::fs::metadata(out_path) {
|
||||
Ok(meta) if meta.len() > 0 => {
|
||||
crate::cli_v!(
|
||||
"[LLVM] object emitted: {} ({} bytes)",
|
||||
out_path,
|
||||
meta.len()
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
_ => Err(format!("harness output not found or empty: {}", out_path)),
|
||||
}
|
||||
}
|
||||
|
||||
/// Resolve ny-llvmc executable path with env/PATH fallbacks
|
||||
fn resolve_ny_llvmc() -> std::path::PathBuf {
|
||||
std::env::var("NYASH_NY_LLVM_COMPILER")
|
||||
.ok()
|
||||
.and_then(|s| if !s.is_empty() { Some(std::path::PathBuf::from(s)) } else { None })
|
||||
.or_else(|| which::which("ny-llvmc").ok())
|
||||
.unwrap_or_else(|| std::path::PathBuf::from("target/release/ny-llvmc"))
|
||||
}
|
||||
|
||||
fn hint_ny_llvmc_missing(path: &std::path::Path) -> String {
|
||||
format!(
|
||||
"ny-llvmc not found (tried: {}).\nHints:\n - Build it: cargo build -p nyash-llvm-compiler --release\n - Use the built binary: target/release/ny-llvmc\n - Or set env NYASH_NY_LLVM_COMPILER=/full/path/to/ny-llvmc\n - Or add it to PATH\n",
|
||||
path.display()
|
||||
)
|
||||
}
|
||||
|
||||
/// Emit native executable via ny-llvmc (lib-side MIR)
|
||||
#[allow(dead_code)]
|
||||
pub fn ny_llvmc_emit_exe_lib(
|
||||
module: &nyash_rust::mir::MirModule,
|
||||
exe_out: &str,
|
||||
nyrt_dir: Option<&str>,
|
||||
extra_libs: Option<&str>,
|
||||
) -> Result<(), String> {
|
||||
let tmp_dir = std::path::Path::new("tmp");
|
||||
let _ = std::fs::create_dir_all(tmp_dir);
|
||||
let json_path = tmp_dir.join("nyash_cli_emit.json");
|
||||
crate::runner::mir_json_emit::emit_mir_json_for_harness(module, &json_path)
|
||||
.map_err(|e| format!("MIR JSON emit error: {}", e))?;
|
||||
let ny_llvmc = resolve_ny_llvmc();
|
||||
if !ny_llvmc.exists() {
|
||||
return Err(hint_ny_llvmc_missing(&ny_llvmc));
|
||||
}
|
||||
let mut cmd = std::process::Command::new(ny_llvmc);
|
||||
cmd.arg("--in")
|
||||
.arg(&json_path)
|
||||
.arg("--emit")
|
||||
.arg("exe")
|
||||
.arg("--out")
|
||||
.arg(exe_out);
|
||||
if let Some(dir) = nyrt_dir { cmd.arg("--nyrt").arg(dir); } else { cmd.arg("--nyrt").arg("target/release"); }
|
||||
if let Some(flags) = extra_libs { if !flags.trim().is_empty() { cmd.arg("--libs").arg(flags); } }
|
||||
let status = cmd.status().map_err(|e| {
|
||||
let prog_path = std::path::Path::new(cmd.get_program());
|
||||
format!(
|
||||
"failed to spawn ny-llvmc: {}\n{}",
|
||||
e,
|
||||
hint_ny_llvmc_missing(prog_path)
|
||||
)
|
||||
})?;
|
||||
if !status.success() {
|
||||
return Err(format!(
|
||||
"ny-llvmc failed with status: {:?}.\nTry adding --emit-exe-libs (e.g. \"-ldl -lpthread -lm\") or set --emit-exe-nyrt to NyRT dir (e.g. target/release).",
|
||||
status.code()
|
||||
));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Emit native executable via ny-llvmc (bin-side MIR)
|
||||
#[allow(dead_code)]
|
||||
pub fn ny_llvmc_emit_exe_bin(
|
||||
module: &crate::mir::MirModule,
|
||||
exe_out: &str,
|
||||
nyrt_dir: Option<&str>,
|
||||
extra_libs: Option<&str>,
|
||||
) -> Result<(), String> {
|
||||
let tmp_dir = std::path::Path::new("tmp");
|
||||
let _ = std::fs::create_dir_all(tmp_dir);
|
||||
let json_path = tmp_dir.join("nyash_cli_emit.json");
|
||||
crate::runner::mir_json_emit::emit_mir_json_for_harness_bin(module, &json_path)
|
||||
.map_err(|e| format!("MIR JSON emit error: {}", e))?;
|
||||
let ny_llvmc = resolve_ny_llvmc();
|
||||
if !ny_llvmc.exists() {
|
||||
return Err(hint_ny_llvmc_missing(&ny_llvmc));
|
||||
}
|
||||
let mut cmd = std::process::Command::new(ny_llvmc);
|
||||
cmd.arg("--in")
|
||||
.arg(&json_path)
|
||||
.arg("--emit")
|
||||
.arg("exe")
|
||||
.arg("--out")
|
||||
.arg(exe_out);
|
||||
if let Some(dir) = nyrt_dir { cmd.arg("--nyrt").arg(dir); } else { cmd.arg("--nyrt").arg("target/release"); }
|
||||
if let Some(flags) = extra_libs { if !flags.trim().is_empty() { cmd.arg("--libs").arg(flags); } }
|
||||
let status = cmd.status().map_err(|e| {
|
||||
let prog_path = std::path::Path::new(cmd.get_program());
|
||||
format!(
|
||||
"failed to spawn ny-llvmc: {}\n{}",
|
||||
e,
|
||||
hint_ny_llvmc_missing(prog_path)
|
||||
)
|
||||
})?;
|
||||
if !status.success() {
|
||||
return Err(format!(
|
||||
"ny-llvmc failed with status: {:?}.\nTry adding --emit-exe-libs (e.g. \"-ldl -lpthread -lm\") or set --emit-exe-nyrt to NyRT dir (e.g. target/release).",
|
||||
status.code()
|
||||
));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Run an executable with arguments and a timeout. Returns (exit_code, timed_out).
|
||||
#[allow(dead_code)]
|
||||
pub fn run_executable(exe_path: &str, args: &[&str], timeout_ms: u64) -> Result<(i32, bool), String> {
|
||||
let mut cmd = std::process::Command::new(exe_path);
|
||||
for a in args { cmd.arg(a); }
|
||||
let out = super::io::spawn_with_timeout(cmd, timeout_ms)
|
||||
.map_err(|e| format!("spawn exe: {}", e))?;
|
||||
let code = out.exit_code.unwrap_or(1);
|
||||
Ok((code, out.timed_out))
|
||||
}
|
||||
@ -8,3 +8,5 @@ pub mod pyvm;
|
||||
pub mod selfhost_exe;
|
||||
pub mod io;
|
||||
pub mod selfhost;
|
||||
pub mod resolve;
|
||||
pub mod exec;
|
||||
|
||||
@ -38,3 +38,42 @@ pub fn run_pyvm_harness(module: &crate::mir::MirModule, tag: &str) -> Result<i32
|
||||
}
|
||||
Ok(code)
|
||||
}
|
||||
|
||||
/// Run PyVM harness over a nyash_rust (lib) MIR module, returning the exit code
|
||||
#[allow(dead_code)]
|
||||
pub fn run_pyvm_harness_lib(module: &nyash_rust::mir::MirModule, tag: &str) -> Result<i32, String> {
|
||||
let py3 = which::which("python3").map_err(|e| format!("python3 not found: {}", e))?;
|
||||
let runner = std::path::Path::new("tools/pyvm_runner.py");
|
||||
if !runner.exists() {
|
||||
return Err(format!("PyVM runner not found: {}", runner.display()));
|
||||
}
|
||||
let tmp_dir = std::path::Path::new("tmp");
|
||||
let _ = std::fs::create_dir_all(tmp_dir);
|
||||
let mir_json_path = tmp_dir.join("nyash_pyvm_mir.json");
|
||||
crate::runner::mir_json_emit::emit_mir_json_for_harness(module, &mir_json_path)
|
||||
.map_err(|e| format!("PyVM MIR JSON emit error: {}", e))?;
|
||||
crate::cli_v!("[Runner] using PyVM ({} ) → {}", tag, mir_json_path.display());
|
||||
// Determine entry function hint (prefer Main.main if present)
|
||||
let entry = if module.functions.contains_key("Main.main") {
|
||||
"Main.main"
|
||||
} else if module.functions.contains_key("main") {
|
||||
"main"
|
||||
} else {
|
||||
"Main.main"
|
||||
};
|
||||
let status = std::process::Command::new(py3)
|
||||
.args([
|
||||
runner.to_string_lossy().as_ref(),
|
||||
"--in",
|
||||
&mir_json_path.display().to_string(),
|
||||
"--entry",
|
||||
entry,
|
||||
])
|
||||
.status()
|
||||
.map_err(|e| format!("spawn pyvm: {}", e))?;
|
||||
let code = status.code().unwrap_or(1);
|
||||
if !status.success() {
|
||||
crate::cli_v!("❌ PyVM ({}) failed (status={})", tag, code);
|
||||
}
|
||||
Ok(code)
|
||||
}
|
||||
|
||||
81
src/runner/modes/common_util/resolve.rs
Normal file
81
src/runner/modes/common_util/resolve.rs
Normal file
@ -0,0 +1,81 @@
|
||||
use crate::NyashRunner;
|
||||
|
||||
/// Strip `using` lines and register modules/aliases into the runtime registry.
|
||||
/// Returns cleaned source. No-op when `NYASH_ENABLE_USING` is not set.
|
||||
#[allow(dead_code)]
|
||||
pub fn strip_using_and_register(
|
||||
runner: &NyashRunner,
|
||||
code: &str,
|
||||
filename: &str,
|
||||
) -> Result<String, String> {
|
||||
if !crate::config::env::enable_using() {
|
||||
return Ok(code.to_string());
|
||||
}
|
||||
let mut out = String::with_capacity(code.len());
|
||||
let mut used_names: Vec<(String, Option<String>)> = Vec::new();
|
||||
for line in code.lines() {
|
||||
let t = line.trim_start();
|
||||
if t.starts_with("using ") {
|
||||
crate::cli_v!("[using] stripped line: {}", line);
|
||||
let rest0 = t.strip_prefix("using ").unwrap().trim();
|
||||
let rest0 = rest0.strip_suffix(';').unwrap_or(rest0).trim();
|
||||
let (target, alias) = if let Some(pos) = rest0.find(" as ") {
|
||||
(rest0[..pos].trim().to_string(), Some(rest0[pos + 4..].trim().to_string()))
|
||||
} else {
|
||||
(rest0.to_string(), None)
|
||||
};
|
||||
let is_path = target.starts_with('"')
|
||||
|| target.starts_with("./")
|
||||
|| target.starts_with('/')
|
||||
|| target.ends_with(".nyash");
|
||||
if is_path {
|
||||
let path = target.trim_matches('"').to_string();
|
||||
let name = alias.clone().unwrap_or_else(|| {
|
||||
std::path::Path::new(&path)
|
||||
.file_stem()
|
||||
.and_then(|s| s.to_str())
|
||||
.unwrap_or("module")
|
||||
.to_string()
|
||||
});
|
||||
used_names.push((name, Some(path)));
|
||||
} else {
|
||||
used_names.push((target, alias));
|
||||
}
|
||||
continue;
|
||||
}
|
||||
out.push_str(line);
|
||||
out.push('\n');
|
||||
}
|
||||
|
||||
// Register modules with resolver (aliases/modules/paths)
|
||||
let using_ctx = runner.init_using_context();
|
||||
let strict = std::env::var("NYASH_USING_STRICT").ok().as_deref() == Some("1");
|
||||
let verbose = crate::config::env::cli_verbose();
|
||||
let ctx_dir = std::path::Path::new(filename).parent();
|
||||
for (ns_or_alias, alias_or_path) in used_names {
|
||||
if let Some(path) = alias_or_path {
|
||||
let sb = crate::box_trait::StringBox::new(path);
|
||||
crate::runtime::modules_registry::set(ns_or_alias, Box::new(sb));
|
||||
} else {
|
||||
match crate::runner::pipeline::resolve_using_target(
|
||||
&ns_or_alias,
|
||||
false,
|
||||
&using_ctx.pending_modules,
|
||||
&using_ctx.using_paths,
|
||||
&using_ctx.aliases,
|
||||
ctx_dir,
|
||||
strict,
|
||||
verbose,
|
||||
) {
|
||||
Ok(value) => {
|
||||
let sb = crate::box_trait::StringBox::new(value);
|
||||
crate::runtime::modules_registry::set(ns_or_alias, Box::new(sb));
|
||||
}
|
||||
Err(e) => {
|
||||
return Err(format!("using: {}", e));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(out)
|
||||
}
|
||||
Reference in New Issue
Block a user