chore(fmt): add legacy stubs and strip trailing whitespace to unblock cargo fmt

This commit is contained in:
Selfhosting Dev
2025-09-17 07:43:07 +09:00
parent fcf8ce1f3c
commit adbb0201a9
385 changed files with 35622 additions and 15004 deletions

View File

@ -6,8 +6,8 @@
* to surface aliases defined in nyash.toml/env.
*/
use std::collections::{HashMap, HashSet};
use once_cell::sync::Lazy;
use std::collections::{HashMap, HashSet};
use std::sync::RwLock;
#[derive(Clone, Default)]
@ -27,16 +27,21 @@ impl BoxIndex {
if let Ok(doc) = toml::from_str::<toml::Value>(&text) {
if let Some(alias_tbl) = doc.get("aliases").and_then(|v| v.as_table()) {
for (k, v) in alias_tbl.iter() {
if let Some(target) = v.as_str() { aliases.insert(k.to_string(), target.to_string()); }
if let Some(target) = v.as_str() {
aliases.insert(k.to_string(), target.to_string());
}
}
}
}
}
if let Ok(raw) = std::env::var("NYASH_ALIASES") {
for ent in raw.split(',') {
if let Some((k,v)) = ent.split_once('=') {
let k = k.trim(); let v = v.trim();
if !k.is_empty() && !v.is_empty() { aliases.insert(k.to_string(), v.to_string()); }
if let Some((k, v)) = ent.split_once('=') {
let k = k.trim();
let v = v.trim();
if !k.is_empty() && !v.is_empty() {
aliases.insert(k.to_string(), v.to_string());
}
}
}
}
@ -58,14 +63,29 @@ impl BoxIndex {
for (k, v) in plugins_tbl.iter() {
// Skip non-table entries (string entries are plugin roots)
if let Some(t) = v.as_table() {
let prefix = t.get("prefix").and_then(|x| x.as_str()).map(|s| s.to_string());
let require_prefix = t.get("require_prefix").and_then(|x| x.as_bool()).unwrap_or(false);
let expose_short_names = t.get("expose_short_names").and_then(|x| x.as_bool()).unwrap_or(true);
let meta = PluginMeta { prefix, require_prefix, expose_short_names };
let prefix = t
.get("prefix")
.and_then(|x| x.as_str())
.map(|s| s.to_string());
let require_prefix = t
.get("require_prefix")
.and_then(|x| x.as_bool())
.unwrap_or(false);
let expose_short_names = t
.get("expose_short_names")
.and_then(|x| x.as_bool())
.unwrap_or(true);
let meta = PluginMeta {
prefix,
require_prefix,
expose_short_names,
};
plugin_meta.insert(k.clone(), meta.clone());
if let Some(arr) = t.get("boxes").and_then(|x| x.as_array()) {
for b in arr {
if let Some(name) = b.as_str() { plugin_meta_by_box.insert(name.to_string(), meta.clone()); }
if let Some(name) = b.as_str() {
plugin_meta_by_box.insert(name.to_string(), meta.clone());
}
}
}
}
@ -87,22 +107,43 @@ impl BoxIndex {
}
}
Self { aliases, plugin_boxes, plugin_meta, plugin_meta_by_box, plugins_require_prefix_global }
Self {
aliases,
plugin_boxes,
plugin_meta,
plugin_meta_by_box,
plugins_require_prefix_global,
}
}
pub fn is_known_plugin_short(name: &str) -> bool {
// Prefer global index view
if GLOBAL.read().ok().map(|g| g.plugin_boxes.contains(name)).unwrap_or(false) {
if GLOBAL
.read()
.ok()
.map(|g| g.plugin_boxes.contains(name))
.unwrap_or(false)
{
return true;
}
// Env override list
if let Ok(raw) = std::env::var("NYASH_KNOWN_PLUGIN_SHORTNAMES") {
let set: HashSet<String> = raw.split(',').map(|s| s.trim().to_string()).collect();
if set.contains(name) { return true; }
if set.contains(name) {
return true;
}
}
// Minimal fallback set
const KNOWN: &[&str] = &[
"ArrayBox","MapBox","StringBox","ConsoleBox","FileBox","PathBox","MathBox","IntegerBox","TOMLBox"
"ArrayBox",
"MapBox",
"StringBox",
"ConsoleBox",
"FileBox",
"PathBox",
"MathBox",
"IntegerBox",
"TOMLBox",
];
KNOWN.iter().any(|k| *k == name)
}
@ -112,11 +153,14 @@ impl BoxIndex {
static GLOBAL: Lazy<RwLock<BoxIndex>> = Lazy::new(|| RwLock::new(BoxIndex::default()));
// Global resolve cache (keyed by tgt|base|strict|paths)
static RESOLVE_CACHE: Lazy<RwLock<HashMap<String, String>>> = Lazy::new(|| RwLock::new(HashMap::new()));
static RESOLVE_CACHE: Lazy<RwLock<HashMap<String, String>>> =
Lazy::new(|| RwLock::new(HashMap::new()));
pub fn refresh_box_index() {
let next = BoxIndex::build_current();
if let Ok(mut w) = GLOBAL.write() { *w = next; }
if let Ok(mut w) = GLOBAL.write() {
*w = next;
}
}
pub fn get_box_index() -> BoxIndex {
@ -128,11 +172,15 @@ pub fn cache_get(key: &str) -> Option<String> {
}
pub fn cache_put(key: &str, value: String) {
if let Ok(mut m) = RESOLVE_CACHE.write() { m.insert(key.to_string(), value); }
if let Ok(mut m) = RESOLVE_CACHE.write() {
m.insert(key.to_string(), value);
}
}
pub fn cache_clear() {
if let Ok(mut m) = RESOLVE_CACHE.write() { m.clear(); }
if let Ok(mut m) = RESOLVE_CACHE.write() {
m.clear();
}
}
#[derive(Clone, Debug, Default)]
@ -143,5 +191,8 @@ pub struct PluginMeta {
}
pub fn get_plugin_meta(plugin: &str) -> Option<PluginMeta> {
GLOBAL.read().ok().and_then(|g| g.plugin_meta.get(plugin).cloned())
GLOBAL
.read()
.ok()
.and_then(|g| g.plugin_meta.get(plugin).cloned())
}

View File

@ -3,33 +3,65 @@ use std::path::{Path, PathBuf};
pub(super) fn run_build_mvp_impl(runner: &NyashRunner, cfg_path: &str) -> Result<(), String> {
let cwd = std::env::current_dir().unwrap_or(PathBuf::from("."));
let cfg_abspath = if Path::new(cfg_path).is_absolute() { PathBuf::from(cfg_path) } else { cwd.join(cfg_path) };
let cfg_abspath = if Path::new(cfg_path).is_absolute() {
PathBuf::from(cfg_path)
} else {
cwd.join(cfg_path)
};
// 1) Load nyash.toml
let text = std::fs::read_to_string(&cfg_abspath).map_err(|e| format!("read {}: {}", cfg_abspath.display(), e))?;
let doc = toml::from_str::<toml::Value>(&text).map_err(|e| format!("parse {}: {}", cfg_abspath.display(), e))?;
let text = std::fs::read_to_string(&cfg_abspath)
.map_err(|e| format!("read {}: {}", cfg_abspath.display(), e))?;
let doc = toml::from_str::<toml::Value>(&text)
.map_err(|e| format!("parse {}: {}", cfg_abspath.display(), e))?;
// 2) Apply [env]
if let Some(env_tbl) = doc.get("env").and_then(|v| v.as_table()) {
for (k, v) in env_tbl.iter() { if let Some(s) = v.as_str() { std::env::set_var(k, s); } }
for (k, v) in env_tbl.iter() {
if let Some(s) = v.as_str() {
std::env::set_var(k, s);
}
}
}
// Derive options
let profile = runner.config.build_profile.clone().unwrap_or_else(|| "release".into());
let aot = runner.config.build_aot.clone().unwrap_or_else(|| "cranelift".into());
let profile = runner
.config
.build_profile
.clone()
.unwrap_or_else(|| "release".into());
let aot = runner
.config
.build_aot
.clone()
.unwrap_or_else(|| "cranelift".into());
let out = runner.config.build_out.clone();
let target = runner.config.build_target.clone();
// 3) Build plugins: read [plugins] values as paths and build each
if let Some(pl_tbl) = doc.get("plugins").and_then(|v| v.as_table()) {
for (name, v) in pl_tbl.iter() {
if let Some(path) = v.as_str() {
let p = if Path::new(path).is_absolute() { PathBuf::from(path) } else { cwd.join(path) };
let p = if Path::new(path).is_absolute() {
PathBuf::from(path)
} else {
cwd.join(path)
};
let mut cmd = std::process::Command::new("cargo");
cmd.arg("build");
if profile == "release" { cmd.arg("--release"); }
if let Some(t) = &target { cmd.args(["--target", t]); }
if profile == "release" {
cmd.arg("--release");
}
if let Some(t) = &target {
cmd.args(["--target", t]);
}
cmd.current_dir(&p);
println!("[build] plugin {} at {}", name, p.display());
let status = cmd.status().map_err(|e| format!("spawn cargo (plugin {}): {}", name, e))?;
let status = cmd
.status()
.map_err(|e| format!("spawn cargo (plugin {}): {}", name, e))?;
if !status.success() {
return Err(format!("plugin build failed: {} (dir={})", name, p.display()));
return Err(format!(
"plugin build failed: {} (dir={})",
name,
p.display()
));
}
}
}
@ -38,29 +70,63 @@ pub(super) fn run_build_mvp_impl(runner: &NyashRunner, cfg_path: &str) -> Result
{
let mut cmd = std::process::Command::new("cargo");
cmd.arg("build");
if profile == "release" { cmd.arg("--release"); }
match aot.as_str() { "llvm" => { cmd.args(["--features","llvm"]); }, _ => { cmd.args(["--features","cranelift-jit"]); } }
if let Some(t) = &target { cmd.args(["--target", t]); }
println!("[build] nyash core ({}, features={})", profile, if aot=="llvm" {"llvm"} else {"cranelift-jit"});
let status = cmd.status().map_err(|e| format!("spawn cargo (core): {}", e))?;
if !status.success() { return Err("nyash core build failed".into()); }
if profile == "release" {
cmd.arg("--release");
}
match aot.as_str() {
"llvm" => {
cmd.args(["--features", "llvm"]);
}
_ => {
cmd.args(["--features", "cranelift-jit"]);
}
}
if let Some(t) = &target {
cmd.args(["--target", t]);
}
println!(
"[build] nyash core ({}, features={})",
profile,
if aot == "llvm" {
"llvm"
} else {
"cranelift-jit"
}
);
let status = cmd
.status()
.map_err(|e| format!("spawn cargo (core): {}", e))?;
if !status.success() {
return Err("nyash core build failed".into());
}
}
// 5) Determine app entry
let app = if let Some(a) = runner.config.build_app.clone() { a } else {
let app = if let Some(a) = runner.config.build_app.clone() {
a
} else {
// try [build].app, else suggest
if let Some(tbl) = doc.get("build").and_then(|v| v.as_table()) {
if let Some(s) = tbl.get("app").and_then(|v| v.as_str()) { s.to_string() } else { String::new() }
} else { String::new() }
if let Some(s) = tbl.get("app").and_then(|v| v.as_str()) {
s.to_string()
} else {
String::new()
}
} else {
String::new()
}
};
let app = if !app.is_empty() { app } else {
let app = if !app.is_empty() {
app
} else {
// collect candidates under apps/**/main.nyash
let mut cand: Vec<String> = Vec::new();
fn walk(dir: &Path, acc: &mut Vec<String>) {
if let Ok(rd) = std::fs::read_dir(dir) {
for e in rd.flatten() {
let p = e.path();
if p.is_dir() { walk(&p, acc); }
else if p.file_name().map(|n| n=="main.nyash").unwrap_or(false) {
if p.is_dir() {
walk(&p, acc);
} else if p.file_name().map(|n| n == "main.nyash").unwrap_or(false) {
acc.push(p.display().to_string());
}
}
@ -70,7 +136,10 @@ pub(super) fn run_build_mvp_impl(runner: &NyashRunner, cfg_path: &str) -> Result
let msg = if cand.is_empty() {
"no app specified (--app) and no apps/**/main.nyash found".to_string()
} else {
format!("no app specified (--app). Candidates:\n - {}", cand.join("\n - "))
format!(
"no app specified (--app). Candidates:\n - {}",
cand.join("\n - ")
)
};
return Err(msg);
};
@ -79,47 +148,101 @@ pub(super) fn run_build_mvp_impl(runner: &NyashRunner, cfg_path: &str) -> Result
let _ = std::fs::create_dir_all(&obj_dir);
let obj_path = obj_dir.join("main.o");
if aot == "llvm" {
if std::env::var("LLVM_SYS_180_PREFIX").ok().is_none() && std::env::var("LLVM_SYS_181_PREFIX").ok().is_none() {
return Err("LLVM 18 not configured. Set LLVM_SYS_180_PREFIX or install LLVM 18 (llvm-config)".into());
if std::env::var("LLVM_SYS_180_PREFIX").ok().is_none()
&& std::env::var("LLVM_SYS_181_PREFIX").ok().is_none()
{
return Err(
"LLVM 18 not configured. Set LLVM_SYS_180_PREFIX or install LLVM 18 (llvm-config)"
.into(),
);
}
std::env::set_var("NYASH_LLVM_OBJ_OUT", &obj_path);
println!("[emit] LLVM object → {}", obj_path.display());
let status = std::process::Command::new(cwd.join("target").join(profile.clone()).join(if cfg!(windows) {"nyash.exe"} else {"nyash"}))
.args(["--backend","llvm", &app])
.status().map_err(|e| format!("spawn nyash llvm: {}", e))?;
if !status.success() { return Err("LLVM emit failed".into()); }
let status = std::process::Command::new(
cwd.join("target")
.join(profile.clone())
.join(if cfg!(windows) { "nyash.exe" } else { "nyash" }),
)
.args(["--backend", "llvm", &app])
.status()
.map_err(|e| format!("spawn nyash llvm: {}", e))?;
if !status.success() {
return Err("LLVM emit failed".into());
}
} else {
std::env::set_var("NYASH_AOT_OBJECT_OUT", &obj_dir);
println!("[emit] Cranelift object → {} (directory)", obj_dir.display());
let status = std::process::Command::new(cwd.join("target").join(profile.clone()).join(if cfg!(windows) {"nyash.exe"} else {"nyash"}))
.args(["--backend","vm", &app])
.status().map_err(|e| format!("spawn nyash jit-aot: {}", e))?;
if !status.success() { return Err("Cranelift emit failed".into()); }
println!(
"[emit] Cranelift object → {} (directory)",
obj_dir.display()
);
let status = std::process::Command::new(
cwd.join("target")
.join(profile.clone())
.join(if cfg!(windows) { "nyash.exe" } else { "nyash" }),
)
.args(["--backend", "vm", &app])
.status()
.map_err(|e| format!("spawn nyash jit-aot: {}", e))?;
if !status.success() {
return Err("Cranelift emit failed".into());
}
}
if !obj_path.exists() {
// In Cranelift path we produce target/aot_objects/<name>.o; fall back to main.o default
if !obj_dir.join("main.o").exists() { return Err(format!("object not generated under {}", obj_dir.display())); }
if !obj_dir.join("main.o").exists() {
return Err(format!("object not generated under {}", obj_dir.display()));
}
}
let out_path = if let Some(o) = out { PathBuf::from(o) } else { if cfg!(windows) { cwd.join("app.exe") } else { cwd.join("app") } };
let out_path = if let Some(o) = out {
PathBuf::from(o)
} else {
if cfg!(windows) {
cwd.join("app.exe")
} else {
cwd.join("app")
}
};
// 7) Link
println!("[link] → {}", out_path.display());
#[cfg(windows)]
{
// Prefer MSVC link.exe, then clang fallback
if let Ok(link) = which::which("link") {
let status = std::process::Command::new(&link).args(["/NOLOGO", &format!("/OUT:{}", out_path.display().to_string())])
let status = std::process::Command::new(&link)
.args([
"/NOLOGO",
&format!("/OUT:{}", out_path.display().to_string()),
])
.arg(&obj_path)
.arg(cwd.join("target").join("release").join("nyrt.lib"))
.status().map_err(|e| format!("spawn link.exe: {}", e))?;
if status.success() { println!("OK"); return Ok(()); }
.status()
.map_err(|e| format!("spawn link.exe: {}", e))?;
if status.success() {
println!("OK");
return Ok(());
}
}
if let Ok(clang) = which::which("clang") {
let status = std::process::Command::new(&clang)
.args(["-o", &out_path.display().to_string(), &obj_path.display().to_string()])
.arg(cwd.join("target").join("release").join("nyrt.lib").display().to_string())
.args([
"-o",
&out_path.display().to_string(),
&obj_path.display().to_string(),
])
.arg(
cwd.join("target")
.join("release")
.join("nyrt.lib")
.display()
.to_string(),
)
.arg("-lntdll")
.status().map_err(|e| format!("spawn clang: {}", e))?;
if status.success() { println!("OK"); return Ok(()); }
.status()
.map_err(|e| format!("spawn clang: {}", e))?;
if status.success() {
println!("OK");
return Ok(());
}
return Err("link failed on Windows (tried link.exe and clang)".into());
}
return Err("no linker found (need Visual Studio link.exe or LLVM clang)".into());
@ -128,13 +251,25 @@ pub(super) fn run_build_mvp_impl(runner: &NyashRunner, cfg_path: &str) -> Result
{
let status = std::process::Command::new("cc")
.arg(&obj_path)
.args(["-L", &cwd.join("target").join("release").display().to_string()])
.args(["-Wl,--whole-archive", "-lnyrt", "-Wl,--no-whole-archive", "-lpthread", "-ldl", "-lm"])
.args([
"-L",
&cwd.join("target").join("release").display().to_string(),
])
.args([
"-Wl,--whole-archive",
"-lnyrt",
"-Wl,--no-whole-archive",
"-lpthread",
"-ldl",
"-lm",
])
.args(["-o", &out_path.display().to_string()])
.status().map_err(|e| format!("spawn cc: {}", e))?;
if !status.success() { return Err("link failed (cc)".into()); }
.status()
.map_err(|e| format!("spawn cc: {}", e))?;
if !status.success() {
return Err("link failed (cc)".into());
}
}
println!("✅ Success: {}", out_path.display());
Ok(())
}

View File

@ -20,7 +20,9 @@ pub(super) fn apply_cli_directives_from_source(
for (i, line) in code.lines().take(128).enumerate() {
let l = line.trim();
if !(l.starts_with("//") || l.starts_with("#!") || l.is_empty()) {
if i > 0 { break; }
if i > 0 {
break;
}
}
if let Some(rest) = l.strip_prefix("//") {
let rest = rest.trim();
@ -28,7 +30,9 @@ pub(super) fn apply_cli_directives_from_source(
if let Some((k, v)) = dir.split_once('=') {
let key = k.trim();
let val = v.trim();
if !key.is_empty() { std::env::set_var(key, val); }
if !key.is_empty() {
std::env::set_var(key, val);
}
}
} else if rest == "@plugin-builtins" {
std::env::set_var("NYASH_USE_PLUGIN_BUILTINS", "1");
@ -53,4 +57,3 @@ pub(super) fn apply_cli_directives_from_source(
// Lint: enforce fields at top-of-box (delegated)
super::pipeline::lint_fields_top(code, strict_fields, verbose)
}

View File

@ -1,9 +1,9 @@
//! Runner demo helpers (moved out of mod.rs to reduce file size)
use nyash_rust::box_trait::{StringBox, IntegerBox, BoolBox, VoidBox, AddBox, NyashBox, BoxCore};
use nyash_rust::tokenizer::NyashTokenizer;
use nyash_rust::ast::ASTNode;
use nyash_rust::parser::NyashParser;
use nyash_rust::box_trait::{AddBox, BoolBox, BoxCore, IntegerBox, NyashBox, StringBox, VoidBox};
use nyash_rust::interpreter::NyashInterpreter;
use nyash_rust::parser::NyashParser;
use nyash_rust::tokenizer::NyashTokenizer;
pub(super) fn demo_basic_boxes() {
println!("\n📦 1. Basic Box Creation:");
@ -33,7 +33,10 @@ pub(super) fn demo_box_operations() {
let str1 = StringBox::new("Hello, ".to_string());
let str2 = StringBox::new("World!".to_string());
let concat_box = AddBox::new(Box::new(str1), Box::new(str2));
println!(" \"Hello, \" + \"World!\" = {}", concat_box.to_string_box().value);
println!(
" \"Hello, \" + \"World!\" = {}",
concat_box.to_string_box().value
);
}
pub(super) fn demo_box_collections() {
@ -55,7 +58,7 @@ pub(super) fn demo_tokenizer_system() {
match tokenizer.tokenize() {
Ok(tokens) => {
println!(" Tokenized {} tokens successfully", tokens.len());
},
}
Err(e) => println!(" Tokenization error: {}", e),
}
}
@ -82,7 +85,7 @@ pub(super) fn demo_parser_system() {
println!(" [{}] {}", i, stmt.info());
}
}
},
}
Err(e) => println!(" Parser error: {}", e),
}
}
@ -103,7 +106,7 @@ pub(super) fn demo_interpreter_system() {
match interpreter.execute(ast) {
Ok(result) => {
println!(" ✅ Result: {}", result.to_string_box().value);
},
}
Err(e) => {
println!(" ❌ Execution error: {}", e);
}
@ -125,7 +128,7 @@ pub(super) fn demo_interpreter_system() {
match interpreter.execute(ast) {
Ok(result) => {
println!(" ✅ Result: {}", result.to_string_box().value);
},
}
Err(e) => {
println!(" ❌ Execution error: {}", e);
}

View File

@ -9,130 +9,164 @@ use std::{fs, process};
/// Thin file dispatcher: select backend and delegate to mode executors
pub(crate) fn execute_file_with_backend(runner: &NyashRunner, filename: &str) {
// Selfhost pipeline (Ny -> JSON v0) behind env gate
if std::env::var("NYASH_USE_NY_COMPILER").ok().as_deref() == Some("1") {
if runner.try_run_selfhost_pipeline(filename) {
// Selfhost pipeline (Ny -> JSON v0) behind env gate
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)");
}
}
// Direct v0 bridge when requested via CLI/env
let use_ny_parser = runner.config.parser_ny
|| std::env::var("NYASH_USE_NY_PARSER").ok().as_deref() == Some("1");
if use_ny_parser {
let code = match fs::read_to_string(filename) {
Ok(content) => content,
Err(e) => {
eprintln!("❌ Error reading file {}: {}", filename, e);
process::exit(1);
}
};
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
);
}
runner.execute_mir_module(&module);
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)");
}
}
// Direct v0 bridge when requested via CLI/env
let use_ny_parser = runner.config.parser_ny || std::env::var("NYASH_USE_NY_PARSER").ok().as_deref() == Some("1");
if use_ny_parser {
let code = match fs::read_to_string(filename) {
Ok(content) => content,
Err(e) => { eprintln!("❌ Error reading file {}: {}", filename, e); process::exit(1); }
};
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);
}
runner.execute_mir_module(&module);
return;
}
Err(e) => { eprintln!("❌ Direct bridge parse error: {}", e); process::exit(1); }
}
}
// AST dump mode
if runner.config.dump_ast {
println!("🧠 Nyash AST Dump - Processing file: {}", filename);
let code = match fs::read_to_string(filename) {
Ok(content) => content,
Err(e) => { eprintln!("❌ Error reading file {}: {}", filename, e); process::exit(1); }
};
let ast = match NyashParser::parse_from_string(&code) {
Ok(ast) => ast,
Err(e) => { eprintln!("❌ Parse error: {}", e); process::exit(1); }
};
println!("{:#?}", ast);
return;
}
// 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);
}
runner.execute_mir_mode(filename);
return;
}
// WASM / AOT (feature-gated)
if runner.config.compile_wasm {
#[cfg(feature = "wasm-backend")]
{ super::modes::wasm::execute_wasm_mode(runner, filename); return; }
#[cfg(not(feature = "wasm-backend"))]
{ eprintln!("❌ WASM backend not available. Please rebuild with: cargo build --features wasm-backend"); process::exit(1); }
}
if runner.config.compile_native {
#[cfg(feature = "cranelift-jit")]
{
runner.execute_aot_mode(filename);
return;
}
#[cfg(not(feature = "cranelift-jit"))]
{ eprintln!("❌ Native AOT compilation requires Cranelift. Please rebuild: cargo build --features cranelift-jit"); process::exit(1); }
}
// 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);
}
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);
}
runner.execute_vm_mode(filename);
}
#[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);
}
#[cfg(feature = "cranelift-jit")]
{
// Use independent JIT-direct runner method (no VM execute loop)
runner.run_file_jit_direct(filename);
}
#[cfg(not(feature = "cranelift-jit"))]
{
eprintln!("❌ Cranelift backend not available. Please rebuild with: cargo build --features cranelift-jit");
process::exit(1);
}
}
"llvm" => {
if std::env::var("NYASH_CLI_VERBOSE").ok().as_deref() == Some("1") {
println!("⚡ Nyash LLVM Backend - Executing file: {}", filename);
}
runner.execute_llvm_mode(filename);
}
_ => {
if std::env::var("NYASH_CLI_VERBOSE").ok().as_deref() == Some("1") {
println!("🦀 Nyash Rust Implementation - Executing file: {} 🦀", filename);
if let Some(fuel) = runner.config.debug_fuel {
println!("🔥 Debug fuel limit: {} iterations", fuel);
} else {
println!("🔥 Debug fuel limit: unlimited");
}
println!("====================================================");
}
super::modes::interpreter::execute_nyash_file(filename, runner.config.debug_fuel.clone());
Err(e) => {
eprintln!("❌ Direct bridge parse error: {}", e);
process::exit(1);
}
}
}
// AST dump mode
if runner.config.dump_ast {
println!("🧠 Nyash AST Dump - Processing file: {}", filename);
let code = match fs::read_to_string(filename) {
Ok(content) => content,
Err(e) => {
eprintln!("❌ Error reading file {}: {}", filename, e);
process::exit(1);
}
};
let ast = match NyashParser::parse_from_string(&code) {
Ok(ast) => ast,
Err(e) => {
eprintln!("❌ Parse error: {}", e);
process::exit(1);
}
};
println!("{:#?}", ast);
return;
}
// 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);
}
runner.execute_mir_mode(filename);
return;
}
// WASM / AOT (feature-gated)
if runner.config.compile_wasm {
#[cfg(feature = "wasm-backend")]
{
super::modes::wasm::execute_wasm_mode(runner, filename);
return;
}
#[cfg(not(feature = "wasm-backend"))]
{
eprintln!("❌ WASM backend not available. Please rebuild with: cargo build --features wasm-backend");
process::exit(1);
}
}
if runner.config.compile_native {
#[cfg(feature = "cranelift-jit")]
{
runner.execute_aot_mode(filename);
return;
}
#[cfg(not(feature = "cranelift-jit"))]
{
eprintln!("❌ Native AOT compilation requires Cranelift. Please rebuild: cargo build --features cranelift-jit");
process::exit(1);
}
}
// 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);
}
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);
}
runner.execute_vm_mode(filename);
}
#[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
);
}
#[cfg(feature = "cranelift-jit")]
{
// Use independent JIT-direct runner method (no VM execute loop)
runner.run_file_jit_direct(filename);
}
#[cfg(not(feature = "cranelift-jit"))]
{
eprintln!("❌ Cranelift backend not available. Please rebuild with: cargo build --features cranelift-jit");
process::exit(1);
}
}
"llvm" => {
if std::env::var("NYASH_CLI_VERBOSE").ok().as_deref() == Some("1") {
println!("⚡ Nyash LLVM Backend - Executing file: {}", filename);
}
runner.execute_llvm_mode(filename);
}
_ => {
if std::env::var("NYASH_CLI_VERBOSE").ok().as_deref() == Some("1") {
println!(
"🦀 Nyash Rust Implementation - Executing file: {} 🦀",
filename
);
if let Some(fuel) = runner.config.debug_fuel {
println!("🔥 Debug fuel limit: {} iterations", fuel);
} else {
println!("🔥 Debug fuel limit: unlimited");
}
println!("====================================================");
}
super::modes::interpreter::execute_nyash_file(
filename,
runner.config.debug_fuel.clone(),
);
}
}
}
impl NyashRunner {
pub(crate) fn execute_mir_module(&self, module: &crate::mir::MirModule) {
use crate::backend::MirInterpreter;
use crate::box_trait::{IntegerBox, BoolBox, StringBox};
use crate::box_trait::{BoolBox, IntegerBox, StringBox};
use crate::boxes::FloatBox;
use crate::mir::MirType;
@ -147,26 +181,34 @@ impl NyashRunner {
("Float", format!("{}", fb.value))
} else if let Some(ib) = result.as_any().downcast_ref::<IntegerBox>() {
("Float", format!("{}", ib.value as f64))
} else { ("Float", result.to_string_box().value) }
} else {
("Float", result.to_string_box().value)
}
}
MirType::Integer => {
if let Some(ib) = result.as_any().downcast_ref::<IntegerBox>() {
("Integer", ib.value.to_string())
} else { ("Integer", result.to_string_box().value) }
} else {
("Integer", result.to_string_box().value)
}
}
MirType::Bool => {
if let Some(bb) = result.as_any().downcast_ref::<BoolBox>() {
("Bool", bb.value.to_string())
} else if let Some(ib) = result.as_any().downcast_ref::<IntegerBox>() {
("Bool", (ib.value != 0).to_string())
} else { ("Bool", result.to_string_box().value) }
} else {
("Bool", result.to_string_box().value)
}
}
MirType::String => {
if let Some(sb) = result.as_any().downcast_ref::<StringBox>() {
("String", sb.value.clone())
} else { ("String", result.to_string_box().value) }
} else {
("String", result.to_string_box().value)
}
}
_ => { (result.type_name(), result.to_string_box().value) }
_ => (result.type_name(), result.to_string_box().value),
};
println!("ResultType(MIR): {}", ety);
println!("Result: {}", sval);

