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) {
|
|
|
|
|
// 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-09-26 00:38:14 +09:00
|
|
|
// Using preprocessing with AST-prelude merge (when NYASH_USING_AST=1)
|
2025-09-24 21:45:27 +09:00
|
|
|
let mut code2 = code;
|
2025-09-26 14:34:42 +09:00
|
|
|
let use_ast_prelude =
|
|
|
|
|
crate::config::env::enable_using() && crate::config::env::using_ast_enabled();
|
2025-09-25 16:03:29 +09:00
|
|
|
let mut prelude_asts: Vec<nyash_rust::ast::ASTNode> = Vec::new();
|
2025-09-24 21:45:27 +09:00
|
|
|
if crate::config::env::enable_using() {
|
2025-09-26 14:34:42 +09:00
|
|
|
match crate::runner::modes::common_util::resolve::resolve_prelude_paths_profiled(
|
|
|
|
|
self, &code2, filename,
|
|
|
|
|
) {
|
2025-09-26 00:27:02 +09:00
|
|
|
Ok((clean, paths)) => {
|
|
|
|
|
code2 = clean;
|
|
|
|
|
if !paths.is_empty() && !use_ast_prelude {
|
|
|
|
|
eprintln!("❌ using: AST prelude merge is disabled in this profile. Enable NYASH_USING_AST=1 or remove 'using' lines.");
|
|
|
|
|
process::exit(1);
|
|
|
|
|
}
|
2025-09-26 14:34:42 +09:00
|
|
|
for prelude_path in paths {
|
|
|
|
|
match std::fs::read_to_string(&prelude_path) {
|
|
|
|
|
Ok(src) => {
|
|
|
|
|
match crate::runner::modes::common_util::resolve::collect_using_and_strip(self, &src, &prelude_path) {
|
|
|
|
|
Ok((clean_src, nested)) => {
|
|
|
|
|
// Nested entries have already been expanded by DFS; ignore `nested` here.
|
|
|
|
|
match NyashParser::parse_from_string(&clean_src) {
|
|
|
|
|
Ok(ast) => prelude_asts.push(ast),
|
|
|
|
|
Err(e) => {
|
|
|
|
|
eprintln!("❌ Parse error in using prelude {}: {}", prelude_path, e);
|
|
|
|
|
process::exit(1);
|
2025-09-26 03:30:59 +09:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2025-09-26 14:34:42 +09:00
|
|
|
Err(e) => {
|
|
|
|
|
eprintln!("❌ {}", e);
|
|
|
|
|
process::exit(1);
|
2025-09-26 03:30:59 +09:00
|
|
|
}
|
|
|
|
|
}
|
2025-09-26 14:34:42 +09:00
|
|
|
}
|
|
|
|
|
Err(e) => {
|
|
|
|
|
eprintln!("❌ Error reading using prelude {}: {}", prelude_path, e);
|
|
|
|
|
process::exit(1);
|
|
|
|
|
}
|
2025-09-25 16:03:29 +09:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2025-09-26 14:34:42 +09:00
|
|
|
Err(e) => {
|
|
|
|
|
eprintln!("❌ {}", e);
|
|
|
|
|
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-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) => {
|
|
|
|
|
eprintln!("❌ Parse error: {}", e);
|
|
|
|
|
process::exit(1);
|
|
|
|
|
}
|
2025-09-24 21:45:27 +09:00
|
|
|
};
|
2025-09-25 16:03:29 +09:00
|
|
|
// When using AST prelude mode, combine prelude ASTs + main AST into one Program before macro expansion
|
|
|
|
|
let ast_combined = if use_ast_prelude && !prelude_asts.is_empty() {
|
|
|
|
|
use nyash_rust::ast::ASTNode;
|
|
|
|
|
let mut combined: Vec<ASTNode> = Vec::new();
|
|
|
|
|
for a in prelude_asts {
|
2025-09-26 14:34:42 +09:00
|
|
|
if let ASTNode::Program { statements, .. } = a {
|
|
|
|
|
combined.extend(statements);
|
|
|
|
|
}
|
2025-09-25 16:03:29 +09:00
|
|
|
}
|
|
|
|
|
if let ASTNode::Program { statements, .. } = main_ast.clone() {
|
|
|
|
|
combined.extend(statements);
|
|
|
|
|
}
|
2025-09-26 14:34:42 +09:00
|
|
|
ASTNode::Program {
|
|
|
|
|
statements: combined,
|
|
|
|
|
span: nyash_rust::ast::Span::unknown(),
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
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();
|
|
|
|
|
if std::env::var("NYASH_VM_ESCAPE_ANALYSIS").ok().as_deref() == Some("1") {
|
|
|
|
|
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)
|
|
|
|
|
if std::env::var("NYASH_VM_DUMP_MIR").ok().as_deref() == Some("1") {
|
|
|
|
|
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)
|
|
|
|
|
if std::env::var("NYASH_VM_VERIFY_MIR").ok().as_deref() == Some("1") {
|
|
|
|
|
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) {
|
|
|
|
|
Ok(_ret) => { /* interpreter already prints via println/console in program */ }
|
|
|
|
|
Err(e) => {
|
|
|
|
|
eprintln!("❌ VM fallback error: {}", e);
|
|
|
|
|
process::exit(1);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|