Files
hakorune/src/runner/modes/pyvm.rs

153 lines
6.8 KiB
Rust
Raw Normal View History

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.
pub fn execute_pyvm_only(runner: &NyashRunner, filename: &str) {
if std::env::var("NYASH_PYVM_TRACE").ok().as_deref() == Some("1") { eprintln!("[pyvm] entry"); }
// 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);
}
};
// Using handling: AST-prelude collection (legacy inlining removed)
let mut code = if crate::config::env::enable_using() {
match crate::runner::modes::common_util::resolve::resolve_prelude_paths_profiled(runner, &code, filename) {
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
}
Err(e) => { eprintln!("{}", e); process::exit(1); }
}
} else { code };
// 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);
if c == '\n' { in_line = false; }
continue;
}
if in_block {
out.push(c);
if c == '*' && matches!(it.peek(), Some('/')) { out.push('/'); it.next(); in_block = false; }
continue;
}
if in_str {
out.push(c);
if c == '\\' { if let Some(nc) = it.next() { out.push(nc); } continue; }
if c == '"' { in_str = false; }
continue;
}
match c {
'"' => { 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('#'); }
'|' => {
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('|'); }
}
'&' => {
if matches!(it.peek(), Some('&')) { out.push_str(" and "); it.next(); } else { out.push('&'); }
}
_ => out.push(c),
}
}
out
}
code = normalize_logical_ops(&code);
// Parse to AST
if std::env::var("NYASH_PYVM_DUMP_CODE").ok().as_deref() == Some("1") {
eprintln!("[pyvm-code]\n{}", code);
}
let ast = match NyashParser::parse_from_string(&code) {
Ok(ast) => ast,
Err(e) => {
feat(stage-b): Add FLOW keyword support + fix Stage-3 keyword conflicts ## ✅ Fixed Issues ### 1. `local` keyword tokenization (commit 9aab64f7) - Added Stage-3 gate for LOCAL/TRY/CATCH/THROW keywords - LOCAL now only active when NYASH_PARSER_STAGE3=1 ### 2. `env.local.get` keyword conflict - File: `lang/src/compiler/entry/compiler_stageb.hako:21-23` - Problem: `.local` in member access tokenized as `.LOCAL` keyword - Fix: Commented out `env.local.get("HAKO_SOURCE")` line - Fallback: Use `--source` argument (still functional) ### 3. `flow` keyword missing - Added FLOW to TokenType enum (`src/tokenizer/kinds.rs`) - Added "flow" → TokenType::FLOW mapping (`src/tokenizer/lex_ident.rs`) - Added FLOW to Stage-3 gate (requires NYASH_PARSER_STAGE3=1) - Added FLOW to parser statement dispatch (`src/parser/statements/mod.rs`) - Added FLOW to declaration handler (`src/parser/statements/declarations.rs`) - Updated box_declaration parser to accept BOX or FLOW (`src/parser/declarations/box_definition.rs`) - Treat `flow FooBox {}` as syntactic sugar for `box FooBox {}` ### 4. Module namespace conversion - Renamed `lang.compiler.builder.ssa.local` → `localvar` (avoid keyword) - Renamed file `local.hako` → `local_ssa.hako` - Converted 152 path-based using statements to namespace format - Added 26+ entries to `nyash.toml` [modules] section ## ⚠️ Remaining Issues ### Stage-B selfhost compiler performance - Stage-B compiler not producing output (hangs/times out after 10+ seconds) - Excessive PHI debug output suggests compilation loop issue - Needs investigation: infinite loop or N² algorithm in hako compiler ### Fallback JSON version mismatch - Rust fallback (`--emit-mir-json`) emits MIR v1 JSON (schema_version: "1.0") - Smoke tests expect MIR v0 JSON (`"version":0, "kind":"Program"`) - stageb_helpers.sh fallback needs adjustment ## Test Status - Parse errors: FIXED ✅ - Keyword conflicts: FIXED ✅ - Stage-B smoke tests: STILL FAILING ❌ (performance issue) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-02 04:13:17 +09:00
eprintln!("❌ Parse error in {}: {}", filename, e);
process::exit(1);
}
};
let ast = crate::r#macro::maybe_expand_and_dump(&ast, false);
let ast = crate::runner::modes::macro_child::normalize_core_pass(&ast);
// 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
if std::env::var("NYASH_VM_ESCAPE_ANALYSIS").ok().as_deref() == Some("1") {
let removed = nyash_rust::mir::passes::escape::escape_elide_barriers_vm(&mut compile_result.module);
if removed > 0 { crate::cli_v!("[PyVM] escape_elide_barriers: removed {} barriers", removed); }
}
// Optional: delegate to Ny selfhost executor (Stage 0 scaffold: no-op)
if std::env::var("NYASH_SELFHOST_EXEC").ok().as_deref() == Some("1") {
// 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");
if let Err(e) = crate::runner::mir_json_emit::emit_mir_json_for_harness_bin(&compile_result.module, &mir_json_path) {
eprintln!("❌ Selfhost MIR JSON emit error: {}", e);
process::exit(1);
}
// Resolve nyash executable and runner path
let exe = std::env::current_exe().unwrap_or_else(|_| std::path::PathBuf::from("target/release/nyash"));
let runner = std::path::Path::new("apps/selfhost-runtime/runner.nyash");
if !runner.exists() {
eprintln!("❌ Selfhost runner missing: {}", runner.display());
process::exit(1);
}
let mut cmd = std::process::Command::new(&exe);
crate::runner::child_env::apply_core_wrapper_env(&mut cmd);
cmd.arg("--backend").arg("vm")
.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()
.unwrap_or_else(|e| { eprintln!("❌ spawn selfhost runner failed: {}", e); std::process::exit(1); });
let code = status.code().unwrap_or(1);
process::exit(code);
}
// Delegate to common PyVM harness
match crate::runner::modes::common_util::pyvm::run_pyvm_harness_lib(&compile_result.module, "pyvm") {
Ok(code) => { process::exit(code); }
Err(e) => { eprintln!("❌ PyVM error: {}", e); process::exit(1); }
}
}