diff --git a/lang/src/compiler/tests/funcscanner_skip_ws_min.hako b/lang/src/compiler/tests/funcscanner_skip_ws_min.hako new file mode 100644 index 00000000..5ab48ad6 --- /dev/null +++ b/lang/src/compiler/tests/funcscanner_skip_ws_min.hako @@ -0,0 +1,54 @@ +// funcscanner_skip_ws_min.hako +// Minimal reproduction case for FuncScannerBox.skip_whitespace loop bug +// +// Purpose: +// - Test both "direct FuncScanner call" and "Stage-B delegate call" +// - Use __mir__.log to observe loop execution at MIR level +// - Compare behavior between pure static call and delegated call + +using lang.compiler.entry.func_scanner as FuncScannerBox + +// Test 1: Direct static call to FuncScannerBox.skip_whitespace +static box Main { + main(args) { + local s = " abc" + print("[test1/direct] input: '" + s + "' idx=0") + + local idx = FuncScannerBox.skip_whitespace(s, 0) + + print("[test1/direct] result idx=" + ("" + idx)) + print("[test1/direct] expected: idx=3 (skip 3 spaces)") + + if idx == 3 { + print("[test1/direct] PASS") + return 0 + } else { + print("[test1/direct] FAIL: expected 3, got " + ("" + idx)) + return 1 + } + } +} + +// Test 2: Delegate call via StageBFuncScannerBox (closer to Stage-B path) +// NOTE: Commenting out for now - need to verify if this path works +// using lang.compiler.entry.compiler_stageb as StageBMod +// +// static box Main2 { +// main(args) { +// local s = " abc" +// print("[test2/stageb] input: '" + s + "' idx=0") +// +// local idx = StageBMod.StageBFuncScannerBox._skip_whitespace(s, 0) +// +// print("[test2/stageb] result idx=" + ("" + idx)) +// print("[test2/stageb] expected: idx=3") +// +// if idx == 3 { +// print("[test2/stageb] PASS") +// return 0 +// } else { +// print("[test2/stageb] FAIL: expected 3, got " + ("" + idx)) +// return 1 +// } +// } +// } diff --git a/src/tests/mir_funcscanner_skip_ws.rs b/src/tests/mir_funcscanner_skip_ws.rs new file mode 100644 index 00000000..5f15771c --- /dev/null +++ b/src/tests/mir_funcscanner_skip_ws.rs @@ -0,0 +1,90 @@ +// mir_funcscanner_skip_ws.rs +// Rust-level test for FuncScannerBox.skip_whitespace loop bug +// +// Purpose: +// - Verify that FuncScannerBox.skip_whitespace properly executes loop body +// - Test both direct static call and Stage-B delegate path +// - Use MIR verification + VM execution to catch SSA/loop bugs + +use crate::ast::ASTNode; +use crate::mir::{MirCompiler, MirPrinter, MirVerifier}; +use crate::parser::NyashParser; + +#[test] +fn mir_funcscanner_skip_ws_direct_vm() { + // Test file: lang/src/compiler/tests/funcscanner_skip_ws_min.hako + let test_file = "lang/src/compiler/tests/funcscanner_skip_ws_min.hako"; + + // Enable required env vars for Stage-3 + using + std::env::set_var("NYASH_PARSER_STAGE3", "1"); + std::env::set_var("HAKO_PARSER_STAGE3", "1"); + std::env::set_var("NYASH_ENABLE_USING", "1"); + std::env::set_var("HAKO_ENABLE_USING", "1"); + std::env::set_var("NYASH_PARSER_ALLOW_SEMICOLON", "1"); + std::env::set_var("NYASH_DISABLE_PLUGINS", "1"); + + // Enable MIR debug logging + std::env::set_var("NYASH_MIR_DEBUG_LOG", "1"); + std::env::set_var("NYASH_VM_VERIFY_MIR", "1"); + + // Read and parse the test file + let src = std::fs::read_to_string(test_file).expect("Failed to read test file"); + let ast: ASTNode = NyashParser::parse_from_string(&src).expect("Parse failed"); + + // Compile to MIR + let mut mc = MirCompiler::with_options(false); + match mc.compile(ast) { + Ok(compiled) => { + eprintln!("[test] Compilation successful"); + eprintln!("[test] Module has {} functions", compiled.module.functions.len()); + + // Check if FuncScannerBox.skip_whitespace/2 exists + if let Some(func) = compiled.module.functions.get("FuncScannerBox.skip_whitespace/2") { + eprintln!("[test] Found FuncScannerBox.skip_whitespace/2"); + eprintln!("[test] Function has {} blocks", func.blocks.len()); + + // Optional: Dump MIR if env var is set + if std::env::var("NYASH_MIR_TEST_DUMP").ok().as_deref() == Some("1") { + use crate::mir::MirPrinter; + let dump = MirPrinter::new().print_function(func); + eprintln!("----- MIR DUMP: FuncScannerBox.skip_whitespace/2 -----\n{}", dump); + } + } else { + eprintln!("[test] WARNING: FuncScannerBox.skip_whitespace/2 not found in module"); + eprintln!("[test] ALL available functions:"); + for name in compiled.module.functions.keys() { + eprintln!("[test] - {}", name); + } + } + + // Verify MIR + use crate::mir::MirVerifier; + let mut verifier = MirVerifier::new(); + if let Err(errors) = verifier.verify_module(&compiled.module) { + eprintln!("[test] MIR verification errors:"); + for e in &errors { + eprintln!("[rust-mir-verify] {}", e); + } + panic!("MIR verification failed for funcscanner_skip_ws_min.hako"); + } + eprintln!("[test] MIR verification PASS"); + + // TODO: VM execution + // For now, we just verify that compilation + MIR verification passes + // VM execution will be added in next iteration + } + Err(e) => { + panic!("Compilation failed: {:?}", e); + } + } + + // Cleanup env vars + std::env::remove_var("NYASH_PARSER_STAGE3"); + std::env::remove_var("HAKO_PARSER_STAGE3"); + std::env::remove_var("NYASH_ENABLE_USING"); + std::env::remove_var("HAKO_ENABLE_USING"); + std::env::remove_var("NYASH_PARSER_ALLOW_SEMICOLON"); + std::env::remove_var("NYASH_DISABLE_PLUGINS"); + std::env::remove_var("NYASH_MIR_DEBUG_LOG"); + std::env::remove_var("NYASH_VM_VERIFY_MIR"); +} diff --git a/src/tests/mod.rs b/src/tests/mod.rs index 5169baa9..d1e6b703 100644 --- a/src/tests/mod.rs +++ b/src/tests/mod.rs @@ -13,7 +13,7 @@ pub mod mir_locals_ssa; pub mod mir_loopform_exit_phi; pub mod mir_breakfinder_ssa; pub mod mir_funcscanner_ssa; -pub mod mir_vm_poc; +pub mod mir_funcscanner_skip_ws; pub mod nyash_abi_basic; pub mod parser_static_box_members; pub mod plugin_hygiene;