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:
@ -13,8 +13,8 @@ pub(crate) fn execute_file_with_backend(runner: &NyashRunner, filename: &str) {
|
||||
if std::env::var("NYASH_USE_NY_COMPILER").ok().as_deref() == Some("1") {
|
||||
if runner.try_run_selfhost_pipeline(filename) {
|
||||
return;
|
||||
} else if std::env::var("NYASH_CLI_VERBOSE").ok().as_deref() == Some("1") {
|
||||
eprintln!("[ny-compiler] fallback to default path (MVP unavailable for this input)");
|
||||
} else {
|
||||
crate::cli_v!("[ny-compiler] fallback to default path (MVP unavailable for this input)");
|
||||
}
|
||||
}
|
||||
|
||||
@ -31,12 +31,7 @@ pub(crate) fn execute_file_with_backend(runner: &NyashRunner, filename: &str) {
|
||||
};
|
||||
match json_v0_bridge::parse_source_v0_to_module(&code) {
|
||||
Ok(module) => {
|
||||
if std::env::var("NYASH_CLI_VERBOSE").ok().as_deref() == Some("1") {
|
||||
println!(
|
||||
"🚀 Nyash MIR Interpreter - (parser=ny) Executing file: {} 🚀",
|
||||
filename
|
||||
);
|
||||
}
|
||||
crate::cli_v!("🚀 Nyash MIR Interpreter - (parser=ny) Executing file: {} 🚀", filename);
|
||||
runner.execute_mir_module(&module);
|
||||
return;
|
||||
}
|
||||
@ -70,9 +65,7 @@ pub(crate) fn execute_file_with_backend(runner: &NyashRunner, filename: &str) {
|
||||
|
||||
// MIR dump/verify
|
||||
if runner.config.dump_mir || runner.config.verify_mir {
|
||||
if std::env::var("NYASH_CLI_VERBOSE").ok().as_deref() == Some("1") {
|
||||
println!("🚀 Nyash MIR Compiler - Processing file: {} 🚀", filename);
|
||||
}
|
||||
crate::cli_v!("🚀 Nyash MIR Compiler - Processing file: {} 🚀", filename);
|
||||
runner.execute_mir_mode(filename);
|
||||
return;
|
||||
}
|
||||
@ -106,15 +99,11 @@ pub(crate) fn execute_file_with_backend(runner: &NyashRunner, filename: &str) {
|
||||
// Backend selection
|
||||
match runner.config.backend.as_str() {
|
||||
"mir" => {
|
||||
if std::env::var("NYASH_CLI_VERBOSE").ok().as_deref() == Some("1") {
|
||||
println!("🚀 Nyash MIR Interpreter - Executing file: {} 🚀", filename);
|
||||
}
|
||||
crate::cli_v!("🚀 Nyash MIR Interpreter - Executing file: {} 🚀", filename);
|
||||
runner.execute_mir_mode(filename);
|
||||
}
|
||||
"vm" => {
|
||||
if std::env::var("NYASH_CLI_VERBOSE").ok().as_deref() == Some("1") {
|
||||
println!("🚀 Nyash VM Backend - Executing file: {} 🚀", filename);
|
||||
}
|
||||
crate::cli_v!("🚀 Nyash VM Backend - Executing file: {} 🚀", filename);
|
||||
#[cfg(feature = "vm-legacy")]
|
||||
{
|
||||
runner.execute_vm_mode(filename);
|
||||
@ -139,12 +128,7 @@ pub(crate) fn execute_file_with_backend(runner: &NyashRunner, filename: &str) {
|
||||
}
|
||||
#[cfg(feature = "cranelift-jit")]
|
||||
"jit-direct" => {
|
||||
if std::env::var("NYASH_CLI_VERBOSE").ok().as_deref() == Some("1") {
|
||||
println!(
|
||||
"⚡ Nyash JIT-Direct Backend - Executing file: {} ⚡",
|
||||
filename
|
||||
);
|
||||
}
|
||||
crate::cli_v!("⚡ Nyash JIT-Direct Backend - Executing file: {} ⚡", filename);
|
||||
#[cfg(feature = "cranelift-jit")]
|
||||
{
|
||||
// Use independent JIT-direct runner method (no VM execute loop)
|
||||
@ -157,9 +141,7 @@ pub(crate) fn execute_file_with_backend(runner: &NyashRunner, filename: &str) {
|
||||
}
|
||||
}
|
||||
"llvm" => {
|
||||
if std::env::var("NYASH_CLI_VERBOSE").ok().as_deref() == Some("1") {
|
||||
println!("⚡ Nyash LLVM Backend - Executing file: {} ⚡", filename);
|
||||
}
|
||||
crate::cli_v!("⚡ Nyash LLVM Backend - Executing file: {} ⚡", filename);
|
||||
runner.execute_llvm_mode(filename);
|
||||
}
|
||||
other => {
|
||||
@ -183,41 +165,13 @@ impl NyashRunner {
|
||||
}
|
||||
// If CLI requested EXE emit, generate JSON then invoke ny-llvmc to link NyRT and exit.
|
||||
if let Some(exe_out) = self.config.emit_exe.as_ref() {
|
||||
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");
|
||||
if let Err(e) = crate::runner::mir_json_emit::emit_mir_json_for_harness_bin(module, &json_path) {
|
||||
eprintln!("❌ MIR JSON emit error: {}", e);
|
||||
std::process::exit(1);
|
||||
}
|
||||
// Resolve ny-llvmc
|
||||
let ny_llvmc = 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"));
|
||||
// Build command
|
||||
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) = self.config.emit_exe_nyrt.as_ref() {
|
||||
cmd.arg("--nyrt").arg(dir);
|
||||
} else {
|
||||
// default hint
|
||||
cmd.arg("--nyrt").arg("target/release");
|
||||
}
|
||||
if let Some(flags) = self.config.emit_exe_libs.as_ref() {
|
||||
if !flags.trim().is_empty() {
|
||||
cmd.arg("--libs").arg(flags);
|
||||
}
|
||||
}
|
||||
let status = cmd.status().unwrap_or_else(|e| {
|
||||
eprintln!("❌ failed to spawn ny-llvmc: {}", e);
|
||||
std::process::exit(1);
|
||||
});
|
||||
if !status.success() {
|
||||
eprintln!("❌ ny-llvmc failed with status: {:?}", status.code());
|
||||
if let Err(e) = crate::runner::modes::common_util::exec::ny_llvmc_emit_exe_bin(
|
||||
module,
|
||||
exe_out,
|
||||
self.config.emit_exe_nyrt.as_deref(),
|
||||
self.config.emit_exe_libs.as_deref(),
|
||||
) {
|
||||
eprintln!("❌ {}", e);
|
||||
std::process::exit(1);
|
||||
}
|
||||
println!("EXE written: {}", exe_out);
|
||||
|
||||
@ -15,6 +15,7 @@ pub(super) mod try_catch;
|
||||
pub(super) mod expr;
|
||||
pub(super) mod ternary; // placeholder (not wired)
|
||||
pub(super) mod peek; // placeholder (not wired)
|
||||
pub(super) mod throw_ctx; // thread-local ctx for Result-mode throw routing
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub(super) struct LoopContext {
|
||||
@ -28,19 +29,96 @@ pub(super) struct BridgeEnv {
|
||||
pub(super) mir_no_phi: bool,
|
||||
pub(super) allow_me_dummy: bool,
|
||||
pub(super) me_class: String,
|
||||
pub(super) try_result_mode: bool,
|
||||
}
|
||||
|
||||
impl BridgeEnv {
|
||||
pub(super) fn load() -> Self {
|
||||
let trm = crate::config::env::try_result_mode();
|
||||
let no_phi = crate::config::env::mir_no_phi();
|
||||
if crate::config::env::cli_verbose() {
|
||||
eprintln!("[Bridge] load: try_result_mode={} mir_no_phi={}", trm, no_phi);
|
||||
}
|
||||
Self {
|
||||
throw_enabled: std::env::var("NYASH_BRIDGE_THROW_ENABLE").ok().as_deref() == Some("1"),
|
||||
mir_no_phi: crate::config::env::mir_no_phi(),
|
||||
mir_no_phi: no_phi,
|
||||
allow_me_dummy: std::env::var("NYASH_BRIDGE_ME_DUMMY").ok().as_deref() == Some("1"),
|
||||
me_class: std::env::var("NYASH_BRIDGE_ME_CLASS").unwrap_or_else(|_| "Main".to_string()),
|
||||
try_result_mode: trm,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Small helper: set Jump terminator and record predecessor on the target.
|
||||
fn jump_with_pred(f: &mut MirFunction, cur_bb: BasicBlockId, target: BasicBlockId) {
|
||||
if let Some(bb) = f.get_block_mut(cur_bb) {
|
||||
bb.set_terminator(MirInstruction::Jump { target });
|
||||
}
|
||||
if let Some(succ) = f.get_block_mut(target) {
|
||||
succ.add_predecessor(cur_bb);
|
||||
}
|
||||
}
|
||||
|
||||
/// Strip Phi instructions by inserting edge copies on each predecessor.
|
||||
/// This normalizes MIR to PHI-off form for downstream harnesses that synthesize PHIs.
|
||||
fn strip_phi_functions(f: &mut MirFunction) {
|
||||
// Collect block ids to avoid borrow issues while mutating
|
||||
let block_ids: Vec<BasicBlockId> = f.blocks.keys().copied().collect();
|
||||
for bbid in block_ids {
|
||||
// Snapshot phi instructions at the head
|
||||
let mut phi_entries: Vec<(ValueId, Vec<(BasicBlockId, ValueId)>)> = Vec::new();
|
||||
if let Some(bb) = f.blocks.get(&bbid) {
|
||||
for inst in &bb.instructions {
|
||||
if let MirInstruction::Phi { dst, inputs } = inst {
|
||||
phi_entries.push((*dst, inputs.clone()));
|
||||
} else {
|
||||
// PHIs must be at the beginning; once we see non-Phi, stop
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if phi_entries.is_empty() {
|
||||
continue;
|
||||
}
|
||||
// Insert copies on predecessors
|
||||
for (dst, inputs) in &phi_entries {
|
||||
for (pred, val) in inputs {
|
||||
if let Some(pbb) = f.blocks.get_mut(pred) {
|
||||
pbb.add_instruction(MirInstruction::Copy { dst: *dst, src: *val });
|
||||
}
|
||||
}
|
||||
}
|
||||
// Remove Phi instructions from the merge block
|
||||
if let Some(bb) = f.blocks.get_mut(&bbid) {
|
||||
let non_phi: Vec<MirInstruction> = bb
|
||||
.instructions
|
||||
.iter()
|
||||
.cloned()
|
||||
.skip_while(|inst| matches!(inst, MirInstruction::Phi { .. }))
|
||||
.collect();
|
||||
bb.instructions = non_phi;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn lower_break_stmt(f: &mut MirFunction, cur_bb: BasicBlockId, exit_bb: BasicBlockId) {
|
||||
jump_with_pred(f, cur_bb, exit_bb);
|
||||
crate::jit::events::emit_lower(
|
||||
serde_json::json!({ "id": "loop_break","exit_bb": exit_bb.0,"decision": "lower" }),
|
||||
"loop",
|
||||
"<json_v0>",
|
||||
);
|
||||
}
|
||||
|
||||
fn lower_continue_stmt(f: &mut MirFunction, cur_bb: BasicBlockId, cond_bb: BasicBlockId) {
|
||||
jump_with_pred(f, cur_bb, cond_bb);
|
||||
crate::jit::events::emit_lower(
|
||||
serde_json::json!({ "id": "loop_continue","cond_bb": cond_bb.0,"decision": "lower" }),
|
||||
"loop",
|
||||
"<json_v0>",
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
pub(super) fn lower_stmt_with_vars(
|
||||
f: &mut MirFunction,
|
||||
@ -86,31 +164,13 @@ pub(super) fn lower_stmt_with_vars(
|
||||
}
|
||||
StmtV0::Break => {
|
||||
if let Some(ctx) = loop_stack.last().copied() {
|
||||
if let Some(bb) = f.get_block_mut(cur_bb) {
|
||||
bb.set_terminator(MirInstruction::Jump {
|
||||
target: ctx.exit_bb,
|
||||
});
|
||||
}
|
||||
crate::jit::events::emit_lower(
|
||||
serde_json::json!({ "id": "loop_break","exit_bb": ctx.exit_bb.0,"decision": "lower" }),
|
||||
"loop",
|
||||
"<json_v0>",
|
||||
);
|
||||
lower_break_stmt(f, cur_bb, ctx.exit_bb);
|
||||
}
|
||||
Ok(cur_bb)
|
||||
}
|
||||
StmtV0::Continue => {
|
||||
if let Some(ctx) = loop_stack.last().copied() {
|
||||
if let Some(bb) = f.get_block_mut(cur_bb) {
|
||||
bb.set_terminator(MirInstruction::Jump {
|
||||
target: ctx.cond_bb,
|
||||
});
|
||||
}
|
||||
crate::jit::events::emit_lower(
|
||||
serde_json::json!({ "id": "loop_continue","cond_bb": ctx.cond_bb.0,"decision": "lower" }),
|
||||
"loop",
|
||||
"<json_v0>",
|
||||
);
|
||||
lower_continue_stmt(f, cur_bb, ctx.cond_bb);
|
||||
}
|
||||
Ok(cur_bb)
|
||||
}
|
||||
@ -194,12 +254,16 @@ pub(super) fn lower_program(prog: ProgramV0) -> Result<MirModule, String> {
|
||||
}
|
||||
}
|
||||
f.signature.return_type = MirType::Unknown;
|
||||
// PHI-off normalization for Bridge output
|
||||
if env.mir_no_phi {
|
||||
strip_phi_functions(&mut f);
|
||||
}
|
||||
module.add_function(f);
|
||||
Ok(module)
|
||||
}
|
||||
|
||||
pub(super) fn maybe_dump_mir(module: &MirModule) {
|
||||
if std::env::var("NYASH_CLI_VERBOSE").ok().as_deref() == Some("1") {
|
||||
if crate::config::env::cli_verbose() {
|
||||
let p = MirPrinter::new();
|
||||
println!("{}", p.print_module(module));
|
||||
}
|
||||
|
||||
@ -78,6 +78,15 @@ fn lower_throw(
|
||||
cur_bb: BasicBlockId,
|
||||
exception_value: ValueId,
|
||||
) -> (ValueId, BasicBlockId) {
|
||||
// Result-mode try context active: route to current catch via Jump and record incoming
|
||||
if env.try_result_mode && super::throw_ctx::is_active() {
|
||||
if crate::config::env::cli_verbose() {
|
||||
eprintln!("[Bridge] lower_throw: routing to catch (Result-mode)");
|
||||
}
|
||||
let _ = super::throw_ctx::record_throw(f, cur_bb, exception_value);
|
||||
return (exception_value, cur_bb);
|
||||
}
|
||||
// Legacy path: emit MIR Throw (if enabled) or degrade to const 0
|
||||
if env.throw_enabled {
|
||||
if let Some(bb) = f.get_block_mut(cur_bb) {
|
||||
bb.set_terminator(MirInstruction::Throw {
|
||||
@ -89,10 +98,7 @@ fn lower_throw(
|
||||
} else {
|
||||
let dst = f.next_value_id();
|
||||
if let Some(bb) = f.get_block_mut(cur_bb) {
|
||||
bb.add_instruction(MirInstruction::Const {
|
||||
dst,
|
||||
value: ConstValue::Integer(0),
|
||||
});
|
||||
bb.add_instruction(MirInstruction::Const { dst, value: ConstValue::Integer(0) });
|
||||
}
|
||||
(dst, cur_bb)
|
||||
}
|
||||
|
||||
@ -48,9 +48,10 @@ pub(super) fn lower_if_stmt(
|
||||
}
|
||||
(else_bb, base_vars.clone())
|
||||
};
|
||||
// PHI-off policy (edge-copy) is the default in Phase 15; enforce for stability
|
||||
merge_var_maps(
|
||||
f,
|
||||
env.mir_no_phi,
|
||||
true,
|
||||
merge_bb,
|
||||
tend,
|
||||
else_end_pred,
|
||||
|
||||
52
src/runner/json_v0_bridge/lowering/throw_ctx.rs
Normal file
52
src/runner/json_v0_bridge/lowering/throw_ctx.rs
Normal file
@ -0,0 +1,52 @@
|
||||
use crate::mir::{BasicBlockId, MirFunction, MirInstruction, ValueId};
|
||||
use std::cell::RefCell;
|
||||
|
||||
thread_local! {
|
||||
static THROW_CTX: RefCell<Option<ThrowCtx>> = RefCell::new(None);
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub(super) struct ThrowCtx {
|
||||
pub(super) catch_bb: BasicBlockId,
|
||||
pub(super) incoming: Vec<(BasicBlockId, ValueId)>,
|
||||
}
|
||||
|
||||
impl ThrowCtx {
|
||||
fn new(catch_bb: BasicBlockId) -> Self {
|
||||
Self { catch_bb, incoming: Vec::new() }
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn set(catch_bb: BasicBlockId) {
|
||||
THROW_CTX.with(|slot| {
|
||||
*slot.borrow_mut() = Some(ThrowCtx::new(catch_bb));
|
||||
});
|
||||
}
|
||||
|
||||
pub(super) fn take() -> Option<ThrowCtx> {
|
||||
THROW_CTX.with(|slot| slot.borrow_mut().take())
|
||||
}
|
||||
|
||||
pub(super) fn is_active() -> bool {
|
||||
THROW_CTX.with(|slot| slot.borrow().is_some())
|
||||
}
|
||||
|
||||
/// Record a throw from `from_bb` with value `exc_val`. Sets terminator Jump to catch and
|
||||
/// appends predecessor+value to the incoming list. Returns the catch block id if active.
|
||||
pub(super) fn record_throw(f: &mut MirFunction, from_bb: BasicBlockId, exc_val: ValueId) -> Option<BasicBlockId> {
|
||||
THROW_CTX.with(|slot| {
|
||||
if let Some(ctx) = slot.borrow_mut().as_mut() {
|
||||
let target = ctx.catch_bb;
|
||||
if let Some(bb) = f.get_block_mut(from_bb) {
|
||||
bb.set_terminator(MirInstruction::Jump { target });
|
||||
}
|
||||
if let Some(succ) = f.get_block_mut(target) {
|
||||
succ.add_predecessor(from_bb);
|
||||
}
|
||||
ctx.incoming.push((from_bb, exc_val));
|
||||
Some(target)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
}
|
||||
@ -16,7 +16,164 @@ pub(super) fn lower_try_stmt(
|
||||
env: &BridgeEnv,
|
||||
) -> Result<BasicBlockId, String> {
|
||||
let try_enabled = std::env::var("NYASH_BRIDGE_TRY_ENABLE").ok().as_deref() == Some("1");
|
||||
if !try_enabled || catches.is_empty() || catches.len() > 1 {
|
||||
// Result-mode lowering: structured blocks without MIR Throw/Catch
|
||||
if env.try_result_mode {
|
||||
// Only support 0 or 1 catch for MVP
|
||||
let has_catch = !catches.is_empty();
|
||||
if catches.len() > 1 {
|
||||
// Fallback to safe lowering (ignore catches) for multi-catch
|
||||
let mut tmp_vars = vars.clone();
|
||||
let mut next_bb = super::lower_stmt_list_with_vars(f, cur_bb, try_body, &mut tmp_vars, loop_stack, env)?;
|
||||
if !finally.is_empty() {
|
||||
next_bb = super::lower_stmt_list_with_vars(f, next_bb, finally, &mut tmp_vars, loop_stack, env)?;
|
||||
}
|
||||
*vars = tmp_vars;
|
||||
return Ok(next_bb);
|
||||
}
|
||||
|
||||
let base_vars = vars.clone();
|
||||
let try_bb = new_block(f);
|
||||
let catch_bb_opt = if has_catch { Some(new_block(f)) } else { None };
|
||||
let finally_bb = if !finally.is_empty() { Some(new_block(f)) } else { None };
|
||||
let exit_bb = new_block(f);
|
||||
|
||||
if let Some(bb) = f.get_block_mut(cur_bb) { bb.set_terminator(MirInstruction::Jump { target: try_bb }); }
|
||||
if let Some(succ) = f.get_block_mut(try_bb) { succ.add_predecessor(cur_bb); }
|
||||
|
||||
// Install thread-local throw context so nested throw expressions jump to catch_bb
|
||||
if has_catch {
|
||||
let catch_bb = catch_bb_opt.expect("catch_bb must exist when has_catch");
|
||||
if crate::config::env::cli_verbose() {
|
||||
eprintln!("[Bridge] try_result_mode: set ThrowCtx (catch_bb={:?})", catch_bb);
|
||||
}
|
||||
super::throw_ctx::set(catch_bb);
|
||||
} else if crate::config::env::cli_verbose() {
|
||||
eprintln!("[Bridge] try_result_mode: no catch present; ThrowCtx not set");
|
||||
}
|
||||
let mut try_vars = base_vars.clone();
|
||||
let try_end = super::lower_stmt_list_with_vars(f, try_bb, try_body, &mut try_vars, loop_stack, env)?;
|
||||
// Take recorded incoming exceptions
|
||||
let incoming_exc = if has_catch { super::throw_ctx::take().map(|c| c.incoming).unwrap_or_default() } else { Vec::new() };
|
||||
if let Some(bb) = f.get_block_mut(try_end) {
|
||||
if !bb.is_terminated() {
|
||||
let target = finally_bb.unwrap_or(exit_bb);
|
||||
bb.set_terminator(MirInstruction::Jump { target });
|
||||
if let Some(succ) = f.get_block_mut(target) { succ.add_predecessor(try_end); }
|
||||
}
|
||||
}
|
||||
let try_branch_vars = try_vars.clone();
|
||||
|
||||
// Lower catch block if present and reachable
|
||||
let (catch_end, catch_branch_vars) = if has_catch {
|
||||
let catch_bb = catch_bb_opt.expect("catch_bb must exist when has_catch");
|
||||
// Prepare catch var mapping; optionally bind param via PHI from incoming throw sites.
|
||||
let catch_clause = &catches[0];
|
||||
let mut catch_vars = base_vars.clone();
|
||||
if let Some(param) = &catch_clause.param {
|
||||
if !env.mir_no_phi && !incoming_exc.is_empty() {
|
||||
let phi_dst = f.next_value_id();
|
||||
if let Some(bb) = f.get_block_mut(catch_bb) {
|
||||
let mut inputs = incoming_exc.clone();
|
||||
inputs.sort_by_key(|(bbid, _)| bbid.0);
|
||||
bb.insert_instruction_after_phis(MirInstruction::Phi { dst: phi_dst, inputs });
|
||||
}
|
||||
catch_vars.insert(param.clone(), phi_dst);
|
||||
}
|
||||
}
|
||||
let end = super::lower_stmt_list_with_vars(
|
||||
f,
|
||||
catch_bb,
|
||||
&catch_clause.body,
|
||||
&mut catch_vars,
|
||||
loop_stack,
|
||||
env,
|
||||
)?;
|
||||
if let Some(bb) = f.get_block_mut(end) {
|
||||
if !bb.is_terminated() {
|
||||
let target = finally_bb.unwrap_or(exit_bb);
|
||||
bb.set_terminator(MirInstruction::Jump { target });
|
||||
if let Some(succ) = f.get_block_mut(target) { succ.add_predecessor(end); }
|
||||
}
|
||||
}
|
||||
(end, catch_vars)
|
||||
} else {
|
||||
(try_end, base_vars.clone())
|
||||
};
|
||||
|
||||
// Finally or direct exit; merge variables across branches
|
||||
use std::collections::HashSet;
|
||||
if let Some(finally_block) = finally_bb {
|
||||
// Compute merged var map from try_end + catch_end (if has_catch)
|
||||
let branch_vars: Vec<(BasicBlockId, HashMap<String, ValueId>)> = if has_catch {
|
||||
vec![(try_end, try_branch_vars.clone()), (catch_end, catch_branch_vars.clone())]
|
||||
} else {
|
||||
vec![(try_end, try_branch_vars.clone())]
|
||||
};
|
||||
let mut names: HashSet<String> = base_vars.keys().cloned().collect();
|
||||
for (_, map) in &branch_vars { names.extend(map.keys().cloned()); }
|
||||
let mut merged_vars = base_vars.clone();
|
||||
let mut phi_entries: Vec<(ValueId, Vec<(BasicBlockId, ValueId)>)> = Vec::new();
|
||||
for name in names {
|
||||
let mut inputs: Vec<(BasicBlockId, ValueId)> = Vec::new();
|
||||
for (bbid, map) in &branch_vars {
|
||||
if let Some(&v) = map.get(&name) { inputs.push((*bbid, v)); }
|
||||
}
|
||||
if inputs.is_empty() {
|
||||
if let Some(&b) = base_vars.get(&name) { merged_vars.insert(name.clone(), b); }
|
||||
continue;
|
||||
}
|
||||
let uniq: HashSet<ValueId> = inputs.iter().map(|(_, v)| *v).collect();
|
||||
if uniq.len() == 1 {
|
||||
merged_vars.insert(name.clone(), inputs[0].1);
|
||||
continue;
|
||||
}
|
||||
let dst = f.next_value_id();
|
||||
inputs.sort_by_key(|(bbid, _)| bbid.0);
|
||||
phi_entries.push((dst, inputs));
|
||||
merged_vars.insert(name.clone(), dst);
|
||||
}
|
||||
if let Some(bb) = f.get_block_mut(finally_block) {
|
||||
for (dst, inputs) in phi_entries { bb.insert_instruction_after_phis(MirInstruction::Phi { dst, inputs }); }
|
||||
}
|
||||
let mut finally_vars = merged_vars.clone();
|
||||
let final_end = super::lower_stmt_list_with_vars(f, finally_block, finally, &mut finally_vars, loop_stack, env)?;
|
||||
if let Some(bb) = f.get_block_mut(final_end) {
|
||||
if !bb.is_terminated() {
|
||||
bb.set_terminator(MirInstruction::Jump { target: exit_bb });
|
||||
if let Some(succ) = f.get_block_mut(exit_bb) { succ.add_predecessor(final_end); }
|
||||
}
|
||||
}
|
||||
*vars = finally_vars;
|
||||
return Ok(exit_bb);
|
||||
} else {
|
||||
// Merge at exit_bb
|
||||
let branch_vars: Vec<(BasicBlockId, HashMap<String, ValueId>)> = if has_catch {
|
||||
vec![(try_end, try_branch_vars), (catch_end, catch_branch_vars)]
|
||||
} else {
|
||||
vec![(try_end, try_branch_vars)]
|
||||
};
|
||||
let mut names: HashSet<String> = base_vars.keys().cloned().collect();
|
||||
for (_, map) in &branch_vars { names.extend(map.keys().cloned()); }
|
||||
let mut merged_vars = base_vars.clone();
|
||||
let mut phi_entries: Vec<(ValueId, Vec<(BasicBlockId, ValueId)>)> = Vec::new();
|
||||
for name in names {
|
||||
let mut inputs: Vec<(BasicBlockId, ValueId)> = Vec::new();
|
||||
for (bbid, map) in &branch_vars { if let Some(&v) = map.get(&name) { inputs.push((*bbid, v)); } }
|
||||
if inputs.is_empty() { if let Some(&b) = base_vars.get(&name) { merged_vars.insert(name.clone(), b); } continue; }
|
||||
let uniq: HashSet<ValueId> = inputs.iter().map(|(_, v)| *v).collect();
|
||||
if uniq.len() == 1 { merged_vars.insert(name.clone(), inputs[0].1); continue; }
|
||||
let dst = f.next_value_id();
|
||||
inputs.sort_by_key(|(bbid, _)| bbid.0);
|
||||
phi_entries.push((dst, inputs));
|
||||
merged_vars.insert(name.clone(), dst);
|
||||
}
|
||||
if let Some(bb) = f.get_block_mut(exit_bb) {
|
||||
for (dst, inputs) in phi_entries { bb.insert_instruction_after_phis(MirInstruction::Phi { dst, inputs }); }
|
||||
}
|
||||
*vars = merged_vars;
|
||||
return Ok(exit_bb);
|
||||
}
|
||||
} else if !try_enabled || catches.is_empty() || catches.len() > 1 {
|
||||
let mut tmp_vars = vars.clone();
|
||||
let mut next_bb = lower_stmt_list_with_vars(f, cur_bb, try_body, &mut tmp_vars, loop_stack, env)?;
|
||||
if !finally.is_empty() {
|
||||
@ -124,7 +281,16 @@ pub(super) fn lower_try_stmt(
|
||||
phi_entries.push((dst, inputs));
|
||||
merged_vars.insert(name.clone(), dst);
|
||||
}
|
||||
if let Some(bb) = f.get_block_mut(finally_block) {
|
||||
if env.mir_no_phi {
|
||||
// Emit edge copies on predecessors instead of Phi at merge
|
||||
for (dst, inputs) in phi_entries {
|
||||
for (pred, val) in inputs {
|
||||
if let Some(pbb) = f.get_block_mut(pred) {
|
||||
pbb.add_instruction(MirInstruction::Copy { dst, src: val });
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if let Some(bb) = f.get_block_mut(finally_block) {
|
||||
for (dst, inputs) in phi_entries {
|
||||
bb.insert_instruction_after_phis(MirInstruction::Phi { dst, inputs });
|
||||
}
|
||||
@ -178,7 +344,15 @@ pub(super) fn lower_try_stmt(
|
||||
phi_entries.push((dst, inputs));
|
||||
merged_vars.insert(name.clone(), dst);
|
||||
}
|
||||
if let Some(bb) = f.get_block_mut(exit_bb) {
|
||||
if env.mir_no_phi {
|
||||
for (dst, inputs) in phi_entries {
|
||||
for (pred, val) in inputs {
|
||||
if let Some(pbb) = f.get_block_mut(pred) {
|
||||
pbb.add_instruction(MirInstruction::Copy { dst, src: val });
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if let Some(bb) = f.get_block_mut(exit_bb) {
|
||||
for (dst, inputs) in phi_entries {
|
||||
bb.insert_instruction_after_phis(MirInstruction::Phi { dst, inputs });
|
||||
}
|
||||
|
||||
@ -2,12 +2,20 @@ mod ast;
|
||||
mod lexer;
|
||||
mod lowering;
|
||||
|
||||
use ast::ProgramV0;
|
||||
use ast::{ProgramV0, StmtV0};
|
||||
use lowering::lower_program;
|
||||
|
||||
pub fn parse_json_v0_to_module(json: &str) -> Result<crate::mir::MirModule, String> {
|
||||
let prog: ProgramV0 =
|
||||
serde_json::from_str(json).map_err(|e| format!("invalid JSON v0: {}", e))?;
|
||||
if crate::config::env::cli_verbose() {
|
||||
let first = prog
|
||||
.body
|
||||
.get(1)
|
||||
.map(|s| match s { StmtV0::Try { .. } => "Try", _ => "Other" })
|
||||
.unwrap_or("<none>");
|
||||
eprintln!("[Bridge] JSON v0: body_len={} first_type={}", prog.body.len(), first);
|
||||
}
|
||||
if prog.version != 0 || prog.kind != "Program" {
|
||||
return Err("unsupported IR: expected {version:0, kind:\"Program\"}".into());
|
||||
}
|
||||
|
||||
@ -16,10 +16,38 @@ pub fn emit_mir_json_for_harness(
|
||||
for bid in ids {
|
||||
if let Some(bb) = f.blocks.get(&bid) {
|
||||
let mut insts = Vec::new();
|
||||
// Pre-scan: collect values defined anywhere in this block (to delay use-before-def copies)
|
||||
let mut block_defines: std::collections::HashSet<u32> = std::collections::HashSet::new();
|
||||
for inst in &bb.instructions {
|
||||
match inst {
|
||||
I::Copy { dst, .. }
|
||||
| I::UnaryOp { dst, .. }
|
||||
| I::Const { dst, .. }
|
||||
| I::BinOp { dst, .. }
|
||||
| I::Compare { dst, .. }
|
||||
| I::Call { dst: Some(dst), .. }
|
||||
| I::ExternCall { dst: Some(dst), .. }
|
||||
| I::BoxCall { dst: Some(dst), .. }
|
||||
| I::NewBox { dst, .. }
|
||||
| I::Phi { dst, .. } => {
|
||||
block_defines.insert(dst.as_u32());
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
// Track which values have been emitted (to order copies after their sources)
|
||||
let mut emitted_defs: std::collections::HashSet<u32> = std::collections::HashSet::new();
|
||||
// PHI first(オプション)
|
||||
for inst in &bb.instructions {
|
||||
if let I::Copy { dst, src } = inst {
|
||||
insts.push(json!({"op":"copy","dst": dst.as_u32(), "src": src.as_u32()}));
|
||||
// For copies whose source will be defined later in this block, delay emission
|
||||
let s = src.as_u32();
|
||||
if block_defines.contains(&s) && !emitted_defs.contains(&s) {
|
||||
// delayed; will be emitted after non-PHI pass
|
||||
} else {
|
||||
insts.push(json!({"op":"copy","dst": dst.as_u32(), "src": src.as_u32()}));
|
||||
emitted_defs.insert(dst.as_u32());
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if let I::Phi { dst, inputs } = inst {
|
||||
@ -49,14 +77,19 @@ pub fn emit_mir_json_for_harness(
|
||||
}
|
||||
}
|
||||
// Non-PHI
|
||||
// Non-PHI
|
||||
let mut delayed_copies: Vec<(u32, u32)> = Vec::new();
|
||||
for inst in &bb.instructions {
|
||||
match inst {
|
||||
I::Copy { dst, src } => {
|
||||
insts.push(json!({
|
||||
"op": "copy",
|
||||
"dst": dst.as_u32(),
|
||||
"src": src.as_u32()
|
||||
}));
|
||||
let d = dst.as_u32();
|
||||
let s = src.as_u32();
|
||||
if block_defines.contains(&s) && !emitted_defs.contains(&s) {
|
||||
delayed_copies.push((d, s));
|
||||
} else {
|
||||
insts.push(json!({"op":"copy","dst": d, "src": s}));
|
||||
emitted_defs.insert(d);
|
||||
}
|
||||
}
|
||||
I::UnaryOp { dst, op, operand } => {
|
||||
let kind = match op {
|
||||
@ -220,6 +253,7 @@ pub fn emit_mir_json_for_harness(
|
||||
obj["dst_type"] = t;
|
||||
}
|
||||
insts.push(obj);
|
||||
if let Some(d) = dst.map(|v| v.as_u32()) { emitted_defs.insert(d); }
|
||||
}
|
||||
I::NewBox {
|
||||
dst,
|
||||
@ -228,6 +262,7 @@ pub fn emit_mir_json_for_harness(
|
||||
} => {
|
||||
let args_a: Vec<_> = args.iter().map(|v| json!(v.as_u32())).collect();
|
||||
insts.push(json!({"op":"newbox","type": box_type, "args": args_a, "dst": dst.as_u32()}));
|
||||
emitted_defs.insert(dst.as_u32());
|
||||
}
|
||||
I::Branch {
|
||||
condition,
|
||||
@ -245,6 +280,10 @@ pub fn emit_mir_json_for_harness(
|
||||
_ => { /* skip non-essential ops for initial harness */ }
|
||||
}
|
||||
}
|
||||
// Emit delayed copies now (sources should be available)
|
||||
for (d, s) in delayed_copies {
|
||||
insts.push(json!({"op":"copy","dst": d, "src": s}));
|
||||
}
|
||||
if let Some(term) = &bb.terminator {
|
||||
match term {
|
||||
I::Return { value } => insts.push(json!({"op":"ret","value": value.map(|v| v.as_u32())})),
|
||||
@ -279,6 +318,23 @@ pub fn emit_mir_json_for_harness_bin(
|
||||
for bid in ids {
|
||||
if let Some(bb) = f.blocks.get(&bid) {
|
||||
let mut insts = Vec::new();
|
||||
// Pre-scan to collect values defined in this block
|
||||
let mut block_defines: std::collections::HashSet<u32> = std::collections::HashSet::new();
|
||||
for inst in &bb.instructions {
|
||||
match inst {
|
||||
I::Copy { dst, .. }
|
||||
| I::Const { dst, .. }
|
||||
| I::BinOp { dst, .. }
|
||||
| I::Compare { dst, .. }
|
||||
| I::Call { dst: Some(dst), .. }
|
||||
| I::ExternCall { dst: Some(dst), .. }
|
||||
| I::BoxCall { dst: Some(dst), .. }
|
||||
| I::NewBox { dst, .. }
|
||||
| I::Phi { dst, .. } => { block_defines.insert(dst.as_u32()); }
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
let mut emitted_defs: std::collections::HashSet<u32> = std::collections::HashSet::new();
|
||||
for inst in &bb.instructions {
|
||||
if let I::Phi { dst, inputs } = inst {
|
||||
let incoming: Vec<_> = inputs
|
||||
@ -303,41 +359,48 @@ pub fn emit_mir_json_for_harness_bin(
|
||||
json!({"op":"phi","dst": dst.as_u32(), "incoming": incoming}),
|
||||
);
|
||||
}
|
||||
emitted_defs.insert(dst.as_u32());
|
||||
}
|
||||
}
|
||||
let mut delayed_copies: Vec<(u32, u32)> = Vec::new();
|
||||
for inst in &bb.instructions {
|
||||
match inst {
|
||||
I::Copy { dst, src } => {
|
||||
insts.push(json!({
|
||||
"op": "copy",
|
||||
"dst": dst.as_u32(),
|
||||
"src": src.as_u32()
|
||||
}));
|
||||
let d = dst.as_u32(); let s = src.as_u32();
|
||||
if block_defines.contains(&s) && !emitted_defs.contains(&s) {
|
||||
delayed_copies.push((d, s));
|
||||
} else {
|
||||
insts.push(json!({"op":"copy","dst": d, "src": s}));
|
||||
emitted_defs.insert(d);
|
||||
}
|
||||
}
|
||||
I::Const { dst, value } => match value {
|
||||
crate::mir::ConstValue::Integer(i) => {
|
||||
insts.push(json!({"op":"const","dst": dst.as_u32(), "value": {"type": "i64", "value": i}}));
|
||||
I::Const { dst, value } => {
|
||||
match value {
|
||||
crate::mir::ConstValue::Integer(i) => {
|
||||
insts.push(json!({"op":"const","dst": dst.as_u32(), "value": {"type": "i64", "value": i}}));
|
||||
}
|
||||
crate::mir::ConstValue::Float(fv) => {
|
||||
insts.push(json!({"op":"const","dst": dst.as_u32(), "value": {"type": "f64", "value": fv}}));
|
||||
}
|
||||
crate::mir::ConstValue::Bool(b) => {
|
||||
insts.push(json!({"op":"const","dst": dst.as_u32(), "value": {"type": "i64", "value": if *b {1} else {0}}}));
|
||||
}
|
||||
crate::mir::ConstValue::String(s) => {
|
||||
insts.push(json!({
|
||||
"op":"const",
|
||||
"dst": dst.as_u32(),
|
||||
"value": {
|
||||
"type": {"kind":"handle","box_type":"StringBox"},
|
||||
"value": s
|
||||
}
|
||||
}));
|
||||
}
|
||||
crate::mir::ConstValue::Null | crate::mir::ConstValue::Void => {
|
||||
insts.push(json!({"op":"const","dst": dst.as_u32(), "value": {"type": "void", "value": 0}}));
|
||||
}
|
||||
}
|
||||
crate::mir::ConstValue::Float(fv) => {
|
||||
insts.push(json!({"op":"const","dst": dst.as_u32(), "value": {"type": "f64", "value": fv}}));
|
||||
}
|
||||
crate::mir::ConstValue::Bool(b) => {
|
||||
insts.push(json!({"op":"const","dst": dst.as_u32(), "value": {"type": "i64", "value": if *b {1} else {0}}}));
|
||||
}
|
||||
crate::mir::ConstValue::String(s) => {
|
||||
insts.push(json!({
|
||||
"op":"const",
|
||||
"dst": dst.as_u32(),
|
||||
"value": {
|
||||
"type": {"kind":"handle","box_type":"StringBox"},
|
||||
"value": s
|
||||
}
|
||||
}));
|
||||
}
|
||||
crate::mir::ConstValue::Null | crate::mir::ConstValue::Void => {
|
||||
insts.push(json!({"op":"const","dst": dst.as_u32(), "value": {"type": "void", "value": 0}}));
|
||||
}
|
||||
},
|
||||
emitted_defs.insert(dst.as_u32());
|
||||
}
|
||||
I::BinOp { dst, op, lhs, rhs } => {
|
||||
let op_s = match op {
|
||||
B::Add => "+",
|
||||
@ -371,6 +434,7 @@ pub fn emit_mir_json_for_harness_bin(
|
||||
}
|
||||
}
|
||||
insts.push(obj);
|
||||
emitted_defs.insert(dst.as_u32());
|
||||
}
|
||||
I::Compare { dst, op, lhs, rhs } => {
|
||||
let op_s = match op {
|
||||
@ -382,6 +446,7 @@ pub fn emit_mir_json_for_harness_bin(
|
||||
C::Ge => ">=",
|
||||
};
|
||||
insts.push(json!({"op":"compare","operation": op_s, "lhs": lhs.as_u32(), "rhs": rhs.as_u32(), "dst": dst.as_u32()}));
|
||||
emitted_defs.insert(dst.as_u32());
|
||||
}
|
||||
I::ExternCall {
|
||||
dst,
|
||||
@ -401,6 +466,7 @@ pub fn emit_mir_json_for_harness_bin(
|
||||
}
|
||||
}
|
||||
insts.push(obj);
|
||||
if let Some(d) = dst.map(|v| v.as_u32()) { emitted_defs.insert(d); }
|
||||
}
|
||||
I::BoxCall {
|
||||
dst,
|
||||
@ -430,6 +496,7 @@ pub fn emit_mir_json_for_harness_bin(
|
||||
obj["dst_type"] = t;
|
||||
}
|
||||
insts.push(obj);
|
||||
if let Some(d) = dst.map(|v| v.as_u32()) { emitted_defs.insert(d); }
|
||||
}
|
||||
I::NewBox {
|
||||
dst,
|
||||
@ -438,6 +505,7 @@ pub fn emit_mir_json_for_harness_bin(
|
||||
} => {
|
||||
let args_a: Vec<_> = args.iter().map(|v| json!(v.as_u32())).collect();
|
||||
insts.push(json!({"op":"newbox","type": box_type, "args": args_a, "dst": dst.as_u32()}));
|
||||
emitted_defs.insert(dst.as_u32());
|
||||
}
|
||||
I::Branch {
|
||||
condition,
|
||||
@ -455,6 +523,8 @@ pub fn emit_mir_json_for_harness_bin(
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
// Append delayed copies after their sources
|
||||
for (d, s) in delayed_copies { insts.push(json!({"op":"copy","dst": d, "src": s})); }
|
||||
if let Some(term) = &bb.terminator {
|
||||
match term {
|
||||
I::Return { value } => insts.push(json!({"op":"ret","value": value.map(|v| v.as_u32())})),
|
||||
|
||||
@ -114,7 +114,7 @@ impl NyashRunner {
|
||||
root_info = format!(" root='{}'", r);
|
||||
}
|
||||
}
|
||||
eprintln!(
|
||||
crate::cli_v!(
|
||||
"[deps] loaded {} bytes from{} {}",
|
||||
bytes,
|
||||
if root_info.is_empty() { "" } else { ":" },
|
||||
@ -122,7 +122,7 @@ impl NyashRunner {
|
||||
);
|
||||
}
|
||||
Err(e) => {
|
||||
eprintln!("[deps] read error: {}", e);
|
||||
crate::cli_v!("[deps] read error: {}", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -199,7 +199,7 @@ impl NyashRunner {
|
||||
}
|
||||
// Resolve pending using with clear precedence and ambiguity handling
|
||||
let strict = std::env::var("NYASH_USING_STRICT").ok().as_deref() == Some("1");
|
||||
let verbose = std::env::var("NYASH_CLI_VERBOSE").ok().as_deref() == Some("1");
|
||||
let verbose = crate::config::env::cli_verbose();
|
||||
let ctx = std::path::Path::new(filename).parent();
|
||||
for (ns, alias) in pending_using.iter() {
|
||||
let value = match resolve_using_target(
|
||||
|
||||
@ -61,9 +61,7 @@ impl NyashRunner {
|
||||
|
||||
// MIR dump/verify
|
||||
if self.config.dump_mir || self.config.verify_mir {
|
||||
if crate::config::env::cli_verbose() {
|
||||
println!("🚀 Nyash MIR Compiler - Processing file: {} 🚀", filename);
|
||||
}
|
||||
crate::cli_v!("🚀 Nyash MIR Compiler - Processing file: {} 🚀", filename);
|
||||
self.execute_mir_mode(filename);
|
||||
return;
|
||||
}
|
||||
@ -85,21 +83,17 @@ impl NyashRunner {
|
||||
// Backend selection
|
||||
match self.config.backend.as_str() {
|
||||
"mir" => {
|
||||
if crate::config::env::cli_verbose() {
|
||||
println!("🚀 Nyash MIR Interpreter - Executing file: {} 🚀", filename);
|
||||
}
|
||||
crate::cli_v!("🚀 Nyash MIR Interpreter - Executing file: {} 🚀", filename);
|
||||
self.execute_mir_interpreter_mode(filename);
|
||||
}
|
||||
"vm" => {
|
||||
if crate::config::env::cli_verbose() {
|
||||
println!("🚀 Nyash VM Backend - Executing file: {} 🚀", filename);
|
||||
}
|
||||
crate::cli_v!("🚀 Nyash VM Backend - Executing file: {} 🚀", filename);
|
||||
self.execute_vm_mode(filename);
|
||||
}
|
||||
"cranelift" => {
|
||||
#[cfg(feature = "cranelift-jit")]
|
||||
{
|
||||
if cli_verbose() { println!("⚙️ Nyash Cranelift JIT - Executing file: {}", filename); }
|
||||
crate::cli_v!("⚙️ Nyash Cranelift JIT - Executing file: {}", filename);
|
||||
self.execute_cranelift_mode(filename);
|
||||
}
|
||||
#[cfg(not(feature = "cranelift-jit"))]
|
||||
@ -109,7 +103,7 @@ impl NyashRunner {
|
||||
}
|
||||
}
|
||||
"llvm" => {
|
||||
if cli_verbose() { println!("⚡ Nyash LLVM Backend - Executing file: {} ⚡", filename); }
|
||||
crate::cli_v!("⚡ Nyash LLVM Backend - Executing file: {} ⚡", filename);
|
||||
self.execute_llvm_mode(filename);
|
||||
}
|
||||
_ => {
|
||||
@ -494,79 +488,11 @@ impl NyashRunner {
|
||||
|
||||
// Optional Phase-15: strip `using` lines (gate) for minimal acceptance
|
||||
let mut code_ref: &str = &code;
|
||||
let enable_using = crate::config::env::enable_using();
|
||||
let cleaned_code_owned;
|
||||
if enable_using {
|
||||
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 ") {
|
||||
// Skip `using ns` or `using ns as alias` lines (MVP)
|
||||
if crate::config::env::cli_verbose() {
|
||||
eprintln!("[using] stripped line: {}", line);
|
||||
}
|
||||
// Parse namespace or path and optional alias
|
||||
let rest0 = t.strip_prefix("using ").unwrap().trim();
|
||||
// allow trailing semicolon
|
||||
let rest0 = rest0.strip_suffix(';').unwrap_or(rest0).trim();
|
||||
// Split alias
|
||||
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) };
|
||||
// If quoted or looks like relative/absolute path, treat as path; else as namespace
|
||||
let is_path = target.starts_with('"') || target.starts_with("./") || target.starts_with('/') || target.ends_with(".nyash");
|
||||
if is_path {
|
||||
let mut path = target.trim_matches('"').to_string();
|
||||
// existence check and strict handling
|
||||
let missing = !std::path::Path::new(&path).exists();
|
||||
if missing {
|
||||
if std::env::var("NYASH_USING_STRICT").ok().as_deref() == Some("1") {
|
||||
eprintln!("❌ using: path not found: {}", path);
|
||||
std::process::exit(1);
|
||||
} else if crate::config::env::cli_verbose() {
|
||||
eprintln!("[using] path not found (continuing): {}", path);
|
||||
}
|
||||
}
|
||||
// choose alias or derive from filename stem
|
||||
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()
|
||||
});
|
||||
// register alias only (path-backed)
|
||||
used_names.push((name, Some(path)));
|
||||
} else {
|
||||
used_names.push((target, alias));
|
||||
}
|
||||
continue;
|
||||
}
|
||||
out.push_str(line);
|
||||
out.push('\n');
|
||||
}
|
||||
cleaned_code_owned = out;
|
||||
code_ref = &cleaned_code_owned;
|
||||
|
||||
// Register modules with resolver (aliases/modules/paths)
|
||||
let using_ctx = self.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 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) => {
|
||||
eprintln!("❌ using: {}", e);
|
||||
std::process::exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
if crate::config::env::enable_using() {
|
||||
match crate::runner::modes::common_util::resolve::strip_using_and_register(self, &code, filename) {
|
||||
Ok(s) => { cleaned_code_owned = s; code_ref = &cleaned_code_owned; }
|
||||
Err(e) => { eprintln!("❌ {}", e); std::process::exit(1); }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
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)
|
||||
}
|
||||
@ -47,91 +47,19 @@ impl NyashRunner {
|
||||
#[allow(unused_mut)]
|
||||
let mut module = compile_result.module.clone();
|
||||
let injected = inject_method_ids(&mut module);
|
||||
if injected > 0 && std::env::var("NYASH_CLI_VERBOSE").ok().as_deref() == Some("1") {
|
||||
eprintln!("[LLVM] method_id injected: {} places", injected);
|
||||
}
|
||||
if injected > 0 { crate::cli_v!("[LLVM] method_id injected: {} places", injected); }
|
||||
|
||||
// If explicit object path is requested, emit object only
|
||||
if let Ok(_out_path) = std::env::var("NYASH_LLVM_OBJ_OUT") {
|
||||
#[cfg(feature = "llvm-harness")]
|
||||
{
|
||||
// Harness path (optional): if NYASH_LLVM_USE_HARNESS=1, try Python/llvmlite first.
|
||||
let use_harness = crate::config::env::llvm_use_harness();
|
||||
if use_harness {
|
||||
if let Some(parent) = std::path::Path::new(&_out_path).parent() {
|
||||
let _ = std::fs::create_dir_all(parent);
|
||||
if crate::config::env::llvm_use_harness() {
|
||||
if let Err(e) = crate::runner::modes::common_util::exec::llvmlite_emit_object(&module, &_out_path, 20_000) {
|
||||
eprintln!("❌ {}", e);
|
||||
process::exit(1);
|
||||
}
|
||||
let py = which::which("python3").ok();
|
||||
if let Some(py3) = py {
|
||||
let harness = std::path::Path::new("tools/llvmlite_harness.py");
|
||||
if harness.exists() {
|
||||
// 1) Emit MIR(JSON) to a temp file
|
||||
let tmp_dir = std::path::Path::new("tmp");
|
||||
let _ = std::fs::create_dir_all(tmp_dir);
|
||||
let mir_json_path = tmp_dir.join("nyash_harness_mir.json");
|
||||
if let Err(e) = crate::runner::mir_json_emit::emit_mir_json_for_harness(
|
||||
&module,
|
||||
&mir_json_path,
|
||||
) {
|
||||
eprintln!("❌ MIR JSON emit error: {}", e);
|
||||
process::exit(1);
|
||||
}
|
||||
if std::env::var("NYASH_CLI_VERBOSE").ok().as_deref() == Some("1") {
|
||||
eprintln!(
|
||||
"[Runner/LLVM] using llvmlite harness → {} (mir={})",
|
||||
_out_path,
|
||||
mir_json_path.display()
|
||||
);
|
||||
}
|
||||
// 2) Run harness with --in/--out(失敗時は即エラー)
|
||||
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 = crate::runner::modes::common_util::io::spawn_with_timeout(cmd, 20_000)
|
||||
.map_err(|e| format!("spawn harness: {}", e))
|
||||
.unwrap();
|
||||
if out.timed_out || !out.status_ok {
|
||||
eprintln!(
|
||||
"❌ llvmlite harness failed (timeout={} code={:?})",
|
||||
out.timed_out,
|
||||
out.exit_code
|
||||
);
|
||||
process::exit(1);
|
||||
}
|
||||
// Verify
|
||||
match std::fs::metadata(&_out_path) {
|
||||
Ok(meta) if meta.len() > 0 => {
|
||||
if std::env::var("NYASH_CLI_VERBOSE").ok().as_deref()
|
||||
== Some("1")
|
||||
{
|
||||
eprintln!(
|
||||
"[LLVM] object emitted by harness: {} ({} bytes)",
|
||||
_out_path,
|
||||
meta.len()
|
||||
);
|
||||
}
|
||||
return;
|
||||
}
|
||||
_ => {
|
||||
eprintln!(
|
||||
"❌ harness output not found or empty: {}",
|
||||
_out_path
|
||||
);
|
||||
process::exit(1);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
eprintln!("❌ harness script not found: {}", harness.display());
|
||||
process::exit(1);
|
||||
}
|
||||
}
|
||||
eprintln!("❌ python3 not found in PATH. Install Python 3 to use the harness.");
|
||||
process::exit(1);
|
||||
return;
|
||||
}
|
||||
// Verify object presence and size (>0)
|
||||
match std::fs::metadata(&_out_path) {
|
||||
@ -165,28 +93,24 @@ impl NyashRunner {
|
||||
if let Some(parent) = std::path::Path::new(&_out_path).parent() {
|
||||
let _ = std::fs::create_dir_all(parent);
|
||||
}
|
||||
if std::env::var("NYASH_CLI_VERBOSE").ok().as_deref() == Some("1") {
|
||||
eprintln!(
|
||||
"[Runner/LLVM] emitting object to {} (cwd={})",
|
||||
_out_path,
|
||||
std::env::current_dir()
|
||||
.map(|p| p.display().to_string())
|
||||
.unwrap_or_default()
|
||||
);
|
||||
}
|
||||
crate::cli_v!(
|
||||
"[Runner/LLVM] emitting object to {} (cwd={})",
|
||||
_out_path,
|
||||
std::env::current_dir()
|
||||
.map(|p| p.display().to_string())
|
||||
.unwrap_or_default()
|
||||
);
|
||||
if let Err(e) = llvm_compile_to_object(&module, &_out_path) {
|
||||
eprintln!("❌ LLVM object emit error: {}", e);
|
||||
process::exit(1);
|
||||
}
|
||||
match std::fs::metadata(&_out_path) {
|
||||
Ok(meta) if meta.len() > 0 => {
|
||||
if std::env::var("NYASH_CLI_VERBOSE").ok().as_deref() == Some("1") {
|
||||
eprintln!(
|
||||
"[LLVM] object emitted: {} ({} bytes)",
|
||||
_out_path,
|
||||
meta.len()
|
||||
);
|
||||
}
|
||||
crate::cli_v!(
|
||||
"[LLVM] object emitted: {} ({} bytes)",
|
||||
_out_path,
|
||||
meta.len()
|
||||
);
|
||||
}
|
||||
_ => {
|
||||
eprintln!("❌ LLVM object not found or empty: {}", _out_path);
|
||||
@ -202,6 +126,40 @@ impl NyashRunner {
|
||||
}
|
||||
}
|
||||
|
||||
// Execute via LLVM backend (harness preferred)
|
||||
#[cfg(feature = "llvm-harness")]
|
||||
{
|
||||
if crate::config::env::llvm_use_harness() {
|
||||
// Prefer producing a native executable via ny-llvmc, then execute it
|
||||
let exe_out = "tmp/nyash_llvm_run";
|
||||
let libs = std::env::var("NYASH_LLVM_EXE_LIBS").ok();
|
||||
match crate::runner::modes::common_util::exec::ny_llvmc_emit_exe_lib(
|
||||
&module,
|
||||
exe_out,
|
||||
None,
|
||||
libs.as_deref(),
|
||||
) {
|
||||
Ok(()) => {
|
||||
match crate::runner::modes::common_util::exec::run_executable(exe_out, &[], 20_000) {
|
||||
Ok((code, _timed_out)) => {
|
||||
println!("✅ LLVM (harness) execution completed (exit={})", code);
|
||||
std::process::exit(code);
|
||||
}
|
||||
Err(e) => {
|
||||
eprintln!("❌ run executable error: {}", e);
|
||||
std::process::exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
eprintln!("❌ ny-llvmc emit-exe error: {}", e);
|
||||
eprintln!(" Hint: build ny-llvmc: cargo build -p nyash-llvm-compiler --release");
|
||||
std::process::exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Execute via LLVM backend (mock or real)
|
||||
#[cfg(feature = "llvm-inkwell-legacy")]
|
||||
{
|
||||
|
||||
@ -78,38 +78,13 @@ impl NyashRunner {
|
||||
|
||||
// Emit native executable via ny-llvmc (crate) and exit
|
||||
if let Some(exe_out) = self.config.emit_exe.as_ref() {
|
||||
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");
|
||||
if let Err(e) = crate::runner::mir_json_emit::emit_mir_json_for_harness(&compile_result.module, &json_path) {
|
||||
eprintln!("❌ MIR JSON emit error: {}", e);
|
||||
std::process::exit(1);
|
||||
}
|
||||
let ny_llvmc = 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"));
|
||||
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) = self.config.emit_exe_nyrt.as_ref() {
|
||||
cmd.arg("--nyrt").arg(dir);
|
||||
} else {
|
||||
cmd.arg("--nyrt").arg("target/release");
|
||||
}
|
||||
if let Some(flags) = self.config.emit_exe_libs.as_ref() {
|
||||
if !flags.trim().is_empty() {
|
||||
cmd.arg("--libs").arg(flags);
|
||||
}
|
||||
}
|
||||
let status = cmd.status().unwrap_or_else(|e| {
|
||||
eprintln!("❌ failed to spawn ny-llvmc: {}", e);
|
||||
std::process::exit(1);
|
||||
});
|
||||
if !status.success() {
|
||||
eprintln!("❌ ny-llvmc failed with status: {:?}", status.code());
|
||||
if let Err(e) = crate::runner::modes::common_util::exec::ny_llvmc_emit_exe_lib(
|
||||
&compile_result.module,
|
||||
exe_out,
|
||||
self.config.emit_exe_nyrt.as_deref(),
|
||||
self.config.emit_exe_libs.as_deref(),
|
||||
) {
|
||||
eprintln!("❌ {}", e);
|
||||
std::process::exit(1);
|
||||
}
|
||||
println!("EXE written: {}", exe_out);
|
||||
|
||||
@ -45,9 +45,7 @@ impl NyashRunner {
|
||||
let mut module_interp = compile_result.module.clone();
|
||||
if std::env::var("NYASH_VM_ESCAPE_ANALYSIS").ok().as_deref() == Some("1") {
|
||||
let removed = nyash_rust::mir::passes::escape::escape_elide_barriers_vm(&mut module_interp);
|
||||
if removed > 0 && std::env::var("NYASH_CLI_VERBOSE").ok().as_deref() == Some("1") {
|
||||
eprintln!("[MIR-Interp] escape_elide_barriers: removed {} barriers", removed);
|
||||
}
|
||||
if removed > 0 { crate::cli_v!("[MIR-Interp] escape_elide_barriers: removed {} barriers", removed); }
|
||||
}
|
||||
|
||||
// Execute with MIR interpreter
|
||||
|
||||
@ -35,65 +35,12 @@ pub fn execute_pyvm_only(_runner: &NyashRunner, filename: &str) {
|
||||
// Optional: VM-only escape analysis elision pass retained for parity with VM path
|
||||
if std::env::var("NYASH_VM_ESCAPE_ANALYSIS").ok().as_deref() == Some("1") {
|
||||
let removed = nyash_rust::mir::passes::escape::escape_elide_barriers_vm(&mut compile_result.module);
|
||||
if removed > 0 && std::env::var("NYASH_CLI_VERBOSE").ok().as_deref() == Some("1") {
|
||||
eprintln!("[PyVM] escape_elide_barriers: removed {} barriers", removed);
|
||||
}
|
||||
if removed > 0 { crate::cli_v!("[PyVM] escape_elide_barriers: removed {} barriers", removed); }
|
||||
}
|
||||
|
||||
// Emit MIR JSON for PyVM harness
|
||||
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");
|
||||
if let Err(e) = crate::runner::mir_json_emit::emit_mir_json_for_harness(
|
||||
&compile_result.module,
|
||||
&mir_json_path,
|
||||
) {
|
||||
eprintln!("❌ PyVM MIR JSON emit error: {}", e);
|
||||
process::exit(1);
|
||||
}
|
||||
|
||||
// Pick entry: prefer Main.main or main
|
||||
let entry = if compile_result.module.functions.contains_key("Main.main") {
|
||||
"Main.main"
|
||||
} else if compile_result.module.functions.contains_key("main") {
|
||||
"main"
|
||||
} else {
|
||||
"Main.main"
|
||||
};
|
||||
|
||||
// Locate python3 and run harness
|
||||
let py = which::which("python3").ok();
|
||||
if let Some(py3) = py {
|
||||
let runner = std::path::Path::new("tools/pyvm_runner.py");
|
||||
if !runner.exists() {
|
||||
eprintln!("❌ PyVM runner not found: {}", runner.display());
|
||||
process::exit(1);
|
||||
}
|
||||
if std::env::var("NYASH_CLI_VERBOSE").ok().as_deref() == Some("1") {
|
||||
eprintln!(
|
||||
"[Runner/PyVM] {} (mir={})",
|
||||
filename,
|
||||
mir_json_path.display()
|
||||
);
|
||||
}
|
||||
let mut cmd = std::process::Command::new(py3);
|
||||
cmd.args([
|
||||
runner.to_string_lossy().as_ref(),
|
||||
"--in",
|
||||
&mir_json_path.display().to_string(),
|
||||
"--entry",
|
||||
entry,
|
||||
]);
|
||||
let out = crate::runner::modes::common_util::io::spawn_with_timeout(cmd, 10_000)
|
||||
.map_err(|e| format!("spawn pyvm: {}", e))
|
||||
.unwrap();
|
||||
let code = if out.timed_out { 1 } else { out.exit_code.unwrap_or(1) };
|
||||
if out.timed_out && std::env::var("NYASH_CLI_VERBOSE").ok().as_deref() == Some("1") {
|
||||
eprintln!("❌ PyVM timeout");
|
||||
}
|
||||
process::exit(code);
|
||||
} else {
|
||||
eprintln!("❌ python3 not found in PATH. Install Python 3 to use PyVM.");
|
||||
process::exit(1);
|
||||
// Delegate to common PyVM harness
|
||||
match crate::runner::modes::common_util::pyvm::run_pyvm_harness_lib(&compile_result.module, "pyvm") {
|
||||
Ok(code) => { process::exit(code); }
|
||||
Err(e) => { eprintln!("❌ PyVM error: {}", e); process::exit(1); }
|
||||
}
|
||||
}
|
||||
|
||||
@ -101,20 +101,12 @@ impl NyashRunner {
|
||||
}
|
||||
};
|
||||
|
||||
// Optional Phase-15: strip `using` lines (gate) for minimal acceptance in VM path
|
||||
let enable_using = crate::config::env::enable_using();
|
||||
let code = if enable_using {
|
||||
let mut out = String::with_capacity(code.len());
|
||||
for line in code.lines() {
|
||||
let t = line.trim_start();
|
||||
if t.starts_with("using ") {
|
||||
// Strip using lines (module resolution handled by nyash.toml elsewhere)
|
||||
continue;
|
||||
}
|
||||
out.push_str(line);
|
||||
out.push('\n');
|
||||
// Optional Phase-15: strip `using` lines and register aliases/modules
|
||||
let code = if crate::config::env::enable_using() {
|
||||
match crate::runner::modes::common::resolve::strip_using_and_register(self, &code, filename) {
|
||||
Ok(s) => s,
|
||||
Err(e) => { eprintln!("❌ {}", e); process::exit(1); }
|
||||
}
|
||||
out
|
||||
} else { code };
|
||||
|
||||
// Parse to AST
|
||||
@ -185,67 +177,14 @@ impl NyashRunner {
|
||||
let mut module_vm = compile_result.module.clone();
|
||||
if std::env::var("NYASH_VM_ESCAPE_ANALYSIS").ok().as_deref() == Some("1") {
|
||||
let removed = nyash_rust::mir::passes::escape::escape_elide_barriers_vm(&mut module_vm);
|
||||
if removed > 0 && std::env::var("NYASH_CLI_VERBOSE").ok().as_deref() == Some("1") {
|
||||
eprintln!("[VM] escape_elide_barriers: removed {} barriers", removed);
|
||||
}
|
||||
if removed > 0 { crate::cli_v!("[VM] escape_elide_barriers: removed {} barriers", removed); }
|
||||
}
|
||||
|
||||
// Optional: PyVM path. When NYASH_VM_USE_PY=1, emit MIR(JSON) and delegate execution to tools/pyvm_runner.py
|
||||
if std::env::var("NYASH_VM_USE_PY").ok().as_deref() == Some("1") {
|
||||
let py = which::which("python3").ok();
|
||||
if let Some(py3) = py {
|
||||
let runner = std::path::Path::new("tools/pyvm_runner.py");
|
||||
if runner.exists() {
|
||||
// Emit MIR(JSON)
|
||||
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");
|
||||
if let Err(e) = crate::runner::mir_json_emit::emit_mir_json_for_harness(
|
||||
&module_vm,
|
||||
&mir_json_path,
|
||||
) {
|
||||
eprintln!("❌ PyVM MIR JSON emit error: {}", e);
|
||||
process::exit(1);
|
||||
}
|
||||
if std::env::var("NYASH_CLI_VERBOSE").ok().as_deref() == Some("1") {
|
||||
eprintln!(
|
||||
"[Runner/VM] using PyVM → {} (mir={})",
|
||||
filename,
|
||||
mir_json_path.display()
|
||||
);
|
||||
}
|
||||
// Determine entry function hint (prefer Main.main if present)
|
||||
let entry = if module_vm.functions.contains_key("Main.main") {
|
||||
"Main.main"
|
||||
} else if module_vm.functions.contains_key("main") {
|
||||
"main"
|
||||
} else {
|
||||
"Main.main"
|
||||
};
|
||||
// Spawn runner
|
||||
let mut cmd = std::process::Command::new(py3);
|
||||
cmd.args([
|
||||
runner.to_string_lossy().as_ref(),
|
||||
"--in",
|
||||
&mir_json_path.display().to_string(),
|
||||
"--entry",
|
||||
entry,
|
||||
]);
|
||||
let out = super::common_util::io::spawn_with_timeout(cmd, 10_000)
|
||||
.map_err(|e| format!("spawn pyvm: {}", e))
|
||||
.unwrap();
|
||||
let code = if out.timed_out { 1 } else { out.exit_code.unwrap_or(1) };
|
||||
if out.timed_out && std::env::var("NYASH_CLI_VERBOSE").ok().as_deref() == Some("1") {
|
||||
eprintln!("❌ PyVM timeout");
|
||||
}
|
||||
process::exit(code);
|
||||
} else {
|
||||
eprintln!("❌ PyVM runner not found: {}", runner.display());
|
||||
process::exit(1);
|
||||
}
|
||||
} else {
|
||||
eprintln!("❌ python3 not found in PATH. Install Python 3 to use PyVM.");
|
||||
process::exit(1);
|
||||
match super::common_util::pyvm::run_pyvm_harness_lib(&module_vm, "vm") {
|
||||
Ok(code) => { process::exit(code); }
|
||||
Err(e) => { eprintln!("❌ PyVM error: {}", e); process::exit(1); }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -56,12 +56,7 @@ impl NyashRunner {
|
||||
eprintln!("❌ PyVM MIR JSON emit error: {}", e);
|
||||
std::process::exit(1);
|
||||
}
|
||||
if std::env::var("NYASH_CLI_VERBOSE").ok().as_deref() == Some("1") {
|
||||
eprintln!(
|
||||
"[Bridge] using PyVM (pipe) → {}",
|
||||
mir_json_path.display()
|
||||
);
|
||||
}
|
||||
crate::cli_v!("[Bridge] using PyVM (pipe) → {}", mir_json_path.display());
|
||||
// Determine entry function hint (prefer Main.main if present)
|
||||
let entry = if module.functions.contains_key("Main.main") {
|
||||
"Main.main"
|
||||
@ -82,11 +77,7 @@ impl NyashRunner {
|
||||
.map_err(|e| format!("spawn pyvm: {}", e))
|
||||
.unwrap();
|
||||
let code = status.code().unwrap_or(1);
|
||||
if !status.success() {
|
||||
if std::env::var("NYASH_CLI_VERBOSE").ok().as_deref() == Some("1") {
|
||||
eprintln!("❌ PyVM (pipe) failed (status={})", code);
|
||||
}
|
||||
}
|
||||
if !status.success() { crate::cli_v!("❌ PyVM (pipe) failed (status={})", code); }
|
||||
std::process::exit(code);
|
||||
} else {
|
||||
eprintln!("❌ PyVM runner not found: {}", runner.display());
|
||||
|
||||
@ -22,63 +22,12 @@ impl NyashRunner {
|
||||
}
|
||||
};
|
||||
// Optional Phase-15: strip `using` lines and register modules (same policy as execute_nyash_file)
|
||||
let enable_using = crate::config::env::enable_using();
|
||||
let mut code_ref: std::borrow::Cow<'_, str> = std::borrow::Cow::Borrowed(&code);
|
||||
if enable_using {
|
||||
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 ") {
|
||||
if crate::config::env::cli_verbose() {
|
||||
eprintln!("[using] stripped(line→selfhost): {}", 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');
|
||||
if crate::config::env::enable_using() {
|
||||
match crate::runner::modes::common_util::resolve::strip_using_and_register(self, &code, filename) {
|
||||
Ok(s) => { code_ref = std::borrow::Cow::Owned(s); }
|
||||
Err(e) => { eprintln!("[ny-compiler] {}", e); return false; }
|
||||
}
|
||||
// Register modules into minimal registry with best-effort path resolution
|
||||
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 {
|
||||
let rel = format!("apps/{}.nyash", ns_or_alias.replace('.', "/"));
|
||||
let exists = std::path::Path::new(&rel).exists();
|
||||
let path_or_ns = if exists { rel } else { ns_or_alias.clone() };
|
||||
let sb = crate::box_trait::StringBox::new(path_or_ns);
|
||||
crate::runtime::modules_registry::set(ns_or_alias, Box::new(sb));
|
||||
}
|
||||
}
|
||||
code_ref = std::borrow::Cow::Owned(out);
|
||||
}
|
||||
|
||||
// Write to tmp/ny_parser_input.ny (as expected by Ny parser v0), unless forced to reuse existing tmp
|
||||
@ -183,57 +132,11 @@ impl NyashRunner {
|
||||
return false;
|
||||
}
|
||||
// Prefer PyVM for selfhost pipeline (parity reference)
|
||||
if std::env::var("NYASH_VM_USE_PY").ok().as_deref()
|
||||
== Some("1")
|
||||
{
|
||||
// Reuse the common PyVM runner path
|
||||
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");
|
||||
if let Err(e) = crate::runner::mir_json_emit::emit_mir_json_for_harness_bin(&module, &mir_json_path) {
|
||||
eprintln!("❌ PyVM MIR JSON emit error: {}", e);
|
||||
process::exit(1);
|
||||
}
|
||||
if std::env::var("NYASH_CLI_VERBOSE").ok().as_deref()
|
||||
== Some("1")
|
||||
{
|
||||
eprintln!(
|
||||
"[Bridge] using PyVM (selfhost-py) → {}",
|
||||
mir_json_path.display()
|
||||
);
|
||||
}
|
||||
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([
|
||||
"tools/pyvm_runner.py",
|
||||
"--in",
|
||||
&mir_json_path.display().to_string(),
|
||||
"--entry",
|
||||
entry,
|
||||
])
|
||||
.status()
|
||||
.map_err(|e| format!("spawn pyvm: {}", e))
|
||||
.unwrap();
|
||||
let code = status.code().unwrap_or(1);
|
||||
if !status.success() {
|
||||
if std::env::var("NYASH_CLI_VERBOSE")
|
||||
.ok()
|
||||
.as_deref()
|
||||
== Some("1")
|
||||
{
|
||||
eprintln!(
|
||||
"❌ PyVM (selfhost-py) failed (status={})",
|
||||
code
|
||||
);
|
||||
}
|
||||
}
|
||||
if std::env::var("NYASH_VM_USE_PY").ok().as_deref() == Some("1") {
|
||||
let code = match crate::runner::modes::common_util::pyvm::run_pyvm_harness(&module, "selfhost-py") {
|
||||
Ok(c) => c,
|
||||
Err(e) => { eprintln!("❌ PyVM error: {}", e); 1 }
|
||||
};
|
||||
println!("Result: {}", code);
|
||||
std::process::exit(code);
|
||||
}
|
||||
@ -300,9 +203,7 @@ impl NyashRunner {
|
||||
eprintln!("❌ PyVM MIR JSON emit error: {}", e);
|
||||
process::exit(1);
|
||||
}
|
||||
if std::env::var("NYASH_CLI_VERBOSE").ok().as_deref() == Some("1") {
|
||||
eprintln!("[Bridge] using PyVM (selfhost) → {}", mir_json_path.display());
|
||||
}
|
||||
crate::cli_v!("[Bridge] using PyVM (selfhost) → {}", mir_json_path.display());
|
||||
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)
|
||||
|
||||
Reference in New Issue
Block a user