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-17 11:45:57 +09:00
use nyash_rust ::parser ::NyashParser ;
2025-09-16 00:01:31 +09:00
use std ::io ::Read ;
use std ::process ::Stdio ;
use std ::thread ::sleep ;
2025-09-17 07:43:07 +09:00
use std ::time ::{ Duration , Instant } ;
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)
2025-09-17 06:55:39 +09:00
let enable_using = crate ::config ::env ::enable_using ( ) ;
2025-09-16 00:01:31 +09:00
let mut code_ref : std ::borrow ::Cow < '_ , str > = std ::borrow ::Cow ::Borrowed ( & code ) ;
if enable_using {
let mut out = String ::with_capacity ( code . len ( ) ) ;
let mut used_names : Vec < ( String , Option < String > ) > = Vec ::new ( ) ;
for line in code . lines ( ) {
let t = line . trim_start ( ) ;
if t . starts_with ( " using " ) {
2025-09-17 06:55:39 +09:00
if crate ::config ::env ::cli_verbose ( ) {
2025-09-16 00:01:31 +09:00
eprintln! ( " [using] stripped(line→selfhost): {} " , line ) ;
}
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 " ) {
2025-09-17 07:43:07 +09:00
(
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 " ) ;
2025-09-16 00:01:31 +09:00
if is_path {
let path = target . trim_matches ( '"' ) . to_string ( ) ;
let name = alias . clone ( ) . unwrap_or_else ( | | {
2025-09-17 07:43:07 +09:00
std ::path ::Path ::new ( & path )
. file_stem ( )
. and_then ( | s | s . to_str ( ) )
. unwrap_or ( " module " )
. to_string ( )
2025-09-16 00:01:31 +09:00
} ) ;
used_names . push ( ( name , Some ( path ) ) ) ;
} else {
used_names . push ( ( target , alias ) ) ;
}
continue ;
}
out . push_str ( line ) ;
out . push ( '\n' ) ;
}
// Register modules into minimal registry with best-effort path resolution
for ( ns_or_alias , alias_or_path ) in used_names {
if let Some ( path ) = alias_or_path {
let sb = crate ::box_trait ::StringBox ::new ( path ) ;
crate ::runtime ::modules_registry ::set ( ns_or_alias , Box ::new ( sb ) ) ;
} else {
let rel = format! ( " apps/ {} .nyash " , ns_or_alias . replace ( '.' , " / " ) ) ;
let exists = std ::path ::Path ::new ( & rel ) . exists ( ) ;
let path_or_ns = if exists { rel } else { ns_or_alias . clone ( ) } ;
let sb = crate ::box_trait ::StringBox ::new ( path_or_ns ) ;
crate ::runtime ::modules_registry ::set ( ns_or_alias , Box ::new ( sb ) ) ;
}
}
code_ref = std ::borrow ::Cow ::Owned ( out ) ;
}
// 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 ;
}
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-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 ( ) {
let mut cmd = std ::process ::Command ::new ( & exe ) ;
cmd . arg ( " --backend " ) . arg ( " vm " ) . arg ( parser_prog ) ;
// Forward minimal args to child parser program
2025-09-17 06:55:39 +09:00
if crate ::config ::env ::ny_compiler_min_json ( ) {
2025-09-17 01:55:08 +09:00
cmd . arg ( " -- " ) . arg ( " --min-json " ) ;
}
// Always feed input via tmp file written by the parent pipeline
cmd . arg ( " -- " ) . arg ( " --read-tmp " ) ;
2025-09-17 06:55:39 +09:00
if crate ::config ::env ::ny_compiler_stage3 ( ) {
2025-09-17 01:55:08 +09:00
cmd . arg ( " -- " ) . arg ( " --stage3 " ) ;
}
// Suppress parent noise and keep only JSON from child
cmd . env_remove ( " NYASH_USE_NY_COMPILER " ) ;
cmd . env_remove ( " NYASH_CLI_VERBOSE " ) ;
cmd . env ( " NYASH_JSON_ONLY " , " 1 " ) ;
2025-09-17 06:55:39 +09:00
let timeout_ms : u64 = crate ::config ::env ::ny_compiler_timeout_ms ( ) ;
2025-09-17 07:43:07 +09:00
let mut cmd = cmd
. stdout ( std ::process ::Stdio ::piped ( ) )
. stderr ( std ::process ::Stdio ::piped ( ) ) ;
2025-09-17 01:55:08 +09:00
if let Ok ( mut child ) = cmd . spawn ( ) {
let mut ch_stdout = child . stdout . take ( ) ;
let mut ch_stderr = child . stderr . take ( ) ;
let start = std ::time ::Instant ::now ( ) ;
let mut timed_out = false ;
loop {
match child . try_wait ( ) {
Ok ( Some ( _ ) ) = > break ,
Ok ( None ) = > {
if start . elapsed ( ) > = std ::time ::Duration ::from_millis ( timeout_ms ) {
2025-09-17 07:43:07 +09:00
let _ = child . kill ( ) ;
let _ = child . wait ( ) ;
timed_out = true ;
break ;
2025-09-17 01:55:08 +09:00
}
std ::thread ::sleep ( std ::time ::Duration ::from_millis ( 10 ) ) ;
}
Err ( _ ) = > break ,
}
}
let mut out_buf = Vec ::new ( ) ;
let mut err_buf = Vec ::new ( ) ;
2025-09-17 07:43:07 +09:00
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 ) ;
}
2025-09-17 01:55:08 +09:00
if timed_out {
2025-09-17 07:43:07 +09:00
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 " )
) ;
2025-09-17 01:55:08 +09:00
}
let stdout = String ::from_utf8_lossy ( & out_buf ) . to_string ( ) ;
let mut json_line = String ::new ( ) ;
2025-09-17 07:43:07 +09:00
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 ;
}
}
2025-09-17 01:55:08 +09:00
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 ) ;
2025-09-17 06:55:39 +09:00
let emit_only = crate ::config ::env ::ny_compiler_emit_only ( ) ;
2025-09-17 07:43:07 +09:00
if emit_only {
return false ;
}
2025-09-17 01:55:08 +09:00
// Prefer PyVM path when requested
2025-09-17 06:55:39 +09:00
if crate ::config ::env ::vm_use_py ( ) {
2025-09-17 01:55:08 +09:00
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 ) ;
std ::process ::exit ( 1 ) ;
}
2025-09-17 07:43:07 +09:00
let entry =
if module . functions . contains_key ( " Main.main " ) {
" Main.main "
} else if module . functions . contains_key ( " main " ) {
" main "
} else {
" Main.main "
} ;
2025-09-17 01:55:08 +09:00
let status = std ::process ::Command ::new ( py3 )
2025-09-17 07:43:07 +09:00
. args ( [
" tools/pyvm_runner.py " ,
" --in " ,
& mir_json_path . display ( ) . to_string ( ) ,
" --entry " ,
entry ,
] )
. status ( )
. map_err ( | e | format! ( " spawn pyvm: {} " , e ) )
. unwrap ( ) ;
2025-09-17 01:55:08 +09:00
let code = status . code ( ) . unwrap_or ( 1 ) ;
println! ( " Result: {} " , code ) ;
std ::process ::exit ( code ) ;
}
}
}
self . execute_mir_module ( & module ) ;
return true ;
}
2025-09-17 07:43:07 +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-17 07:43:07 +09:00
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 ) {
2025-09-16 14:57:05 +09:00
eprintln! ( " ❌ PyVM MIR JSON emit error: {} " , e ) ;
process ::exit ( 1 ) ;
}
2025-09-17 07:43:07 +09:00
if std ::env ::var ( " NYASH_CLI_VERBOSE " ) . ok ( ) . as_deref ( )
= = Some ( " 1 " )
{
eprintln! (
" [Bridge] using PyVM (selfhost-py) → {} " ,
mir_json_path . display ( )
) ;
2025-09-16 14:57:05 +09:00
}
2025-09-17 07:43:07 +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 ) ;
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 ) ;
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 ) ;
}
if std ::env ::var ( " NYASH_CLI_VERBOSE " ) . ok ( ) . as_deref ( ) = = Some ( " 1 " ) {
eprintln! ( " [Bridge] using PyVM (selfhost) → {} " , mir_json_path . display ( ) ) ;
2025-09-16 00:01:31 +09:00
}
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-16 00:01:31 +09:00
let mut raw = String ::new ( ) ;
{
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-17 20:33:19 +09:00
raw = String ::from_utf8_lossy ( & out . stdout ) . to_string ( ) ;
2025-09-16 00:01:31 +09:00
}
let mut json_line = String ::new ( ) ;
for line in raw . lines ( ) {
let t = line . trim ( ) ;
2025-09-17 07:43:07 +09:00
if t . starts_with ( '{' ) & & t . contains ( " \" version \" " ) & & t . contains ( " \" kind \" " ) {
json_line = t . to_string ( ) ;
break ;
}
}
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-16 00:01:31 +09:00
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 " ) ;
2025-09-17 07:43:07 +09:00
if let Err ( e ) =
crate ::runner ::mir_json_emit ::emit_mir_json_for_harness_bin (
& module ,
& mir_json_path ,
)
{
2025-09-16 00:01:31 +09:00
eprintln! ( " ❌ PyVM MIR JSON emit error: {} " , e ) ;
process ::exit ( 1 ) ;
}
if std ::env ::var ( " NYASH_CLI_VERBOSE " ) . ok ( ) . as_deref ( ) = = Some ( " 1 " ) {
2025-09-17 07:43:07 +09:00
let mode = if prefer_pyvm {
" selfhost "
} else {
" selfhost-fallback "
} ;
eprintln! (
" [Bridge] using PyVM ({}) → {} " ,
mode ,
mir_json_path . display ( )
) ;
2025-09-16 00:01:31 +09:00
}
2025-09-17 07:43:07 +09:00
let entry = if module . functions . contains_key ( " Main.main " ) {
" Main.main "
} else if module . functions . contains_key ( " main " ) {
" main "
} else {
" Main.main "
} ;
2025-09-16 00:01:31 +09:00
let status = std ::process ::Command ::new ( py3 )
2025-09-17 07:43:07 +09:00
. args ( [
" tools/pyvm_runner.py " ,
" --in " ,
& mir_json_path . display ( ) . to_string ( ) ,
" --entry " ,
entry ,
] )
. status ( )
. map_err ( | e | format! ( " spawn pyvm: {} " , e ) )
. unwrap ( ) ;
2025-09-16 00:01:31 +09:00
let code = status . code ( ) . unwrap_or ( 1 ) ;
if ! status . success ( ) {
if std ::env ::var ( " NYASH_CLI_VERBOSE " ) . ok ( ) . as_deref ( ) = = Some ( " 1 " ) {
2025-09-17 07:43:07 +09:00
eprintln! (
" ❌ PyVM (selfhost-fallback) failed (status={}) " ,
code
) ;
2025-09-16 00:01:31 +09:00
}
}
// Harmonize with interpreter path for smokes
println! ( " Result: {} " , code ) ;
std ::process ::exit ( code ) ;
}
}
}
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
}
}
}