View File

@ -10,17 +10,31 @@ pub(super) struct ProgramV0 {
#[derive(Debug, Deserialize, Serialize, Clone)]
#[serde(tag = "type")]
pub(super) enum StmtV0 {
Return { expr: ExprV0 },
Extern { iface: String, method: String, args: Vec<ExprV0> },
Expr { expr: ExprV0 },
Local { name: String, expr: ExprV0 },
Return {
expr: ExprV0,
},
Extern {
iface: String,
method: String,
args: Vec<ExprV0>,
},
Expr {
expr: ExprV0,
},
Local {
name: String,
expr: ExprV0,
},
If {
cond: ExprV0,
then: Vec<StmtV0>,
#[serde(rename = "else", default)]
r#else: Option<Vec<StmtV0>>,
},
Loop { cond: ExprV0, body: Vec<StmtV0> },
Loop {
cond: ExprV0,
body: Vec<StmtV0>,
},
Break,
Continue,
Try {
@ -46,17 +60,52 @@ pub(super) struct CatchV0 {
#[derive(Debug, Deserialize, Serialize, Clone)]
#[serde(tag = "type")]
pub(super) enum ExprV0 {
Int { value: serde_json::Value },
Str { value: String },
Bool { value: bool },
Binary { op: String, lhs: Box<ExprV0>, rhs: Box<ExprV0> },
Extern { iface: String, method: String, args: Vec<ExprV0> },
Compare { op: String, lhs: Box<ExprV0>, rhs: Box<ExprV0> },
Logical { op: String, lhs: Box<ExprV0>, rhs: Box<ExprV0> },
Call { name: String, args: Vec<ExprV0> },
Method { recv: Box<ExprV0>, method: String, args: Vec<ExprV0> },
New { class: String, args: Vec<ExprV0> },
Var { name: String },
Throw { expr: Box<ExprV0> },
Int {
value: serde_json::Value,
},
Str {
value: String,
},
Bool {
value: bool,
},
Binary {
op: String,
lhs: Box<ExprV0>,
rhs: Box<ExprV0>,
},
Extern {
iface: String,
method: String,
args: Vec<ExprV0>,
},
Compare {
op: String,
lhs: Box<ExprV0>,
rhs: Box<ExprV0>,
},
Logical {
op: String,
lhs: Box<ExprV0>,
rhs: Box<ExprV0>,
},
Call {
name: String,
args: Vec<ExprV0>,
},
Method {
recv: Box<ExprV0>,
method: String,
args: Vec<ExprV0>,
},
New {
class: String,
args: Vec<ExprV0>,
},
Var {
name: String,
},
Throw {
expr: Box<ExprV0>,
},
}

View File

@ -18,25 +18,60 @@ fn lex(input: &str) -> Result<Vec<Tok>, String> {
let mut toks = Vec::new();
while let Some(&c) = chars.peek() {
match c {
' ' | '\n' | '\t' | '\r' => { chars.next(); }
'+' => { chars.next(); toks.push(Tok::Plus); }
'-' => { chars.next(); toks.push(Tok::Minus); }
'*' => { chars.next(); toks.push(Tok::Star); }
'/' => { chars.next(); toks.push(Tok::Slash); }
'(' => { chars.next(); toks.push(Tok::LParen); }
')' => { chars.next(); toks.push(Tok::RParen); }
' ' | '\n' | '\t' | '\r' => {
chars.next();
}
'+' => {
chars.next();
toks.push(Tok::Plus);
}
'-' => {
chars.next();
toks.push(Tok::Minus);
}
'*' => {
chars.next();
toks.push(Tok::Star);
}
'/' => {
chars.next();
toks.push(Tok::Slash);
}
'(' => {
chars.next();
toks.push(Tok::LParen);
}
')' => {
chars.next();
toks.push(Tok::RParen);
}
'0'..='9' => {
let mut n = 0i64;
while let Some(&d) = chars.peek() {
if d.is_ascii_digit() { n = n * 10 + (d as i64 - '0' as i64); chars.next(); } else { break; }
if d.is_ascii_digit() {
n = n * 10 + (d as i64 - '0' as i64);
chars.next();
} else {
break;
}
}
toks.push(Tok::Int(n));
}
'r' => {
let kw = "return";
let mut it = kw.chars();
let mut ok = true; for _ in 0..kw.len() { if Some(chars.next().unwrap_or('\0')) != it.next() { ok=false; break; } }
if ok { toks.push(Tok::Return); } else { return Err("unexpected 'r'".into()); }
let mut ok = true;
for _ in 0..kw.len() {
if Some(chars.next().unwrap_or('\0')) != it.next() {
ok = false;
break;
}
}
if ok {
toks.push(Tok::Return);
} else {
return Err("unexpected 'r'".into());
}
}
_ => return Err(format!("unexpected char '{}'", c)),
}
@ -45,39 +80,111 @@ fn lex(input: &str) -> Result<Vec<Tok>, String> {
Ok(toks)
}
struct P { toks: Vec<Tok>, pos: usize }
struct P {
toks: Vec<Tok>,
pos: usize,
}
impl P {
fn new(toks: Vec<Tok>) -> Self { Self { toks, pos: 0 } }
fn peek(&self) -> &Tok { self.toks.get(self.pos).unwrap() }
fn next(&mut self) -> Tok { let t = self.toks.get(self.pos).unwrap().clone(); self.pos += 1; t }
fn expect_return(&mut self) -> Result<(), String> { match self.next() { Tok::Return => Ok(()), _ => Err("expected 'return'".into()) } }
fn parse_program(&mut self) -> Result<ExprV0, String> { self.expect_return()?; self.parse_expr() }
fn new(toks: Vec<Tok>) -> Self {
Self { toks, pos: 0 }
}
fn peek(&self) -> &Tok {
self.toks.get(self.pos).unwrap()
}
fn next(&mut self) -> Tok {
let t = self.toks.get(self.pos).unwrap().clone();
self.pos += 1;
t
}
fn expect_return(&mut self) -> Result<(), String> {
match self.next() {
Tok::Return => Ok(()),
_ => Err("expected 'return'".into()),
}
}
fn parse_program(&mut self) -> Result<ExprV0, String> {
self.expect_return()?;
self.parse_expr()
}
fn parse_expr(&mut self) -> Result<ExprV0, String> {
let mut left = self.parse_term()?;
loop { match self.peek() { Tok::Plus => { self.next(); let r = self.parse_term()?; left = ExprV0::Binary{op:"+".into(), lhs: Box::new(left), rhs: Box::new(r)}; }
Tok::Minus => { self.next(); let r = self.parse_term()?; left = ExprV0::Binary{op:"-".into(), lhs: Box::new(left), rhs: Box::new(r)}; }
_ => break } }
loop {
match self.peek() {
Tok::Plus => {
self.next();
let r = self.parse_term()?;
left = ExprV0::Binary {
op: "+".into(),
lhs: Box::new(left),
rhs: Box::new(r),
};
}
Tok::Minus => {
self.next();
let r = self.parse_term()?;
left = ExprV0::Binary {
op: "-".into(),
lhs: Box::new(left),
rhs: Box::new(r),
};
}
_ => break,
}
}
Ok(left)
}
fn parse_term(&mut self) -> Result<ExprV0, String> {
let mut left = self.parse_factor()?;
loop { match self.peek() { Tok::Star => { self.next(); let r = self.parse_factor()?; left = ExprV0::Binary{op:"*".into(), lhs: Box::new(left), rhs: Box::new(r)}; }
Tok::Slash => { self.next(); let r = self.parse_factor()?; left = ExprV0::Binary{op:"/".into(), lhs: Box::new(left), rhs: Box::new(r)}; }
_ => break } }
loop {
match self.peek() {
Tok::Star => {
self.next();
let r = self.parse_factor()?;
left = ExprV0::Binary {
op: "*".into(),
lhs: Box::new(left),
rhs: Box::new(r),
};
}
Tok::Slash => {
self.next();
let r = self.parse_factor()?;
left = ExprV0::Binary {
op: "/".into(),
lhs: Box::new(left),
rhs: Box::new(r),
};
}
_ => break,
}
}
Ok(left)
}
fn parse_factor(&mut self) -> Result<ExprV0, String> {
match self.next() {
Tok::Int(v) => Ok(ExprV0::Int { value: serde_json::Value::from(v) }),
Tok::LParen => { let e = self.parse_expr()?; match self.next() { Tok::RParen => Ok(e), _ => Err(") expected".into()) } }
Tok::Int(v) => Ok(ExprV0::Int {
value: serde_json::Value::from(v),
}),
Tok::LParen => {
let e = self.parse_expr()?;
match self.next() {
Tok::RParen => Ok(e),
_ => Err(") expected".into()),
}
}
_ => Err("factor expected".into()),
}
}
}
pub(super) fn parse_source_v0_to_json(input: &str) -> Result<String, String> {
let toks = lex(input)?; let mut p = P::new(toks); let expr = p.parse_program()?;
let prog = ProgramV0 { version: 0, kind: "Program".into(), body: vec![StmtV0::Return { expr }] };
let toks = lex(input)?;
let mut p = P::new(toks);
let expr = p.parse_program()?;
let prog = ProgramV0 {
version: 0,
kind: "Program".into(),
body: vec![StmtV0::Return { expr }],
};
serde_json::to_string(&prog).map_err(|e| e.to_string())
}

View File

@ -103,7 +103,12 @@ fn next_block_id(f: &MirFunction) -> BasicBlockId {
BasicBlockId::new(mx)
}
fn lower_throw(env: &BridgeEnv, f: &mut MirFunction, cur_bb: BasicBlockId, exception_value: ValueId) -> (ValueId, BasicBlockId) {
fn lower_throw(
env: &BridgeEnv,
f: &mut MirFunction,
cur_bb: BasicBlockId,
exception_value: ValueId,
) -> (ValueId, BasicBlockId) {
if env.throw_enabled {
if let Some(bb) = f.get_block_mut(cur_bb) {
bb.set_terminator(MirInstruction::Throw {
@ -115,7 +120,10 @@ fn lower_throw(env: &BridgeEnv, f: &mut MirFunction, cur_bb: BasicBlockId, excep
} 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)
}
@ -139,21 +147,30 @@ fn lower_expr_with_scope<S: VarScope>(
};
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(ival) });
bb.add_instruction(MirInstruction::Const {
dst,
value: ConstValue::Integer(ival),
});
}
Ok((dst, cur_bb))
}
ExprV0::Str { value } => {
let dst = f.next_value_id();
if let Some(bb) = f.get_block_mut(cur_bb) {
bb.add_instruction(MirInstruction::Const { dst, value: ConstValue::String(value.clone()) });
bb.add_instruction(MirInstruction::Const {
dst,
value: ConstValue::String(value.clone()),
});
}
Ok((dst, cur_bb))
}
ExprV0::Bool { value } => {
let dst = f.next_value_id();
if let Some(bb) = f.get_block_mut(cur_bb) {
bb.add_instruction(MirInstruction::Const { dst, value: ConstValue::Bool(*value) });
bb.add_instruction(MirInstruction::Const {
dst,
value: ConstValue::Bool(*value),
});
}
Ok((dst, cur_bb))
}
@ -169,15 +186,30 @@ fn lower_expr_with_scope<S: VarScope>(
};
let dst = f.next_value_id();
if let Some(bb) = f.get_block_mut(cur_after_r) {
bb.add_instruction(MirInstruction::BinOp { dst, op: bop, lhs: l, rhs: r });
bb.add_instruction(MirInstruction::BinOp {
dst,
op: bop,
lhs: l,
rhs: r,
});
}
Ok((dst, cur_after_r))
}
ExprV0::Extern { iface, method, args } => {
ExprV0::Extern {
iface,
method,
args,
} => {
let (arg_ids, cur2) = lower_args_with_scope(env, f, cur_bb, args, vars)?;
let dst = f.next_value_id();
if let Some(bb) = f.get_block_mut(cur2) {
bb.add_instruction(MirInstruction::ExternCall { dst: Some(dst), iface_name: iface.clone(), method_name: method.clone(), args: arg_ids, effects: EffectMask::IO });
bb.add_instruction(MirInstruction::ExternCall {
dst: Some(dst),
iface_name: iface.clone(),
method_name: method.clone(),
args: arg_ids,
effects: EffectMask::IO,
});
}
Ok((dst, cur2))
}
@ -195,7 +227,12 @@ fn lower_expr_with_scope<S: VarScope>(
};
let dst = f.next_value_id();
if let Some(bb) = f.get_block_mut(cur_after_r) {
bb.add_instruction(MirInstruction::Compare { dst, op: cop, lhs: l, rhs: r });
bb.add_instruction(MirInstruction::Compare {
dst,
op: cop,
lhs: l,
rhs: r,
});
}
Ok((dst, cur_after_r))
}
@ -210,9 +247,17 @@ fn lower_expr_with_scope<S: VarScope>(
let is_and = matches!(op.as_str(), "&&" | "and");
if let Some(bb) = f.get_block_mut(cur_after_l) {
if is_and {
bb.set_terminator(MirInstruction::Branch { condition: l, then_bb: rhs_bb, else_bb: fall_bb });
bb.set_terminator(MirInstruction::Branch {
condition: l,
then_bb: rhs_bb,
else_bb: fall_bb,
});
} else {
bb.set_terminator(MirInstruction::Branch { condition: l, then_bb: fall_bb, else_bb: rhs_bb });
bb.set_terminator(MirInstruction::Branch {
condition: l,
then_bb: fall_bb,
else_bb: rhs_bb,
});
}
}
crate::jit::events::emit_lower(
@ -222,19 +267,44 @@ fn lower_expr_with_scope<S: VarScope>(
);
let cdst = f.next_value_id();
if let Some(bb) = f.get_block_mut(fall_bb) {
let cval = if is_and { ConstValue::Bool(false) } else { ConstValue::Bool(true) };
bb.add_instruction(MirInstruction::Const { dst: cdst, value: cval });
let cval = if is_and {
ConstValue::Bool(false)
} else {
ConstValue::Bool(true)
};
bb.add_instruction(MirInstruction::Const {
dst: cdst,
value: cval,
});
bb.set_terminator(MirInstruction::Jump { target: merge_bb });
}
let (rval, rhs_end) = lower_expr_with_scope(env, f, rhs_bb, rhs, vars)?;
if let Some(bb) = f.get_block_mut(rhs_end) { if !bb.is_terminated() { bb.set_terminator(MirInstruction::Jump { target: merge_bb }); } }
if let Some(bb) = f.get_block_mut(rhs_end) {
if !bb.is_terminated() {
bb.set_terminator(MirInstruction::Jump { target: merge_bb });
}
}
let out = f.next_value_id();
if env.mir_no_phi {
if let Some(bb) = f.get_block_mut(fall_bb) { bb.add_instruction(MirInstruction::Copy { dst: out, src: cdst }); }
if let Some(bb) = f.get_block_mut(rhs_end) { bb.add_instruction(MirInstruction::Copy { dst: out, src: rval }); }
if let Some(bb) = f.get_block_mut(fall_bb) {
bb.add_instruction(MirInstruction::Copy {
dst: out,
src: cdst,
});
}
if let Some(bb) = f.get_block_mut(rhs_end) {
bb.add_instruction(MirInstruction::Copy {
dst: out,
src: rval,
});
}
} else if let Some(bb) = f.get_block_mut(merge_bb) {
let mut inputs: Vec<(BasicBlockId, ValueId)> = vec![(fall_bb, cdst)];
if rhs_end != fall_bb { inputs.push((rhs_end, rval)); } else { inputs.push((fall_bb, rval)); }
if rhs_end != fall_bb {
inputs.push((rhs_end, rval));
} else {
inputs.push((fall_bb, rval));
}
inputs.sort_by_key(|(bbid, _)| bbid.0);
bb.insert_instruction_after_phis(MirInstruction::Phi { dst: out, inputs });
}
@ -243,62 +313,133 @@ fn lower_expr_with_scope<S: VarScope>(
ExprV0::Call { name, args } => {
if name == "array.of" {
let arr = f.next_value_id();
if let Some(bb) = f.get_block_mut(cur_bb) { bb.add_instruction(MirInstruction::NewBox { dst: arr, box_type: "ArrayBox".into(), args: vec![] }); }
if let Some(bb) = f.get_block_mut(cur_bb) {
bb.add_instruction(MirInstruction::NewBox {
dst: arr,
box_type: "ArrayBox".into(),
args: vec![],
});
}
let mut cur = cur_bb;
for e in args {
let (v, c) = lower_expr_with_scope(env, f, cur, e, vars)?; cur = c;
let (v, c) = lower_expr_with_scope(env, f, cur, e, vars)?;
cur = c;
let tmp = f.next_value_id();
if let Some(bb) = f.get_block_mut(cur) { bb.add_instruction(MirInstruction::BoxCall { dst: Some(tmp), box_val: arr, method: "push".into(), method_id: None, args: vec![v], effects: EffectMask::READ }); }
if let Some(bb) = f.get_block_mut(cur) {
bb.add_instruction(MirInstruction::BoxCall {
dst: Some(tmp),
box_val: arr,
method: "push".into(),
method_id: None,
args: vec![v],
effects: EffectMask::READ,
});
}
}
return Ok((arr, cur));
}
if name == "map.of" {
let mapv = f.next_value_id();
if let Some(bb) = f.get_block_mut(cur_bb) { bb.add_instruction(MirInstruction::NewBox { dst: mapv, box_type: "MapBox".into(), args: vec![] }); }
let mut cur = cur_bb; let mut it = args.iter();
if let Some(bb) = f.get_block_mut(cur_bb) {
bb.add_instruction(MirInstruction::NewBox {
dst: mapv,
box_type: "MapBox".into(),
args: vec![],
});
}
let mut cur = cur_bb;
let mut it = args.iter();
while let Some(k) = it.next() {
if let Some(v) = it.next() {
let (kv, cur2) = lower_expr_with_scope(env, f, cur, k, vars)?; cur = cur2;
let (vv, cur3) = lower_expr_with_scope(env, f, cur, v, vars)?; cur = cur3;
let (kv, cur2) = lower_expr_with_scope(env, f, cur, k, vars)?;
cur = cur2;
let (vv, cur3) = lower_expr_with_scope(env, f, cur, v, vars)?;
cur = cur3;
let tmp = f.next_value_id();
if let Some(bb) = f.get_block_mut(cur) { bb.add_instruction(MirInstruction::BoxCall { dst: Some(tmp), box_val: mapv, method: "set".into(), method_id: None, args: vec![kv, vv], effects: EffectMask::READ }); }
} else { break; }
if let Some(bb) = f.get_block_mut(cur) {
bb.add_instruction(MirInstruction::BoxCall {
dst: Some(tmp),
box_val: mapv,
method: "set".into(),
method_id: None,
args: vec![kv, vv],
effects: EffectMask::READ,
});
}
} else {
break;
}
}
return Ok((mapv, cur));
}
let (arg_ids, cur) = lower_args_with_scope(env, f, cur_bb, args, vars)?;
let fun_val = f.next_value_id();
if let Some(bb) = f.get_block_mut(cur) { bb.add_instruction(MirInstruction::Const { dst: fun_val, value: ConstValue::String(name.clone()) }); }
if let Some(bb) = f.get_block_mut(cur) {
bb.add_instruction(MirInstruction::Const {
dst: fun_val,
value: ConstValue::String(name.clone()),
});
}
let dst = f.next_value_id();
if let Some(bb) = f.get_block_mut(cur) { bb.add_instruction(MirInstruction::Call { dst: Some(dst), func: fun_val, args: arg_ids, effects: EffectMask::READ }); }
if let Some(bb) = f.get_block_mut(cur) {
bb.add_instruction(MirInstruction::Call {
dst: Some(dst),
func: fun_val,
args: arg_ids,
effects: EffectMask::READ,
});
}
Ok((dst, cur))
}
ExprV0::Method { recv, method, args } => {
let recv_is_console_new = matches!(&**recv, ExprV0::New { class, .. } if class == "ConsoleBox");
if recv_is_console_new && (method == "println" || method == "print" || method == "log") {
let recv_is_console_new =
matches!(&**recv, ExprV0::New { class, .. } if class == "ConsoleBox");
if recv_is_console_new && (method == "println" || method == "print" || method == "log")
{
let (arg_ids, cur2) = lower_args_with_scope(env, f, cur_bb, args, vars)?;
let dst = f.next_value_id();
if let Some(bb) = f.get_block_mut(cur2) { bb.add_instruction(MirInstruction::ExternCall { dst: Some(dst), iface_name: "env.console".into(), method_name: "log".into(), args: arg_ids, effects: EffectMask::READ }); }
if let Some(bb) = f.get_block_mut(cur2) {
bb.add_instruction(MirInstruction::ExternCall {
dst: Some(dst),
iface_name: "env.console".into(),
method_name: "log".into(),
args: arg_ids,
effects: EffectMask::READ,
});
}
return Ok((dst, cur2));
}
let (recv_v, cur) = lower_expr_with_scope(env, f, cur_bb, recv, vars)?;
let (arg_ids, cur2) = lower_args_with_scope(env, f, cur, args, vars)?;
let dst = f.next_value_id();
if let Some(bb) = f.get_block_mut(cur2) { bb.add_instruction(MirInstruction::BoxCall { dst: Some(dst), box_val: recv_v, method: method.clone(), method_id: None, args: arg_ids, effects: EffectMask::READ }); }
if let Some(bb) = f.get_block_mut(cur2) {
bb.add_instruction(MirInstruction::BoxCall {
dst: Some(dst),
box_val: recv_v,
method: method.clone(),
method_id: None,
args: arg_ids,
effects: EffectMask::READ,
});
}
Ok((dst, cur2))
}
ExprV0::New { class, args } => {
let (arg_ids, cur) = lower_args_with_scope(env, f, cur_bb, args, vars)?;
let dst = f.next_value_id();
if let Some(bb) = f.get_block_mut(cur) { bb.add_instruction(MirInstruction::NewBox { dst, box_type: class.clone(), args: arg_ids }); }
if let Some(bb) = f.get_block_mut(cur) {
bb.add_instruction(MirInstruction::NewBox {
dst,
box_type: class.clone(),
args: arg_ids,
});
}
Ok((dst, cur))
}
ExprV0::Var { name } => {
match vars.resolve(env, f, cur_bb, name)? {
Some(v) => Ok((v, cur_bb)),
None => Err(format!("undefined variable: {}", name)),
}
}
ExprV0::Var { name } => match vars.resolve(env, f, cur_bb, name)? {
Some(v) => Ok((v, cur_bb)),
None => Err(format!("undefined variable: {}", name)),
},
ExprV0::Throw { expr } => {
let (exc, cur) = lower_expr_with_scope(env, f, cur_bb, expr, vars)?;
Ok(lower_throw(env, f, cur, exc))
@ -306,7 +447,13 @@ fn lower_expr_with_scope<S: VarScope>(
}
}
fn lower_args_with_scope<S: VarScope>(env: &BridgeEnv, f: &mut MirFunction, cur_bb: BasicBlockId, args: &[ExprV0], scope: &mut S) -> Result<(Vec<ValueId>, BasicBlockId), String> {
fn lower_args_with_scope<S: VarScope>(
env: &BridgeEnv,
f: &mut MirFunction,
cur_bb: BasicBlockId,
args: &[ExprV0],
scope: &mut S,
) -> Result<(Vec<ValueId>, BasicBlockId), String> {
let mut out = Vec::with_capacity(args.len());
let mut cur = cur_bb;
for a in args {
@ -317,22 +464,44 @@ fn lower_args_with_scope<S: VarScope>(env: &BridgeEnv, f: &mut MirFunction, cur_
Ok((out, cur))
}
fn lower_expr(env: &BridgeEnv, f: &mut MirFunction, cur_bb: BasicBlockId, e: &ExprV0) -> Result<(ValueId, BasicBlockId), String> {
fn lower_expr(
env: &BridgeEnv,
f: &mut MirFunction,
cur_bb: BasicBlockId,
e: &ExprV0,
) -> Result<(ValueId, BasicBlockId), String> {
let mut scope = NoVars;
lower_expr_with_scope(env, f, cur_bb, e, &mut scope)
}
fn lower_expr_with_vars(env: &BridgeEnv, f: &mut MirFunction, cur_bb: BasicBlockId, e: &ExprV0, vars: &mut HashMap<String, ValueId>) -> Result<(ValueId, BasicBlockId), String> {
fn lower_expr_with_vars(
env: &BridgeEnv,
f: &mut MirFunction,
cur_bb: BasicBlockId,
e: &ExprV0,
vars: &mut HashMap<String, ValueId>,
) -> Result<(ValueId, BasicBlockId), String> {
let mut scope = MapVars::new(vars);
lower_expr_with_scope(env, f, cur_bb, e, &mut scope)
}
fn lower_args(env: &BridgeEnv, f: &mut MirFunction, cur_bb: BasicBlockId, args: &[ExprV0]) -> Result<(Vec<ValueId>, BasicBlockId), String> {
fn lower_args(
env: &BridgeEnv,
f: &mut MirFunction,
cur_bb: BasicBlockId,
args: &[ExprV0],
) -> Result<(Vec<ValueId>, BasicBlockId), String> {
let mut scope = NoVars;
lower_args_with_scope(env, f, cur_bb, args, &mut scope)
}
fn lower_args_with_vars(env: &BridgeEnv, f: &mut MirFunction, cur_bb: BasicBlockId, args: &[ExprV0], vars: &mut HashMap<String, ValueId>) -> Result<(Vec<ValueId>, BasicBlockId), String> {
fn lower_args_with_vars(
env: &BridgeEnv,
f: &mut MirFunction,
cur_bb: BasicBlockId,
args: &[ExprV0],
vars: &mut HashMap<String, ValueId>,
) -> Result<(Vec<ValueId>, BasicBlockId), String> {
let mut scope = MapVars::new(vars);
lower_args_with_scope(env, f, cur_bb, args, &mut scope)
}
@ -348,12 +517,26 @@ fn lower_stmt_with_vars(
match s {
StmtV0::Return { expr } => {
let (v, cur) = lower_expr_with_vars(env, f, cur_bb, expr, vars)?;
if let Some(bb) = f.get_block_mut(cur) { bb.set_terminator(MirInstruction::Return { value: Some(v) }); }
if let Some(bb) = f.get_block_mut(cur) {
bb.set_terminator(MirInstruction::Return { value: Some(v) });
}
Ok(cur)
}
StmtV0::Extern { iface, method, args } => {
StmtV0::Extern {
iface,
method,
args,
} => {
let (arg_ids, cur) = lower_args_with_vars(env, f, cur_bb, args, vars)?;
if let Some(bb) = f.get_block_mut(cur) { bb.add_instruction(MirInstruction::ExternCall { dst: None, iface_name: iface.clone(), method_name: method.clone(), args: arg_ids, effects: EffectMask::IO }); }
if let Some(bb) = f.get_block_mut(cur) {
bb.add_instruction(MirInstruction::ExternCall {
dst: None,
iface_name: iface.clone(),
method_name: method.clone(),
args: arg_ids,
effects: EffectMask::IO,
});
}
Ok(cur)
}
StmtV0::Expr { expr } => {
@ -367,7 +550,11 @@ 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 }); }
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",
@ -378,7 +565,11 @@ fn lower_stmt_with_vars(
}
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 }); }
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",
@ -387,40 +578,89 @@ fn lower_stmt_with_vars(
}
Ok(cur_bb)
}
StmtV0::Try { try_body, catches, finally } => {
StmtV0::Try {
try_body,
catches,
finally,
} => {
let try_enabled = std::env::var("NYASH_BRIDGE_TRY_ENABLE").ok().as_deref() == Some("1");
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)?;
let mut next_bb =
lower_stmt_list_with_vars(f, cur_bb, try_body, &mut tmp_vars, loop_stack, env)?;
if !finally.is_empty() {
next_bb = lower_stmt_list_with_vars(f, next_bb, finally, &mut tmp_vars, loop_stack, env)?;
next_bb = 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 = next_block_id(f); f.add_block(BasicBlock::new(try_bb));
let try_bb = next_block_id(f);
f.add_block(BasicBlock::new(try_bb));
let catch_clause = &catches[0];
let catch_bb = next_block_id(f); f.add_block(BasicBlock::new(catch_bb));
let finally_bb = if !finally.is_empty() { let id = next_block_id(f); f.add_block(BasicBlock::new(id)); Some(id) } else { None };
let exit_bb = next_block_id(f); f.add_block(BasicBlock::new(exit_bb));
let catch_bb = next_block_id(f);
f.add_block(BasicBlock::new(catch_bb));
let finally_bb = if !finally.is_empty() {
let id = next_block_id(f);
f.add_block(BasicBlock::new(id));
Some(id)
} else {
None
};
let exit_bb = next_block_id(f);
f.add_block(BasicBlock::new(exit_bb));
let handler_target = finally_bb.unwrap_or(exit_bb);
let exception_value = f.next_value_id();
if let Some(bb) = f.get_block_mut(cur_bb) {
bb.add_instruction(MirInstruction::Catch { exception_type: catch_clause.type_hint.clone(), exception_value, handler_bb: catch_bb });
bb.add_instruction(MirInstruction::Catch {
exception_type: catch_clause.type_hint.clone(),
exception_value,
handler_bb: catch_bb,
});
bb.set_terminator(MirInstruction::Jump { target: try_bb });
}
let mut try_vars = vars.clone();
let try_end = lower_stmt_list_with_vars(f, try_bb, try_body, &mut try_vars, loop_stack, env)?;
if let Some(bb) = f.get_block_mut(try_end) { if !bb.is_terminated() { bb.set_terminator(MirInstruction::Jump { target: handler_target }); } }
let try_end =
lower_stmt_list_with_vars(f, try_bb, try_body, &mut try_vars, loop_stack, env)?;
if let Some(bb) = f.get_block_mut(try_end) {
if !bb.is_terminated() {
bb.set_terminator(MirInstruction::Jump {
target: handler_target,
});
}
}
let try_branch_vars = try_vars.clone();
let mut catch_vars = base_vars.clone();
if let Some(param) = &catch_clause.param { catch_vars.insert(param.clone(), exception_value); }
let catch_end = lower_stmt_list_with_vars(f, catch_bb, &catch_clause.body, &mut catch_vars, loop_stack, env)?;
if let Some(param) = &catch_clause.param { catch_vars.remove(param); }
if let Some(bb) = f.get_block_mut(catch_end) { if !bb.is_terminated() { bb.set_terminator(MirInstruction::Jump { target: handler_target }); } }
if let Some(param) = &catch_clause.param {
catch_vars.insert(param.clone(), exception_value);
}
let catch_end = lower_stmt_list_with_vars(
f,
catch_bb,
&catch_clause.body,
&mut catch_vars,
loop_stack,
env,
)?;
if let Some(param) = &catch_clause.param {
catch_vars.remove(param);
}
if let Some(bb) = f.get_block_mut(catch_end) {
if !bb.is_terminated() {
bb.set_terminator(MirInstruction::Jump {
target: handler_target,
});
}
}
let catch_branch_vars = catch_vars.clone();
use std::collections::HashSet;
@ -428,78 +668,220 @@ fn lower_stmt_with_vars(
if let Some(finally_block) = finally_bb {
let names: HashSet<String> = {
let mut set: HashSet<String> = base_vars.keys().cloned().collect();
for (_, map) in &branch_vars { set.extend(map.keys().cloned()); }
for (_, map) in &branch_vars {
set.extend(map.keys().cloned());
}
set
};
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(&val) = map.get(&name) { inputs.push((*bbid, val)); } }
if inputs.is_empty() { if let Some(&base_val) = base_vars.get(&name) { merged_vars.insert(name.clone(), base_val); } continue; }
for (bbid, map) in &branch_vars {
if let Some(&val) = map.get(&name) {
inputs.push((*bbid, val));
}
}
if inputs.is_empty() {
if let Some(&base_val) = base_vars.get(&name) {
merged_vars.insert(name.clone(), base_val);
}
continue;
}
let unique: HashSet<ValueId> = inputs.iter().map(|(_, v)| *v).collect();
if unique.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 unique.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 });
}
}
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 = 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 }); } }
*vars = finally_vars; Ok(exit_bb)
let final_end = 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 });
}
}
*vars = finally_vars;
Ok(exit_bb)
} else {
let names: HashSet<String> = {
let mut set: HashSet<String> = base_vars.keys().cloned().collect();
for (_, map) in &branch_vars { set.extend(map.keys().cloned()); }
for (_, map) in &branch_vars {
set.extend(map.keys().cloned());
}
set
};
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(&val) = map.get(&name) { inputs.push((*bbid, val)); } }
if inputs.is_empty() { if let Some(&base_val) = base_vars.get(&name) { merged_vars.insert(name.clone(), base_val); } continue; }
for (bbid, map) in &branch_vars {
if let Some(&val) = map.get(&name) {
inputs.push((*bbid, val));
}
}
if inputs.is_empty() {
if let Some(&base_val) = base_vars.get(&name) {
merged_vars.insert(name.clone(), base_val);
}
continue;
}
let unique: HashSet<ValueId> = inputs.iter().map(|(_, v)| *v).collect();
if unique.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 unique.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; Ok(exit_bb)
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;
Ok(exit_bb)
}
}
StmtV0::If { cond, then, r#else } => {
let (cval, cur) = lower_expr_with_vars(env, f, cur_bb, cond, vars)?;
let then_bb = next_block_id(f); let else_bb = BasicBlockId::new(then_bb.0 + 1); let merge_bb = BasicBlockId::new(then_bb.0 + 2);
f.add_block(BasicBlock::new(then_bb)); f.add_block(BasicBlock::new(else_bb)); f.add_block(BasicBlock::new(merge_bb));
if let Some(bb) = f.get_block_mut(cur) { bb.set_terminator(MirInstruction::Branch { condition: cval, then_bb, else_bb }); }
let base_vars = vars.clone(); let mut then_vars = base_vars.clone();
let tend = lower_stmt_list_with_vars(f, then_bb, then, &mut then_vars, loop_stack, env)?;
if let Some(bb) = f.get_block_mut(tend) { if !bb.is_terminated() { bb.set_terminator(MirInstruction::Jump { target: merge_bb }); } }
let then_bb = next_block_id(f);
let else_bb = BasicBlockId::new(then_bb.0 + 1);
let merge_bb = BasicBlockId::new(then_bb.0 + 2);
f.add_block(BasicBlock::new(then_bb));
f.add_block(BasicBlock::new(else_bb));
f.add_block(BasicBlock::new(merge_bb));
if let Some(bb) = f.get_block_mut(cur) {
bb.set_terminator(MirInstruction::Branch {
condition: cval,
then_bb,
else_bb,
});
}
let base_vars = vars.clone();
let mut then_vars = base_vars.clone();
let tend =
lower_stmt_list_with_vars(f, then_bb, then, &mut then_vars, loop_stack, env)?;
if let Some(bb) = f.get_block_mut(tend) {
if !bb.is_terminated() {
bb.set_terminator(MirInstruction::Jump { target: merge_bb });
}
}
let (else_end_pred, else_vars) = if let Some(elses) = r#else {
let mut ev = base_vars.clone();
let eend = lower_stmt_list_with_vars(f, else_bb, elses, &mut ev, loop_stack, env)?;
if let Some(bb) = f.get_block_mut(eend) { if !bb.is_terminated() { bb.set_terminator(MirInstruction::Jump { target: merge_bb }); } }
if let Some(bb) = f.get_block_mut(eend) {
if !bb.is_terminated() {
bb.set_terminator(MirInstruction::Jump { target: merge_bb });
}
}
(eend, ev)
} else {
if let Some(bb) = f.get_block_mut(else_bb) { bb.set_terminator(MirInstruction::Jump { target: merge_bb }); }
if let Some(bb) = f.get_block_mut(else_bb) {
bb.set_terminator(MirInstruction::Jump { target: merge_bb });
}
(else_bb, base_vars.clone())
};
use std::collections::HashSet;
let no_phi = env.mir_no_phi;
let mut names: HashSet<String> = base_vars.keys().cloned().collect(); for k in then_vars.keys() { names.insert(k.clone()); } for k in else_vars.keys() { names.insert(k.clone()); }
let mut names: HashSet<String> = base_vars.keys().cloned().collect();
for k in then_vars.keys() {
names.insert(k.clone());
}
for k in else_vars.keys() {
names.insert(k.clone());
}
for name in names {
let tv = then_vars.get(&name).copied(); let ev = else_vars.get(&name).copied(); let exists_base = base_vars.contains_key(&name);
let tv = then_vars.get(&name).copied();
let ev = else_vars.get(&name).copied();
let exists_base = base_vars.contains_key(&name);
match (tv, ev, exists_base) {
(Some(tval), Some(eval), _) => {
let merged = if tval == eval { tval } else { let dst = f.next_value_id(); if no_phi { if let Some(bb) = f.get_block_mut(tend) { bb.add_instruction(MirInstruction::Copy { dst, src: tval }); } if let Some(bb) = f.get_block_mut(else_end_pred) { bb.add_instruction(MirInstruction::Copy { dst, src: eval }); } } else if let Some(bb) = f.get_block_mut(merge_bb) { bb.insert_instruction_after_phis(MirInstruction::Phi { dst, inputs: vec![(tend, tval), (else_end_pred, eval)] }); } dst }; vars.insert(name, merged);
let merged = if tval == eval {
tval
} else {
let dst = f.next_value_id();
if no_phi {
if let Some(bb) = f.get_block_mut(tend) {
bb.add_instruction(MirInstruction::Copy { dst, src: tval });
}
if let Some(bb) = f.get_block_mut(else_end_pred) {
bb.add_instruction(MirInstruction::Copy { dst, src: eval });
}
} else if let Some(bb) = f.get_block_mut(merge_bb) {
bb.insert_instruction_after_phis(MirInstruction::Phi {
dst,
inputs: vec![(tend, tval), (else_end_pred, eval)],
});
}
dst
};
vars.insert(name, merged);
}
(Some(tval), None, true) => {
if let Some(&bval) = base_vars.get(&name) {
let merged = if tval == bval { tval } else { let dst = f.next_value_id(); if no_phi { if let Some(bb) = f.get_block_mut(tend) { bb.add_instruction(MirInstruction::Copy { dst, src: tval }); } if let Some(bb) = f.get_block_mut(else_end_pred) { bb.add_instruction(MirInstruction::Copy { dst, src: bval }); } } else if let Some(bb) = f.get_block_mut(merge_bb) { bb.insert_instruction_after_phis(MirInstruction::Phi { dst, inputs: vec![(tend, tval), (else_end_pred, bval)] }); } dst }; vars.insert(name, merged);
let merged = if tval == bval {
tval
} else {
let dst = f.next_value_id();
if no_phi {
if let Some(bb) = f.get_block_mut(tend) {
bb.add_instruction(MirInstruction::Copy { dst, src: tval });
}
if let Some(bb) = f.get_block_mut(else_end_pred) {
bb.add_instruction(MirInstruction::Copy { dst, src: bval });
}
} else if let Some(bb) = f.get_block_mut(merge_bb) {
bb.insert_instruction_after_phis(MirInstruction::Phi {
dst,
inputs: vec![(tend, tval), (else_end_pred, bval)],
});
}
dst
};
vars.insert(name, merged);
}
}
(None, Some(eval), true) => {
if let Some(&bval) = base_vars.get(&name) {
let merged = if eval == bval { eval } else { let dst = f.next_value_id(); if no_phi { if let Some(bb) = f.get_block_mut(tend) { bb.add_instruction(MirInstruction::Copy { dst, src: bval }); } if let Some(bb) = f.get_block_mut(else_end_pred) { bb.add_instruction(MirInstruction::Copy { dst, src: eval }); } } else if let Some(bb) = f.get_block_mut(merge_bb) { bb.insert_instruction_after_phis(MirInstruction::Phi { dst, inputs: vec![(tend, bval), (else_end_pred, eval)] }); } dst }; vars.insert(name, merged);
let merged = if eval == bval {
eval
} else {
let dst = f.next_value_id();
if no_phi {
if let Some(bb) = f.get_block_mut(tend) {
bb.add_instruction(MirInstruction::Copy { dst, src: bval });
}
if let Some(bb) = f.get_block_mut(else_end_pred) {
bb.add_instruction(MirInstruction::Copy { dst, src: eval });
}
} else if let Some(bb) = f.get_block_mut(merge_bb) {
bb.insert_instruction_after_phis(MirInstruction::Phi {
dst,
inputs: vec![(tend, bval), (else_end_pred, eval)],
});
}
dst
};
vars.insert(name, merged);
}
}
_ => {}
@ -508,37 +890,90 @@ fn lower_stmt_with_vars(
Ok(merge_bb)
}
StmtV0::Loop { cond, body } => {
let cond_bb = next_block_id(f); let body_bb = BasicBlockId::new(cond_bb.0 + 1); let exit_bb = BasicBlockId::new(cond_bb.0 + 2);
f.add_block(BasicBlock::new(cond_bb)); f.add_block(BasicBlock::new(body_bb)); f.add_block(BasicBlock::new(exit_bb));
if let Some(bb) = f.get_block_mut(cur_bb) { if !bb.is_terminated() { bb.add_instruction(MirInstruction::Jump { target: cond_bb }); } }
let cond_bb = next_block_id(f);
let body_bb = BasicBlockId::new(cond_bb.0 + 1);
let exit_bb = BasicBlockId::new(cond_bb.0 + 2);
f.add_block(BasicBlock::new(cond_bb));
f.add_block(BasicBlock::new(body_bb));
f.add_block(BasicBlock::new(exit_bb));
if let Some(bb) = f.get_block_mut(cur_bb) {
if !bb.is_terminated() {
bb.add_instruction(MirInstruction::Jump { target: cond_bb });
}
}
let no_phi = env.mir_no_phi;
let base_vars = vars.clone(); let orig_names: Vec<String> = base_vars.keys().cloned().collect();
let base_vars = vars.clone();
let orig_names: Vec<String> = base_vars.keys().cloned().collect();
let mut phi_map: HashMap<String, ValueId> = HashMap::new();
for name in &orig_names {
if let Some(&bval) = base_vars.get(name) {
let dst = f.next_value_id();
if no_phi {
if let Some(bb) = f.get_block_mut(cur_bb) { bb.add_instruction(MirInstruction::Copy { dst, src: bval }); }
} else if let Some(bb) = f.get_block_mut(cond_bb) { bb.insert_instruction_after_phis(MirInstruction::Phi { dst, inputs: vec![(cur_bb, bval)] }); }
if let Some(bb) = f.get_block_mut(cur_bb) {
bb.add_instruction(MirInstruction::Copy { dst, src: bval });
}
} else if let Some(bb) = f.get_block_mut(cond_bb) {
bb.insert_instruction_after_phis(MirInstruction::Phi {
dst,
inputs: vec![(cur_bb, bval)],
});
}
phi_map.insert(name.clone(), dst);
}
}
for (name, &phi) in &phi_map { vars.insert(name.clone(), phi); }
for (name, &phi) in &phi_map {
vars.insert(name.clone(), phi);
}
let (cval, _cend) = lower_expr_with_vars(env, f, cond_bb, cond, vars)?;
if let Some(bb) = f.get_block_mut(cond_bb) { bb.set_terminator(MirInstruction::Branch { condition: cval, then_bb: body_bb, else_bb: exit_bb }); }
let mut body_vars = vars.clone(); loop_stack.push(LoopContext { cond_bb, exit_bb });
let bend_res = lower_stmt_list_with_vars(f, body_bb, body, &mut body_vars, loop_stack, env); loop_stack.pop();
if let Some(bb) = f.get_block_mut(cond_bb) {
bb.set_terminator(MirInstruction::Branch {
condition: cval,
then_bb: body_bb,
else_bb: exit_bb,
});
}
let mut body_vars = vars.clone();
loop_stack.push(LoopContext { cond_bb, exit_bb });
let bend_res =
lower_stmt_list_with_vars(f, body_bb, body, &mut body_vars, loop_stack, env);
loop_stack.pop();
let bend = bend_res?;
if let Some(bb) = f.get_block_mut(bend) { if !bb.is_terminated() { bb.set_terminator(MirInstruction::Jump { target: cond_bb }); } }
if let Some(bb) = f.get_block_mut(bend) {
if !bb.is_terminated() {
bb.set_terminator(MirInstruction::Jump { target: cond_bb });
}
}
let backedge_to_cond = matches!(f.blocks.get(&bend).and_then(|bb| bb.terminator.as_ref()), Some(MirInstruction::Jump { target, .. }) if *target == cond_bb);
if backedge_to_cond {
if no_phi {
for (name, &phi_dst) in &phi_map { if let Some(&latch_val) = body_vars.get(name) { if let Some(bb) = f.get_block_mut(bend) { bb.add_instruction(MirInstruction::Copy { dst: phi_dst, src: latch_val }); } } }
for (name, &phi_dst) in &phi_map {
if let Some(&latch_val) = body_vars.get(name) {
if let Some(bb) = f.get_block_mut(bend) {
bb.add_instruction(MirInstruction::Copy {
dst: phi_dst,
src: latch_val,
});
}
}
}
} else if let Some(bb) = f.get_block_mut(cond_bb) {
for (name, &phi_dst) in &phi_map { if let Some(&latch_val) = body_vars.get(name) { for inst in &mut bb.instructions { if let MirInstruction::Phi { dst, inputs } = inst { if *dst == phi_dst { inputs.push((bend, latch_val)); break; } } } } }
for (name, &phi_dst) in &phi_map {
if let Some(&latch_val) = body_vars.get(name) {
for inst in &mut bb.instructions {
if let MirInstruction::Phi { dst, inputs } = inst {
if *dst == phi_dst {
inputs.push((bend, latch_val));
break;
}
}
}
}
}
}
}
for (name, &phi) in &phi_map { vars.insert(name.clone(), phi); }
for (name, &phi) in &phi_map {
vars.insert(name.clone(), phi);
}
Ok(exit_bb)
}
}
@ -555,29 +990,53 @@ fn lower_stmt_list_with_vars(
let mut cur = start_bb;
for s in stmts {
cur = lower_stmt_with_vars(f, cur, s, vars, loop_stack, env)?;
if let Some(bb) = f.blocks.get(&cur) { if bb.is_terminated() { break; } }
if let Some(bb) = f.blocks.get(&cur) {
if bb.is_terminated() {
break;
}
}
}
Ok(cur)
}
pub(super) fn lower_program(prog: ProgramV0) -> Result<MirModule, String> {
if prog.body.is_empty() { return Err("empty body".into()); }
if prog.body.is_empty() {
return Err("empty body".into());
}
let env = BridgeEnv::load();
let mut module = MirModule::new("ny_json_v0".into());
let sig = FunctionSignature { name: "main".into(), params: vec![], return_type: MirType::Integer, effects: EffectMask::PURE };
let sig = FunctionSignature {
name: "main".into(),
params: vec![],
return_type: MirType::Integer,
effects: EffectMask::PURE,
};
let entry = BasicBlockId::new(0);
let mut f = MirFunction::new(sig, entry);
let mut var_map: HashMap<String, ValueId> = HashMap::new();
let mut loop_stack: Vec<LoopContext> = Vec::new();
let start_bb = f.entry_block;
let end_bb = lower_stmt_list_with_vars(&mut f, start_bb, &prog.body, &mut var_map, &mut loop_stack, &env)?;
let end_bb = lower_stmt_list_with_vars(
&mut f,
start_bb,
&prog.body,
&mut var_map,
&mut loop_stack,
&env,
)?;
let need_default_ret = f.blocks.iter().any(|(_k, b)| !b.is_terminated());
if need_default_ret {
let target_bb = end_bb; let dst_id = f.next_value_id();
let target_bb = end_bb;
let dst_id = f.next_value_id();
if let Some(bb) = f.get_block_mut(target_bb) {
if !bb.is_terminated() {
bb.add_instruction(MirInstruction::Const { dst: dst_id, value: ConstValue::Integer(0) });
bb.set_terminator(MirInstruction::Return { value: Some(dst_id) });
bb.add_instruction(MirInstruction::Const {
dst: dst_id,
value: ConstValue::Integer(0),
});
bb.set_terminator(MirInstruction::Return {
value: Some(dst_id),
});
}
}
}
@ -592,4 +1051,3 @@ pub(super) fn maybe_dump_mir(module: &MirModule) {
println!("{}", p.print_module(module));
}
}

