2025-09-24 21:45:27 +09:00
use super ::super ::NyashRunner ;
2025-09-26 03:30:59 +09:00
use crate ::{
backend ::MirInterpreter ,
box_factory ::{ BoxFactory , RuntimeError } ,
core ::model ::BoxDeclaration as CoreBoxDecl ,
instance_v2 ::InstanceBox ,
mir ::MirCompiler ,
parser ::NyashParser ,
} ;
use std ::sync ::{ Arc , RwLock } ;
2025-09-24 21:45:27 +09:00
use std ::{ fs , process } ;
impl NyashRunner {
/// Lightweight VM fallback using the in-crate MIR interpreter.
/// - Respects using preprocessing done earlier in the pipeline
/// - Relies on global plugin host initialized by runner
pub ( crate ) fn execute_vm_fallback_interpreter ( & self , filename : & str ) {
2025-11-04 20:46:43 +09:00
// Note: hv1 direct route is now handled at main.rs entry point (before plugin initialization).
// This function is only called after plugin initialization has already occurred.
2025-09-24 21:45:27 +09:00
// Read source
let code = match fs ::read_to_string ( filename ) {
Ok ( s ) = > s ,
2025-09-26 14:34:42 +09:00
Err ( e ) = > {
eprintln! ( " ❌ Error reading file {} : {} " , filename , e ) ;
process ::exit ( 1 ) ;
}
2025-09-24 21:45:27 +09:00
} ;
2025-11-07 19:32:44 +09:00
// Using preprocessing: AST prelude merge( .hako/Hakoライクは強制AST)
2025-11-06 15:41:52 +09:00
let mut code2 = code . clone ( ) ;
2025-09-24 21:45:27 +09:00
if crate ::config ::env ::enable_using ( ) {
2025-11-07 19:32:44 +09:00
let mut use_ast = crate ::config ::env ::using_ast_enabled ( ) ;
let is_hako = filename . ends_with ( " .hako " )
| | crate ::runner ::modes ::common_util ::hako ::looks_like_hako_code ( & code2 ) ;
if is_hako { use_ast = true ; }
if use_ast {
match crate ::runner ::modes ::common_util ::resolve ::resolve_prelude_paths_profiled ( self , & code2 , filename ) {
Ok ( ( clean , paths ) ) = > {
// If any prelude is .hako, prefer text-merge (Hakorune surface is not Nyash AST)
let has_hako = paths . iter ( ) . any ( | p | p . ends_with ( " .hako " ) ) ;
if has_hako {
match crate ::runner ::modes ::common_util ::resolve ::merge_prelude_text ( self , & code2 , filename ) {
Ok ( merged ) = > {
if std ::env ::var ( " NYASH_RESOLVE_TRACE " ) . ok ( ) . as_deref ( ) = = Some ( " 1 " ) {
eprintln! ( " [using/text-merge] preludes= {} (vm-fallback) " , paths . len ( ) ) ;
}
code2 = merged ;
}
Err ( e ) = > { eprintln! ( " ❌ {} " , e ) ; process ::exit ( 1 ) ; }
}
// Fall through to normal parse of merged text below
} else {
// AST prelude merge path
code2 = clean ;
let preexpanded = crate ::runner ::modes ::common_util ::resolve ::preexpand_at_local ( & code2 ) ;
code2 = preexpanded ;
if crate ::runner ::modes ::common_util ::hako ::looks_like_hako_code ( & code2 ) {
code2 = crate ::runner ::modes ::common_util ::hako ::strip_local_decl ( & code2 ) ;
}
let main_ast = match NyashParser ::parse_from_string ( & code2 ) {
Ok ( ast ) = > ast ,
Err ( e ) = > { eprintln! ( " ❌ Parse error in {} : {} " , filename , e ) ; process ::exit ( 1 ) ; }
} ;
if ! paths . is_empty ( ) {
match crate ::runner ::modes ::common_util ::resolve ::parse_preludes_to_asts ( self , & paths ) {
Ok ( v ) = > {
if std ::env ::var ( " NYASH_RESOLVE_TRACE " ) . ok ( ) . as_deref ( ) = = Some ( " 1 " ) {
eprintln! ( " [using/ast-merge] preludes= {} (vm-fallback) " , v . len ( ) ) ;
}
let ast = crate ::runner ::modes ::common_util ::resolve ::merge_prelude_asts_with_main ( v , & main_ast ) ;
self . execute_vm_fallback_from_ast ( filename , ast ) ;
return ; // done
}
Err ( e ) = > { eprintln! ( " ❌ {} " , e ) ; process ::exit ( 1 ) ; }
}
} else {
self . execute_vm_fallback_from_ast ( filename , main_ast ) ;
return ;
}
}
2025-11-04 16:33:04 +09:00
}
2025-11-07 19:32:44 +09:00
Err ( e ) = > { eprintln! ( " ❌ {} " , e ) ; process ::exit ( 1 ) ; }
}
} else {
// Fallback: text-prelude merge( 言語非依存)
match crate ::runner ::modes ::common_util ::resolve ::merge_prelude_text ( self , & code2 , filename ) {
Ok ( merged ) = > {
if std ::env ::var ( " NYASH_RESOLVE_TRACE " ) . ok ( ) . as_deref ( ) = = Some ( " 1 " ) {
eprintln! ( " [using/text-merge] applied (vm-fallback): {} bytes " , merged . len ( ) ) ;
}
code2 = merged ;
}
Err ( e ) = > { eprintln! ( " ❌ using text merge error: {} " , e ) ; process ::exit ( 1 ) ; }
2025-09-26 14:34:42 +09:00
}
2025-11-06 15:41:52 +09:00
}
} else {
// using disabled: detect and fail fast if present
if code . contains ( " \n using " ) | | code . trim_start ( ) . starts_with ( " using " ) {
eprintln! (
" ❌ using: prelude merge is disabled in this profile. Enable NYASH_USING_AST=1 or remove 'using' lines. "
) ;
process ::exit ( 1 ) ;
2025-09-24 21:45:27 +09:00
}
}
// Dev sugar pre-expand: @name = expr → local name = expr
code2 = crate ::runner ::modes ::common_util ::resolve ::preexpand_at_local ( & code2 ) ;
2025-11-04 16:33:04 +09:00
// Hako-friendly normalize: strip leading `local ` at line head for Nyash parser compatibility.
2025-11-05 18:57:03 +09:00
if crate ::runner ::modes ::common_util ::hako ::looks_like_hako_code ( & code2 ) {
code2 = crate ::runner ::modes ::common_util ::hako ::strip_local_decl ( & code2 ) ;
2025-11-04 16:33:04 +09:00
}
// Fail‑ Fast (opt‑ in): Hako 構文を Nyash VM 経路で実行しない
// 目的: .hako は Hakorune VM、MIR は Core/LLVM に役割分離するためのガード
{
2025-11-05 18:57:03 +09:00
let on = crate ::runner ::modes ::common_util ::hako ::fail_fast_on_hako ( ) ;
2025-11-04 16:33:04 +09:00
if on {
let s = code2 . as_str ( ) ;
let hako_like = s . contains ( " static box " ) | | s . contains ( " using selfhost. " ) | | s . contains ( " using hakorune. " ) ;
if hako_like {
eprintln! (
" ❌ Hako-like source detected in Nyash VM path. Use Hakorune VM (v1 dispatcher) or Core/LLVM for MIR. \n hint: set HAKO_VERIFY_PRIMARY=hakovm in verify path "
) ;
process ::exit ( 1 ) ;
}
}
}
2025-09-24 21:45:27 +09:00
2025-09-25 16:03:29 +09:00
// Parse main code
let main_ast = match NyashParser ::parse_from_string ( & code2 ) {
2025-09-24 21:45:27 +09:00
Ok ( ast ) = > ast ,
2025-09-26 14:34:42 +09:00
Err ( e ) = > {
2025-11-02 04:13:17 +09:00
eprintln! ( " ❌ Parse error in {} : {} " , filename , e ) ;
2025-09-26 14:34:42 +09:00
process ::exit ( 1 ) ;
}
2025-09-24 21:45:27 +09:00
} ;
2025-11-07 19:32:44 +09:00
// No AST preludes (text path or no using) → use the parsed main AST as-is
2025-11-04 16:33:04 +09:00
let ast_combined = main_ast ;
2025-09-25 16:03:29 +09:00
// Optional: dump AST statement kinds for quick diagnostics
if std ::env ::var ( " NYASH_AST_DUMP " ) . ok ( ) . as_deref ( ) = = Some ( " 1 " ) {
use nyash_rust ::ast ::ASTNode ;
eprintln! ( " [ast] dump start (vm-fallback) " ) ;
if let ASTNode ::Program { statements , .. } = & ast_combined {
for ( i , st ) in statements . iter ( ) . enumerate ( ) . take ( 50 ) {
let kind = match st {
2025-09-26 14:34:42 +09:00
ASTNode ::BoxDeclaration {
is_static , name , ..
} = > {
if * is_static {
format! ( " StaticBox( {} ) " , name )
} else {
format! ( " Box( {} ) " , name )
}
2025-09-25 16:03:29 +09:00
}
ASTNode ::FunctionDeclaration { name , .. } = > format! ( " FuncDecl( {} ) " , name ) ,
ASTNode ::FunctionCall { name , .. } = > format! ( " FuncCall( {} ) " , name ) ,
ASTNode ::MethodCall { method , .. } = > format! ( " MethodCall( {} ) " , method ) ,
ASTNode ::ScopeBox { .. } = > " ScopeBox " . to_string ( ) ,
ASTNode ::ImportStatement { path , .. } = > format! ( " Import( {} ) " , path ) ,
2025-09-26 14:34:42 +09:00
ASTNode ::UsingStatement { namespace_name , .. } = > {
format! ( " Using( {} ) " , namespace_name )
}
2025-09-25 16:03:29 +09:00
_ = > format! ( " {:?} " , st ) ,
} ;
eprintln! ( " [ast] {} : {} " , i , kind ) ;
}
}
eprintln! ( " [ast] dump end " ) ;
}
let ast = crate ::r#macro ::maybe_expand_and_dump ( & ast_combined , false ) ;
2025-09-26 03:30:59 +09:00
// Minimal user-defined Box support (Option A):
// Collect BoxDeclaration entries from AST and register a lightweight
// factory into the unified registry so `new UserBox()` works on the
// VM fallback path as well.
{
use nyash_rust ::ast ::ASTNode ;
// Collect user-defined (non-static) box declarations at program level.
2025-09-26 14:34:42 +09:00
// Additionally, record static box names so we can alias
// `StaticBoxName` -> `StaticBoxNameInstance` when such a
// concrete instance box exists (common pattern in libs).
let mut nonstatic_decls : std ::collections ::HashMap < String , CoreBoxDecl > =
2025-09-26 03:30:59 +09:00
std ::collections ::HashMap ::new ( ) ;
2025-09-26 14:34:42 +09:00
let mut static_names : Vec < String > = Vec ::new ( ) ;
2025-09-26 03:30:59 +09:00
if let ASTNode ::Program { statements , .. } = & ast {
for st in statements {
if let ASTNode ::BoxDeclaration {
name ,
fields ,
public_fields ,
private_fields ,
methods ,
constructors ,
init_fields ,
weak_fields ,
is_interface ,
extends ,
implements ,
type_parameters ,
is_static ,
..
2025-09-26 14:34:42 +09:00
} = st {
2025-09-26 03:30:59 +09:00
if * is_static {
2025-09-26 14:34:42 +09:00
static_names . push ( name . clone ( ) ) ;
continue ; // modules/static boxes are not user-instantiable directly
2025-09-26 03:30:59 +09:00
}
let decl = CoreBoxDecl {
name : name . clone ( ) ,
fields : fields . clone ( ) ,
public_fields : public_fields . clone ( ) ,
private_fields : private_fields . clone ( ) ,
methods : methods . clone ( ) ,
constructors : constructors . clone ( ) ,
init_fields : init_fields . clone ( ) ,
weak_fields : weak_fields . clone ( ) ,
is_interface : * is_interface ,
extends : extends . clone ( ) ,
implements : implements . clone ( ) ,
type_parameters : type_parameters . clone ( ) ,
} ;
2025-09-26 14:34:42 +09:00
nonstatic_decls . insert ( name . clone ( ) , decl ) ;
2025-09-26 03:30:59 +09:00
}
}
}
2025-09-26 14:34:42 +09:00
// Build final map with optional aliases for StaticName -> StaticNameInstance
let mut decls = nonstatic_decls . clone ( ) ;
for s in static_names . into_iter ( ) {
let inst = format! ( " {} Instance " , s ) ;
if let Some ( d ) = nonstatic_decls . get ( & inst ) {
decls . insert ( s , d . clone ( ) ) ;
}
}
2025-09-26 03:30:59 +09:00
if ! decls . is_empty ( ) {
// Inline factory: minimal User factory backed by collected declarations
struct InlineUserBoxFactory {
decls : Arc < RwLock < std ::collections ::HashMap < String , CoreBoxDecl > > > ,
}
impl BoxFactory for InlineUserBoxFactory {
fn create_box (
& self ,
name : & str ,
args : & [ Box < dyn crate ::box_trait ::NyashBox > ] ,
2025-09-26 14:34:42 +09:00
) -> Result < Box < dyn crate ::box_trait ::NyashBox > , RuntimeError >
{
2025-09-26 03:30:59 +09:00
let opt = { self . decls . read ( ) . unwrap ( ) . get ( name ) . cloned ( ) } ;
let decl = match opt {
Some ( d ) = > d ,
None = > {
return Err ( RuntimeError ::InvalidOperation {
message : format ! ( " Unknown Box type: {} " , name ) ,
} )
}
} ;
let mut inst = InstanceBox ::from_declaration (
decl . name . clone ( ) ,
decl . fields . clone ( ) ,
decl . methods . clone ( ) ,
) ;
let _ = inst . init ( args ) ;
Ok ( Box ::new ( inst ) )
}
2025-09-26 14:34:42 +09:00
fn box_types ( & self ) -> Vec < & str > {
vec! [ ]
}
2025-09-26 03:30:59 +09:00
2025-09-26 14:34:42 +09:00
fn is_available ( & self ) -> bool {
true
}
2025-09-26 03:30:59 +09:00
2025-09-26 14:34:42 +09:00
fn factory_type ( & self ) -> crate ::box_factory ::FactoryType {
2025-09-26 03:30:59 +09:00
crate ::box_factory ::FactoryType ::User
}
}
2025-09-26 14:34:42 +09:00
let factory = InlineUserBoxFactory {
decls : Arc ::new ( RwLock ::new ( decls ) ) ,
} ;
2025-09-26 03:30:59 +09:00
crate ::runtime ::unified_registry ::register_user_defined_factory ( Arc ::new ( factory ) ) ;
}
}
2025-09-24 21:45:27 +09:00
let mut compiler = MirCompiler ::with_options ( ! self . config . no_optimize ) ;
let compile = match compiler . compile ( ast ) {
Ok ( c ) = > c ,
2025-09-26 14:34:42 +09:00
Err ( e ) = > {
eprintln! ( " ❌ MIR compilation error: {} " , e ) ;
process ::exit ( 1 ) ;
}
2025-09-24 21:45:27 +09:00
} ;
// Optional barrier-elision for parity with VM path
let mut module_vm = compile . module . clone ( ) ;
2025-11-06 15:41:52 +09:00
if crate ::config ::env ::env_bool ( " NYASH_VM_ESCAPE_ANALYSIS " ) {
2025-09-24 21:45:27 +09:00
let removed = crate ::mir ::passes ::escape ::escape_elide_barriers_vm ( & mut module_vm ) ;
2025-09-26 14:34:42 +09:00
if removed > 0 {
crate ::cli_v! (
" [VM-fallback] escape_elide_barriers: removed {} barriers " ,
removed
) ;
}
}
// Optional: dump MIR for diagnostics (parity with vm path)
2025-11-06 15:41:52 +09:00
if crate ::config ::env ::env_bool ( " NYASH_VM_DUMP_MIR " ) {
2025-09-26 14:34:42 +09:00
let p = crate ::mir ::MirPrinter ::new ( ) ;
eprintln! ( " {} " , p . print_module ( & module_vm ) ) ;
2025-09-24 21:45:27 +09:00
}
// Execute via MIR interpreter
let mut vm = MirInterpreter ::new ( ) ;
2025-09-26 14:34:42 +09:00
// Optional: verify MIR before execution (dev-only)
2025-11-06 15:41:52 +09:00
if crate ::config ::env ::env_bool ( " NYASH_VM_VERIFY_MIR " ) {
2025-09-26 14:34:42 +09:00
let mut verifier = crate ::mir ::verification ::MirVerifier ::new ( ) ;
for ( name , func ) in module_vm . functions . iter ( ) {
if let Err ( errors ) = verifier . verify_function ( func ) {
if ! errors . is_empty ( ) {
eprintln! ( " [vm-verify] function: {} " , name ) ;
for er in errors { eprintln! ( " • {} " , er ) ; }
}
}
}
}
2025-09-25 16:03:29 +09:00
if std ::env ::var ( " NYASH_DUMP_FUNCS " ) . ok ( ) . as_deref ( ) = = Some ( " 1 " ) {
eprintln! ( " [vm] functions available: " ) ;
for k in module_vm . functions . keys ( ) {
eprintln! ( " - {} " , k ) ;
}
}
2025-09-24 21:45:27 +09:00
match vm . execute_module ( & module_vm ) {
2025-11-03 16:09:19 +09:00
Ok ( ret ) = > {
use crate ::box_trait ::{ NyashBox , IntegerBox , BoolBox } ;
// Extract exit code from return value
let exit_code = if let Some ( ib ) = ret . as_any ( ) . downcast_ref ::< IntegerBox > ( ) {
ib . value as i32
} else if let Some ( bb ) = ret . as_any ( ) . downcast_ref ::< BoolBox > ( ) {
if bb . value { 1 } else { 0 }
} else {
// For non-integer/bool returns, default to 0 (success)
0
} ;
// Exit with the return value as exit code
process ::exit ( exit_code ) ;
}
2025-09-24 21:45:27 +09:00
Err ( e ) = > {
eprintln! ( " ❌ VM fallback error: {} " , e ) ;
process ::exit ( 1 ) ;
}
}
}
}
2025-11-07 19:32:44 +09:00
impl NyashRunner {
/// Small helper to continue fallback execution once AST is prepared
fn execute_vm_fallback_from_ast ( & self , filename : & str , ast : nyash_rust ::ast ::ASTNode ) {
use crate ::{
backend ::MirInterpreter ,
box_factory ::{ BoxFactory , RuntimeError } ,
core ::model ::BoxDeclaration as CoreBoxDecl ,
instance_v2 ::InstanceBox ,
mir ::MirCompiler ,
} ;
use std ::sync ::{ Arc , RwLock } ;
use std ::process ;
// Macro expand (if enabled)
let ast = crate ::r#macro ::maybe_expand_and_dump ( & ast , false ) ;
// Minimal user-defined Box support (inline factory)
{
use nyash_rust ::ast ::ASTNode ;
let mut nonstatic_decls : std ::collections ::HashMap < String , CoreBoxDecl > = std ::collections ::HashMap ::new ( ) ;
let mut static_names : Vec < String > = Vec ::new ( ) ;
if let ASTNode ::Program { statements , .. } = & ast {
for st in statements {
if let ASTNode ::BoxDeclaration { name , fields , public_fields , private_fields , methods , constructors , init_fields , weak_fields , is_interface , extends , implements , type_parameters , is_static , .. } = st {
if * is_static { static_names . push ( name . clone ( ) ) ; continue ; }
let decl = CoreBoxDecl { name : name . clone ( ) , fields : fields . clone ( ) , public_fields : public_fields . clone ( ) , private_fields : private_fields . clone ( ) , methods : methods . clone ( ) , constructors : constructors . clone ( ) , init_fields : init_fields . clone ( ) , weak_fields : weak_fields . clone ( ) , is_interface : * is_interface , extends : extends . clone ( ) , implements : implements . clone ( ) , type_parameters : type_parameters . clone ( ) } ;
nonstatic_decls . insert ( name . clone ( ) , decl ) ;
}
}
}
let mut decls = nonstatic_decls . clone ( ) ;
for s in static_names . into_iter ( ) {
let inst = format! ( " {} Instance " , s ) ;
if let Some ( d ) = nonstatic_decls . get ( & inst ) {
decls . insert ( s , d . clone ( ) ) ;
}
}
if ! decls . is_empty ( ) {
struct InlineUserBoxFactory {
decls : Arc < RwLock < std ::collections ::HashMap < String , CoreBoxDecl > > > ,
}
impl BoxFactory for InlineUserBoxFactory {
fn create_box (
& self ,
name : & str ,
args : & [ Box < dyn crate ::box_trait ::NyashBox > ] ,
) -> Result < Box < dyn crate ::box_trait ::NyashBox > , RuntimeError > {
let opt = { self . decls . read ( ) . unwrap ( ) . get ( name ) . cloned ( ) } ;
let decl = match opt {
Some ( d ) = > d ,
None = > {
return Err ( RuntimeError ::InvalidOperation {
message : format ! ( " Unknown Box type: {} " , name ) ,
} )
}
} ;
let mut inst = InstanceBox ::from_declaration (
decl . name . clone ( ) ,
decl . fields . clone ( ) ,
decl . methods . clone ( ) ,
) ;
let _ = inst . init ( args ) ;
Ok ( Box ::new ( inst ) )
}
fn box_types ( & self ) -> Vec < & str > { vec! [ ] }
fn is_available ( & self ) -> bool { true }
fn factory_type ( & self ) -> crate ::box_factory ::FactoryType {
crate ::box_factory ::FactoryType ::User
}
}
let factory = InlineUserBoxFactory {
decls : Arc ::new ( RwLock ::new ( decls ) ) ,
} ;
crate ::runtime ::unified_registry ::register_user_defined_factory ( Arc ::new ( factory ) ) ;
}
}
// Compile to MIR and execute via interpreter
let mut compiler = MirCompiler ::with_options ( ! self . config . no_optimize ) ;
let module = match compiler . compile ( ast ) {
Ok ( r ) = > r . module ,
Err ( e ) = > { eprintln! ( " ❌ MIR compilation error: {} " , e ) ; process ::exit ( 1 ) ; }
} ;
let mut interp = MirInterpreter ::new ( ) ;
match interp . execute_module ( & module ) {
Ok ( result ) = > {
// Normalize display (avoid nonexistent coerce_to_exit_code here)
use nyash_rust ::box_trait ::{ BoolBox , IntegerBox } ;
let rc = if let Some ( ib ) = result . as_any ( ) . downcast_ref ::< IntegerBox > ( ) {
ib . value as i32
} else if let Some ( bb ) = result . as_any ( ) . downcast_ref ::< BoolBox > ( ) {
if bb . value { 1 } else { 0 }
} else {
0
} ;
// For C‑ API pure pipeline, suppress "RC:" text to keep last line = exe path
let capi = std ::env ::var ( " NYASH_LLVM_USE_CAPI " ) . ok ( ) . as_deref ( ) = = Some ( " 1 " ) ;
let pure = std ::env ::var ( " HAKO_CAPI_PURE " ) . ok ( ) . as_deref ( ) = = Some ( " 1 " ) ;
if capi & & pure {
process ::exit ( rc ) ;
} else {
println! ( " RC: {} " , rc ) ;
}
}
Err ( e ) = > { eprintln! ( " ❌ VM fallback runtime error: {} " , e ) ; process ::exit ( 1 ) ; }
}
}
}