2025-09-17 16:11:01 +09:00
|
|
|
use super::super::NyashRunner;
|
|
|
|
|
use nyash_rust::{mir::MirCompiler, parser::NyashParser};
|
|
|
|
|
use std::{fs, process};
|
|
|
|
|
|
|
|
|
|
/// Execute using PyVM only (no Rust VM runtime). Emits MIR(JSON) and invokes tools/pyvm_runner.py.
|
2025-09-21 08:53:00 +09:00
|
|
|
pub fn execute_pyvm_only(runner: &NyashRunner, filename: &str) {
|
2025-11-21 06:25:17 +09:00
|
|
|
if crate::config::env::env_bool("NYASH_PYVM_TRACE") {
|
|
|
|
|
eprintln!("[pyvm] entry");
|
|
|
|
|
}
|
2025-09-17 16:11:01 +09:00
|
|
|
// 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);
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
2025-09-26 00:27:02 +09:00
|
|
|
// Using handling: AST-prelude collection (legacy inlining removed)
|
2025-09-22 07:54:25 +09:00
|
|
|
let mut code = if crate::config::env::enable_using() {
|
2025-11-21 06:25:17 +09:00
|
|
|
match crate::runner::modes::common_util::resolve::resolve_prelude_paths_profiled(
|
|
|
|
|
runner, &code, filename,
|
|
|
|
|
) {
|
2025-09-26 00:27:02 +09:00
|
|
|
Ok((clean, paths)) => {
|
|
|
|
|
if !paths.is_empty() && !crate::config::env::using_ast_enabled() {
|
|
|
|
|
eprintln!("❌ using: AST prelude merge is disabled in this profile. Enable NYASH_USING_AST=1 or remove 'using' lines.");
|
|
|
|
|
process::exit(1);
|
|
|
|
|
}
|
|
|
|
|
// PyVM pipeline currently does not merge prelude ASTs here; rely on main/common path for that.
|
|
|
|
|
clean
|
|
|
|
|
}
|
2025-11-21 06:25:17 +09:00
|
|
|
Err(e) => {
|
|
|
|
|
eprintln!("❌ {}", e);
|
|
|
|
|
process::exit(1);
|
|
|
|
|
}
|
2025-09-21 08:53:00 +09:00
|
|
|
}
|
2025-11-21 06:25:17 +09:00
|
|
|
} else {
|
|
|
|
|
code
|
|
|
|
|
};
|
2025-09-21 08:53:00 +09:00
|
|
|
|
2025-09-22 07:54:25 +09:00
|
|
|
// Dev sugar pre-expand: line-head @name[:T] = expr → local name[:T] = expr
|
|
|
|
|
code = crate::runner::modes::common_util::resolve::preexpand_at_local(&code);
|
|
|
|
|
|
|
|
|
|
// Normalize logical operators for Stage-2 parser: translate '||'/'&&' to 'or'/'and' outside strings/comments
|
|
|
|
|
fn normalize_logical_ops(src: &str) -> String {
|
|
|
|
|
let mut out = String::with_capacity(src.len());
|
|
|
|
|
let mut it = src.chars().peekable();
|
|
|
|
|
let mut in_str = false;
|
|
|
|
|
let mut in_line = false;
|
|
|
|
|
let mut in_block = false;
|
|
|
|
|
while let Some(c) = it.next() {
|
|
|
|
|
if in_line {
|
|
|
|
|
out.push(c);
|
2025-11-21 06:25:17 +09:00
|
|
|
if c == '\n' {
|
|
|
|
|
in_line = false;
|
|
|
|
|
}
|
2025-09-22 07:54:25 +09:00
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
if in_block {
|
|
|
|
|
out.push(c);
|
2025-11-21 06:25:17 +09:00
|
|
|
if c == '*' && matches!(it.peek(), Some('/')) {
|
|
|
|
|
out.push('/');
|
|
|
|
|
it.next();
|
|
|
|
|
in_block = false;
|
|
|
|
|
}
|
2025-09-22 07:54:25 +09:00
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
if in_str {
|
|
|
|
|
out.push(c);
|
2025-11-21 06:25:17 +09:00
|
|
|
if c == '\\' {
|
|
|
|
|
if let Some(nc) = it.next() {
|
|
|
|
|
out.push(nc);
|
|
|
|
|
}
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
if c == '"' {
|
|
|
|
|
in_str = false;
|
|
|
|
|
}
|
2025-09-22 07:54:25 +09:00
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
match c {
|
2025-11-21 06:25:17 +09:00
|
|
|
'"' => {
|
|
|
|
|
in_str = true;
|
|
|
|
|
out.push(c);
|
|
|
|
|
}
|
|
|
|
|
'/' => match it.peek() {
|
|
|
|
|
Some('/') => {
|
|
|
|
|
out.push('/');
|
|
|
|
|
out.push('/');
|
|
|
|
|
it.next();
|
|
|
|
|
in_line = true;
|
|
|
|
|
}
|
|
|
|
|
Some('*') => {
|
|
|
|
|
out.push('/');
|
|
|
|
|
out.push('*');
|
|
|
|
|
it.next();
|
|
|
|
|
in_block = true;
|
|
|
|
|
}
|
|
|
|
|
_ => out.push('/'),
|
|
|
|
|
},
|
|
|
|
|
'#' => {
|
|
|
|
|
in_line = true;
|
|
|
|
|
out.push('#');
|
2025-09-22 07:54:25 +09:00
|
|
|
}
|
|
|
|
|
'|' => {
|
2025-11-21 06:25:17 +09:00
|
|
|
if matches!(it.peek(), Some('|')) {
|
|
|
|
|
out.push_str(" or ");
|
|
|
|
|
it.next();
|
|
|
|
|
} else if matches!(it.peek(), Some('>')) {
|
|
|
|
|
out.push('|');
|
|
|
|
|
out.push('>');
|
|
|
|
|
it.next();
|
|
|
|
|
} else {
|
|
|
|
|
out.push('|');
|
|
|
|
|
}
|
2025-09-22 07:54:25 +09:00
|
|
|
}
|
|
|
|
|
'&' => {
|
2025-11-21 06:25:17 +09:00
|
|
|
if matches!(it.peek(), Some('&')) {
|
|
|
|
|
out.push_str(" and ");
|
|
|
|
|
it.next();
|
|
|
|
|
} else {
|
|
|
|
|
out.push('&');
|
|
|
|
|
}
|
2025-09-22 07:54:25 +09:00
|
|
|
}
|
|
|
|
|
_ => out.push(c),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
out
|
|
|
|
|
}
|
|
|
|
|
code = normalize_logical_ops(&code);
|
|
|
|
|
|
2025-09-17 16:11:01 +09:00
|
|
|
// Parse to AST
|
2025-11-06 15:41:52 +09:00
|
|
|
if crate::config::env::env_bool("NYASH_PYVM_DUMP_CODE") {
|
2025-09-22 07:54:25 +09:00
|
|
|
eprintln!("[pyvm-code]\n{}", code);
|
|
|
|
|
}
|
2025-11-21 06:25:17 +09:00
|
|
|
let ast = match NyashParser::parse_from_string(&code) {
|
|
|
|
|
Ok(ast) => ast,
|
|
|
|
|
Err(e) => {
|
|
|
|
|
crate::runner::modes::common_util::diag::print_parse_error_with_context(
|
|
|
|
|
filename,
|
|
|
|
|
&code,
|
|
|
|
|
&e,
|
|
|
|
|
);
|
|
|
|
|
process::exit(1);
|
|
|
|
|
}
|
|
|
|
|
};
|
2025-09-19 22:27:59 +09:00
|
|
|
let ast = crate::r#macro::maybe_expand_and_dump(&ast, false);
|
2025-09-20 08:39:40 +09:00
|
|
|
let ast = crate::runner::modes::macro_child::normalize_core_pass(&ast);
|
2025-09-17 16:11:01 +09:00
|
|
|
|
|
|
|
|
// Compile to MIR (respect default optimizer setting)
|
|
|
|
|
let mut mir_compiler = MirCompiler::with_options(true);
|
|
|
|
|
let mut compile_result = match mir_compiler.compile(ast) {
|
|
|
|
|
Ok(result) => result,
|
|
|
|
|
Err(e) => {
|
|
|
|
|
eprintln!("❌ MIR compilation error: {}", e);
|
|
|
|
|
process::exit(1);
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// Optional: VM-only escape analysis elision pass retained for parity with VM path
|
2025-11-06 15:41:52 +09:00
|
|
|
if crate::config::env::env_bool("NYASH_VM_ESCAPE_ANALYSIS") {
|
2025-11-21 06:25:17 +09:00
|
|
|
let removed =
|
|
|
|
|
nyash_rust::mir::passes::escape::escape_elide_barriers_vm(&mut compile_result.module);
|
|
|
|
|
if removed > 0 {
|
|
|
|
|
crate::cli_v!("[PyVM] escape_elide_barriers: removed {} barriers", removed);
|
|
|
|
|
}
|
2025-09-17 16:11:01 +09:00
|
|
|
}
|
|
|
|
|
|
2025-09-22 21:52:39 +09:00
|
|
|
// Optional: delegate to Ny selfhost executor (Stage 0 scaffold: no-op)
|
2025-11-06 15:41:52 +09:00
|
|
|
if crate::config::env::env_bool("NYASH_SELFHOST_EXEC") {
|
2025-09-22 21:52:39 +09:00
|
|
|
// Emit MIR JSON to a temp file and invoke Ny runner script.
|
|
|
|
|
let tmp_dir = std::path::Path::new("tmp");
|
|
|
|
|
let _ = std::fs::create_dir_all(tmp_dir);
|
|
|
|
|
let mir_json_path = tmp_dir.join("nyash_selfhost_mir.json");
|
2025-11-21 06:25:17 +09:00
|
|
|
if let Err(e) = crate::runner::mir_json_emit::emit_mir_json_for_harness_bin(
|
|
|
|
|
&compile_result.module,
|
|
|
|
|
&mir_json_path,
|
|
|
|
|
) {
|
2025-09-22 21:52:39 +09:00
|
|
|
eprintln!("❌ Selfhost MIR JSON emit error: {}", e);
|
|
|
|
|
process::exit(1);
|
|
|
|
|
}
|
|
|
|
|
// Resolve nyash executable and runner path
|
2025-11-21 06:25:17 +09:00
|
|
|
let exe = std::env::current_exe()
|
|
|
|
|
.unwrap_or_else(|_| std::path::PathBuf::from("target/release/nyash"));
|
2025-11-06 15:41:52 +09:00
|
|
|
let runner = std::path::Path::new("apps/selfhost-runtime/runner.hako");
|
2025-09-22 21:52:39 +09:00
|
|
|
if !runner.exists() {
|
|
|
|
|
eprintln!("❌ Selfhost runner missing: {}", runner.display());
|
|
|
|
|
process::exit(1);
|
|
|
|
|
}
|
|
|
|
|
let mut cmd = std::process::Command::new(&exe);
|
2025-11-02 15:43:43 +09:00
|
|
|
crate::runner::child_env::apply_core_wrapper_env(&mut cmd);
|
2025-11-21 06:25:17 +09:00
|
|
|
cmd.arg("--backend")
|
|
|
|
|
.arg("vm")
|
2025-09-22 21:52:39 +09:00
|
|
|
.arg(runner)
|
|
|
|
|
.arg("--")
|
|
|
|
|
.arg(mir_json_path.display().to_string());
|
|
|
|
|
// Optional: pass box pref to child (ny|plugin)
|
|
|
|
|
if let Ok(pref) = std::env::var("NYASH_SELFHOST_BOX_PREF") {
|
|
|
|
|
let p = pref.to_lowercase();
|
|
|
|
|
if p == "ny" || p == "plugin" {
|
|
|
|
|
cmd.arg(format!("--box-pref={}", p));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
let status = cmd
|
|
|
|
|
// Avoid recursive selfhost delegation inside the child.
|
|
|
|
|
.env_remove("NYASH_SELFHOST_EXEC")
|
|
|
|
|
.status()
|
2025-11-21 06:25:17 +09:00
|
|
|
.unwrap_or_else(|e| {
|
|
|
|
|
eprintln!("❌ spawn selfhost runner failed: {}", e);
|
|
|
|
|
std::process::exit(1);
|
|
|
|
|
});
|
2025-09-22 21:52:39 +09:00
|
|
|
let code = status.code().unwrap_or(1);
|
|
|
|
|
process::exit(code);
|
|
|
|
|
}
|
|
|
|
|
|
2025-09-19 02:07:38 +09:00
|
|
|
// Delegate to common PyVM harness
|
2025-11-21 06:25:17 +09:00
|
|
|
match crate::runner::modes::common_util::pyvm::run_pyvm_harness_lib(
|
|
|
|
|
&compile_result.module,
|
|
|
|
|
"pyvm",
|
|
|
|
|
) {
|
|
|
|
|
Ok(code) => {
|
|
|
|
|
process::exit(code);
|
|
|
|
|
}
|
|
|
|
|
Err(e) => {
|
|
|
|
|
eprintln!("❌ PyVM error: {}", e);
|
|
|
|
|
process::exit(1);
|
|
|
|
|
}
|
2025-09-17 16:11:01 +09:00
|
|
|
}
|
|
|
|
|
}
|