View File

@ -1,12 +1,13 @@
mod ast;
mod lowering;
mod lexer;
mod lowering;
use ast::ProgramV0;
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))?;
let prog: ProgramV0 =
serde_json::from_str(json).map_err(|e| format!("invalid JSON v0: {}", e))?;
if prog.version != 0 || prog.kind != "Program" {
return Err("unsupported IR: expected {version:0, kind:\"Program\"}".into());
}
@ -21,4 +22,6 @@ pub fn parse_source_v0_to_module(input: &str) -> Result<crate::mir::MirModule, S
parse_json_v0_to_module(&json)
}
pub fn maybe_dump_mir(module: &crate::mir::MirModule) { lowering::maybe_dump_mir(module) }
pub fn maybe_dump_mir(module: &crate::mir::MirModule) {
lowering::maybe_dump_mir(module)
}

View File

@ -7,7 +7,7 @@ pub fn emit_mir_json_for_harness(
module: &nyash_rust::mir::MirModule,
path: &std::path::Path,
) -> Result<(), String> {
use nyash_rust::mir::{MirInstruction as I, BinaryOp as B, CompareOp as C, MirType};
use nyash_rust::mir::{BinaryOp as B, CompareOp as C, MirInstruction as I, MirType};
let mut funs = Vec::new();
for (name, f) in &module.functions {
let mut blocks = Vec::new();
@ -24,20 +24,23 @@ pub fn emit_mir_json_for_harness(
.map(|(b, v)| json!([v.as_u32(), b.as_u32()]))
.collect();
// dst_type hint: if all incoming values are String-ish, annotate result as String handle
let all_str = inputs.iter().all(|(_b, v)| {
match f.metadata.value_types.get(v) {
Some(MirType::String) => true,
Some(MirType::Box(bt)) if bt == "StringBox" => true,
_ => false,
}
});
let all_str =
inputs
.iter()
.all(|(_b, v)| match f.metadata.value_types.get(v) {
Some(MirType::String) => true,
Some(MirType::Box(bt)) if bt == "StringBox" => true,
_ => false,
});
if all_str {
insts.push(json!({
"op":"phi","dst": dst.as_u32(), "incoming": incoming,
"dst_type": {"kind":"handle","box_type":"StringBox"}
}));
} else {
insts.push(json!({"op":"phi","dst": dst.as_u32(), "incoming": incoming}));
insts.push(
json!({"op":"phi","dst": dst.as_u32(), "incoming": incoming}),
);
}
}
}
@ -45,7 +48,11 @@ pub fn emit_mir_json_for_harness(
for inst in &bb.instructions {
match inst {
I::UnaryOp { dst, op, operand } => {
let kind = match op { nyash_rust::mir::UnaryOp::Neg => "neg", nyash_rust::mir::UnaryOp::Not => "not", nyash_rust::mir::UnaryOp::BitNot => "bitnot" };
let kind = match op {
nyash_rust::mir::UnaryOp::Neg => "neg",
nyash_rust::mir::UnaryOp::Not => "not",
nyash_rust::mir::UnaryOp::BitNot => "bitnot",
};
insts.push(json!({"op":"unop","kind": kind, "src": operand.as_u32(), "dst": dst.as_u32()}));
}
I::Const { dst, value } => {
@ -70,13 +77,27 @@ pub fn emit_mir_json_for_harness(
}
}));
}
nyash_rust::mir::ConstValue::Null | nyash_rust::mir::ConstValue::Void => {
nyash_rust::mir::ConstValue::Null
| nyash_rust::mir::ConstValue::Void => {
insts.push(json!({"op":"const","dst": dst.as_u32(), "value": {"type": "void", "value": 0}}));
}
}
}
I::BinOp { dst, op, lhs, rhs } => {
let op_s = match op { B::Add=>"+",B::Sub=>"-",B::Mul=>"*",B::Div=>"/",B::Mod=>"%",B::BitAnd=>"&",B::BitOr=>"|",B::BitXor=>"^",B::Shl=>"<<",B::Shr=>">>",B::And=>"&",B::Or=>"|"};
let op_s = match op {
B::Add => "+",
B::Sub => "-",
B::Mul => "*",
B::Div => "/",
B::Mod => "%",
B::BitAnd => "&",
B::BitOr => "|",
B::BitXor => "^",
B::Shl => "<<",
B::Shr => ">>",
B::And => "&",
B::Or => "|",
};
let mut obj = json!({"op":"binop","operation": op_s, "lhs": lhs.as_u32(), "rhs": rhs.as_u32(), "dst": dst.as_u32()});
// dst_type hint for string concatenation: if either side is String-ish and op is '+', mark result as String handle
if matches!(op, B::Add) {
@ -91,16 +112,24 @@ pub fn emit_mir_json_for_harness(
_ => false,
};
if lhs_is_str || rhs_is_str {
obj["dst_type"] = json!({"kind":"handle","box_type":"StringBox"});
obj["dst_type"] =
json!({"kind":"handle","box_type":"StringBox"});
}
}
insts.push(obj);
}
I::Compare { dst, op, lhs, rhs } => {
let op_s = match op { C::Lt=>"<", C::Le=>"<=", C::Gt=>">", C::Ge=>">=", C::Eq=>"==", C::Ne=>"!=" };
let op_s = match op {
C::Lt => "<",
C::Le => "<=",
C::Gt => ">",
C::Ge => ">=",
C::Eq => "==",
C::Ne => "!=",
};
let mut obj = json!({"op":"compare","operation": op_s, "lhs": lhs.as_u32(), "rhs": rhs.as_u32(), "dst": dst.as_u32()});
// cmp_kind hint for string equality
if matches!(op, C::Eq|C::Ne) {
if matches!(op, C::Eq | C::Ne) {
let lhs_is_str = match f.metadata.value_types.get(lhs) {
Some(MirType::String) => true,
Some(MirType::Box(bt)) if bt == "StringBox" => true,
@ -117,15 +146,25 @@ pub fn emit_mir_json_for_harness(
}
insts.push(obj);
}
I::Call { dst, func, args, .. } => {
I::Call {
dst, func, args, ..
} => {
let args_a: Vec<_> = args.iter().map(|v| json!(v.as_u32())).collect();
insts.push(json!({"op":"call","func": func.as_u32(), "args": args_a, "dst": dst.map(|d| d.as_u32())}));
}
I::ExternCall { dst, iface_name, method_name, args, .. } => {
I::ExternCall {
dst,
iface_name,
method_name,
args,
..
} => {
let args_a: Vec<_> = args.iter().map(|v| json!(v.as_u32())).collect();
let func_name = if iface_name == "env.console" {
format!("nyash.console.{}", method_name)
} else { format!("{}.{}", iface_name, method_name) };
} else {
format!("{}.{}", iface_name, method_name)
};
let mut obj = json!({
"op": "externcall",
"func": func_name,
@ -135,30 +174,55 @@ pub fn emit_mir_json_for_harness(
// Minimal dst_type hints for known externs
if iface_name == "env.console" {
// console.* returns i64 status (ignored by user code)
if dst.is_some() { obj["dst_type"] = json!("i64"); }
if dst.is_some() {
obj["dst_type"] = json!("i64");
}
}
insts.push(obj);
}
I::BoxCall { dst, box_val, method, args, .. } => {
I::BoxCall {
dst,
box_val,
method,
args,
..
} => {
let args_a: Vec<_> = args.iter().map(|v| json!(v.as_u32())).collect();
// Minimal dst_type hints
let mut obj = json!({
"op":"boxcall","box": box_val.as_u32(), "method": method, "args": args_a, "dst": dst.map(|d| d.as_u32())
});
let m = method.as_str();
let dst_ty = if m == "substring" || m == "dirname" || m == "join" || m == "read_all" || m == "read" {
let dst_ty = if m == "substring"
|| m == "dirname"
|| m == "join"
|| m == "read_all"
|| m == "read"
{
Some(json!({"kind":"handle","box_type":"StringBox"}))
} else if m == "length" || m == "lastIndexOf" {
Some(json!("i64"))
} else { None };
if let Some(t) = dst_ty { obj["dst_type"] = t; }
} else {
None
};
if let Some(t) = dst_ty {
obj["dst_type"] = t;
}
insts.push(obj);
}
I::NewBox { dst, box_type, args } => {
I::NewBox {
dst,
box_type,
args,
} => {
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()}));
}
I::Branch { condition, then_bb, else_bb } => {
I::Branch {
condition,
then_bb,
else_bb,
} => {
insts.push(json!({"op":"branch","cond": condition.as_u32(), "then": then_bb.as_u32(), "else": else_bb.as_u32()}));
}
I::Jump { target } => {
@ -195,7 +259,7 @@ pub fn emit_mir_json_for_harness_bin(
module: &crate::mir::MirModule,
path: &std::path::Path,
) -> Result<(), String> {
use crate::mir::{MirInstruction as I, BinaryOp as B, CompareOp as C, MirType};
use crate::mir::{BinaryOp as B, CompareOp as C, MirInstruction as I, MirType};
let mut funs = Vec::new();
for (name, f) in &module.functions {
let mut blocks = Vec::new();
@ -210,53 +274,67 @@ pub fn emit_mir_json_for_harness_bin(
.iter()
.map(|(b, v)| json!([v.as_u32(), b.as_u32()]))
.collect();
let all_str = inputs.iter().all(|(_b, v)| {
match f.metadata.value_types.get(v) {
Some(MirType::String) => true,
Some(MirType::Box(bt)) if bt == "StringBox" => true,
_ => false,
}
});
let all_str =
inputs
.iter()
.all(|(_b, v)| match f.metadata.value_types.get(v) {
Some(MirType::String) => true,
Some(MirType::Box(bt)) if bt == "StringBox" => true,
_ => false,
});
if all_str {
insts.push(json!({
"op":"phi","dst": dst.as_u32(), "incoming": incoming,
"dst_type": {"kind":"handle","box_type":"StringBox"}
}));
} else {
insts.push(json!({"op":"phi","dst": dst.as_u32(), "incoming": incoming}));
insts.push(
json!({"op":"phi","dst": dst.as_u32(), "incoming": incoming}),
);
}
}
}
for inst in &bb.instructions {
match inst {
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}}));
}
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}}));
}
},
I::BinOp { dst, op, lhs, rhs } => {
let op_s = match op { B::Add=>"+",B::Sub=>"-",B::Mul=>"*",B::Div=>"/",B::Mod=>"%",B::BitAnd=>"&",B::BitOr=>"|",B::BitXor=>"^",B::Shl=>"<<",B::Shr=>">>",B::And=>"&",B::Or=>"|"};
let op_s = match op {
B::Add => "+",
B::Sub => "-",
B::Mul => "*",
B::Div => "/",
B::Mod => "%",
B::BitAnd => "&",
B::BitOr => "|",
B::BitXor => "^",
B::Shl => "<<",
B::Shr => ">>",
B::And => "&",
B::Or => "|",
};
let mut obj = json!({"op":"binop","operation": op_s, "lhs": lhs.as_u32(), "rhs": rhs.as_u32(), "dst": dst.as_u32()});
if matches!(op, B::Add) {
let lhs_is_str = match f.metadata.value_types.get(lhs) {
@ -270,43 +348,84 @@ pub fn emit_mir_json_for_harness_bin(
_ => false,
};
if lhs_is_str || rhs_is_str {
obj["dst_type"] = json!({"kind":"handle","box_type":"StringBox"});
obj["dst_type"] =
json!({"kind":"handle","box_type":"StringBox"});
}
}
insts.push(obj);
}
I::Compare { dst, op, lhs, rhs } => {
let op_s = match op { C::Eq=>"==",C::Ne=>"!=",C::Lt=>"<",C::Le=>"<=",C::Gt=>">",C::Ge=>">=" };
let op_s = match op {
C::Eq => "==",
C::Ne => "!=",
C::Lt => "<",
C::Le => "<=",
C::Gt => ">",
C::Ge => ">=",
};
insts.push(json!({"op":"compare","operation": op_s, "lhs": lhs.as_u32(), "rhs": rhs.as_u32(), "dst": dst.as_u32()}));
}
I::ExternCall { dst, iface_name, method_name, args, .. } => {
I::ExternCall {
dst,
iface_name,
method_name,
args,
..
} => {
let args_a: Vec<_> = args.iter().map(|v| json!(v.as_u32())).collect();
let mut obj = json!({
"op":"externcall","func": format!("{}.{}", iface_name, method_name), "args": args_a,
"dst": dst.map(|d| d.as_u32()),
});
if iface_name == "env.console" { if dst.is_some() { obj["dst_type"] = json!("i64"); } }
if iface_name == "env.console" {
if dst.is_some() {
obj["dst_type"] = json!("i64");
}
}
insts.push(obj);
}
I::BoxCall { dst, box_val, method, args, .. } => {
I::BoxCall {
dst,
box_val,
method,
args,
..
} => {
let args_a: Vec<_> = args.iter().map(|v| json!(v.as_u32())).collect();
let mut obj = json!({
"op":"boxcall","box": box_val.as_u32(), "method": method, "args": args_a, "dst": dst.map(|d| d.as_u32())
});
let m = method.as_str();
let dst_ty = if m == "substring" || m == "dirname" || m == "join" || m == "read_all" || m == "read" {
let dst_ty = if m == "substring"
|| m == "dirname"
|| m == "join"
|| m == "read_all"
|| m == "read"
{
Some(json!({"kind":"handle","box_type":"StringBox"}))
} else if m == "length" || m == "lastIndexOf" {
Some(json!("i64"))
} else { None };
if let Some(t) = dst_ty { obj["dst_type"] = t; }
} else {
None
};
if let Some(t) = dst_ty {
obj["dst_type"] = t;
}
insts.push(obj);
}
I::NewBox { dst, box_type, args } => {
I::NewBox {
dst,
box_type,
args,
} => {
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()}));
}
I::Branch { condition, then_bb, else_bb } => {
I::Branch {
condition,
then_bb,
else_bb,
} => {
insts.push(json!({"op":"branch","cond": condition.as_u32(), "then": then_bb.as_u32(), "else": else_bb.as_u32()}));
}
I::Jump { target } => {
@ -315,14 +434,16 @@ pub fn emit_mir_json_for_harness_bin(
I::Return { value } => {
insts.push(json!({"op":"ret","value": value.map(|v| v.as_u32())}));
}
_ => { }
_ => {}
}
}
if let Some(term) = &bb.terminator { match term {
if let Some(term) = &bb.terminator {
match term {
I::Return { value } => insts.push(json!({"op":"ret","value": value.map(|v| v.as_u32())})),
I::Jump { target } => insts.push(json!({"op":"jump","target": target.as_u32()})),
I::Branch { condition, then_bb, else_bb } => insts.push(json!({"op":"branch","cond": condition.as_u32(), "then": then_bb.as_u32(), "else": else_bb.as_u32()})),
_ => {} } }
_ => {} }
}
blocks.push(json!({"id": bid.as_u32(), "instructions": insts}));
}
}

