2025-08-26 04:34:14 +09:00
use super ::super ::NyashRunner ;
2025-09-17 07:43:07 +09:00
use nyash_rust ::{
ast ::ASTNode ,
parser ::NyashParser ,
2025-11-07 19:32:44 +09:00
mir ::MirCompiler ,
2025-09-17 07:43:07 +09:00
} ;
use std ::{ fs , process } ;
2025-08-26 04:34:14 +09:00
impl NyashRunner {
2025-11-07 19:32:44 +09:00
/// Execute VM mode with full plugin initialization and AST prelude merge
2025-08-26 04:34:14 +09:00
pub ( crate ) fn execute_vm_mode ( & 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-11-04 16:33:04 +09:00
2025-09-15 18:44:49 +09:00
// Quiet mode for child pipelines (e.g., selfhost compiler JSON emit)
2025-11-06 15:41:52 +09:00
let quiet_pipe = crate ::config ::env ::env_bool ( " NYASH_JSON_ONLY " ) ;
2025-11-07 19:32:44 +09:00
2025-09-08 01:08:59 +09:00
// Enforce plugin-first policy for VM on this branch (deterministic):
// - Initialize plugin host if not yet loaded
// - Prefer plugin implementations for core boxes
// - Optionally fail fast when plugins are missing (NYASH_VM_PLUGIN_STRICT=1)
{
// Initialize unified registry globals (idempotent)
nyash_rust ::runtime ::init_global_unified_registry ( ) ;
2025-11-07 19:32:44 +09:00
2025-09-08 01:08:59 +09:00
// Init plugin host from nyash.toml if not yet loaded
let need_init = {
let host = nyash_rust ::runtime ::get_global_plugin_host ( ) ;
2025-09-17 07:43:07 +09:00
host . read ( )
. map ( | h | h . config_ref ( ) . is_none ( ) )
. unwrap_or ( true )
2025-09-08 01:08:59 +09:00
} ;
if need_init {
2025-11-06 15:41:52 +09:00
// Let init_bid_plugins resolve hakorune.toml/nyash.toml and configure
2025-09-08 01:08:59 +09:00
crate ::runner_plugin_init ::init_bid_plugins ( ) ;
}
2025-11-07 19:32:44 +09:00
2025-09-08 01:08:59 +09:00
// Prefer plugin-builtins for core types unless explicitly disabled
if std ::env ::var ( " NYASH_USE_PLUGIN_BUILTINS " ) . ok ( ) . is_none ( ) {
std ::env ::set_var ( " NYASH_USE_PLUGIN_BUILTINS " , " 1 " ) ;
}
2025-11-07 19:32:44 +09:00
2025-09-08 01:08:59 +09:00
// Build stable override list
2025-09-17 07:43:07 +09:00
let mut override_types : Vec < String > =
if let Ok ( list ) = std ::env ::var ( " NYASH_PLUGIN_OVERRIDE_TYPES " ) {
list . split ( ',' )
. map ( | s | s . trim ( ) . to_string ( ) )
. filter ( | s | ! s . is_empty ( ) )
. collect ( )
} else {
vec! [ ]
} ;
2025-09-08 01:08:59 +09:00
for t in [
2025-09-17 07:43:07 +09:00
" FileBox " ,
" TOMLBox " , // IO/config
" ConsoleBox " ,
" StringBox " ,
" IntegerBox " , // core value-ish
" ArrayBox " ,
" MapBox " , // collections
" MathBox " ,
" TimeBox " , // math/time helpers
2025-09-08 01:08:59 +09:00
] {
2025-09-17 07:43:07 +09:00
if ! override_types . iter ( ) . any ( | x | x = = t ) {
override_types . push ( t . to_string ( ) ) ;
}
2025-09-08 01:08:59 +09:00
}
std ::env ::set_var ( " NYASH_PLUGIN_OVERRIDE_TYPES " , override_types . join ( " , " ) ) ;
// Strict mode: verify providers exist for override types
2025-11-06 15:41:52 +09:00
if crate ::config ::env ::env_bool ( " NYASH_VM_PLUGIN_STRICT " ) {
2025-09-08 01:08:59 +09:00
let v2 = nyash_rust ::runtime ::get_global_registry ( ) ;
let mut missing : Vec < String > = Vec ::new ( ) ;
2025-09-17 07:43:07 +09:00
for t in [
" FileBox " ,
" ConsoleBox " ,
" ArrayBox " ,
" MapBox " ,
" StringBox " ,
" IntegerBox " ,
] {
if v2 . get_provider ( t ) . is_none ( ) {
missing . push ( t . to_string ( ) ) ;
}
2025-09-08 01:08:59 +09:00
}
if ! missing . is_empty ( ) {
2025-09-17 07:43:07 +09:00
eprintln! (
" ❌ VM plugin-first strict: missing providers for: {:?} " ,
missing
) ;
2025-09-08 01:08:59 +09:00
std ::process ::exit ( 1 ) ;
}
}
}
2025-08-26 04:34:14 +09:00
// Read the file
let code = match fs ::read_to_string ( filename ) {
Ok ( content ) = > content ,
2025-09-17 07:43:07 +09:00
Err ( e ) = > {
eprintln! ( " ❌ Error reading file {} : {} " , filename , e ) ;
process ::exit ( 1 ) ;
}
2025-08-26 04:34:14 +09:00
} ;
2025-11-07 21:04:01 +09:00
// Unified using/prelude handling (SSOT):
// - resolve_prelude_paths_profiled: discover preludes (DFS, operator boxes, etc.)
// - merge_prelude_text: text-based merge for all VM paths
// * .hako プレリュードは AST に突っ込まない( Parse error防止)
// * .hako は text-merge + Stage-3 parser で一貫処理
let trace = crate ::config ::env ::cli_verbose ( )
| | crate ::config ::env ::env_bool ( " NYASH_RESOLVE_TRACE " ) ;
// When using is enabled, resolve preludes/profile; otherwise, keep original code.
let mut code_final = if crate ::config ::env ::enable_using ( ) {
2025-11-07 19:32:44 +09:00
match crate ::runner ::modes ::common_util ::resolve ::resolve_prelude_paths_profiled (
2025-11-07 21:04:01 +09:00
self ,
& code ,
filename ,
2025-11-01 13:28:56 +09:00
) {
2025-11-07 21:04:01 +09:00
Ok ( ( _ , prelude_paths ) ) = > {
if ! prelude_paths . is_empty ( ) {
// SSOT: always text-merge for VM (includes .hako-safe handling inside)
match crate ::runner ::modes ::common_util ::resolve ::merge_prelude_text (
self ,
& code ,
filename ,
) {
2025-11-07 19:32:44 +09:00
Ok ( merged ) = > {
2025-11-07 21:04:01 +09:00
if trace {
eprintln! (
" [using/text-merge] preludes={} (vm) " ,
prelude_paths . len ( )
) ;
}
merged
}
Err ( e ) = > {
eprintln! ( " ❌ {} " , e ) ;
process ::exit ( 1 ) ;
2025-11-07 19:32:44 +09:00
}
}
2025-11-07 21:04:01 +09:00
} else {
code . clone ( )
2025-11-07 19:32:44 +09:00
}
2025-11-01 13:28:56 +09:00
}
Err ( e ) = > {
eprintln! ( " ❌ {} " , e ) ;
process ::exit ( 1 ) ;
2025-09-26 00:27:02 +09:00
}
2025-09-18 06:35:49 +09:00
}
2025-11-04 16:33:04 +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-11-07 21:04:01 +09:00
code
} ;
2025-09-18 06:35:49 +09:00
2025-11-07 19:32:44 +09:00
// Dev sugar pre-expand: @name = expr → local name = expr
2025-11-07 21:04:01 +09:00
code_final =
crate ::runner ::modes ::common_util ::resolve ::preexpand_at_local ( & code_final ) ;
2025-11-04 16:33:04 +09:00
2025-11-07 19:32:44 +09:00
// Hako-friendly normalize: strip leading `local ` at line head for Nyash parser compatibility.
2025-11-07 21:04:01 +09:00
if crate ::runner ::modes ::common_util ::hako ::looks_like_hako_code ( & code_final )
| | filename . ends_with ( " .hako " )
{
code_final =
crate ::runner ::modes ::common_util ::hako ::strip_local_decl ( & code_final ) ;
}
if trace & & ( std ::env ::var ( " NYASH_PARSER_STAGE3 " ) . ok ( ) = = Some ( " 1 " . into ( ) )
| | std ::env ::var ( " HAKO_PARSER_STAGE3 " ) . ok ( ) = = Some ( " 1 " . into ( ) ) )
{
eprintln! ( " [vm] Stage-3: enabled (env) for {} " , filename ) ;
2025-11-04 16:33:04 +09:00
}
2025-11-07 19:32:44 +09:00
// Fail‑ Fast (opt‑ in): Hako 構文を Nyash VM 経路で実行しない
// 目的: .hako は Hakorune VM、MIR は Core/LLVM に役割分離するためのガード
2025-11-04 16:33:04 +09:00
{
2025-11-07 19:32:44 +09:00
let on = crate ::runner ::modes ::common_util ::hako ::fail_fast_on_hako ( ) ;
if on {
let hako_like = code_final . contains ( " static box " )
| | code_final . contains ( " using selfhost. " )
| | code_final . 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-11-01 21:48:12 +09:00
}
}
2025-11-07 19:32:44 +09:00
2025-11-07 21:04:01 +09:00
// Parse main code (after text-merge and Hako normalization)
let ast_combined = match NyashParser ::parse_from_string ( & code_final ) {
2025-08-26 04:34:14 +09:00
Ok ( ast ) = > ast ,
2025-09-17 07:43:07 +09:00
Err ( e ) = > {
2025-11-07 19:32:44 +09:00
eprintln! ( " ❌ Parse error in {} : {} " , filename , e ) ;
2025-09-17 07:43:07 +09:00
process ::exit ( 1 ) ;
}
2025-08-26 04:34:14 +09:00
} ;
2025-11-07 19:32:44 +09:00
// Optional: dump AST statement kinds for quick diagnostics
if std ::env ::var ( " NYASH_AST_DUMP " ) . ok ( ) . as_deref ( ) = = Some ( " 1 " ) {
eprintln! ( " [ast] dump start (vm) " ) ;
if let ASTNode ::Program { statements , .. } = & ast_combined {
for ( i , st ) in statements . iter ( ) . enumerate ( ) . take ( 50 ) {
let kind = match st {
ASTNode ::BoxDeclaration {
is_static , name , ..
} = > {
if * is_static {
format! ( " StaticBox( {} ) " , name )
} else {
format! ( " Box( {} ) " , name )
}
}
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 ) ,
ASTNode ::UsingStatement { namespace_name , .. } = > {
format! ( " Using( {} ) " , namespace_name )
}
_ = > format! ( " {:?} " , st ) ,
} ;
eprintln! ( " [ast] {} : {} " , i , kind ) ;
}
2025-08-27 17:06:46 +09:00
}
2025-11-07 19:32:44 +09:00
eprintln! ( " [ast] dump end " ) ;
}
// Macro expand (if enabled)
let ast = crate ::r#macro ::maybe_expand_and_dump ( & ast_combined , false ) ;
// Minimal user-defined Box support (inline factory)
let static_box_decls = {
use crate ::{
box_factory ::{ BoxFactory , RuntimeError } ,
core ::model ::BoxDeclaration as CoreBoxDecl ,
instance_v2 ::InstanceBox ,
} ;
use std ::sync ::{ Arc , RwLock } ;
// Collect user-defined (non-static) box declarations at program level.
// Additionally, record static box names so we can alias
// `StaticBoxName` -> `StaticBoxNameInstance` when such a
// concrete instance box exists (common pattern in libs).
// Also collect static box declarations for VM singleton persistence.
let mut nonstatic_decls : std ::collections ::HashMap < String , CoreBoxDecl > =
std ::collections ::HashMap ::new ( ) ;
let mut static_names : Vec < String > = Vec ::new ( ) ;
let mut static_box_decls : std ::collections ::HashMap < String , CoreBoxDecl > =
std ::collections ::HashMap ::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 ( ) ) ;
// Store static box declaration for VM singleton persistence
let static_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 ( ) ,
} ;
static_box_decls . insert ( name . clone ( ) , static_decl ) ;
continue ; // modules/static boxes are not user-instantiable directly
}
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 ) ;
}
}
}
// 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 ( ) ) ;
}
}
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 > ] ,
) -> 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 ( std ::sync ::Arc ::new ( factory ) ) ;
2025-09-17 07:43:07 +09:00
}
2025-11-07 19:32:44 +09:00
// Return static_box_decls for VM registration
static_box_decls
2025-08-26 04:34:14 +09:00
} ;
2025-11-07 19:32:44 +09:00
// Compile to MIR
let mut compiler = MirCompiler ::with_options ( ! self . config . no_optimize ) ;
let compile = match compiler . compile ( ast ) {
Ok ( c ) = > c ,
2025-09-17 07:43:07 +09:00
Err ( e ) = > {
eprintln! ( " ❌ MIR compilation error: {} " , e ) ;
process ::exit ( 1 ) ;
}
2025-08-26 04:34:14 +09:00
} ;
2025-11-07 19:32:44 +09:00
// Optional barrier-elision for parity with fallback path
let mut module_vm = compile . module . clone ( ) ;
if crate ::config ::env ::env_bool ( " NYASH_VM_ESCAPE_ANALYSIS " ) {
let removed = crate ::mir ::passes ::escape ::escape_elide_barriers_vm ( & mut module_vm ) ;
if removed > 0 {
crate ::cli_v! (
" [VM] escape_elide_barriers: removed {} barriers " ,
removed
2025-09-17 07:43:07 +09:00
) ;
2025-08-27 17:06:46 +09:00
}
}
2025-09-06 06:24:08 +09:00
// Optional: dump MIR for diagnostics
2025-11-06 15:41:52 +09:00
if crate ::config ::env ::env_bool ( " NYASH_VM_DUMP_MIR " ) {
2025-11-07 19:32:44 +09:00
let p = crate ::mir ::MirPrinter ::new ( ) ;
eprintln! ( " {} " , p . print_module ( & module_vm ) ) ;
2025-09-06 06:24:08 +09:00
}
2025-11-07 19:32:44 +09:00
// Execute via MIR interpreter
use crate ::backend ::MirInterpreter ;
let mut vm = MirInterpreter ::new ( ) ;
// Register static box declarations for singleton persistence
for ( name , decl ) in static_box_decls {
vm . register_static_box_decl ( name , decl ) ;
2025-08-31 03:03:04 +09:00
}
2025-11-07 19:32:44 +09:00
// Optional: verify MIR before execution (dev-only)
if crate ::config ::env ::env_bool ( " NYASH_VM_VERIFY_MIR " ) {
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-24 21:45:27 +09:00
}
}
2025-09-14 04:51:33 +09:00
}
}
2025-11-07 19:32:44 +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-01 23:44:34 +09:00
2025-08-31 03:03:04 +09:00
match vm . execute_module ( & module_vm ) {
2025-11-07 19:32:44 +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 }
2025-08-28 22:31:51 +09:00
} else {
2025-11-07 19:32:44 +09:00
// For non-integer/bool returns, default to 0 (success)
0
2025-09-08 01:08:59 +09:00
} ;
2025-11-07 19:32:44 +09:00
// Quiet mode: suppress "RC:" output for JSON-only pipelines
2025-09-15 18:44:49 +09:00
if ! quiet_pipe {
2025-11-07 19:32:44 +09:00
println! ( " RC: {} " , exit_code ) ;
2025-09-15 18:44:49 +09:00
}
2025-11-07 19:32:44 +09:00
// Exit with the return value as exit code
process ::exit ( exit_code ) ;
2025-09-17 07:43:07 +09:00
}
Err ( e ) = > {
2025-11-07 19:32:44 +09:00
eprintln! ( " ❌ VM error: {} " , e ) ;
2025-09-17 07:43:07 +09:00
process ::exit ( 1 ) ;
}
2025-08-26 04:34:14 +09:00
}
}
}