2025-09-16 00:01:31 +09:00
/*!
* Runner selfhost helpers — Ny compiler pipeline ( Ny -> JSON v0 )
*
* Transitional shim : provides a stable entrypoint from callers , while the
* heavy implementation currently lives in modes / common . rs . Next step will
* migrate the full implementation here .
* /
use super ::* ;
2025-09-19 22:27:59 +09:00
use nyash_rust ::{ mir ::MirCompiler , parser ::NyashParser } ;
2025-09-17 07:43:07 +09:00
use std ::{ fs , process } ;
2025-09-16 00:01:31 +09:00
impl NyashRunner {
/// Selfhost (Ny -> JSON v0) pipeline: EXE/VM/Python フォールバック含む
pub ( crate ) fn try_run_selfhost_pipeline ( & self , filename : & str ) -> bool {
use std ::io ::Write ;
// Read input source
let code = match fs ::read_to_string ( filename ) {
Ok ( c ) = > c ,
2025-09-17 07:43:07 +09:00
Err ( e ) = > {
eprintln! ( " [ny-compiler] read error: {} " , e ) ;
return false ;
}
2025-09-16 00:01:31 +09:00
} ;
// Optional Phase-15: strip `using` lines and register modules (same policy as execute_nyash_file)
let mut code_ref : std ::borrow ::Cow < '_ , str > = std ::borrow ::Cow ::Borrowed ( & code ) ;
2025-09-19 02:07:38 +09:00
if crate ::config ::env ::enable_using ( ) {
match crate ::runner ::modes ::common_util ::resolve ::strip_using_and_register ( self , & code , filename ) {
Ok ( s ) = > { code_ref = std ::borrow ::Cow ::Owned ( s ) ; }
Err ( e ) = > { eprintln! ( " [ny-compiler] {} " , e ) ; return false ; }
2025-09-16 00:01:31 +09:00
}
}
// Write to tmp/ny_parser_input.ny (as expected by Ny parser v0), unless forced to reuse existing tmp
2025-09-17 06:55:39 +09:00
let use_tmp_only = crate ::config ::env ::ny_compiler_use_tmp_only ( ) ;
2025-09-16 00:01:31 +09:00
let tmp_dir = std ::path ::Path ::new ( " tmp " ) ;
if let Err ( e ) = std ::fs ::create_dir_all ( tmp_dir ) {
eprintln! ( " [ny-compiler] mkdir tmp failed: {} " , e ) ;
return false ;
}
2025-09-19 22:27:59 +09:00
// Optional macro pre‑ expand path for selfhost
// Default: auto when macro engine is enabled (safe: PyVM only)
// Gate: NYASH_MACRO_SELFHOST_PRE_EXPAND={1|auto|0}
{
let preenv = std ::env ::var ( " NYASH_MACRO_SELFHOST_PRE_EXPAND " )
. ok ( )
. or_else ( | | if crate ::r#macro ::enabled ( ) { Some ( " auto " . to_string ( ) ) } else { None } ) ;
let do_pre = match preenv . as_deref ( ) {
Some ( " 1 " ) = > true ,
Some ( " auto " ) = > crate ::r#macro ::enabled ( ) & & crate ::config ::env ::vm_use_py ( ) ,
_ = > false ,
} ;
if do_pre & & crate ::r#macro ::enabled ( ) {
crate ::cli_v! ( " [ny-compiler] selfhost macro pre-expand: engaging (mode={:?}) " , preenv ) ;
match NyashParser ::parse_from_string ( code_ref . as_ref ( ) ) {
Ok ( ast0 ) = > {
let ast = crate ::r#macro ::maybe_expand_and_dump ( & ast0 , false ) ;
// Compile to MIR and execute (respect VM/PyVM policy similar to vm mode)
let mut mir_compiler = MirCompiler ::with_options ( true ) ;
match mir_compiler . compile ( ast ) {
Ok ( result ) = > {
let prefer_pyvm = crate ::config ::env ::vm_use_py ( ) ;
if prefer_pyvm {
if let Ok ( code ) = crate ::runner ::modes ::common_util ::pyvm ::run_pyvm_harness_lib ( & result . module , " selfhost-preexpand " ) {
println! ( " Result: {} " , code ) ;
std ::process ::exit ( code ) ;
} else {
eprintln! ( " ❌ PyVM error (selfhost-preexpand) " ) ;
std ::process ::exit ( 1 ) ;
}
} else {
// For now, only PyVM path is supported in pre-expand mode; fall back otherwise.
crate ::cli_v! ( " [ny-compiler] pre-expand path requires NYASH_VM_USE_PY=1; falling back to default selfhost " ) ;
return false ;
}
}
Err ( e ) = > {
eprintln! ( " [ny-compiler] pre-expand compile error: {} " , e ) ;
return false ;
}
}
}
Err ( e ) = > {
eprintln! ( " [ny-compiler] pre-expand parse error: {} " , e ) ;
return false ;
}
}
}
}
2025-09-16 00:01:31 +09:00
let tmp_path = tmp_dir . join ( " ny_parser_input.ny " ) ;
if ! use_tmp_only {
match std ::fs ::File ::create ( & tmp_path ) {
Ok ( mut f ) = > {
if let Err ( e ) = f . write_all ( code_ref . as_bytes ( ) ) {
eprintln! ( " [ny-compiler] write tmp failed: {} " , e ) ;
return false ;
}
}
2025-09-17 07:43:07 +09:00
Err ( e ) = > {
eprintln! ( " [ny-compiler] open tmp failed: {} " , e ) ;
return false ;
}
2025-09-16 00:01:31 +09:00
}
}
2025-09-17 01:55:08 +09:00
// Preferred: run Ny selfhost compiler program (apps/selfhost-compiler/compiler.nyash)
// This avoids inline embedding pitfalls and supports Stage-3 gating via args.
{
2025-09-18 13:35:38 +09:00
use crate ::runner ::modes ::common_util ::selfhost ::{ child , json } ;
2025-09-17 07:43:07 +09:00
let exe = std ::env ::current_exe ( )
. unwrap_or_else ( | _ | std ::path ::PathBuf ::from ( " target/release/nyash " ) ) ;
2025-09-17 01:55:08 +09:00
let parser_prog = std ::path ::Path ::new ( " apps/selfhost-compiler/compiler.nyash " ) ;
if parser_prog . exists ( ) {
2025-09-18 13:35:38 +09:00
// Build extra args forwarded to child program
let mut extra : Vec < & str > = Vec ::new ( ) ;
2025-09-17 06:55:39 +09:00
if crate ::config ::env ::ny_compiler_min_json ( ) {
2025-09-18 13:35:38 +09:00
extra . extend ( [ " -- " , " --min-json " ] ) ;
2025-09-17 01:55:08 +09:00
}
2025-09-18 13:35:38 +09:00
extra . extend ( [ " -- " , " --read-tmp " ] ) ;
2025-09-17 06:55:39 +09:00
if crate ::config ::env ::ny_compiler_stage3 ( ) {
2025-09-18 13:35:38 +09:00
extra . extend ( [ " -- " , " --stage3 " ] ) ;
2025-09-17 01:55:08 +09:00
}
2025-09-17 06:55:39 +09:00
let timeout_ms : u64 = crate ::config ::env ::ny_compiler_timeout_ms ( ) ;
2025-09-18 13:35:38 +09:00
if let Some ( line ) = child ::run_ny_program_capture_json (
& exe ,
parser_prog ,
timeout_ms ,
& extra ,
& [ " NYASH_USE_NY_COMPILER " , " NYASH_CLI_VERBOSE " ] ,
& [ ( " NYASH_JSON_ONLY " , " 1 " ) ] ,
) {
match json ::parse_json_v0_line ( & 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 ;
2025-09-17 01:55:08 +09:00
}
2025-09-18 13:35:38 +09:00
// Prefer PyVM path when requested
if crate ::config ::env ::vm_use_py ( ) {
if let Some ( code ) = crate ::runner ::modes ::common_util ::selfhost ::json ::run_pyvm_module ( & module , " selfhost " ) {
println! ( " Result: {} " , code ) ;
std ::process ::exit ( code ) ;
2025-09-17 01:55:08 +09:00
}
}
self . execute_mir_module ( & module ) ;
return true ;
}
2025-09-18 13:35:38 +09:00
Err ( e ) = > {
eprintln! ( " [ny-compiler] json parse error (child): {} " , e ) ;
2025-09-17 01:55:08 +09:00
}
}
}
}
}
// 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 " ) {
2025-09-17 07:43:07 +09:00
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 ) ;
2025-09-17 20:33:19 +09:00
let timeout_ms : u64 = std ::env ::var ( " NYASH_NY_COMPILER_TIMEOUT_MS " )
. ok ( )
. and_then ( | s | s . parse ( ) . ok ( ) )
. unwrap_or ( 2000 ) ;
let out = match super ::modes ::common_util ::io ::spawn_with_timeout ( cmd , timeout_ms ) {
2025-09-17 07:43:07 +09:00
Ok ( o ) = > o ,
2025-09-17 20:33:19 +09:00
Err ( e ) = > { eprintln! ( " [ny-compiler] python harness failed: {} " , e ) ; return false ; }
2025-09-17 07:43:07 +09:00
} ;
2025-09-17 20:33:19 +09:00
if ! out . timed_out {
2025-09-18 13:35:38 +09:00
if let Ok ( s ) = String ::from_utf8 ( out . stdout ) {
if let Some ( line ) = crate ::runner ::modes ::common_util ::selfhost ::json ::first_json_v0_line ( & s ) {
2025-09-17 07:43:07 +09:00
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)
2025-09-19 02:07:38 +09:00
if std ::env ::var ( " NYASH_VM_USE_PY " ) . ok ( ) . as_deref ( ) = = Some ( " 1 " ) {
let code = match crate ::runner ::modes ::common_util ::pyvm ::run_pyvm_harness ( & module , " selfhost-py " ) {
Ok ( c ) = > c ,
Err ( e ) = > { eprintln! ( " ❌ PyVM error: {} " , e ) ; 1 }
} ;
2025-09-17 07:43:07 +09:00
println! ( " Result: {} " , code ) ;
std ::process ::exit ( code ) ;
2025-09-16 14:57:05 +09:00
}
2025-09-17 07:43:07 +09:00
self . execute_mir_module ( & module ) ;
return true ;
}
Err ( e ) = > {
eprintln! ( " [ny-compiler] json parse error: {} " , e ) ;
return false ;
2025-09-16 14:57:05 +09:00
}
}
}
}
}
}
}
2025-09-17 07:43:07 +09:00
}
2025-09-16 00:01:31 +09:00
// 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
let exe_path = if let Ok ( p ) = std ::env ::var ( " NYASH_NY_COMPILER_EXE_PATH " ) {
std ::path ::PathBuf ::from ( p )
} else {
let mut p = std ::path ::PathBuf ::from ( " dist/nyash_compiler " ) ;
#[ cfg(windows) ]
2025-09-17 07:43:07 +09:00
{
p . push ( " nyash_compiler.exe " ) ;
}
2025-09-16 00:01:31 +09:00
#[ cfg(not(windows)) ]
2025-09-17 07:43:07 +09:00
{
p . push ( " nyash_compiler " ) ;
}
2025-09-16 00:01:31 +09:00
if ! p . exists ( ) {
// Try PATH
2025-09-17 07:43:07 +09:00
if let Ok ( w ) = which ::which ( " nyash_compiler " ) {
w
} else {
p
}
} else {
p
}
2025-09-16 00:01:31 +09:00
} ;
if exe_path . exists ( ) {
2025-09-17 07:43:07 +09:00
let timeout_ms : u64 = std ::env ::var ( " NYASH_NY_COMPILER_TIMEOUT_MS " )
. ok ( )
. and_then ( | s | s . parse ( ) . ok ( ) )
. unwrap_or ( 2000 ) ;
2025-09-17 20:33:19 +09:00
if let Some ( module ) = super ::modes ::common_util ::selfhost_exe ::exe_try_parse_json_v0 ( filename , timeout_ms ) {
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 when requested (reference semantics)
if std ::env ::var ( " NYASH_VM_USE_PY " ) . ok ( ) . as_deref ( ) = = Some ( " 1 " ) {
if let Ok ( py3 ) = which ::which ( " python3 " ) {
let runner = std ::path ::Path ::new ( " tools/pyvm_runner.py " ) ;
if runner . exists ( ) {
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 ) ;
}
2025-09-19 02:07:38 +09:00
crate ::cli_v! ( " [Bridge] using PyVM (selfhost) → {} " , mir_json_path . display ( ) ) ;
2025-09-17 20:33:19 +09:00
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 ) ;
println! ( " Result: {} " , code ) ;
std ::process ::exit ( code ) ;
2025-09-16 00:01:31 +09:00
}
}
}
2025-09-17 20:33:19 +09:00
self . execute_mir_module ( & module ) ;
return true ;
} else {
return false ;
2025-09-16 00:01:31 +09:00
}
}
}
2025-09-16 14:57:05 +09:00
// Fallback: inline VM run (embed source into a tiny wrapper that prints JSON)
// This avoids CLI arg forwarding complexity and does not require FileBox.
2025-09-18 13:35:38 +09:00
let mut json_line = String ::new ( ) ;
2025-09-16 00:01:31 +09:00
{
2025-09-16 14:57:05 +09:00
// Escape source for embedding as string literal
let mut esc = String ::with_capacity ( code_ref . len ( ) ) ;
for ch in code_ref . chars ( ) {
match ch {
'\\' = > esc . push_str ( " \\ \\ " ) ,
'"' = > esc . push_str ( " \\ \" " ) ,
'\n' = > esc . push_str ( " \n " ) ,
'\r' = > esc . push_str ( " " ) ,
_ = > esc . push ( ch ) ,
}
}
let inline_path = std ::path ::Path ::new ( " tmp " ) . join ( " inline_selfhost_emit.nyash " ) ;
let inline_code = format! (
2025-09-17 01:20:15 +09:00
" include \" apps/selfhost-compiler/boxes/parser_box.nyash \" \n include \" apps/selfhost-compiler/boxes/emitter_box.nyash \" \n static box Main {{ \n main(args) {{ \n local s = \" {} \" \n local p = new ParserBox() \n p.stage3_enable(1) \n local json = p.parse_program2(s) \n local e = new EmitterBox() \n json = e.emit_program(json, \" [] \" ) \n print(json) \n return 0 \n }} \n }} \n " ,
2025-09-16 14:57:05 +09:00
esc
) ;
if let Err ( e ) = std ::fs ::write ( & inline_path , inline_code ) {
eprintln! ( " [ny-compiler] write inline failed: {} " , e ) ;
return false ;
}
2025-09-17 07:43:07 +09:00
let exe = std ::env ::current_exe ( )
. unwrap_or_else ( | _ | std ::path ::PathBuf ::from ( " target/release/nyash " ) ) ;
2025-09-16 00:01:31 +09:00
let mut cmd = std ::process ::Command ::new ( exe ) ;
2025-09-16 14:57:05 +09:00
cmd . arg ( " --backend " ) . arg ( " vm " ) . arg ( & inline_path ) ;
2025-09-16 00:01:31 +09:00
cmd . env_remove ( " NYASH_USE_NY_COMPILER " ) ;
cmd . env_remove ( " NYASH_CLI_VERBOSE " ) ;
cmd . env ( " NYASH_JSON_ONLY " , " 1 " ) ;
2025-09-17 07:43:07 +09:00
let timeout_ms : u64 = std ::env ::var ( " NYASH_NY_COMPILER_TIMEOUT_MS " )
. ok ( )
. and_then ( | s | s . parse ( ) . ok ( ) )
. unwrap_or ( 2000 ) ;
2025-09-17 20:33:19 +09:00
let out = match super ::modes ::common_util ::io ::spawn_with_timeout ( cmd , timeout_ms ) {
Ok ( o ) = > o ,
Err ( e ) = > { eprintln! ( " [ny-compiler] spawn inline vm failed: {} " , e ) ; return false ; }
2025-09-17 07:43:07 +09:00
} ;
2025-09-17 20:33:19 +09:00
if out . timed_out {
let head = String ::from_utf8_lossy ( & out . stdout ) . chars ( ) . take ( 200 ) . collect ::< String > ( ) ;
eprintln! ( " [ny-compiler] inline timeout after {} ms; stdout(head)=' {} ' " , timeout_ms , head . replace ( '\n' , " \\ n " ) ) ;
2025-09-16 00:01:31 +09:00
}
2025-09-18 13:35:38 +09:00
let stdout = String ::from_utf8_lossy ( & out . stdout ) . to_string ( ) ;
if let Some ( line ) = crate ::runner ::modes ::common_util ::selfhost ::json ::first_json_v0_line ( & stdout ) {
json_line = line ;
2025-09-17 07:43:07 +09:00
}
}
if json_line . is_empty ( ) {
return false ;
2025-09-16 00:01:31 +09:00
}
match super ::json_v0_bridge ::parse_json_v0_to_module ( & json_line ) {
Ok ( module ) = > {
super ::json_v0_bridge ::maybe_dump_mir ( & module ) ;
2025-09-17 07:43:07 +09:00
let emit_only = std ::env ::var ( " NYASH_NY_COMPILER_EMIT_ONLY " )
. unwrap_or_else ( | _ | " 1 " . to_string ( ) )
= = " 1 " ;
if emit_only {
return false ;
}
2025-09-16 14:57:05 +09:00
// 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.
2025-09-17 07:43:07 +09:00
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 { .. } )
} )
} )
} ) ;
2025-09-16 14:57:05 +09:00
if prefer_pyvm | | needs_pyvm {
2025-09-18 13:35:38 +09:00
let label = if prefer_pyvm { " selfhost " } else { " selfhost-fallback " } ;
if let Some ( code ) = crate ::runner ::modes ::common_util ::selfhost ::json ::run_pyvm_module ( & module , label ) {
println! ( " Result: {} " , code ) ;
std ::process ::exit ( code ) ;
2025-09-16 00:01:31 +09:00
}
}
self . execute_mir_module ( & module ) ;
true
}
2025-09-17 07:43:07 +09:00
Err ( e ) = > {
eprintln! ( " ❌ JSON v0 bridge error: {} " , e ) ;
false
}
2025-09-16 00:01:31 +09:00
}
}
}