View File

@ -1,6 +1,6 @@
/*!
* Execution Runner Module - Nyash File and Mode Execution Coordinator
*
*
* This module handles all execution logic, backend selection, and mode coordination,
* separated from CLI parsing and the main entry point.
*/
@ -10,28 +10,28 @@ use nyash_rust::cli::CliConfig;
// pruned unused runtime imports in this module
#[cfg(feature = "wasm-backend")]
use nyash_rust::backend::{wasm::WasmBackend, aot::AotBackend};
use nyash_rust::backend::{aot::AotBackend, wasm::WasmBackend};
#[cfg(feature = "llvm-inkwell-legacy")]
use nyash_rust::backend::{llvm_compile_and_execute};
use nyash_rust::backend::llvm_compile_and_execute;
use std::{fs, process};
mod modes;
mod box_index;
mod build;
mod cli_directives;
mod demos;
mod dispatch;
mod json_v0_bridge;
mod mir_json_emit;
mod modes;
mod pipe_io;
mod pipeline;
mod cli_directives;
mod trace;
mod box_index;
mod tasks;
mod build;
mod dispatch;
mod selfhost;
mod tasks;
mod trace;
// v2 plugin system imports
use nyash_rust::runtime;
use nyash_rust::runner_plugin_init;
use nyash_rust::runtime;
// use std::path::PathBuf; // not used in current runner
/// Resolve a using target according to priority: modules > relative > using-paths
@ -68,16 +68,30 @@ impl NyashRunner {
// CLI --using SPEC entries (SPEC: 'ns', 'ns as Alias', '"path" as Alias')
for spec in &self.config.cli_usings {
let s = spec.trim();
if s.is_empty() { continue; }
if s.is_empty() {
continue;
}
let (target, alias) = if let Some(pos) = s.find(" as ") {
(s[..pos].trim().to_string(), Some(s[pos+4..].trim().to_string()))
} else { (s.to_string(), None) };
(
s[..pos].trim().to_string(),
Some(s[pos + 4..].trim().to_string()),
)
} else {
(s.to_string(), None)
};
// Normalize quotes for path
let is_path = target.starts_with('"') || target.starts_with("./") || target.starts_with('/') || target.ends_with(".nyash");
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()
std::path::Path::new(&path)
.file_stem()
.and_then(|s| s.to_str())
.unwrap_or("module")
.to_string()
});
pending_using.push((name, Some(path)));
} else {
@ -96,9 +110,16 @@ impl NyashRunner {
// Try to extract quick hints without failing
let mut root_info = String::new();
if let Ok(v) = serde_json::from_str::<serde_json::Value>(&s) {
if let Some(r) = v.get("root_path").and_then(|x| x.as_str()) { root_info = format!(" root='{}'", r); }
if let Some(r) = v.get("root_path").and_then(|x| x.as_str()) {
root_info = format!(" root='{}'", r);
}
}
eprintln!("[deps] loaded {} bytes from{} {}", bytes, if root_info.is_empty(){""} else {":"}, root_info);
eprintln!(
"[deps] loaded {} bytes from{} {}",
bytes,
if root_info.is_empty() { "" } else { ":" },
root_info
);
}
Err(e) => {
eprintln!("[deps] read error: {}", e);
@ -107,7 +128,9 @@ impl NyashRunner {
}
// Phase-15: JSON IR v0 bridge (stdin/file)
if self.try_run_json_v0_pipe() { return; }
if self.try_run_json_v0_pipe() {
return;
}
// Run named task from nyash.toml (MVP)
if let Some(task) = self.config.run_task.clone() {
if let Err(e) = run_named_task(&task) {
@ -117,7 +140,9 @@ impl NyashRunner {
return;
}
// Verbose CLI flag maps to env for downstream helpers/scripts
if self.config.cli_verbose { std::env::set_var("NYASH_CLI_VERBOSE", "1"); }
if self.config.cli_verbose {
std::env::set_var("NYASH_CLI_VERBOSE", "1");
}
// Script-level env directives (special comments) — parse early
// Supported:
// // @env KEY=VALUE
@ -126,8 +151,13 @@ impl NyashRunner {
if let Some(ref filename) = self.config.file {
if let Ok(code) = fs::read_to_string(filename) {
// Apply script-level directives and lint
let strict_fields = std::env::var("NYASH_FIELDS_TOP_STRICT").ok().as_deref() == Some("1");
if let Err(e) = cli_directives::apply_cli_directives_from_source(&code, strict_fields, self.config.cli_verbose) {
let strict_fields =
std::env::var("NYASH_FIELDS_TOP_STRICT").ok().as_deref() == Some("1");
if let Err(e) = cli_directives::apply_cli_directives_from_source(
&code,
strict_fields,
self.config.cli_verbose,
) {
eprintln!("❌ Lint/Directive error: {}", e);
std::process::exit(1);
}
@ -135,13 +165,23 @@ impl NyashRunner {
// Env overrides for using rules
// Merge late env overrides (if any)
if let Ok(paths) = std::env::var("NYASH_USING_PATH") {
for p in paths.split(':') { let p = p.trim(); if !p.is_empty() { using_ctx.using_paths.push(p.to_string()); } }
for p in paths.split(':') {
let p = p.trim();
if !p.is_empty() {
using_ctx.using_paths.push(p.to_string());
}
}
}
if let Ok(mods) = std::env::var("NYASH_MODULES") {
for ent in mods.split(',') {
if let Some((k,v)) = ent.split_once('=') {
let k = k.trim(); let v = v.trim();
if !k.is_empty() && !v.is_empty() { using_ctx.pending_modules.push((k.to_string(), v.to_string())); }
if let Some((k, v)) = ent.split_once('=') {
let k = k.trim();
let v = v.trim();
if !k.is_empty() && !v.is_empty() {
using_ctx
.pending_modules
.push((k.to_string(), v.to_string()));
}
}
}
}
@ -156,9 +196,21 @@ impl NyashRunner {
let verbose = std::env::var("NYASH_CLI_VERBOSE").ok().as_deref() == Some("1");
let ctx = std::path::Path::new(filename).parent();
for (ns, alias) in pending_using.iter() {
let value = match resolve_using_target(ns, false, &using_ctx.pending_modules, &using_ctx.using_paths, &using_ctx.aliases, ctx, strict, verbose) {
let value = match resolve_using_target(
ns,
false,
&using_ctx.pending_modules,
&using_ctx.using_paths,
&using_ctx.aliases,
ctx,
strict,
verbose,
) {
Ok(v) => v,
Err(e) => { eprintln!("❌ using: {}", e); std::process::exit(1); }
Err(e) => {
eprintln!("❌ using: {}", e);
std::process::exit(1);
}
};
let sb = nyash_rust::box_trait::StringBox::new(value.clone());
nyash_rust::runtime::modules_registry::set(ns.clone(), Box::new(sb));
@ -181,10 +233,10 @@ impl NyashRunner {
}
}
// 🏭 Phase 9.78b: Initialize unified registry
runtime::init_global_unified_registry();
// Try to initialize BID plugins from nyash.toml (best-effort)
// 🏭 Phase 9.78b: Initialize unified registry
runtime::init_global_unified_registry();
// Try to initialize BID plugins from nyash.toml (best-effort)
// Allow disabling during snapshot/CI via NYASH_DISABLE_PLUGINS=1
if std::env::var("NYASH_DISABLE_PLUGINS").ok().as_deref() != Some("1") {
runner_plugin_init::init_bid_plugins();
@ -197,29 +249,53 @@ impl NyashRunner {
std::env::set_var("NYASH_USE_PLUGIN_BUILTINS", "1");
}
// Merge FileBox,TOMLBox with defaults if present
let mut override_types: Vec<String> = if let Ok(list) = std::env::var("NYASH_PLUGIN_OVERRIDE_TYPES") {
list.split(',').map(|s| s.trim().to_string()).filter(|s| !s.is_empty()).collect()
} else {
vec!["ArrayBox".into(), "MapBox".into()]
};
for t in ["FileBox", "TOMLBox"] { if !override_types.iter().any(|x| x==t) { override_types.push(t.into()); } }
let mut override_types: Vec<String> =
if let Ok(list) = std::env::var("NYASH_PLUGIN_OVERRIDE_TYPES") {
list.split(',')
.map(|s| s.trim().to_string())
.filter(|s| !s.is_empty())
.collect()
} else {
vec!["ArrayBox".into(), "MapBox".into()]
};
for t in ["FileBox", "TOMLBox"] {
if !override_types.iter().any(|x| x == t) {
override_types.push(t.into());
}
}
std::env::set_var("NYASH_PLUGIN_OVERRIDE_TYPES", override_types.join(","));
// Opt-in: load Ny script plugins listed in nyash.toml [ny_plugins]
if self.config.load_ny_plugins || std::env::var("NYASH_LOAD_NY_PLUGINS").ok().as_deref() == Some("1") {
if self.config.load_ny_plugins
|| std::env::var("NYASH_LOAD_NY_PLUGINS").ok().as_deref() == Some("1")
{
if let Ok(text) = std::fs::read_to_string("nyash.toml") {
if let Ok(doc) = toml::from_str::<toml::Value>(&text) {
if let Some(np) = doc.get("ny_plugins") {
let mut list: Vec<String> = Vec::new();
if let Some(arr) = np.as_array() {
for v in arr { if let Some(s) = v.as_str() { list.push(s.to_string()); } }
for v in arr {
if let Some(s) = v.as_str() {
list.push(s.to_string());
}
}
} else if let Some(tbl) = np.as_table() {
for (_k, v) in tbl { if let Some(s) = v.as_str() { list.push(s.to_string()); }
else if let Some(arr) = v.as_array() { for e in arr { if let Some(s) = e.as_str() { list.push(s.to_string()); } } }
for (_k, v) in tbl {
if let Some(s) = v.as_str() {
list.push(s.to_string());
} else if let Some(arr) = v.as_array() {
for e in arr {
if let Some(s) = e.as_str() {
list.push(s.to_string());
}
}
}
}
}
if !list.is_empty() {
let list_only = std::env::var("NYASH_NY_PLUGINS_LIST_ONLY").ok().as_deref() == Some("1");
let list_only =
std::env::var("NYASH_NY_PLUGINS_LIST_ONLY").ok().as_deref()
== Some("1");
println!("🧩 Ny script plugins ({}):", list.len());
for p in list {
if list_only {
@ -229,13 +305,20 @@ impl NyashRunner {
// Execute each script best-effort via interpreter
match std::fs::read_to_string(&p) {
Ok(code) => {
match nyash_rust::parser::NyashParser::parse_from_string(&code) {
match nyash_rust::parser::NyashParser::parse_from_string(
&code,
) {
Ok(ast) => {
let mut interpreter = nyash_rust::interpreter::NyashInterpreter::new();
let mut interpreter =
nyash_rust::interpreter::NyashInterpreter::new(
);
match interpreter.execute(ast) {
Ok(_) => println!("[ny_plugins] {}: OK", p),
Err(e) => {
println!("[ny_plugins] {}: FAIL ({})", p, e);
println!(
"[ny_plugins] {}: FAIL ({})",
p, e
);
// continue to next
}
}
@ -267,16 +350,26 @@ impl NyashRunner {
// Optional: JIT controls via CLI flags (centralized)
{
// CLI opt-in for JSONL events
if self.config.jit_events { std::env::set_var("NYASH_JIT_EVENTS", "1"); }
if self.config.jit_events_compile { std::env::set_var("NYASH_JIT_EVENTS_COMPILE", "1"); }
if self.config.jit_events_runtime { std::env::set_var("NYASH_JIT_EVENTS_RUNTIME", "1"); }
if let Some(ref p) = self.config.jit_events_path { std::env::set_var("NYASH_JIT_EVENTS_PATH", p); }
if self.config.jit_events {
std::env::set_var("NYASH_JIT_EVENTS", "1");
}
if self.config.jit_events_compile {
std::env::set_var("NYASH_JIT_EVENTS_COMPILE", "1");
}
if self.config.jit_events_runtime {
std::env::set_var("NYASH_JIT_EVENTS_RUNTIME", "1");
}
if let Some(ref p) = self.config.jit_events_path {
std::env::set_var("NYASH_JIT_EVENTS_PATH", p);
}
let mut jc = nyash_rust::jit::config::JitConfig::from_env();
jc.exec |= self.config.jit_exec;
jc.stats |= self.config.jit_stats;
jc.stats_json |= self.config.jit_stats_json;
jc.dump |= self.config.jit_dump;
if self.config.jit_threshold.is_some() { jc.threshold = self.config.jit_threshold; }
if self.config.jit_threshold.is_some() {
jc.threshold = self.config.jit_threshold;
}
jc.phi_min |= self.config.jit_phi_min;
jc.hostcall |= self.config.jit_hostcall;
jc.handle_debug |= self.config.jit_handle_debug;
@ -286,8 +379,12 @@ impl NyashRunner {
let events_on = std::env::var("NYASH_JIT_EVENTS").ok().as_deref() == Some("1")
|| std::env::var("NYASH_JIT_EVENTS_COMPILE").ok().as_deref() == Some("1")
|| std::env::var("NYASH_JIT_EVENTS_RUNTIME").ok().as_deref() == Some("1");
if events_on && jc.threshold.is_none() { jc.threshold = Some(1); }
if self.config.jit_only { std::env::set_var("NYASH_JIT_ONLY", "1"); }
if events_on && jc.threshold.is_none() {
jc.threshold = Some(1);
}
if self.config.jit_only {
std::env::set_var("NYASH_JIT_ONLY", "1");
}
// Apply runtime capability probe (e.g., disable b1 ABI if unsupported)
let caps = nyash_rust::jit::config::probe_capabilities();
jc = nyash_rust::jit::config::apply_runtime_caps(jc, caps);
@ -316,7 +413,7 @@ impl NyashRunner {
println!("====================================");
println!("Running {} iterations per test...", self.config.iterations);
println!();
self.execute_benchmark_mode();
return;
}
@ -350,12 +447,13 @@ impl NyashRunner {
impl NyashRunner {
/// Run a file through independent JIT engine (no VM execute loop)
fn run_file_jit_direct(&self, filename: &str) {
use nyash_rust::{mir::MirCompiler, parser::NyashParser};
use std::fs;
use nyash_rust::{parser::NyashParser, mir::MirCompiler};
// Small helper for unified error output (text or JSON)
let emit_err = |phase: &str, code: &str, msg: &str| {
if std::env::var("NYASH_JIT_STATS_JSON").ok().as_deref() == Some("1")
|| std::env::var("NYASH_JIT_ERROR_JSON").ok().as_deref() == Some("1") {
|| std::env::var("NYASH_JIT_ERROR_JSON").ok().as_deref() == Some("1")
{
let payload = serde_json::json!({
"kind": "jit_direct_error",
"phase": phase,
@ -371,14 +469,33 @@ impl NyashRunner {
// Require cranelift feature at runtime by attempting compile; if unavailable compile_function returns None
let code = match fs::read_to_string(filename) {
Ok(s) => s,
Err(e) => { emit_err("read_file", "IO", &format!("{}", e)); std::process::exit(1); }
Err(e) => {
emit_err("read_file", "IO", &format!("{}", e));
std::process::exit(1);
}
};
let ast = match NyashParser::parse_from_string(&code) {
Ok(a) => a, Err(e) => { emit_err("parse", "SYNTAX", &format!("{}", e)); std::process::exit(1); }
Ok(a) => a,
Err(e) => {
emit_err("parse", "SYNTAX", &format!("{}", e));
std::process::exit(1);
}
};
let mut mc = MirCompiler::new();
let cr = match mc.compile(ast) { Ok(m) => m, Err(e) => { emit_err("mir", "MIR_COMPILE", &format!("{}", e)); std::process::exit(1); } };
let func = match cr.module.functions.get("main") { Some(f) => f, None => { emit_err("mir", "NO_MAIN", "No main function found"); std::process::exit(1); } };
let cr = match mc.compile(ast) {
Ok(m) => m,
Err(e) => {
emit_err("mir", "MIR_COMPILE", &format!("{}", e));
std::process::exit(1);
}
};
let func = match cr.module.functions.get("main") {
Some(f) => f,
None => {
emit_err("mir", "NO_MAIN", "No main function found");
std::process::exit(1);
}
};
// Guard: refuse write-effects in jit-direct when policy.read_only
{
@ -393,11 +510,20 @@ impl NyashRunner {
}
}
if let Some(term) = &bb.terminator {
if term.effects().contains(Effect::WriteHeap) { writes += 1; }
if term.effects().contains(Effect::WriteHeap) {
writes += 1;
}
}
}
if policy.read_only && writes > 0 {
emit_err("policy", "WRITE_EFFECTS", &format!("write-effects detected ({} ops). jit-direct is read-only at this stage.", writes));
emit_err(
"policy",
"WRITE_EFFECTS",
&format!(
"write-effects detected ({} ops). jit-direct is read-only at this stage.",
writes
),
);
std::process::exit(1);
}
}
@ -417,63 +543,141 @@ impl NyashRunner {
match engine.compile_function("main", func) {
Some(h) => {
// Optional event: compile
nyash_rust::jit::events::emit("compile", &func.signature.name, Some(h), None, serde_json::json!({}));
nyash_rust::jit::events::emit(
"compile",
&func.signature.name,
Some(h),
None,
serde_json::json!({}),
);
// Parse JIT args from env: NYASH_JIT_ARGS (comma-separated), with optional type prefixes
// Formats per arg: i:123, f:3.14, b:true/false, h:42 (handle), or bare numbers (int), true/false (bool)
let mut jit_args: Vec<nyash_rust::jit::abi::JitValue> = Vec::new();
if let Ok(s) = std::env::var("NYASH_JIT_ARGS") {
for raw in s.split(',') {
let t = raw.trim();
if t.is_empty() { continue; }
if t.is_empty() {
continue;
}
let v = if let Some(rest) = t.strip_prefix("i:") {
rest.parse::<i64>().ok().map(nyash_rust::jit::abi::JitValue::I64)
rest.parse::<i64>()
.ok()
.map(nyash_rust::jit::abi::JitValue::I64)
} else if let Some(rest) = t.strip_prefix("f:") {
rest.parse::<f64>().ok().map(nyash_rust::jit::abi::JitValue::F64)
rest.parse::<f64>()
.ok()
.map(nyash_rust::jit::abi::JitValue::F64)
} else if let Some(rest) = t.strip_prefix("b:") {
let b = matches!(rest, "1"|"true"|"True"|"TRUE");
let b = matches!(rest, "1" | "true" | "True" | "TRUE");
Some(nyash_rust::jit::abi::JitValue::Bool(b))
} else if let Some(rest) = t.strip_prefix("h:") {
rest.parse::<u64>().ok().map(nyash_rust::jit::abi::JitValue::Handle)
} else if t.eq_ignore_ascii_case("true") || t == "1" { Some(nyash_rust::jit::abi::JitValue::Bool(true)) }
else if t.eq_ignore_ascii_case("false") || t == "0" { Some(nyash_rust::jit::abi::JitValue::Bool(false)) }
else if let Ok(iv) = t.parse::<i64>() { Some(nyash_rust::jit::abi::JitValue::I64(iv)) }
else if let Ok(fv) = t.parse::<f64>() { Some(nyash_rust::jit::abi::JitValue::F64(fv)) }
else { None };
if let Some(jv) = v { jit_args.push(jv); }
rest.parse::<u64>()
.ok()
.map(nyash_rust::jit::abi::JitValue::Handle)
} else if t.eq_ignore_ascii_case("true") || t == "1" {
Some(nyash_rust::jit::abi::JitValue::Bool(true))
} else if t.eq_ignore_ascii_case("false") || t == "0" {
Some(nyash_rust::jit::abi::JitValue::Bool(false))
} else if let Ok(iv) = t.parse::<i64>() {
Some(nyash_rust::jit::abi::JitValue::I64(iv))
} else if let Ok(fv) = t.parse::<f64>() {
Some(nyash_rust::jit::abi::JitValue::F64(fv))
} else {
None
};
if let Some(jv) = v {
jit_args.push(jv);
}
}
}
// Coerce args to expected MIR types
use nyash_rust::mir::MirType;
let expected = &func.signature.params;
if expected.len() != jit_args.len() {
emit_err("args", "COUNT_MISMATCH", &format!("expected={}, passed={}", expected.len(), jit_args.len()));
emit_err(
"args",
"COUNT_MISMATCH",
&format!("expected={}, passed={}", expected.len(), jit_args.len()),
);
eprintln!("Hint: set NYASH_JIT_ARGS as comma-separated values, e.g., i:42,f:3.14,b:true");
std::process::exit(1);
}
let mut coerced: Vec<nyash_rust::jit::abi::JitValue> = Vec::with_capacity(jit_args.len());
let mut coerced: Vec<nyash_rust::jit::abi::JitValue> =
Vec::with_capacity(jit_args.len());
for (i, (exp, got)) in expected.iter().zip(jit_args.iter()).enumerate() {
let cv = match exp {
MirType::Integer => match got {
nyash_rust::jit::abi::JitValue::I64(v) => nyash_rust::jit::abi::JitValue::I64(*v),
nyash_rust::jit::abi::JitValue::F64(f) => nyash_rust::jit::abi::JitValue::I64(*f as i64),
nyash_rust::jit::abi::JitValue::Bool(b) => nyash_rust::jit::abi::JitValue::I64(if *b {1} else {0}),
_ => { emit_err("args", "TYPE_MISMATCH", &format!("param#{} expects Integer", i)); std::process::exit(1); }
nyash_rust::jit::abi::JitValue::I64(v) => {
nyash_rust::jit::abi::JitValue::I64(*v)
}
nyash_rust::jit::abi::JitValue::F64(f) => {
nyash_rust::jit::abi::JitValue::I64(*f as i64)
}
nyash_rust::jit::abi::JitValue::Bool(b) => {
nyash_rust::jit::abi::JitValue::I64(if *b { 1 } else { 0 })
}
_ => {
emit_err(
"args",
"TYPE_MISMATCH",
&format!("param#{} expects Integer", i),
);
std::process::exit(1);
}
},
MirType::Float => match got {
nyash_rust::jit::abi::JitValue::F64(f) => nyash_rust::jit::abi::JitValue::F64(*f),
nyash_rust::jit::abi::JitValue::I64(v) => nyash_rust::jit::abi::JitValue::F64(*v as f64),
nyash_rust::jit::abi::JitValue::Bool(b) => nyash_rust::jit::abi::JitValue::F64(if *b {1.0} else {0.0}),
_ => { emit_err("args", "TYPE_MISMATCH", &format!("param#{} expects Float", i)); std::process::exit(1); }
nyash_rust::jit::abi::JitValue::F64(f) => {
nyash_rust::jit::abi::JitValue::F64(*f)
}
nyash_rust::jit::abi::JitValue::I64(v) => {
nyash_rust::jit::abi::JitValue::F64(*v as f64)
}
nyash_rust::jit::abi::JitValue::Bool(b) => {
nyash_rust::jit::abi::JitValue::F64(if *b { 1.0 } else { 0.0 })
}
_ => {
emit_err(
"args",
"TYPE_MISMATCH",
&format!("param#{} expects Float", i),
);
std::process::exit(1);
}
},
MirType::Bool => match got {
nyash_rust::jit::abi::JitValue::Bool(b) => nyash_rust::jit::abi::JitValue::Bool(*b),
nyash_rust::jit::abi::JitValue::I64(v) => nyash_rust::jit::abi::JitValue::Bool(*v != 0),
nyash_rust::jit::abi::JitValue::F64(f) => nyash_rust::jit::abi::JitValue::Bool(*f != 0.0),
_ => { emit_err("args", "TYPE_MISMATCH", &format!("param#{} expects Bool", i)); std::process::exit(1); }
nyash_rust::jit::abi::JitValue::Bool(b) => {
nyash_rust::jit::abi::JitValue::Bool(*b)
}
nyash_rust::jit::abi::JitValue::I64(v) => {
nyash_rust::jit::abi::JitValue::Bool(*v != 0)
}
nyash_rust::jit::abi::JitValue::F64(f) => {
nyash_rust::jit::abi::JitValue::Bool(*f != 0.0)
}
_ => {
emit_err(
"args",
"TYPE_MISMATCH",
&format!("param#{} expects Bool", i),
);
std::process::exit(1);
}
},
MirType::String | MirType::Box(_) | MirType::Array(_) | MirType::Future(_) => match got {
nyash_rust::jit::abi::JitValue::Handle(h) => nyash_rust::jit::abi::JitValue::Handle(*h),
_ => { emit_err("args", "TYPE_MISMATCH", &format!("param#{} expects handle (h:<id>)", i)); std::process::exit(1); }
MirType::String
| MirType::Box(_)
| MirType::Array(_)
| MirType::Future(_) => match got {
nyash_rust::jit::abi::JitValue::Handle(h) => {
nyash_rust::jit::abi::JitValue::Handle(*h)
}
_ => {
emit_err(
"args",
"TYPE_MISMATCH",
&format!("param#{} expects handle (h:<id>)", i),
);
std::process::exit(1);
}
},
MirType::Void | MirType::Unknown => {
// Keep as-is
@ -488,32 +692,66 @@ impl NyashRunner {
match out {
Some(v) => {
let ms = t0.elapsed().as_millis();
nyash_rust::jit::events::emit("execute", &func.signature.name, Some(h), Some(ms), serde_json::json!({}));
nyash_rust::jit::events::emit(
"execute",
&func.signature.name,
Some(h),
Some(ms),
serde_json::json!({}),
);
// Normalize result according to MIR return type for friendly output
use nyash_rust::mir::MirType;
let ret_ty = &func.signature.return_type;
let vmv = match (ret_ty, v) {
(MirType::Bool, nyash_rust::jit::abi::JitValue::I64(i)) => nyash_rust::backend::vm::VMValue::Bool(i != 0),
(MirType::Bool, nyash_rust::jit::abi::JitValue::Bool(b)) => nyash_rust::backend::vm::VMValue::Bool(b),
(MirType::Float, nyash_rust::jit::abi::JitValue::F64(f)) => nyash_rust::backend::vm::VMValue::Float(f),
(MirType::Float, nyash_rust::jit::abi::JitValue::I64(i)) => nyash_rust::backend::vm::VMValue::Float(i as f64),
(MirType::Bool, nyash_rust::jit::abi::JitValue::I64(i)) => {
nyash_rust::backend::vm::VMValue::Bool(i != 0)
}
(MirType::Bool, nyash_rust::jit::abi::JitValue::Bool(b)) => {
nyash_rust::backend::vm::VMValue::Bool(b)
}
(MirType::Float, nyash_rust::jit::abi::JitValue::F64(f)) => {
nyash_rust::backend::vm::VMValue::Float(f)
}
(MirType::Float, nyash_rust::jit::abi::JitValue::I64(i)) => {
nyash_rust::backend::vm::VMValue::Float(i as f64)
}
// Default adapter for other combos
_ => nyash_rust::jit::abi::adapter::from_jit_value(v),
};
println!("✅ JIT-direct execution completed successfully!");
// Pretty print with expected type tag
let (ety, sval) = match (ret_ty, &vmv) {
(MirType::Bool, nyash_rust::backend::vm::VMValue::Bool(b)) => ("Bool", b.to_string()),
(MirType::Float, nyash_rust::backend::vm::VMValue::Float(f)) => ("Float", format!("{}", f)),
(MirType::Integer, nyash_rust::backend::vm::VMValue::Integer(i)) => ("Integer", i.to_string()),
(MirType::Bool, nyash_rust::backend::vm::VMValue::Bool(b)) => {
("Bool", b.to_string())
}
(MirType::Float, nyash_rust::backend::vm::VMValue::Float(f)) => {
("Float", format!("{}", f))
}
(MirType::Integer, nyash_rust::backend::vm::VMValue::Integer(i)) => {
("Integer", i.to_string())
}
// Fallbacks
(_, nyash_rust::backend::vm::VMValue::Integer(i)) => ("Integer", i.to_string()),
(_, nyash_rust::backend::vm::VMValue::Float(f)) => ("Float", format!("{}", f)),
(_, nyash_rust::backend::vm::VMValue::Bool(b)) => ("Bool", b.to_string()),
(_, nyash_rust::backend::vm::VMValue::String(s)) => ("String", s.clone()),
(_, nyash_rust::backend::vm::VMValue::BoxRef(arc)) => ("BoxRef", arc.type_name().to_string()),
(_, nyash_rust::backend::vm::VMValue::Future(_)) => ("Future", "<future>".to_string()),
(_, nyash_rust::backend::vm::VMValue::Void) => ("Void", "void".to_string()),
(_, nyash_rust::backend::vm::VMValue::Integer(i)) => {
("Integer", i.to_string())
}
(_, nyash_rust::backend::vm::VMValue::Float(f)) => {
("Float", format!("{}", f))
}
(_, nyash_rust::backend::vm::VMValue::Bool(b)) => {
("Bool", b.to_string())
}
(_, nyash_rust::backend::vm::VMValue::String(s)) => {
("String", s.clone())
}
(_, nyash_rust::backend::vm::VMValue::BoxRef(arc)) => {
("BoxRef", arc.type_name().to_string())
}
(_, nyash_rust::backend::vm::VMValue::Future(_)) => {
("Future", "<future>".to_string())
}
(_, nyash_rust::backend::vm::VMValue::Void) => {
("Void", "void".to_string())
}
};
println!("ResultType(MIR): {}", ety);
println!("Result: {}", sval);
@ -522,7 +760,11 @@ impl NyashRunner {
let cfg = nyash_rust::jit::config::current();
let caps = nyash_rust::jit::config::probe_capabilities();
let (phi_t, phi_b1, ret_b) = engine.last_lower_stats();
let abi_mode = if cfg.native_bool_abi && caps.supports_b1_sig { "b1_bool" } else { "i64_bool" };
let abi_mode = if cfg.native_bool_abi && caps.supports_b1_sig {
"b1_bool"
} else {
"i64_bool"
};
let payload = serde_json::json!({
"version": 1,
"function": func.signature.name,
@ -539,14 +781,28 @@ impl NyashRunner {
}
}
None => {
nyash_rust::jit::events::emit("fallback", &func.signature.name, Some(h), None, serde_json::json!({"reason":"trap_or_missing"}));
emit_err("execute", "TRAP_OR_MISSING", "execution failed (trap or missing handle)");
nyash_rust::jit::events::emit(
"fallback",
&func.signature.name,
Some(h),
None,
serde_json::json!({"reason":"trap_or_missing"}),
);
emit_err(
"execute",
"TRAP_OR_MISSING",
"execution failed (trap or missing handle)",
);
std::process::exit(1);
}
}
}
None => {
emit_err("compile", "UNAVAILABLE", "Build with --features cranelift-jit");
emit_err(
"compile",
"UNAVAILABLE",
"Build with --features cranelift-jit",
);
std::process::exit(1);
}
}

View File

@ -1,6 +1,6 @@
use super::super::NyashRunner;
#[cfg(feature = "cranelift-jit")]
use std::{process::Command, process};
use std::{process, process::Command};
impl NyashRunner {
/// Execute AOT compilation mode (split)
@ -11,7 +11,16 @@ impl NyashRunner {
let status = if cfg!(target_os = "windows") {
// Use PowerShell helper; falls back to bash if available inside the script
Command::new("powershell")
.args(["-ExecutionPolicy","Bypass","-File","tools/build_aot.ps1","-Input", filename, "-Out", &format!("{}.exe", output)])
.args([
"-ExecutionPolicy",
"Bypass",
"-File",
"tools/build_aot.ps1",
"-Input",
filename,
"-Out",
&format!("{}.exe", output),
])
.status()
} else {
Command::new("bash")
@ -20,15 +29,24 @@ impl NyashRunner {
};
match status {
Ok(s) if s.success() => {
println!("✅ AOT compilation successful!\nExecutable written to: {}", output);
println!(
"✅ AOT compilation successful!\nExecutable written to: {}",
output
);
}
Ok(s) => {
eprintln!("❌ AOT compilation failed (exit={} ). See logs above.", s.code().unwrap_or(-1));
eprintln!(
"❌ AOT compilation failed (exit={} ). See logs above.",
s.code().unwrap_or(-1)
);
process::exit(1);
}
Err(e) => {
eprintln!("❌ Failed to invoke build_aot.sh: {}", e);
eprintln!("Hint: ensure bash is available, or run: bash tools/build_aot.sh {} -o {}", filename, output);
eprintln!(
"Hint: ensure bash is available, or run: bash tools/build_aot.sh {} -o {}",
filename, output
);
process::exit(1);
}
}

View File

@ -1,10 +1,15 @@
use super::super::NyashRunner;
use nyash_rust::{parser::NyashParser, interpreter::NyashInterpreter, mir::MirCompiler, backend::VM};
use nyash_rust::{
backend::VM, interpreter::NyashInterpreter, mir::MirCompiler, parser::NyashParser,
};
impl NyashRunner {
/// Execute benchmark mode (split)
pub(crate) fn execute_benchmark_mode(&self) {
println!("🏁 Running benchmark mode with {} iterations", self.config.iterations);
println!(
"🏁 Running benchmark mode with {} iterations",
self.config.iterations
);
// Tests: some run on all backends, some are JIT+f64 only
// Third element indicates JIT+f64 only (skip VM/Interpreter)
let tests: Vec<(&str, &str, bool)> = vec![
@ -63,14 +68,21 @@ impl NyashRunner {
println!("\n====================================");
println!("🧪 Test: {}", name);
if jit_f64_only {
println!("(JIT+f64 only) Skipping VM/Interpreter; requires --features cranelift-jit");
println!(
"(JIT+f64 only) Skipping VM/Interpreter; requires --features cranelift-jit"
);
// Warmup JIT
let warmup = (self.config.iterations / 10).max(1);
self.bench_jit(code, warmup);
// Measured
let jit_time = self.bench_jit(code, self.config.iterations);
println!("\n📊 Performance Summary [{}]:", name);
println!(" JIT f64 ops: {} iters in {:?} ({:.2} ops/sec)", self.config.iterations, jit_time, self.config.iterations as f64 / jit_time.as_secs_f64());
println!(
" JIT f64 ops: {} iters in {:?} ({:.2} ops/sec)",
self.config.iterations,
jit_time,
self.config.iterations as f64 / jit_time.as_secs_f64()
);
} else {
// Quick correctness check across modes (golden): Interpreter vs VM vs VM+JIT
if let Err(e) = self.verify_outputs_match(code) {
@ -93,8 +105,28 @@ impl NyashRunner {
let vm_vs_interp = interpreter_time.as_secs_f64() / vm_time.as_secs_f64();
let jit_vs_vm = vm_time.as_secs_f64() / jit_time.as_secs_f64();
println!("\n📊 Performance Summary [{}]:", name);
println!(" VM is {:.2}x {} than Interpreter", if vm_vs_interp > 1.0 { vm_vs_interp } else { 1.0 / vm_vs_interp }, if vm_vs_interp > 1.0 { "faster" } else { "slower" });
println!(" JIT is {:.2}x {} than VM (note: compile cost included)", if jit_vs_vm > 1.0 { jit_vs_vm } else { 1.0 / jit_vs_vm }, if jit_vs_vm > 1.0 { "faster" } else { "slower" });
println!(
" VM is {:.2}x {} than Interpreter",
if vm_vs_interp > 1.0 {
vm_vs_interp
} else {
1.0 / vm_vs_interp
},
if vm_vs_interp > 1.0 {
"faster"
} else {
"slower"
}
);
println!(
" JIT is {:.2}x {} than VM (note: compile cost included)",
if jit_vs_vm > 1.0 {
jit_vs_vm
} else {
1.0 / jit_vs_vm
},
if jit_vs_vm > 1.0 { "faster" } else { "slower" }
);
}
}
}
@ -110,7 +142,12 @@ impl NyashRunner {
}
}
let elapsed = start.elapsed();
println!(" ⚡ Interpreter: {} iters in {:?} ({:.2} ops/sec)", iters, elapsed, iters as f64 / elapsed.as_secs_f64());
println!(
" ⚡ Interpreter: {} iters in {:?} ({:.2} ops/sec)",
iters,
elapsed,
iters as f64 / elapsed.as_secs_f64()
);
elapsed
}
@ -126,7 +163,12 @@ impl NyashRunner {
}
}
let elapsed = start.elapsed();
println!(" 🚀 VM: {} iters in {:?} ({:.2} ops/sec)", iters, elapsed, iters as f64 / elapsed.as_secs_f64());
println!(
" 🚀 VM: {} iters in {:?} ({:.2} ops/sec)",
iters,
elapsed,
iters as f64 / elapsed.as_secs_f64()
);
elapsed
}
@ -134,8 +176,12 @@ impl NyashRunner {
// Force JIT mode for this run
std::env::set_var("NYASH_JIT_EXEC", "1");
std::env::set_var("NYASH_JIT_THRESHOLD", "1");
if self.config.jit_stats { std::env::set_var("NYASH_JIT_STATS", "1"); }
if self.config.jit_stats_json { std::env::set_var("NYASH_JIT_STATS_JSON", "1"); }
if self.config.jit_stats {
std::env::set_var("NYASH_JIT_STATS", "1");
}
if self.config.jit_stats_json {
std::env::set_var("NYASH_JIT_STATS_JSON", "1");
}
let start = std::time::Instant::now();
for _ in 0..iters {
if let Ok(ast) = NyashParser::parse_from_string(code) {
@ -147,7 +193,12 @@ impl NyashRunner {
}
}
let elapsed = start.elapsed();
println!(" 🔥 JIT: {} iters in {:?} ({:.2} ops/sec)", iters, elapsed, iters as f64 / elapsed.as_secs_f64());
println!(
" 🔥 JIT: {} iters in {:?} ({:.2} ops/sec)",
iters,
elapsed,
iters as f64 / elapsed.as_secs_f64()
);
elapsed
}
@ -155,22 +206,28 @@ impl NyashRunner {
fn verify_outputs_match(&self, code: &str) -> Result<(), String> {
// VM
let vm_out = {
let ast = NyashParser::parse_from_string(code).map_err(|e| format!("vm parse: {}", e))?;
let ast =
NyashParser::parse_from_string(code).map_err(|e| format!("vm parse: {}", e))?;
let mut mc = MirCompiler::new();
let cr = mc.compile(ast).map_err(|e| format!("vm compile: {}", e))?;
let mut vm = VM::new();
let out = vm.execute_module(&cr.module).map_err(|e| format!("vm exec: {}", e))?;
let out = vm
.execute_module(&cr.module)
.map_err(|e| format!("vm exec: {}", e))?;
out.to_string_box().value
};
// VM+JIT
let jit_out = {
std::env::set_var("NYASH_JIT_EXEC", "1");
std::env::set_var("NYASH_JIT_THRESHOLD", "1");
let ast = NyashParser::parse_from_string(code).map_err(|e| format!("jit parse: {}", e))?;
let ast =
NyashParser::parse_from_string(code).map_err(|e| format!("jit parse: {}", e))?;
let mut mc = MirCompiler::new();
let cr = mc.compile(ast).map_err(|e| format!("jit compile: {}", e))?;
let mut vm = VM::new();
let out = vm.execute_module(&cr.module).map_err(|e| format!("jit exec: {}", e))?;
let out = vm
.execute_module(&cr.module)
.map_err(|e| format!("jit exec: {}", e))?;
out.to_string_box().value
};
if vm_out != jit_out {

View File

@ -1,7 +1,7 @@
use crate::parser::NyashParser;
use crate::interpreter::NyashInterpreter;
use crate::runner_plugin_init;
use crate::parser::NyashParser;
use crate::runner::pipeline::{resolve_using_target, UsingContext};
use crate::runner_plugin_init;
use std::{fs, process};
/// Execute Nyash file with interpreter
@ -22,11 +22,11 @@ pub fn execute_nyash_file(filename: &str, debug_fuel: Option<usize>) {
println!("📝 File contents:\n{}", code);
println!("\n🚀 Parsing and executing...\n");
// Test: immediate file creation (use relative path to avoid sandbox issues)
std::fs::create_dir_all("development/debug_hang_issue").ok();
std::fs::write("development/debug_hang_issue/test.txt", "START").ok();
// Optional: using pre-processing (strip lines and register modules)
let enable_using = std::env::var("NYASH_ENABLE_USING").ok().as_deref() == Some("1");
let mut code_ref: std::borrow::Cow<'_, str> = std::borrow::Cow::Borrowed(&code);
@ -39,13 +39,25 @@ pub fn execute_nyash_file(filename: &str, debug_fuel: Option<usize>) {
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");
(
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()
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 {
@ -57,7 +69,11 @@ pub fn execute_nyash_file(filename: &str, debug_fuel: Option<usize>) {
out.push('\n');
}
// Resolve and register
let using_ctx = UsingContext { using_paths: vec!["apps".into(), "lib".into(), ".".into()], pending_modules: vec![], aliases: std::collections::HashMap::new() };
let using_ctx = UsingContext {
using_paths: vec!["apps".into(), "lib".into(), ".".into()],
pending_modules: vec![],
aliases: std::collections::HashMap::new(),
};
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 ctx_dir = std::path::Path::new(filename).parent();
@ -66,12 +82,24 @@ pub fn execute_nyash_file(filename: &str, debug_fuel: Option<usize>) {
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) {
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); }
Err(e) => {
eprintln!("❌ using: {}", e);
std::process::exit(1);
}
}
}
}
@ -84,30 +112,30 @@ pub fn execute_nyash_file(filename: &str, debug_fuel: Option<usize>) {
Ok(ast) => {
eprintln!("🔍 DEBUG: Parse completed, AST created");
ast
},
}
Err(e) => {
eprintln!("❌ Parse error: {}", e);
process::exit(1);
}
};
eprintln!("🔍 DEBUG: About to print parse success message...");
println!("✅ Parse successful!");
eprintln!("🔍 DEBUG: Parse success message printed");
// Debug log file write
if let Ok(mut file) = std::fs::OpenOptions::new()
.create(true)
.append(true)
.open("development/debug_hang_issue/debug_trace.log")
.open("development/debug_hang_issue/debug_trace.log")
{
use std::io::Write;
let _ = writeln!(file, "=== MAIN: Parse successful ===");
let _ = file.flush();
}
eprintln!("🔍 DEBUG: Creating interpreter...");
// Execute the AST
let mut interpreter = NyashInterpreter::new();
eprintln!("🔍 DEBUG: Starting execution...");
@ -116,9 +144,12 @@ pub fn execute_nyash_file(filename: &str, debug_fuel: Option<usize>) {
println!("✅ Execution completed successfully!");
println!("Result: {}", result.to_string_box().value);
// Structured concurrency: best-effort join of spawned tasks at program end
let join_ms: u64 = std::env::var("NYASH_JOIN_ALL_MS").ok().and_then(|s| s.parse().ok()).unwrap_or(2000);
let join_ms: u64 = std::env::var("NYASH_JOIN_ALL_MS")
.ok()
.and_then(|s| s.parse().ok())
.unwrap_or(2000);
crate::runtime::global_hooks::join_all_registered_futures(join_ms);
},
}
Err(e) => {
// Use enhanced error reporting with source context
eprintln!("❌ Runtime error:\n{}", e.detailed_message(Some(&code)));

View File

@ -1,6 +1,9 @@
use super::super::NyashRunner;
use nyash_rust::{parser::NyashParser, mir::{MirCompiler, MirInstruction}};
use nyash_rust::mir::passes::method_id_inject::inject_method_ids;
use nyash_rust::{
mir::{MirCompiler, MirInstruction},
parser::NyashParser,
};
use std::{fs, process};
impl NyashRunner {
@ -12,20 +15,29 @@ impl NyashRunner {
// Read the file
let code = match fs::read_to_string(filename) {
Ok(content) => content,
Err(e) => { eprintln!("❌ Error reading file {}: {}", filename, e); process::exit(1); }
Err(e) => {
eprintln!("❌ Error reading file {}: {}", filename, e);
process::exit(1);
}
};
// Parse to AST
let ast = match NyashParser::parse_from_string(&code) {
Ok(ast) => ast,
Err(e) => { eprintln!("❌ Parse error: {}", e); process::exit(1); }
Err(e) => {
eprintln!("❌ Parse error: {}", e);
process::exit(1);
}
};
// Compile to MIR
let mut mir_compiler = MirCompiler::new();
let compile_result = match mir_compiler.compile(ast) {
Ok(result) => result,
Err(e) => { eprintln!("❌ MIR compilation error: {}", e); process::exit(1); }
Err(e) => {
eprintln!("❌ MIR compilation error: {}", e);
process::exit(1);
}
};
println!("📊 MIR Module compiled successfully!");
@ -46,7 +58,9 @@ impl NyashRunner {
// 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 let Some(parent) = std::path::Path::new(&_out_path).parent() {
let _ = std::fs::create_dir_all(parent);
}
let py = which::which("python3").ok();
if let Some(py3) = py {
let harness = std::path::Path::new("tools/llvmlite_harness.py");
@ -55,34 +69,61 @@ impl NyashRunner {
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) {
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());
eprintln!(
"[Runner/LLVM] using llvmlite harness → {} (mir={})",
_out_path,
mir_json_path.display()
);
}
// 2) Run harness with --in/--out失敗時は即エラー
let status = std::process::Command::new(py3)
.args([harness.to_string_lossy().as_ref(), "--in", &mir_json_path.display().to_string(), "--out", &_out_path])
.status().map_err(|e| format!("spawn harness: {}", e)).unwrap();
.args([
harness.to_string_lossy().as_ref(),
"--in",
&mir_json_path.display().to_string(),
"--out",
&_out_path,
])
.status()
.map_err(|e| format!("spawn harness: {}", e))
.unwrap();
if !status.success() {
eprintln!("❌ llvmlite harness failed (status={})", status.code().unwrap_or(-1));
eprintln!(
"❌ llvmlite harness failed (status={})",
status.code().unwrap_or(-1)
);
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);
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);
@ -99,11 +140,18 @@ impl NyashRunner {
process::exit(1);
}
if std::env::var("NYASH_CLI_VERBOSE").ok().as_deref() == Some("1") {
eprintln!("[LLVM] object emitted: {} ({} bytes)", _out_path, meta.len());
eprintln!(
"[LLVM] object emitted: {} ({} bytes)",
_out_path,
meta.len()
);
}
}
Err(e) => {
eprintln!("❌ harness output not found after emit: {} ({})", _out_path, e);
eprintln!(
"❌ harness output not found after emit: {} ({})",
_out_path, e
);
process::exit(1);
}
}
@ -117,7 +165,13 @@ impl NyashRunner {
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());
eprintln!(
"[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);
@ -126,10 +180,17 @@ impl NyashRunner {
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());
eprintln!(
"[LLVM] object emitted: {} ({} bytes)",
_out_path,
meta.len()
);
}
}
_ => { eprintln!("❌ LLVM object not found or empty: {}", _out_path); process::exit(1); }
_ => {
eprintln!("❌ LLVM object not found or empty: {}", _out_path);
process::exit(1);
}
}
return;
}
@ -156,8 +217,11 @@ impl NyashRunner {
println!("✅ LLVM execution completed (non-integer result)!");
println!("📊 Result: {}", result.to_string_box().value);
}
},
Err(e) => { eprintln!("❌ LLVM execution error: {}", e); process::exit(1); }
}
Err(e) => {
eprintln!("❌ LLVM execution error: {}", e);
process::exit(1);
}
}
}
#[cfg(all(not(feature = "llvm-inkwell-legacy")))]
@ -168,8 +232,14 @@ impl NyashRunner {
for (_bid, block) in &main_func.blocks {
for inst in &block.instructions {
match inst {
MirInstruction::Return { value: Some(_) } => { println!("✅ Mock exit code: 42"); process::exit(42); }
MirInstruction::Return { value: None } => { println!("✅ Mock exit code: 0"); process::exit(0); }
MirInstruction::Return { value: Some(_) } => {
println!("✅ Mock exit code: 42");
process::exit(42);
}
MirInstruction::Return { value: None } => {
println!("✅ Mock exit code: 0");
process::exit(0);
}
_ => {}
}
}

View File

@ -1,5 +1,8 @@
use super::super::NyashRunner;
use nyash_rust::{parser::NyashParser, mir::{MirCompiler, MirPrinter}};
use nyash_rust::{
mir::{MirCompiler, MirPrinter},
parser::NyashParser,
};
use std::{fs, process};
impl NyashRunner {
@ -8,20 +11,29 @@ impl NyashRunner {
// Read the file
let code = match fs::read_to_string(filename) {
Ok(content) => content,
Err(e) => { eprintln!("❌ Error reading file {}: {}", filename, e); process::exit(1); }
Err(e) => {
eprintln!("❌ Error reading file {}: {}", filename, e);
process::exit(1);
}
};
// Parse to AST
let ast = match NyashParser::parse_from_string(&code) {
Ok(ast) => ast,
Err(e) => { eprintln!("❌ Parse error: {}", e); process::exit(1); }
Err(e) => {
eprintln!("❌ Parse error: {}", e);
process::exit(1);
}
};
// Compile to MIR (opt passes configurable)
let mut mir_compiler = MirCompiler::with_options(!self.config.no_optimize);
let compile_result = match mir_compiler.compile(ast) {
Ok(result) => result,
Err(e) => { eprintln!("❌ MIR compilation error: {}", e); process::exit(1); }
Err(e) => {
eprintln!("❌ MIR compilation error: {}", e);
process::exit(1);
}
};
// Verify MIR if requested
@ -31,7 +43,9 @@ impl NyashRunner {
Ok(()) => println!("✅ MIR verification passed!"),
Err(errors) => {
eprintln!("❌ MIR verification failed:");
for error in errors { eprintln!("{}", error); }
for error in errors {
eprintln!("{}", error);
}
process::exit(1);
}
}
@ -39,11 +53,16 @@ impl NyashRunner {
// Dump MIR if requested
if self.config.dump_mir {
let mut printer = if self.config.mir_verbose { MirPrinter::verbose() } else { MirPrinter::new() };
if self.config.mir_verbose_effects { printer.set_show_effects_inline(true); }
let mut printer = if self.config.mir_verbose {
MirPrinter::verbose()
} else {
MirPrinter::new()
};
if self.config.mir_verbose_effects {
printer.set_show_effects_inline(true);
}
println!("🚀 MIR Output for {}:", filename);
println!("{}", printer.print_module(&compile_result.module));
}
}
}

View File

@ -1,8 +1,8 @@
pub mod bench;
pub mod interpreter;
pub mod llvm;
pub mod mir;
pub mod vm;
pub mod llvm;
pub mod bench;
#[cfg(feature = "cranelift-jit")]
pub mod aot;

View File

@ -1,7 +1,16 @@
use super::super::NyashRunner;
use nyash_rust::{parser::NyashParser, mir::MirCompiler, backend::VM, runtime::{NyashRuntime, NyashRuntimeBuilder}, ast::ASTNode, core::model::BoxDeclaration as CoreBoxDecl, interpreter::SharedState, box_factory::user_defined::UserDefinedBoxFactory};
use std::{fs, process};
use nyash_rust::{
ast::ASTNode,
backend::VM,
box_factory::user_defined::UserDefinedBoxFactory,
core::model::BoxDeclaration as CoreBoxDecl,
interpreter::SharedState,
mir::MirCompiler,
parser::NyashParser,
runtime::{NyashRuntime, NyashRuntimeBuilder},
};
use std::sync::Arc;
use std::{fs, process};
impl NyashRunner {
/// Execute VM mode (split)
@ -18,7 +27,9 @@ impl NyashRunner {
// Init plugin host from nyash.toml if not yet loaded
let need_init = {
let host = nyash_rust::runtime::get_global_plugin_host();
host.read().map(|h| h.config_ref().is_none()).unwrap_or(true)
host.read()
.map(|h| h.config_ref().is_none())
.unwrap_or(true)
};
if need_init {
let _ = nyash_rust::runtime::init_global_plugin_host("nyash.toml");
@ -29,16 +40,29 @@ impl NyashRunner {
std::env::set_var("NYASH_USE_PLUGIN_BUILTINS", "1");
}
// Build stable override list
let mut override_types: Vec<String> = if let Ok(list) = std::env::var("NYASH_PLUGIN_OVERRIDE_TYPES") {
list.split(',').map(|s| s.trim().to_string()).filter(|s| !s.is_empty()).collect()
} else { vec![] };
let mut override_types: Vec<String> =
if let Ok(list) = std::env::var("NYASH_PLUGIN_OVERRIDE_TYPES") {
list.split(',')
.map(|s| s.trim().to_string())
.filter(|s| !s.is_empty())
.collect()
} else {
vec![]
};
for t in [
"FileBox", "TOMLBox", // IO/config
"ConsoleBox", "StringBox", "IntegerBox", // core value-ish
"ArrayBox", "MapBox", // collections
"MathBox", "TimeBox" // math/time helpers
"FileBox",
"TOMLBox", // IO/config
"ConsoleBox",
"StringBox",
"IntegerBox", // core value-ish
"ArrayBox",
"MapBox", // collections
"MathBox",
"TimeBox", // math/time helpers
] {
if !override_types.iter().any(|x| x == t) { override_types.push(t.to_string()); }
if !override_types.iter().any(|x| x == t) {
override_types.push(t.to_string());
}
}
std::env::set_var("NYASH_PLUGIN_OVERRIDE_TYPES", override_types.join(","));
@ -46,11 +70,23 @@ impl NyashRunner {
if std::env::var("NYASH_VM_PLUGIN_STRICT").ok().as_deref() == Some("1") {
let v2 = nyash_rust::runtime::get_global_registry();
let mut missing: Vec<String> = Vec::new();
for t in ["FileBox","ConsoleBox","ArrayBox","MapBox","StringBox","IntegerBox"] {
if v2.get_provider(t).is_none() { missing.push(t.to_string()); }
for t in [
"FileBox",
"ConsoleBox",
"ArrayBox",
"MapBox",
"StringBox",
"IntegerBox",
] {
if v2.get_provider(t).is_none() {
missing.push(t.to_string());
}
}
if !missing.is_empty() {
eprintln!("❌ VM plugin-first strict: missing providers for: {:?}", missing);
eprintln!(
"❌ VM plugin-first strict: missing providers for: {:?}",
missing
);
std::process::exit(1);
}
}
@ -59,13 +95,19 @@ impl NyashRunner {
// Read the file
let code = match fs::read_to_string(filename) {
Ok(content) => content,
Err(e) => { eprintln!("❌ Error reading file {}: {}", filename, e); process::exit(1); }
Err(e) => {
eprintln!("❌ Error reading file {}: {}", filename, e);
process::exit(1);
}
};
// Parse to AST
let ast = match NyashParser::parse_from_string(&code) {
Ok(ast) => ast,
Err(e) => { eprintln!("❌ Parse error: {}", e); process::exit(1); }
Err(e) => {
eprintln!("❌ Parse error: {}", e);
process::exit(1);
}
};
// Prepare runtime and collect Box declarations for VM user-defined types
@ -80,7 +122,9 @@ impl NyashRunner {
let mut shared = SharedState::new();
shared.box_declarations = rt.box_declarations.clone();
let udf = Arc::new(UserDefinedBoxFactory::new(shared));
if let Ok(mut reg) = rt.box_registry.lock() { reg.register(udf); }
if let Ok(mut reg) = rt.box_registry.lock() {
reg.register(udf);
}
rt
};
@ -88,20 +132,30 @@ impl NyashRunner {
let mut mir_compiler = MirCompiler::with_options(!self.config.no_optimize);
let compile_result = match mir_compiler.compile(ast) {
Ok(result) => result,
Err(e) => { eprintln!("❌ MIR compilation error: {}", e); process::exit(1); }
Err(e) => {
eprintln!("❌ MIR compilation error: {}", e);
process::exit(1);
}
};
// Optional: demo scheduling hook
if std::env::var("NYASH_SCHED_DEMO").ok().as_deref() == Some("1") {
if let Some(s) = &runtime.scheduler {
// Immediate task
s.spawn("demo-immediate", Box::new(|| {
println!("[SCHED] immediate task ran at safepoint");
}));
s.spawn(
"demo-immediate",
Box::new(|| {
println!("[SCHED] immediate task ran at safepoint");
}),
);
// Delayed task
s.spawn_after(0, "demo-delayed", Box::new(|| {
println!("[SCHED] delayed task ran at safepoint");
}));
s.spawn_after(
0,
"demo-delayed",
Box::new(|| {
println!("[SCHED] delayed task ran at safepoint");
}),
);
}
}
@ -130,17 +184,28 @@ impl NyashRunner {
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) {
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());
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" };
} else if module_vm.functions.contains_key("main") {
"main"
} else {
"Main.main"
};
// Spawn runner
let status = std::process::Command::new(py3)
.args([
@ -178,20 +243,24 @@ impl NyashRunner {
let mut vm = VM::with_runtime(runtime);
match vm.execute_module(&module_vm) {
Ok(result) => {
if !quiet_pipe { println!("✅ VM execution completed successfully!"); }
if !quiet_pipe {
println!("✅ VM execution completed successfully!");
}
// Pretty-print with coercions for plugin-backed values
// Prefer MIR signature when available, but fall back to runtime coercions to keep VM/JIT consistent.
let (ety, sval) = if let Some(func) = compile_result.module.functions.get("main") {
use nyash_rust::mir::MirType;
use nyash_rust::box_trait::{IntegerBox, BoolBox, StringBox};
use nyash_rust::box_trait::{BoolBox, IntegerBox, StringBox};
use nyash_rust::boxes::FloatBox;
use nyash_rust::mir::MirType;
match &func.signature.return_type {
MirType::Float => {
if let Some(fb) = result.as_any().downcast_ref::<FloatBox>() {
("Float", format!("{}", fb.value))
} else if let Some(ib) = result.as_any().downcast_ref::<IntegerBox>() {
("Float", format!("{}", ib.value as f64))
} else if let Some(s) = nyash_rust::runtime::semantics::coerce_to_string(result.as_ref()) {
} else if let Some(s) =
nyash_rust::runtime::semantics::coerce_to_string(result.as_ref())
{
("String", s)
} else {
(result.type_name(), result.to_string_box().value)
@ -200,7 +269,9 @@ impl NyashRunner {
MirType::Integer => {
if let Some(ib) = result.as_any().downcast_ref::<IntegerBox>() {
("Integer", ib.value.to_string())
} else if let Some(i) = nyash_rust::runtime::semantics::coerce_to_i64(result.as_ref()) {
} else if let Some(i) =
nyash_rust::runtime::semantics::coerce_to_i64(result.as_ref())
{
("Integer", i.to_string())
} else {
(result.type_name(), result.to_string_box().value)
@ -218,50 +289,72 @@ impl NyashRunner {
MirType::String => {
if let Some(sb) = result.as_any().downcast_ref::<StringBox>() {
("String", sb.value.clone())
} else if let Some(s) = nyash_rust::runtime::semantics::coerce_to_string(result.as_ref()) {
} else if let Some(s) =
nyash_rust::runtime::semantics::coerce_to_string(result.as_ref())
{
("String", s)
} else {
(result.type_name(), result.to_string_box().value)
}
}
_ => {
if let Some(i) = nyash_rust::runtime::semantics::coerce_to_i64(result.as_ref()) {
if let Some(i) =
nyash_rust::runtime::semantics::coerce_to_i64(result.as_ref())
{
("Integer", i.to_string())
} else if let Some(s) = nyash_rust::runtime::semantics::coerce_to_string(result.as_ref()) {
} else if let Some(s) =
nyash_rust::runtime::semantics::coerce_to_string(result.as_ref())
{
("String", s)
} else { (result.type_name(), result.to_string_box().value) }
} else {
(result.type_name(), result.to_string_box().value)
}
}
}
} else {
if let Some(i) = nyash_rust::runtime::semantics::coerce_to_i64(result.as_ref()) {
if let Some(i) = nyash_rust::runtime::semantics::coerce_to_i64(result.as_ref())
{
("Integer", i.to_string())
} else if let Some(s) = nyash_rust::runtime::semantics::coerce_to_string(result.as_ref()) {
} else if let Some(s) =
nyash_rust::runtime::semantics::coerce_to_string(result.as_ref())
{
("String", s)
} else { (result.type_name(), result.to_string_box().value) }
} else {
(result.type_name(), result.to_string_box().value)
}
};
if !quiet_pipe {
println!("ResultType(MIR): {}", ety);
println!("Result: {}", sval);
}
},
Err(e) => { eprintln!("❌ VM execution error: {}", e); process::exit(1); }
}
Err(e) => {
eprintln!("❌ VM execution error: {}", e);
process::exit(1);
}
}
}
/// Collect Box declarations from AST and register into runtime
pub(crate) fn collect_box_declarations(&self, ast: &ASTNode, runtime: &NyashRuntime) {
fn resolve_include_path(filename: &str) -> String {
if filename.starts_with("./") || filename.starts_with("../") { return filename.to_string(); }
if filename.starts_with("./") || filename.starts_with("../") {
return filename.to_string();
}
let parts: Vec<&str> = filename.splitn(2, '/').collect();
if parts.len() == 2 {
let root = parts[0]; let rest = parts[1];
let root = parts[0];
let rest = parts[1];
let cfg_path = "nyash.toml";
if let Ok(toml_str) = std::fs::read_to_string(cfg_path) {
if let Ok(toml_val) = toml::from_str::<toml::Value>(&toml_str) {
if let Some(include) = toml_val.get("include") {
if let Some(roots) = include.get("roots").and_then(|v| v.as_table()) {
if let Some(base) = roots.get(root).and_then(|v| v.as_str()) {
let mut b = base.to_string(); if !b.ends_with('/') && !b.ends_with('\\') { b.push('/'); }
let mut b = base.to_string();
if !b.ends_with('/') && !b.ends_with('\\') {
b.push('/');
}
return format!("{}{}", b, rest);
}
}
@ -274,10 +367,23 @@ impl NyashRunner {
use std::collections::HashSet;
fn walk_with_state(node: &ASTNode, runtime: &NyashRuntime, stack: &mut Vec<String>, visited: &mut HashSet<String>) {
fn walk_with_state(
node: &ASTNode,
runtime: &NyashRuntime,
stack: &mut Vec<String>,
visited: &mut HashSet<String>,
) {
match node {
ASTNode::Program { statements, .. } => { for st in statements { walk_with_state(st, runtime, stack, visited); } }
ASTNode::FunctionDeclaration { body, .. } => { for st in body { walk_with_state(st, runtime, stack, visited); } }
ASTNode::Program { statements, .. } => {
for st in statements {
walk_with_state(st, runtime, stack, visited);
}
}
ASTNode::FunctionDeclaration { body, .. } => {
for st in body {
walk_with_state(st, runtime, stack, visited);
}
}
ASTNode::Include { filename, .. } => {
let mut path = resolve_include_path(filename);
if std::path::Path::new(&path).is_dir() {
@ -305,42 +411,139 @@ impl NyashRunner {
stack.pop();
}
ASTNode::Assignment { target, value, .. } => {
walk_with_state(target, runtime, stack, visited); walk_with_state(value, runtime, stack, visited);
walk_with_state(target, runtime, stack, visited);
walk_with_state(value, runtime, stack, visited);
}
ASTNode::Return { value, .. } => { if let Some(v) = value { walk_with_state(v, runtime, stack, visited); } }
ASTNode::Print { expression, .. } => { walk_with_state(expression, runtime, stack, visited); }
ASTNode::If { condition, then_body, else_body, .. } => {
ASTNode::Return { value, .. } => {
if let Some(v) = value {
walk_with_state(v, runtime, stack, visited);
}
}
ASTNode::Print { expression, .. } => {
walk_with_state(expression, runtime, stack, visited);
}
ASTNode::If {
condition,
then_body,
else_body,
..
} => {
walk_with_state(condition, runtime, stack, visited);
for st in then_body { walk_with_state(st, runtime, stack, visited); }
if let Some(eb) = else_body { for st in eb { walk_with_state(st, runtime, stack, visited); } }
for st in then_body {
walk_with_state(st, runtime, stack, visited);
}
if let Some(eb) = else_body {
for st in eb {
walk_with_state(st, runtime, stack, visited);
}
}
}
ASTNode::Loop { condition, body, .. } => {
walk_with_state(condition, runtime, stack, visited); for st in body { walk_with_state(st, runtime, stack, visited); }
ASTNode::Loop {
condition, body, ..
} => {
walk_with_state(condition, runtime, stack, visited);
for st in body {
walk_with_state(st, runtime, stack, visited);
}
}
ASTNode::TryCatch { try_body, catch_clauses, finally_body, .. } => {
for st in try_body { walk_with_state(st, runtime, stack, visited); }
for cc in catch_clauses { for st in &cc.body { walk_with_state(st, runtime, stack, visited); } }
if let Some(fb) = finally_body { for st in fb { walk_with_state(st, runtime, stack, visited); } }
ASTNode::TryCatch {
try_body,
catch_clauses,
finally_body,
..
} => {
for st in try_body {
walk_with_state(st, runtime, stack, visited);
}
for cc in catch_clauses {
for st in &cc.body {
walk_with_state(st, runtime, stack, visited);
}
}
if let Some(fb) = finally_body {
for st in fb {
walk_with_state(st, runtime, stack, visited);
}
}
}
ASTNode::Throw { expression, .. } => {
walk_with_state(expression, runtime, stack, visited);
}
ASTNode::Throw { expression, .. } => { walk_with_state(expression, runtime, stack, visited); }
ASTNode::Local { initial_values, .. } => {
for iv in initial_values { if let Some(v) = iv { walk_with_state(v, runtime, stack, visited); } }
for iv in initial_values {
if let Some(v) = iv {
walk_with_state(v, runtime, stack, visited);
}
}
}
ASTNode::Outbox { initial_values, .. } => {
for iv in initial_values { if let Some(v) = iv { walk_with_state(v, runtime, stack, visited); } }
for iv in initial_values {
if let Some(v) = iv {
walk_with_state(v, runtime, stack, visited);
}
}
}
ASTNode::FunctionCall { arguments, .. } => { for a in arguments { walk_with_state(a, runtime, stack, visited); } }
ASTNode::MethodCall { object, arguments, .. } => { walk_with_state(object, runtime, stack, visited); for a in arguments { walk_with_state(a, runtime, stack, visited); } }
ASTNode::FieldAccess { object, .. } => { walk_with_state(object, runtime, stack, visited); }
ASTNode::New { arguments, .. } => { for a in arguments { walk_with_state(a, runtime, stack, visited); } }
ASTNode::BinaryOp { left, right, .. } => { walk_with_state(left, runtime, stack, visited); walk_with_state(right, runtime, stack, visited); }
ASTNode::UnaryOp { operand, .. } => { walk_with_state(operand, runtime, stack, visited); }
ASTNode::AwaitExpression { expression, .. } => { walk_with_state(expression, runtime, stack, visited); }
ASTNode::Arrow { sender, receiver, .. } => { walk_with_state(sender, runtime, stack, visited); walk_with_state(receiver, runtime, stack, visited); }
ASTNode::Nowait { expression, .. } => { walk_with_state(expression, runtime, stack, visited); }
ASTNode::BoxDeclaration { name, fields, public_fields, private_fields, methods, constructors, init_fields, weak_fields, is_interface, extends, implements, type_parameters, .. } => {
for (_mname, mnode) in methods { walk_with_state(mnode, runtime, stack, visited); }
for (_ckey, cnode) in constructors { walk_with_state(cnode, runtime, stack, visited); }
ASTNode::FunctionCall { arguments, .. } => {
for a in arguments {
walk_with_state(a, runtime, stack, visited);
}
}
ASTNode::MethodCall {
object, arguments, ..
} => {
walk_with_state(object, runtime, stack, visited);
for a in arguments {
walk_with_state(a, runtime, stack, visited);
}
}
ASTNode::FieldAccess { object, .. } => {
walk_with_state(object, runtime, stack, visited);
}
ASTNode::New { arguments, .. } => {
for a in arguments {
walk_with_state(a, runtime, stack, visited);
}
}
ASTNode::BinaryOp { left, right, .. } => {
walk_with_state(left, runtime, stack, visited);
walk_with_state(right, runtime, stack, visited);
}
ASTNode::UnaryOp { operand, .. } => {
walk_with_state(operand, runtime, stack, visited);
}
ASTNode::AwaitExpression { expression, .. } => {
walk_with_state(expression, runtime, stack, visited);
}
ASTNode::Arrow {
sender, receiver, ..
} => {
walk_with_state(sender, runtime, stack, visited);
walk_with_state(receiver, runtime, stack, visited);
}
ASTNode::Nowait { expression, .. } => {
walk_with_state(expression, runtime, stack, visited);
}
ASTNode::BoxDeclaration {
name,
fields,
public_fields,
private_fields,
methods,
constructors,
init_fields,
weak_fields,
is_interface,
extends,
implements,
type_parameters,
..
} => {
for (_mname, mnode) in methods {
walk_with_state(mnode, runtime, stack, visited);
}
for (_ckey, cnode) in constructors {
walk_with_state(cnode, runtime, stack, visited);
}
let decl = CoreBoxDecl {
name: name.clone(),
fields: fields.clone(),
@ -355,7 +558,9 @@ impl NyashRunner {
implements: implements.clone(),
type_parameters: type_parameters.clone(),
};
if let Ok(mut map) = runtime.box_declarations.write() { map.insert(name.clone(), decl); }
if let Ok(mut map) = runtime.box_declarations.write() {
map.insert(name.clone(), decl);
}
}
_ => {}
}

View File

@ -21,13 +21,17 @@ impl NyashRunner {
let json = if let Some(path) = &self.config.json_file {
match std::fs::read_to_string(path) {
Ok(s) => s,
Err(e) => { eprintln!("❌ json-file read error: {}", e); std::process::exit(1); }
Err(e) => {
eprintln!("❌ json-file read error: {}", e);
std::process::exit(1);
}
}
} else {
use std::io::Read;
let mut buf = String::new();
if let Err(e) = std::io::stdin().read_to_string(&mut buf) {
eprintln!("❌ stdin read error: {}", e); std::process::exit(1);
eprintln!("❌ stdin read error: {}", e);
std::process::exit(1);
}
buf
};
@ -45,16 +49,27 @@ impl NyashRunner {
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) = super::mir_json_emit::emit_mir_json_for_harness_bin(&module, &mir_json_path) {
if let Err(e) = super::mir_json_emit::emit_mir_json_for_harness_bin(
&module,
&mir_json_path,
) {
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());
eprintln!(
"[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" }
else if module.functions.contains_key("main") { "main" } else { "Main.main" };
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(),

View File

@ -22,7 +22,8 @@ impl NyashRunner {
pub(super) fn init_using_context(&self) -> UsingContext {
let mut using_paths: Vec<String> = Vec::new();
let mut pending_modules: Vec<(String, String)> = Vec::new();
let mut aliases: std::collections::HashMap<String, String> = std::collections::HashMap::new();
let mut aliases: std::collections::HashMap<String, String> =
std::collections::HashMap::new();
// Defaults
using_paths.extend(["apps", "lib", "."].into_iter().map(|s| s.to_string()));
@ -43,7 +44,9 @@ impl NyashRunner {
for p in paths_arr {
if let Some(s) = p.as_str() {
let s = s.trim();
if !s.is_empty() { using_paths.push(s.to_string()); }
if !s.is_empty() {
using_paths.push(s.to_string());
}
}
}
}
@ -75,20 +78,29 @@ impl NyashRunner {
if let Ok(p) = std::env::var("NYASH_USING_PATH") {
for s in p.split(':') {
let s = s.trim();
if !s.is_empty() { using_paths.push(s.to_string()); }
if !s.is_empty() {
using_paths.push(s.to_string());
}
}
}
// Env aliases: comma-separated k=v pairs
if let Ok(raw) = std::env::var("NYASH_ALIASES") {
for ent in raw.split(',') {
if let Some((k,v)) = ent.split_once('=') {
let k = k.trim(); let v = v.trim();
if !k.is_empty() && !v.is_empty() { aliases.insert(k.to_string(), v.to_string()); }
if let Some((k, v)) = ent.split_once('=') {
let k = k.trim();
let v = v.trim();
if !k.is_empty() && !v.is_empty() {
aliases.insert(k.to_string(), v.to_string());
}
}
}
}
UsingContext { using_paths, pending_modules, aliases }
UsingContext {
using_paths,
pending_modules,
aliases,
}
}
}
@ -96,19 +108,25 @@ impl NyashRunner {
pub(super) fn suggest_in_base(base: &str, leaf: &str, out: &mut Vec<String>) {
use std::fs;
fn walk(dir: &std::path::Path, leaf: &str, out: &mut Vec<String>, depth: usize) {
if depth == 0 || out.len() >= 5 { return; }
if depth == 0 || out.len() >= 5 {
return;
}
if let Ok(entries) = fs::read_dir(dir) {
for e in entries.flatten() {
let path = e.path();
if path.is_dir() {
walk(&path, leaf, out, depth - 1);
if out.len() >= 5 { return; }
if out.len() >= 5 {
return;
}
} else if let Some(ext) = path.extension().and_then(|s| s.to_str()) {
if ext == "nyash" {
if let Some(stem) = path.file_stem().and_then(|s| s.to_str()) {
if stem == leaf {
out.push(path.to_string_lossy().to_string());
if out.len() >= 5 { return; }
if out.len() >= 5 {
return;
}
}
}
}
@ -132,17 +150,29 @@ pub(super) fn resolve_using_target(
strict: bool,
verbose: bool,
) -> Result<String, String> {
if is_path { return Ok(tgt.to_string()); }
if is_path {
return Ok(tgt.to_string());
}
let trace = verbose || std::env::var("NYASH_RESOLVE_TRACE").ok().as_deref() == Some("1");
let idx = super::box_index::get_box_index();
let mut strict_effective = strict || idx.plugins_require_prefix_global;
if std::env::var("NYASH_PLUGIN_REQUIRE_PREFIX").ok().as_deref() == Some("1") { strict_effective = true; }
if std::env::var("NYASH_PLUGIN_REQUIRE_PREFIX").ok().as_deref() == Some("1") {
strict_effective = true;
}
let meta_for_target = idx.plugin_meta_by_box.get(tgt).cloned();
let mut require_prefix_target = meta_for_target.as_ref().map(|m| m.require_prefix).unwrap_or(false);
if let Some(m) = &meta_for_target { if !m.expose_short_names { require_prefix_target = true; } }
let mut require_prefix_target = meta_for_target
.as_ref()
.map(|m| m.require_prefix)
.unwrap_or(false);
if let Some(m) = &meta_for_target {
if !m.expose_short_names {
require_prefix_target = true;
}
}
let mut is_plugin_short = meta_for_target.is_some();
if !is_plugin_short {
is_plugin_short = idx.plugin_boxes.contains(tgt) || super::box_index::BoxIndex::is_known_plugin_short(tgt);
is_plugin_short = idx.plugin_boxes.contains(tgt)
|| super::box_index::BoxIndex::is_known_plugin_short(tgt);
}
if (strict_effective || require_prefix_target) && is_plugin_short && !tgt.contains('.') {
let mut msg = format!("plugin short name '{}' requires prefix", tgt);
@ -155,25 +185,40 @@ pub(super) fn resolve_using_target(
}
let key = {
let base = context_dir.and_then(|p| p.to_str()).unwrap_or("");
format!("{}|{}|{}|{}", tgt, base, strict as i32, using_paths.join(":"))
format!(
"{}|{}|{}|{}",
tgt,
base,
strict as i32,
using_paths.join(":")
)
};
if let Some(hit) = crate::runner::box_index::cache_get(&key) {
if trace { crate::runner::trace::log(format!("[using/cache] '{}' -> '{}'", tgt, hit)); }
if trace {
crate::runner::trace::log(format!("[using/cache] '{}' -> '{}'", tgt, hit));
}
return Ok(hit);
}
// Resolve aliases early (provided map)
if let Some(v) = aliases.get(tgt) {
if trace { crate::runner::trace::log(format!("[using/resolve] alias '{}' -> '{}'", tgt, v)); }
if trace {
crate::runner::trace::log(format!("[using/resolve] alias '{}' -> '{}'", tgt, v));
}
crate::runner::box_index::cache_put(&key, v.clone());
return Ok(v.clone());
}
// Also consult env aliases
if let Ok(raw) = std::env::var("NYASH_ALIASES") {
for ent in raw.split(',') {
if let Some((k,v)) = ent.split_once('=') {
if let Some((k, v)) = ent.split_once('=') {
if k.trim() == tgt {
let out = v.trim().to_string();
if trace { crate::runner::trace::log(format!("[using/resolve] env-alias '{}' -> '{}'", tgt, out)); }
if trace {
crate::runner::trace::log(format!(
"[using/resolve] env-alias '{}' -> '{}'",
tgt, out
));
}
crate::runner::box_index::cache_put(&key, out.clone());
return Ok(out);
}
@ -183,17 +228,26 @@ pub(super) fn resolve_using_target(
// 1) modules mapping
if let Some((_, p)) = modules.iter().find(|(n, _)| n == tgt) {
let out = p.clone();
if trace { crate::runner::trace::log(format!("[using/resolve] modules '{}' -> '{}'", tgt, out)); }
if trace {
crate::runner::trace::log(format!("[using/resolve] modules '{}' -> '{}'", tgt, out));
}
crate::runner::box_index::cache_put(&key, out.clone());
return Ok(out);
}
// 2) build candidate list: relative then using-paths
let rel = tgt.replace('.', "/") + ".nyash";
let mut cand: Vec<String> = Vec::new();
if let Some(dir) = context_dir { let c = dir.join(&rel); if c.exists() { cand.push(c.to_string_lossy().to_string()); } }
if let Some(dir) = context_dir {
let c = dir.join(&rel);
if c.exists() {
cand.push(c.to_string_lossy().to_string());
}
}
for base in using_paths {
let c = std::path::Path::new(base).join(&rel);
if c.exists() { cand.push(c.to_string_lossy().to_string()); }
if c.exists() {
cand.push(c.to_string_lossy().to_string());
}
}
if cand.is_empty() {
if trace {
@ -201,10 +255,17 @@ pub(super) fn resolve_using_target(
let leaf = tgt.split('.').last().unwrap_or(tgt);
let mut cands: Vec<String> = Vec::new();
suggest_in_base("apps", leaf, &mut cands);
if cands.len() < 5 { suggest_in_base("lib", leaf, &mut cands); }
if cands.len() < 5 { suggest_in_base(".", leaf, &mut cands); }
if cands.len() < 5 {
suggest_in_base("lib", leaf, &mut cands);
}
if cands.len() < 5 {
suggest_in_base(".", leaf, &mut cands);
}
if cands.is_empty() {
crate::runner::trace::log(format!("[using] unresolved '{}' (searched: rel+paths)", tgt));
crate::runner::trace::log(format!(
"[using] unresolved '{}' (searched: rel+paths)",
tgt
));
} else {
crate::runner::trace::log(format!(
"[using] unresolved '{}' (searched: rel+paths) candidates: {}",
@ -219,7 +280,9 @@ pub(super) fn resolve_using_target(
return Err(format!("ambiguous using '{}': {}", tgt, cand.join(", ")));
}
let out = cand.remove(0);
if trace { crate::runner::trace::log(format!("[using/resolve] '{}' -> '{}'", tgt, out)); }
if trace {
crate::runner::trace::log(format!("[using/resolve] '{}' -> '{}'", tgt, out));
}
crate::runner::box_index::cache_put(&key, out.clone());
Ok(out)
}
@ -246,9 +309,17 @@ pub(super) fn lint_fields_top(code: &str, strict: bool, verbose: bool) -> Result
if !in_box && trimmed.starts_with("box ") || trimmed.starts_with("static box ") {
// capture name
let mut name = String::new();
let after = if let Some(rest) = trimmed.strip_prefix("static box ") { rest } else { trimmed.strip_prefix("box ").unwrap_or("") };
let after = if let Some(rest) = trimmed.strip_prefix("static box ") {
rest
} else {
trimmed.strip_prefix("box ").unwrap_or("")
};
for ch in after.chars() {
if ch.is_alphanumeric() || ch == '_' { name.push(ch); } else { break; }
if ch.is_alphanumeric() || ch == '_' {
name.push(ch);
} else {
break;
}
}
// require K&R brace on same line to start tracking
if opens > 0 {
@ -269,11 +340,29 @@ pub(super) fn lint_fields_top(code: &str, strict: bool, verbose: bool) -> Result
// starts with identifier then '(' and later '{'
let mut it = trimmed.chars();
let mut ident = String::new();
while let Some(c) = it.next() { if c.is_whitespace() { continue; } if c.is_alphabetic() || c=='_' { ident.push(c); break; } else { break; } }
while let Some(c) = it.next() { if c.is_alphanumeric() || c=='_' { ident.push(c); } else { break; } }
while let Some(c) = it.next() {
if c.is_whitespace() {
continue;
}
if c.is_alphabetic() || c == '_' {
ident.push(c);
break;
} else {
break;
}
}
while let Some(c) = it.next() {
if c.is_alphanumeric() || c == '_' {
ident.push(c);
} else {
break;
}
}
trimmed.contains('(') && trimmed.ends_with('{') && !ident.is_empty()
};
if is_method { seen_method = true; }
if is_method {
seen_method = true;
}
// Detect field: ident ':' Type (rough heuristic)
let is_field = {
@ -281,10 +370,22 @@ pub(super) fn lint_fields_top(code: &str, strict: bool, verbose: bool) -> Result
if parts.len() == 2 {
let lhs = parts[0].trim();
let rhs = parts[1].trim();
let lhs_ok = !lhs.is_empty() && lhs.chars().next().map(|c| c.is_alphabetic() || c=='_').unwrap_or(false);
let rhs_ok = !rhs.is_empty() && rhs.chars().next().map(|c| c.is_alphabetic() || c=='_').unwrap_or(false);
let lhs_ok = !lhs.is_empty()
&& lhs
.chars()
.next()
.map(|c| c.is_alphabetic() || c == '_')
.unwrap_or(false);
let rhs_ok = !rhs.is_empty()
&& rhs
.chars()
.next()
.map(|c| c.is_alphabetic() || c == '_')
.unwrap_or(false);
lhs_ok && rhs_ok && !trimmed.contains('(') && !trimmed.contains(')')
} else { false }
} else {
false
}
};
if is_field && seen_method {
violations.push((lno, trimmed.to_string(), cur_box.clone()));
@ -293,7 +394,10 @@ pub(super) fn lint_fields_top(code: &str, strict: bool, verbose: bool) -> Result
}
// Exit box when closing brace reduces depth below box_depth
let post_brace = pre_brace + opens - closes;
if post_brace < box_depth { in_box = false; cur_box.clear(); }
if post_brace < box_depth {
in_box = false;
cur_box.clear();
}
}
// Update brace after processing
@ -305,17 +409,30 @@ pub(super) fn lint_fields_top(code: &str, strict: bool, verbose: bool) -> Result
}
if strict {
// Compose error message
let mut msg = String::from("Field declarations must appear at the top of box. Violations:\n");
let mut msg =
String::from("Field declarations must appear at the top of box. Violations:\n");
for (lno, fld, bx) in violations.iter().take(10) {
msg.push_str(&format!(" line {} in box {}: '{}" , lno, if bx.is_empty(){"<unknown>"} else {bx}, fld));
msg.push_str(&format!(
" line {} in box {}: '{}",
lno,
if bx.is_empty() { "<unknown>" } else { bx },
fld
));
msg.push_str("'\n");
}
if violations.len() > 10 { msg.push_str(&format!(" ... and {} more\n", violations.len()-10)); }
if violations.len() > 10 {
msg.push_str(&format!(" ... and {} more\n", violations.len() - 10));
}
return Err(msg);
}
if verbose || std::env::var("NYASH_RESOLVE_TRACE").ok().as_deref() == Some("1") {
for (lno, fld, bx) in violations {
eprintln!("[lint] fields-top: line {} in box {} -> {}", lno, if bx.is_empty(){"<unknown>"} else {&bx}, fld);
eprintln!(
"[lint] fields-top: line {} in box {} -> {}",
lno,
if bx.is_empty() { "<unknown>" } else { &bx },
fld
);
}
}
Ok(())

View File

@ -8,12 +8,12 @@
use super::*;
use nyash_rust::{parser::NyashParser, interpreter::NyashInterpreter};
use std::{fs, process};
use nyash_rust::{interpreter::NyashInterpreter, parser::NyashParser};
use std::io::Read;
use std::process::Stdio;
use std::time::{Duration, Instant};
use std::thread::sleep;
use std::time::{Duration, Instant};
use std::{fs, process};
impl NyashRunner {
/// Selfhost (Ny -> JSON v0) pipeline: EXE/VM/Python フォールバック含む
@ -22,7 +22,10 @@ impl NyashRunner {
// Read input source
let code = match fs::read_to_string(filename) {
Ok(c) => c,
Err(e) => { eprintln!("[ny-compiler] read error: {}", e); return false; }
Err(e) => {
eprintln!("[ny-compiler] read error: {}", e);
return false;
}
};
// Optional Phase-15: strip `using` lines and register modules (same policy as execute_nyash_file)
let enable_using = crate::config::env::enable_using();
@ -39,13 +42,25 @@ impl NyashRunner {
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");
(
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()
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 {
@ -88,13 +103,17 @@ impl NyashRunner {
return false;
}
}
Err(e) => { eprintln!("[ny-compiler] open tmp failed: {}", e); return false; }
Err(e) => {
eprintln!("[ny-compiler] open tmp failed: {}", e);
return false;
}
}
}
// Preferred: run Ny selfhost compiler program (apps/selfhost-compiler/compiler.nyash)
// This avoids inline embedding pitfalls and supports Stage-3 gating via args.
{
let exe = std::env::current_exe().unwrap_or_else(|_| std::path::PathBuf::from("target/release/nyash"));
let exe = std::env::current_exe()
.unwrap_or_else(|_| std::path::PathBuf::from("target/release/nyash"));
let parser_prog = std::path::Path::new("apps/selfhost-compiler/compiler.nyash");
if parser_prog.exists() {
let mut cmd = std::process::Command::new(&exe);
@ -113,7 +132,9 @@ impl NyashRunner {
cmd.env_remove("NYASH_CLI_VERBOSE");
cmd.env("NYASH_JSON_ONLY", "1");
let timeout_ms: u64 = crate::config::env::ny_compiler_timeout_ms();
let mut cmd = cmd.stdout(std::process::Stdio::piped()).stderr(std::process::Stdio::piped());
let mut cmd = cmd
.stdout(std::process::Stdio::piped())
.stderr(std::process::Stdio::piped());
if let Ok(mut child) = cmd.spawn() {
let mut ch_stdout = child.stdout.take();
let mut ch_stderr = child.stderr.take();
@ -124,7 +145,10 @@ impl NyashRunner {
Ok(Some(_)) => break,
Ok(None) => {
if start.elapsed() >= std::time::Duration::from_millis(timeout_ms) {
let _ = child.kill(); let _ = child.wait(); timed_out = true; break;
let _ = child.kill();
let _ = child.wait();
timed_out = true;
break;
}
std::thread::sleep(std::time::Duration::from_millis(10));
}
@ -133,21 +157,41 @@ impl NyashRunner {
}
let mut out_buf = Vec::new();
let mut err_buf = Vec::new();
if let Some(mut s) = ch_stdout { let _ = s.read_to_end(&mut out_buf); }
if let Some(mut s) = ch_stderr { let _ = s.read_to_end(&mut err_buf); }
if let Some(mut s) = ch_stdout {
let _ = s.read_to_end(&mut out_buf);
}
if let Some(mut s) = ch_stderr {
let _ = s.read_to_end(&mut err_buf);
}
if timed_out {
let head = String::from_utf8_lossy(&out_buf).chars().take(200).collect::<String>();
eprintln!("[ny-compiler] child timeout after {} ms; stdout(head)='{}'", timeout_ms, head.replace('\n', "\\n"));
let head = String::from_utf8_lossy(&out_buf)
.chars()
.take(200)
.collect::<String>();
eprintln!(
"[ny-compiler] child timeout after {} ms; stdout(head)='{}'",
timeout_ms,
head.replace('\n', "\\n")
);
}
let stdout = String::from_utf8_lossy(&out_buf).to_string();
let mut json_line = String::new();
for line in stdout.lines() { let t = line.trim(); if t.starts_with('{') && t.contains("\"version\"") && t.contains("\"kind\"") { json_line = t.to_string(); break; } }
for line in stdout.lines() {
let t = line.trim();
if t.starts_with('{') && t.contains("\"version\"") && t.contains("\"kind\"")
{
json_line = t.to_string();
break;
}
}
if !json_line.is_empty() {
match super::json_v0_bridge::parse_json_v0_to_module(&json_line) {
Ok(module) => {
super::json_v0_bridge::maybe_dump_mir(&module);
let emit_only = crate::config::env::ny_compiler_emit_only();
if emit_only { return false; }
if emit_only {
return false;
}
// Prefer PyVM path when requested
if crate::config::env::vm_use_py() {
if let Ok(py3) = which::which("python3") {
@ -160,10 +204,25 @@ impl NyashRunner {
eprintln!("❌ PyVM MIR JSON emit error: {}", e);
std::process::exit(1);
}
let entry = if module.functions.contains_key("Main.main") { "Main.main" } else if module.functions.contains_key("main") { "main" } else { "Main.main" };
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();
.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);
println!("Result: {}", code);
std::process::exit(code);
@ -173,7 +232,9 @@ impl NyashRunner {
self.execute_mir_module(&module);
return true;
}
Err(e) => { eprintln!("[ny-compiler] json parse error (child): {}", e); }
Err(e) => {
eprintln!("[ny-compiler] json parse error (child): {}", e);
}
}
}
}
@ -182,56 +243,102 @@ impl NyashRunner {
// Python MVP-first: prefer the lightweight harness to produce JSON v0 (unless skipped)
if std::env::var("NYASH_NY_COMPILER_SKIP_PY").ok().as_deref() != Some("1") {
if let Ok(py3) = which::which("python3") {
let py = std::path::Path::new("tools/ny_parser_mvp.py");
if py.exists() {
let mut cmd = std::process::Command::new(&py3);
cmd.arg(py).arg(&tmp_path);
let out = match cmd.output() { Ok(o) => o, Err(e) => { eprintln!("[ny-compiler] python harness failed to spawn: {}", e); return false; } };
if out.status.success() {
if let Ok(line) = String::from_utf8(out.stdout).map(|s| s.lines().next().unwrap_or("").to_string()) {
if line.contains("\"version\"") && line.contains("\"kind\"") {
match super::json_v0_bridge::parse_json_v0_to_module(&line) {
Ok(module) => {
super::json_v0_bridge::maybe_dump_mir(&module);
let emit_only = std::env::var("NYASH_NY_COMPILER_EMIT_ONLY").unwrap_or_else(|_| "1".to_string()) == "1";
if emit_only { 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) {
if let Ok(py3) = which::which("python3") {
let py = std::path::Path::new("tools/ny_parser_mvp.py");
if py.exists() {
let mut cmd = std::process::Command::new(&py3);
cmd.arg(py).arg(&tmp_path);
let out = match cmd.output() {
Ok(o) => o,
Err(e) => {
eprintln!("[ny-compiler] python harness failed to spawn: {}", e);
return false;
}
};
if out.status.success() {
if let Ok(line) = String::from_utf8(out.stdout)
.map(|s| s.lines().next().unwrap_or("").to_string())
{
if line.contains("\"version\"") && line.contains("\"kind\"") {
match super::json_v0_bridge::parse_json_v0_to_module(&line) {
Ok(module) => {
super::json_v0_bridge::maybe_dump_mir(&module);
let emit_only =
std::env::var("NYASH_NY_COMPILER_EMIT_ONLY")
.unwrap_or_else(|_| "1".to_string())
== "1";
if emit_only {
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_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
);
}
}
println!("Result: {}", code);
std::process::exit(code);
}
println!("Result: {}", code);
std::process::exit(code);
self.execute_mir_module(&module);
return true;
}
Err(e) => {
eprintln!("[ny-compiler] json parse error: {}", e);
return false;
}
self.execute_mir_module(&module);
return true;
}
Err(e) => { eprintln!("[ny-compiler] json parse error: {}", e); return false; }
}
}
}
}
}
} }
}
// EXE-first: if requested, try external parser EXE (nyash_compiler)
if std::env::var("NYASH_USE_NY_COMPILER_EXE").ok().as_deref() == Some("1") {
// Resolve parser EXE path
@ -240,56 +347,118 @@ impl NyashRunner {
} else {
let mut p = std::path::PathBuf::from("dist/nyash_compiler");
#[cfg(windows)]
{ p.push("nyash_compiler.exe"); }
{
p.push("nyash_compiler.exe");
}
#[cfg(not(windows))]
{ p.push("nyash_compiler"); }
{
p.push("nyash_compiler");
}
if !p.exists() {
// Try PATH
if let Ok(w) = which::which("nyash_compiler") { w } else { p }
} else { p }
if let Ok(w) = which::which("nyash_compiler") {
w
} else {
p
}
} else {
p
}
};
if exe_path.exists() {
let mut cmd = std::process::Command::new(&exe_path);
// Prefer passing the original filename directly (parser EXE accepts positional path)
cmd.arg(filename);
// Gates
if std::env::var("NYASH_NY_COMPILER_MIN_JSON").ok().as_deref() == Some("1") { cmd.arg("--min-json"); }
if std::env::var("NYASH_SELFHOST_READ_TMP").ok().as_deref() == Some("1") { cmd.arg("--read-tmp"); }
if std::env::var("NYASH_NY_COMPILER_STAGE3").ok().as_deref() == Some("1") { cmd.arg("--stage3"); }
if let Ok(raw) = std::env::var("NYASH_NY_COMPILER_CHILD_ARGS") { for tok in raw.split_whitespace() { cmd.arg(tok); } }
let timeout_ms: u64 = std::env::var("NYASH_NY_COMPILER_TIMEOUT_MS").ok().and_then(|s| s.parse().ok()).unwrap_or(2000);
if std::env::var("NYASH_NY_COMPILER_MIN_JSON").ok().as_deref() == Some("1") {
cmd.arg("--min-json");
}
if std::env::var("NYASH_SELFHOST_READ_TMP").ok().as_deref() == Some("1") {
cmd.arg("--read-tmp");
}
if std::env::var("NYASH_NY_COMPILER_STAGE3").ok().as_deref() == Some("1") {
cmd.arg("--stage3");
}
if let Ok(raw) = std::env::var("NYASH_NY_COMPILER_CHILD_ARGS") {
for tok in raw.split_whitespace() {
cmd.arg(tok);
}
}
let timeout_ms: u64 = std::env::var("NYASH_NY_COMPILER_TIMEOUT_MS")
.ok()
.and_then(|s| s.parse().ok())
.unwrap_or(2000);
let mut cmd = cmd.stdout(Stdio::piped()).stderr(Stdio::piped());
let mut child = match cmd.spawn() { Ok(c) => c, Err(e) => { eprintln!("[ny-compiler] exe spawn failed: {}", e); return false; } };
let mut child = match cmd.spawn() {
Ok(c) => c,
Err(e) => {
eprintln!("[ny-compiler] exe spawn failed: {}", e);
return false;
}
};
let mut ch_stdout = child.stdout.take();
let mut ch_stderr = child.stderr.take();
let start = Instant::now();
let mut timed_out = false;
loop {
match child.try_wait() {
Ok(Some(_status)) => { break; }
Ok(Some(_status)) => {
break;
}
Ok(None) => {
if start.elapsed() >= Duration::from_millis(timeout_ms) { let _ = child.kill(); let _ = child.wait(); timed_out = true; break; }
if start.elapsed() >= Duration::from_millis(timeout_ms) {
let _ = child.kill();
let _ = child.wait();
timed_out = true;
break;
}
sleep(Duration::from_millis(10));
}
Err(e) => { eprintln!("[ny-compiler] exe wait error: {}", e); return false; }
Err(e) => {
eprintln!("[ny-compiler] exe wait error: {}", e);
return false;
}
}
}
let mut out_buf = Vec::new();
let mut err_buf = Vec::new();
if let Some(mut s) = ch_stdout { let _ = s.read_to_end(&mut out_buf); }
if let Some(mut s) = ch_stderr { let _ = s.read_to_end(&mut err_buf); }
if let Some(mut s) = ch_stdout {
let _ = s.read_to_end(&mut out_buf);
}
if let Some(mut s) = ch_stderr {
let _ = s.read_to_end(&mut err_buf);
}
if timed_out {
let head = String::from_utf8_lossy(&out_buf).chars().take(200).collect::<String>();
eprintln!("[ny-compiler] exe timeout after {} ms; stdout(head)='{}'", timeout_ms, head.replace('\n', "\\n"));
let head = String::from_utf8_lossy(&out_buf)
.chars()
.take(200)
.collect::<String>();
eprintln!(
"[ny-compiler] exe timeout after {} ms; stdout(head)='{}'",
timeout_ms,
head.replace('\n', "\\n")
);
return false;
}
let stdout = match String::from_utf8(out_buf) { Ok(s) => s, Err(_) => String::new() };
let stdout = match String::from_utf8(out_buf) {
Ok(s) => s,
Err(_) => String::new(),
};
let mut json_line = String::new();
for line in stdout.lines() { let t = line.trim(); if t.starts_with('{') && t.contains("\"version\"") && t.contains("\"kind\"") { json_line = t.to_string(); break; } }
for line in stdout.lines() {
let t = line.trim();
if t.starts_with('{') && t.contains("\"version\"") && t.contains("\"kind\"") {
json_line = t.to_string();
break;
}
}
if json_line.is_empty() {
if std::env::var("NYASH_CLI_VERBOSE").ok().as_deref() == Some("1") {
let head: String = stdout.chars().take(200).collect();
let errh: String = String::from_utf8_lossy(&err_buf).chars().take(200).collect();
let errh: String = String::from_utf8_lossy(&err_buf)
.chars()
.take(200)
.collect();
eprintln!("[ny-compiler] exe produced no JSON; stdout(head)='{}' stderr(head)='{}'", head.replace('\n', "\\n"), errh.replace('\n', "\\n"));
}
return false;
@ -299,12 +468,15 @@ impl NyashRunner {
Ok(module) => {
println!("🚀 Ny compiler EXE path (ny→json_v0) ON");
super::json_v0_bridge::maybe_dump_mir(&module);
let emit_only = std::env::var("NYASH_NY_COMPILER_EMIT_ONLY").unwrap_or_else(|_| "1".to_string()) == "1";
let emit_only = std::env::var("NYASH_NY_COMPILER_EMIT_ONLY")
.unwrap_or_else(|_| "1".to_string())
== "1";
if emit_only {
return false;
} else {
// Prefer PyVM when requested (reference semantics), regardless of BoxCall presence
let prefer_pyvm = std::env::var("NYASH_VM_USE_PY").ok().as_deref() == Some("1");
let prefer_pyvm =
std::env::var("NYASH_VM_USE_PY").ok().as_deref() == Some("1");
if prefer_pyvm {
if let Ok(py3) = which::which("python3") {
let runner = std::path::Path::new("tools/pyvm_runner.py");
@ -316,17 +488,41 @@ 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());
if std::env::var("NYASH_CLI_VERBOSE").ok().as_deref()
== Some("1")
{
eprintln!(
"[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 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();
.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) failed (status={})", code);
if std::env::var("NYASH_CLI_VERBOSE").ok().as_deref()
== Some("1")
{
eprintln!(
"❌ PyVM (selfhost) failed (status={})",
code
);
}
}
// Harmonize with interpreter path for smokes: print Result then exit code
@ -339,7 +535,10 @@ impl NyashRunner {
return true;
}
}
Err(e) => { eprintln!("[ny-compiler] json parse error: {}", e); return false; }
Err(e) => {
eprintln!("[ny-compiler] json parse error: {}", e);
return false;
}
}
}
}
@ -368,15 +567,25 @@ impl NyashRunner {
eprintln!("[ny-compiler] write inline failed: {}", e);
return false;
}
let exe = std::env::current_exe().unwrap_or_else(|_| std::path::PathBuf::from("target/release/nyash"));
let exe = std::env::current_exe()
.unwrap_or_else(|_| std::path::PathBuf::from("target/release/nyash"));
let mut cmd = std::process::Command::new(exe);
cmd.arg("--backend").arg("vm").arg(&inline_path);
cmd.env_remove("NYASH_USE_NY_COMPILER");
cmd.env_remove("NYASH_CLI_VERBOSE");
cmd.env("NYASH_JSON_ONLY", "1");
let timeout_ms: u64 = std::env::var("NYASH_NY_COMPILER_TIMEOUT_MS").ok().and_then(|s| s.parse().ok()).unwrap_or(2000);
let timeout_ms: u64 = std::env::var("NYASH_NY_COMPILER_TIMEOUT_MS")
.ok()
.and_then(|s| s.parse().ok())
.unwrap_or(2000);
let mut cmd = cmd.stdout(Stdio::piped()).stderr(Stdio::piped());
let mut child = match cmd.spawn() { Ok(c) => c, Err(e) => { eprintln!("[ny-compiler] spawn inline vm failed: {}", e); return false; } };
let mut child = match cmd.spawn() {
Ok(c) => c,
Err(e) => {
eprintln!("[ny-compiler] spawn inline vm failed: {}", e);
return false;
}
};
let mut ch_stdout = child.stdout.take();
let mut ch_stderr = child.stderr.take();
let start = Instant::now();
@ -386,39 +595,68 @@ impl NyashRunner {
Ok(Some(_)) => break,
Ok(None) => {
if start.elapsed() >= Duration::from_millis(timeout_ms) {
let _ = child.kill(); let _ = child.wait(); timed_out = true; break;
let _ = child.kill();
let _ = child.wait();
timed_out = true;
break;
}
sleep(Duration::from_millis(10));
}
Err(e) => { eprintln!("[ny-compiler] inline wait error: {}", e); break; }
Err(e) => {
eprintln!("[ny-compiler] inline wait error: {}", e);
break;
}
}
}
let mut out_buf = Vec::new();
if let Some(mut s) = ch_stdout { let _ = s.read_to_end(&mut out_buf); }
if let Some(mut s) = ch_stdout {
let _ = s.read_to_end(&mut out_buf);
}
if timed_out {
let head = String::from_utf8_lossy(&out_buf).chars().take(200).collect::<String>();
eprintln!("[ny-compiler] inline timeout after {} ms; stdout(head)='{}'", timeout_ms, head.replace('\n', "\\n"));
let head = String::from_utf8_lossy(&out_buf)
.chars()
.take(200)
.collect::<String>();
eprintln!(
"[ny-compiler] inline timeout after {} ms; stdout(head)='{}'",
timeout_ms,
head.replace('\n', "\\n")
);
}
raw = String::from_utf8_lossy(&out_buf).to_string();
}
let mut json_line = String::new();
for line in raw.lines() {
let t = line.trim();
if t.starts_with('{') && t.contains("\"version\"") && t.contains("\"kind\"") { json_line = t.to_string(); break; }
if t.starts_with('{') && t.contains("\"version\"") && t.contains("\"kind\"") {
json_line = t.to_string();
break;
}
}
if json_line.is_empty() {
return false;
}
if json_line.is_empty() { return false; }
match super::json_v0_bridge::parse_json_v0_to_module(&json_line) {
Ok(module) => {
super::json_v0_bridge::maybe_dump_mir(&module);
let emit_only = std::env::var("NYASH_NY_COMPILER_EMIT_ONLY").unwrap_or_else(|_| "1".to_string()) == "1";
if emit_only { return false; }
let emit_only = std::env::var("NYASH_NY_COMPILER_EMIT_ONLY")
.unwrap_or_else(|_| "1".to_string())
== "1";
if emit_only {
return false;
}
// Phase-15 policy: when NYASH_VM_USE_PY=1, prefer PyVM as reference executor
// regardless of BoxCall presence to ensure semantics parity (e.g., PHI merges).
let prefer_pyvm = std::env::var("NYASH_VM_USE_PY").ok().as_deref() == Some("1");
// Backward compatibility: if not preferring PyVM explicitly, still auto-enable when BoxCalls exist.
let needs_pyvm = !prefer_pyvm && module.functions.values().any(|f| {
f.blocks.values().any(|bb| bb.instructions.iter().any(|inst| matches!(inst, crate::mir::MirInstruction::BoxCall { .. })))
});
let needs_pyvm = !prefer_pyvm
&& module.functions.values().any(|f| {
f.blocks.values().any(|bb| {
bb.instructions.iter().any(|inst| {
matches!(inst, crate::mir::MirInstruction::BoxCall { .. })
})
})
});
if prefer_pyvm || needs_pyvm {
if let Ok(py3) = which::which("python3") {
let runner = std::path::Path::new("tools/pyvm_runner.py");
@ -426,22 +664,52 @@ impl NyashRunner {
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) {
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") {
let mode = if prefer_pyvm { "selfhost" } else { "selfhost-fallback" };
eprintln!("[Bridge] using PyVM ({}) → {}", mode, mir_json_path.display());
let mode = if prefer_pyvm {
"selfhost"
} else {
"selfhost-fallback"
};
eprintln!(
"[Bridge] using PyVM ({}) → {}",
mode,
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 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();
.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-fallback) failed (status={})", code);
eprintln!(
"❌ PyVM (selfhost-fallback) failed (status={})",
code
);
}
}
// Harmonize with interpreter path for smokes
@ -453,7 +721,10 @@ impl NyashRunner {
self.execute_mir_module(&module);
true
}
Err(e) => { eprintln!("❌ JSON v0 bridge error: {}", e); false }
Err(e) => {
eprintln!("❌ JSON v0 bridge error: {}", e);
false
}
}
}
}

View File

@ -3,26 +3,51 @@ use std::path::PathBuf;
/// Minimal task runner: read nyash.toml [env] and [tasks], run the named task via shell
pub(super) fn run_named_task(name: &str) -> Result<(), String> {
let cfg_path = "nyash.toml";
let text = std::fs::read_to_string(cfg_path).map_err(|e| format!("read {}: {}", cfg_path, e))?;
let doc = toml::from_str::<toml::Value>(&text).map_err(|e| format!("parse {}: {}", cfg_path, e))?;
let text =
std::fs::read_to_string(cfg_path).map_err(|e| format!("read {}: {}", cfg_path, e))?;
let doc =
toml::from_str::<toml::Value>(&text).map_err(|e| format!("parse {}: {}", cfg_path, e))?;
// Apply [env]
if let Some(env_tbl) = doc.get("env").and_then(|v| v.as_table()) {
for (k, v) in env_tbl.iter() {
if let Some(s) = v.as_str() { std::env::set_var(k, s); }
if let Some(s) = v.as_str() {
std::env::set_var(k, s);
}
}
}
// Lookup [tasks]
let tasks = doc.get("tasks").and_then(|v| v.as_table()).ok_or("[tasks] not found in nyash.toml")?;
let cmd = tasks.get(name).and_then(|v| v.as_str()).ok_or_else(|| format!("task '{}' not found", name))?;
let tasks = doc
.get("tasks")
.and_then(|v| v.as_table())
.ok_or("[tasks] not found in nyash.toml")?;
let cmd = tasks
.get(name)
.and_then(|v| v.as_str())
.ok_or_else(|| format!("task '{}' not found", name))?;
// Basic variable substitution
let root = std::env::current_dir().unwrap_or(PathBuf::from(".")).display().to_string();
let root = std::env::current_dir()
.unwrap_or(PathBuf::from("."))
.display()
.to_string();
let cmd = cmd.replace("{root}", &root);
// Run via shell
#[cfg(windows)]
let status = std::process::Command::new("cmd").args(["/C", &cmd]).status().map_err(|e| e.to_string())?;
let status = std::process::Command::new("cmd")
.args(["/C", &cmd])
.status()
.map_err(|e| e.to_string())?;
#[cfg(not(windows))]
let status = std::process::Command::new("sh").arg("-lc").arg(&cmd).status().map_err(|e| e.to_string())?;
if !status.success() { return Err(format!("task '{}' failed with status {:?}", name, status.code())); }
let status = std::process::Command::new("sh")
.arg("-lc")
.arg(&cmd)
.status()
.map_err(|e| e.to_string())?;
if !status.success() {
return Err(format!(
"task '{}' failed with status {:?}",
name,
status.code()
));
}
Ok(())
}

View File

@ -1,7 +1,9 @@
//! Runner tracing helpers (verbose-guarded)
/// Return whether CLI verbose logging is enabled
pub fn cli_verbose() -> bool { crate::config::env::cli_verbose() }
pub fn cli_verbose() -> bool {
crate::config::env::cli_verbose()
}
#[macro_export]
macro_rules! cli_v {
@ -11,4 +13,6 @@ macro_rules! cli_v {
}
/// Unstructured trace output function used by pipeline helpers
pub fn log<S: AsRef<str>>(msg: S) { eprintln!("{}", msg.as_ref()); }
pub fn log<S: AsRef<str>>(msg: S) {
eprintln!("{}", msg.as_ref());
}