public: publish selfhost snapshot to public repo (SSOT using + AST merge + JSON VM fixes)
- SSOT using profiles (aliases/packages via nyash.toml), AST prelude merge - Parser/member guards; Builder pin/PHI and instance→function rewrite (dev on) - VM refactors (handlers split) and JSON roundtrip/nested stabilization - CURRENT_TASK.md updated with scope and acceptance criteria Notes: dev-only guards remain togglable via env; no default behavior changes for prod.
This commit is contained in:
@ -1,15 +1,15 @@
|
||||
use super::super::NyashRunner;
|
||||
use crate::runner::json_v0_bridge;
|
||||
use nyash_rust::{parser::NyashParser, interpreter::NyashInterpreter};
|
||||
use nyash_rust::{interpreter::NyashInterpreter, parser::NyashParser};
|
||||
// Use the library crate's plugin init module rather than the bin crate root
|
||||
use std::{fs, process};
|
||||
use crate::cli_v;
|
||||
use crate::runner::pipeline::{resolve_using_target, suggest_in_base};
|
||||
use crate::runner::trace::cli_verbose;
|
||||
use std::io::Read;
|
||||
use std::process::Stdio;
|
||||
use std::time::{Duration, Instant};
|
||||
use std::thread::sleep;
|
||||
use crate::runner::pipeline::{suggest_in_base, resolve_using_target};
|
||||
use crate::runner::trace::cli_verbose;
|
||||
use crate::cli_v;
|
||||
use std::time::{Duration, Instant};
|
||||
use std::{fs, process};
|
||||
|
||||
// (moved) suggest_in_base is now in runner/pipeline.rs
|
||||
|
||||
@ -17,13 +17,21 @@ impl NyashRunner {
|
||||
// legacy run_file_legacy removed (was commented out)
|
||||
|
||||
/// Helper: run PyVM harness over a MIR module, returning the exit code
|
||||
fn run_pyvm_harness(&self, module: &nyash_rust::mir::MirModule, tag: &str) -> Result<i32, String> {
|
||||
fn run_pyvm_harness(
|
||||
&self,
|
||||
module: &nyash_rust::mir::MirModule,
|
||||
tag: &str,
|
||||
) -> Result<i32, String> {
|
||||
super::common_util::pyvm::run_pyvm_harness(module, tag)
|
||||
}
|
||||
|
||||
/// Helper: try external selfhost compiler EXE to parse Ny -> JSON v0 and return MIR module
|
||||
/// Returns Some(module) on success, None on failure (timeout/invalid output/missing exe)
|
||||
fn exe_try_parse_json_v0(&self, filename: &str, timeout_ms: u64) -> Option<nyash_rust::mir::MirModule> {
|
||||
fn exe_try_parse_json_v0(
|
||||
&self,
|
||||
filename: &str,
|
||||
timeout_ms: u64,
|
||||
) -> Option<nyash_rust::mir::MirModule> {
|
||||
super::common_util::selfhost_exe::exe_try_parse_json_v0(filename, timeout_ms)
|
||||
}
|
||||
|
||||
@ -42,7 +50,10 @@ impl NyashRunner {
|
||||
// Read the file
|
||||
let code = match fs::read_to_string(filename) {
|
||||
Ok(content) => content,
|
||||
Err(e) => { eprintln!("❌ Error reading file {}: {}", filename, e); process::exit(1); }
|
||||
Err(e) => {
|
||||
eprintln!("❌ Error reading file {}: {}", filename, e);
|
||||
process::exit(1);
|
||||
}
|
||||
};
|
||||
if crate::config::env::cli_verbose() && !quiet_pipe {
|
||||
println!("📝 File contents:\n{}", code);
|
||||
@ -55,100 +66,49 @@ impl NyashRunner {
|
||||
let cleaned_code_owned;
|
||||
let mut prelude_asts: Vec<nyash_rust::ast::ASTNode> = Vec::new();
|
||||
if crate::config::env::enable_using() {
|
||||
match crate::runner::modes::common_util::resolve::resolve_prelude_paths_profiled(self, &code, filename) {
|
||||
match crate::runner::modes::common_util::resolve::resolve_prelude_paths_profiled(
|
||||
self, &code, filename,
|
||||
) {
|
||||
Ok((clean, paths)) => {
|
||||
cleaned_code_owned = clean; code_ref = &cleaned_code_owned;
|
||||
cleaned_code_owned = clean;
|
||||
code_ref = &cleaned_code_owned;
|
||||
if !paths.is_empty() && !use_ast {
|
||||
eprintln!("❌ using: AST prelude merge is disabled in this profile. Enable NYASH_USING_AST=1 or remove 'using' lines.");
|
||||
std::process::exit(1);
|
||||
}
|
||||
if use_ast {
|
||||
// Parse each prelude file into AST after stripping its own using-lines.
|
||||
// Recursively process nested preludes to avoid parse errors.
|
||||
let mut visited = std::collections::HashSet::<String>::new();
|
||||
// Normalize initial paths relative to filename or $NYASH_ROOT
|
||||
let mut stack: Vec<String> = Vec::new();
|
||||
for raw in paths {
|
||||
let mut pb = std::path::PathBuf::from(&raw);
|
||||
if pb.is_relative() {
|
||||
if let Some(dir) = std::path::Path::new(filename).parent() {
|
||||
let cand = dir.join(&pb);
|
||||
if cand.exists() { pb = cand; }
|
||||
}
|
||||
if pb.is_relative() {
|
||||
if let Ok(root) = std::env::var("NYASH_ROOT") {
|
||||
let cand = std::path::Path::new(&root).join(&pb);
|
||||
if cand.exists() { pb = cand; }
|
||||
} else {
|
||||
// Fallback: resolve relative to project root guessed from the nyash binary path
|
||||
if let Ok(exe) = std::env::current_exe() {
|
||||
if let Some(root) = exe.parent().and_then(|p| p.parent()).and_then(|p| p.parent()) {
|
||||
let cand = root.join(&pb);
|
||||
if cand.exists() { pb = cand; }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
stack.push(pb.to_string_lossy().to_string());
|
||||
}
|
||||
while let Some(mut p) = stack.pop() {
|
||||
// Normalize relative path against $NYASH_ROOT as a last resort
|
||||
if std::path::Path::new(&p).is_relative() {
|
||||
if let Ok(root) = std::env::var("NYASH_ROOT") {
|
||||
let cand = std::path::Path::new(&root).join(&p);
|
||||
p = cand.to_string_lossy().to_string();
|
||||
} else if let Ok(exe) = std::env::current_exe() {
|
||||
if let Some(root) = exe.parent().and_then(|p| p.parent()).and_then(|p| p.parent()) {
|
||||
let cand = root.join(&p);
|
||||
p = cand.to_string_lossy().to_string();
|
||||
}
|
||||
}
|
||||
}
|
||||
if !visited.insert(p.clone()) { continue; }
|
||||
match std::fs::read_to_string(&p) {
|
||||
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, &p) {
|
||||
match crate::runner::modes::common_util::resolve::collect_using_and_strip(self, &src, &prelude_path) {
|
||||
Ok((clean_src, nested)) => {
|
||||
// Normalize and push nested first so they are parsed before the current file (DFS)
|
||||
for np in nested {
|
||||
let mut npp = std::path::PathBuf::from(&np);
|
||||
if npp.is_relative() {
|
||||
if let Some(dir) = std::path::Path::new(&p).parent() {
|
||||
let cand = dir.join(&npp);
|
||||
if cand.exists() { npp = cand; }
|
||||
}
|
||||
if npp.is_relative() {
|
||||
if let Ok(root) = std::env::var("NYASH_ROOT") {
|
||||
let cand = std::path::Path::new(&root).join(&npp);
|
||||
if cand.exists() { npp = cand; }
|
||||
} else {
|
||||
if let Ok(exe) = std::env::current_exe() {
|
||||
if let Some(root) = exe.parent().and_then(|p| p.parent()).and_then(|p| p.parent()) {
|
||||
let cand = root.join(&npp);
|
||||
if cand.exists() { npp = cand; }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
let nps = npp.to_string_lossy().to_string();
|
||||
if !visited.contains(&nps) { stack.push(nps); }
|
||||
}
|
||||
// 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 {}: {}", p, e); std::process::exit(1); }
|
||||
Err(e) => {
|
||||
eprintln!("❌ Parse error in using prelude {}: {}", prelude_path, e);
|
||||
std::process::exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(e) => { eprintln!("❌ {}", e); std::process::exit(1); }
|
||||
Err(e) => {
|
||||
eprintln!("❌ {}", e);
|
||||
std::process::exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(e) => { eprintln!("❌ Error reading using prelude {}: {}", p, e); std::process::exit(1); }
|
||||
Err(e) => {
|
||||
eprintln!("❌ Error reading using prelude {}: {}", prelude_path, e);
|
||||
std::process::exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(e) => { eprintln!("❌ {}", e); std::process::exit(1); }
|
||||
Err(e) => {
|
||||
eprintln!("❌ {}", e);
|
||||
std::process::exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
// Optional dev sugar: @name[:T] = expr → local name[:T] = expr (line-head only)
|
||||
@ -161,23 +121,40 @@ impl NyashRunner {
|
||||
|
||||
// Parse the code with debug fuel limit
|
||||
let groups = self.config.as_groups();
|
||||
eprintln!("🔍 DEBUG: Starting parse with fuel: {:?}...", groups.debug.debug_fuel);
|
||||
let main_ast = match NyashParser::parse_from_string_with_fuel(code_ref, groups.debug.debug_fuel) {
|
||||
Ok(ast) => { eprintln!("🔍 DEBUG: Parse completed, AST created"); ast },
|
||||
Err(e) => { eprintln!("❌ Parse error: {}", e); process::exit(1); }
|
||||
};
|
||||
eprintln!(
|
||||
"🔍 DEBUG: Starting parse with fuel: {:?}...",
|
||||
groups.debug.debug_fuel
|
||||
);
|
||||
let main_ast =
|
||||
match NyashParser::parse_from_string_with_fuel(code_ref, groups.debug.debug_fuel) {
|
||||
Ok(ast) => {
|
||||
eprintln!("🔍 DEBUG: Parse completed, AST created");
|
||||
ast
|
||||
}
|
||||
Err(e) => {
|
||||
eprintln!("❌ Parse error: {}", e);
|
||||
process::exit(1);
|
||||
}
|
||||
};
|
||||
// When using AST prelude mode, combine prelude ASTs + main AST into one Program
|
||||
let ast = if use_ast && !prelude_asts.is_empty() {
|
||||
use nyash_rust::ast::ASTNode;
|
||||
let mut combined: Vec<ASTNode> = Vec::new();
|
||||
for a in prelude_asts {
|
||||
if let ASTNode::Program { statements, .. } = a { combined.extend(statements); }
|
||||
if let ASTNode::Program { statements, .. } = a {
|
||||
combined.extend(statements);
|
||||
}
|
||||
}
|
||||
if let ASTNode::Program { statements, .. } = main_ast.clone() {
|
||||
combined.extend(statements);
|
||||
}
|
||||
ASTNode::Program { statements: combined, span: nyash_rust::ast::Span::unknown() }
|
||||
} else { main_ast };
|
||||
ASTNode::Program {
|
||||
statements: combined,
|
||||
span: nyash_rust::ast::Span::unknown(),
|
||||
}
|
||||
} else {
|
||||
main_ast
|
||||
};
|
||||
|
||||
// Optional: dump AST statement kinds for quick diagnostics
|
||||
if std::env::var("NYASH_AST_DUMP").ok().as_deref() == Some("1") {
|
||||
@ -186,15 +163,23 @@ impl NyashRunner {
|
||||
if let ASTNode::Program { statements, .. } = &ast {
|
||||
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::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),
|
||||
ASTNode::UsingStatement { namespace_name, .. } => {
|
||||
format!("Using({})", namespace_name)
|
||||
}
|
||||
_ => format!("{:?}", st),
|
||||
};
|
||||
eprintln!("[ast] {}: {}", i, kind);
|
||||
@ -217,19 +202,32 @@ impl NyashRunner {
|
||||
let exists = p.exists();
|
||||
if !exists {
|
||||
if std::env::var("NYASH_USING_STRICT").ok().as_deref() == Some("1") {
|
||||
eprintln!("❌ import: path not found: {} (from {})", p.display(), filename);
|
||||
eprintln!(
|
||||
"❌ import: path not found: {} (from {})",
|
||||
p.display(),
|
||||
filename
|
||||
);
|
||||
process::exit(1);
|
||||
} else if crate::config::env::cli_verbose() || std::env::var("NYASH_IMPORT_TRACE").ok().as_deref() == Some("1") {
|
||||
} else if crate::config::env::cli_verbose()
|
||||
|| std::env::var("NYASH_IMPORT_TRACE").ok().as_deref() == Some("1")
|
||||
{
|
||||
eprintln!("[import] path not found (continuing): {}", p.display());
|
||||
}
|
||||
}
|
||||
let key = if let Some(a) = alias { a.clone() } else {
|
||||
let key = if let Some(a) = alias {
|
||||
a.clone()
|
||||
} else {
|
||||
std::path::Path::new(path)
|
||||
.file_stem().and_then(|s| s.to_str())
|
||||
.file_stem()
|
||||
.and_then(|s| s.to_str())
|
||||
.unwrap_or(path)
|
||||
.to_string()
|
||||
};
|
||||
let value = if exists { p.to_string_lossy().to_string() } else { path.clone() };
|
||||
let value = if exists {
|
||||
p.to_string_lossy().to_string()
|
||||
} else {
|
||||
path.clone()
|
||||
};
|
||||
let sb = nyash_rust::box_trait::StringBox::new(value);
|
||||
nyash_rust::runtime::modules_registry::set(key, Box::new(sb));
|
||||
}
|
||||
@ -237,7 +235,9 @@ impl NyashRunner {
|
||||
}
|
||||
|
||||
if crate::config::env::cli_verbose() && !quiet_pipe {
|
||||
if crate::config::env::cli_verbose() { println!("✅ Parse successful!"); }
|
||||
if crate::config::env::cli_verbose() {
|
||||
println!("✅ Parse successful!");
|
||||
}
|
||||
}
|
||||
|
||||
// Execute the AST
|
||||
@ -246,48 +246,79 @@ impl NyashRunner {
|
||||
match interpreter.execute(ast) {
|
||||
Ok(result) => {
|
||||
if crate::config::env::cli_verbose() && !quiet_pipe {
|
||||
if crate::config::env::cli_verbose() { println!("✅ Execution completed successfully!"); }
|
||||
if crate::config::env::cli_verbose() {
|
||||
println!("✅ Execution completed successfully!");
|
||||
}
|
||||
}
|
||||
// Normalize display via semantics: prefer numeric, then string, then fallback
|
||||
let disp = {
|
||||
// Special-case: plugin IntegerBox → call .get to fetch numeric value
|
||||
if let Some(p) = result.as_any().downcast_ref::<nyash_rust::runtime::plugin_loader_v2::PluginBoxV2>() {
|
||||
if let Some(p) = result
|
||||
.as_any()
|
||||
.downcast_ref::<nyash_rust::runtime::plugin_loader_v2::PluginBoxV2>(
|
||||
) {
|
||||
if p.box_type == "IntegerBox" {
|
||||
// Scope the lock strictly to this block
|
||||
let fetched = {
|
||||
let host = nyash_rust::runtime::get_global_plugin_host();
|
||||
let res = if let Ok(ro) = host.read() {
|
||||
if let Ok(Some(vb)) = ro.invoke_instance_method("IntegerBox", "get", p.instance_id(), &[]) {
|
||||
if let Some(ib) = vb.as_any().downcast_ref::<nyash_rust::box_trait::IntegerBox>() {
|
||||
if let Ok(Some(vb)) = ro.invoke_instance_method(
|
||||
"IntegerBox",
|
||||
"get",
|
||||
p.instance_id(),
|
||||
&[],
|
||||
) {
|
||||
if let Some(ib) =
|
||||
vb.as_any()
|
||||
.downcast_ref::<nyash_rust::box_trait::IntegerBox>()
|
||||
{
|
||||
Some(ib.value.to_string())
|
||||
} else {
|
||||
Some(vb.to_string_box().value)
|
||||
}
|
||||
} else { None }
|
||||
} else { None };
|
||||
} else {
|
||||
None
|
||||
}
|
||||
} else {
|
||||
None
|
||||
};
|
||||
res
|
||||
};
|
||||
if let Some(s) = fetched { s } else {
|
||||
if let Some(s) = fetched {
|
||||
s
|
||||
} else {
|
||||
nyash_rust::runtime::semantics::coerce_to_i64(result.as_ref())
|
||||
.map(|i| i.to_string())
|
||||
.or_else(|| nyash_rust::runtime::semantics::coerce_to_string(result.as_ref()))
|
||||
.or_else(|| {
|
||||
nyash_rust::runtime::semantics::coerce_to_string(
|
||||
result.as_ref(),
|
||||
)
|
||||
})
|
||||
.unwrap_or_else(|| result.to_string_box().value)
|
||||
}
|
||||
} else {
|
||||
nyash_rust::runtime::semantics::coerce_to_i64(result.as_ref())
|
||||
.map(|i| i.to_string())
|
||||
.or_else(|| nyash_rust::runtime::semantics::coerce_to_string(result.as_ref()))
|
||||
.or_else(|| {
|
||||
nyash_rust::runtime::semantics::coerce_to_string(
|
||||
result.as_ref(),
|
||||
)
|
||||
})
|
||||
.unwrap_or_else(|| result.to_string_box().value)
|
||||
}
|
||||
} else {
|
||||
nyash_rust::runtime::semantics::coerce_to_i64(result.as_ref())
|
||||
.map(|i| i.to_string())
|
||||
.or_else(|| nyash_rust::runtime::semantics::coerce_to_string(result.as_ref()))
|
||||
.or_else(|| {
|
||||
nyash_rust::runtime::semantics::coerce_to_string(result.as_ref())
|
||||
})
|
||||
.unwrap_or_else(|| result.to_string_box().value)
|
||||
}
|
||||
};
|
||||
if !quiet_pipe { println!("Result: {}", disp); }
|
||||
},
|
||||
if !quiet_pipe {
|
||||
println!("Result: {}", disp);
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
eprintln!("❌ Runtime error:\n{}", e.detailed_message(Some(&code)));
|
||||
process::exit(1);
|
||||
|
||||
@ -20,6 +20,21 @@ pub fn collect_using_and_strip(
|
||||
|
||||
let mut out = String::with_capacity(code.len());
|
||||
let mut prelude_paths: Vec<String> = Vec::new();
|
||||
// Determine if this file is inside a declared package root; if so, allow
|
||||
// internal file-using within the package even when file-using is globally disallowed.
|
||||
let filename_canon = std::fs::canonicalize(filename).ok();
|
||||
let mut inside_pkg = false;
|
||||
if let Some(ref fc) = filename_canon {
|
||||
for (_name, pkg) in &using_ctx.packages {
|
||||
let base = std::path::Path::new(&pkg.path);
|
||||
if let Ok(root) = std::fs::canonicalize(base) {
|
||||
if fc.starts_with(&root) {
|
||||
inside_pkg = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
for line in code.lines() {
|
||||
let t = line.trim_start();
|
||||
if t.starts_with("using ") {
|
||||
@ -28,11 +43,22 @@ pub fn collect_using_and_strip(
|
||||
let rest0 = rest0.split('#').next().unwrap_or(rest0).trim();
|
||||
let rest0 = rest0.strip_suffix(';').unwrap_or(rest0).trim();
|
||||
let (target, _alias) = if let Some(pos) = rest0.find(" as ") {
|
||||
(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");
|
||||
(
|
||||
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");
|
||||
if is_path {
|
||||
if prod || !crate::config::env::allow_using_file() {
|
||||
// SSOT: Disallow file-using at top-level; allow only for sources located
|
||||
// under a declared package root (internal package wiring), so that packages
|
||||
// can organize their modules via file paths.
|
||||
if (prod || !crate::config::env::allow_using_file()) && !inside_pkg {
|
||||
return Err(format!(
|
||||
"using: file paths are disallowed in this profile. Add it to nyash.toml [using] (packages/aliases) and reference by name: {}",
|
||||
target
|
||||
@ -42,24 +68,43 @@ pub fn collect_using_and_strip(
|
||||
// Resolve relative to current file dir
|
||||
let mut p = std::path::PathBuf::from(&path);
|
||||
if p.is_relative() {
|
||||
if let Some(dir) = ctx_dir { let cand = dir.join(&p); if cand.exists() { p = cand; } }
|
||||
if let Some(dir) = ctx_dir {
|
||||
let cand = dir.join(&p);
|
||||
if cand.exists() {
|
||||
p = cand;
|
||||
}
|
||||
}
|
||||
// Also try NYASH_ROOT when available (repo-root relative like "apps/...")
|
||||
if p.is_relative() {
|
||||
if let Ok(root) = std::env::var("NYASH_ROOT") {
|
||||
let cand = std::path::Path::new(&root).join(&p);
|
||||
if cand.exists() { p = cand; }
|
||||
if cand.exists() {
|
||||
p = cand;
|
||||
}
|
||||
} else {
|
||||
// Fallback: guess project root from executable path (target/release/nyash)
|
||||
if let Ok(exe) = std::env::current_exe() {
|
||||
if let Some(root) = exe.parent().and_then(|p| p.parent()).and_then(|p| p.parent()) {
|
||||
if let Some(root) = exe
|
||||
.parent()
|
||||
.and_then(|p| p.parent())
|
||||
.and_then(|p| p.parent())
|
||||
{
|
||||
let cand = root.join(&p);
|
||||
if cand.exists() { p = cand; }
|
||||
if cand.exists() {
|
||||
p = cand;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if verbose { crate::runner::trace::log(format!("[using/resolve] file '{}' -> '{}'", target, p.display())); }
|
||||
if verbose {
|
||||
crate::runner::trace::log(format!(
|
||||
"[using/resolve] file '{}' -> '{}'",
|
||||
target,
|
||||
p.display()
|
||||
));
|
||||
}
|
||||
prelude_paths.push(p.to_string_lossy().to_string());
|
||||
continue;
|
||||
}
|
||||
@ -87,8 +132,13 @@ pub fn collect_using_and_strip(
|
||||
} else if base.extension().and_then(|s| s.to_str()) == Some("nyash") {
|
||||
pkg.path.clone()
|
||||
} else {
|
||||
let leaf = base.file_name().and_then(|s| s.to_str()).unwrap_or(&pkg_name);
|
||||
base.join(format!("{}.nyash", leaf)).to_string_lossy().to_string()
|
||||
let leaf = base
|
||||
.file_name()
|
||||
.and_then(|s| s.to_str())
|
||||
.unwrap_or(&pkg_name);
|
||||
base.join(format!("{}.nyash", leaf))
|
||||
.to_string_lossy()
|
||||
.to_string()
|
||||
};
|
||||
prelude_paths.push(out);
|
||||
}
|
||||
@ -114,26 +164,46 @@ pub fn collect_using_and_strip(
|
||||
) {
|
||||
Ok(value) => {
|
||||
// Only file paths are candidates for AST prelude merge
|
||||
if value.ends_with(".nyash") || value.contains('/') || value.contains('\\') {
|
||||
if value.ends_with(".nyash") || value.contains('/') || value.contains('\\')
|
||||
{
|
||||
// Resolve relative
|
||||
let mut p = std::path::PathBuf::from(&value);
|
||||
if p.is_relative() {
|
||||
if let Some(dir) = ctx_dir { let cand = dir.join(&p); if cand.exists() { p = cand; } }
|
||||
if let Some(dir) = ctx_dir {
|
||||
let cand = dir.join(&p);
|
||||
if cand.exists() {
|
||||
p = cand;
|
||||
}
|
||||
}
|
||||
if p.is_relative() {
|
||||
if let Ok(root) = std::env::var("NYASH_ROOT") {
|
||||
let cand = std::path::Path::new(&root).join(&p);
|
||||
if cand.exists() { p = cand; }
|
||||
if cand.exists() {
|
||||
p = cand;
|
||||
}
|
||||
} else {
|
||||
if let Ok(exe) = std::env::current_exe() {
|
||||
if let Some(root) = exe.parent().and_then(|p| p.parent()).and_then(|p| p.parent()) {
|
||||
if let Some(root) = exe
|
||||
.parent()
|
||||
.and_then(|p| p.parent())
|
||||
.and_then(|p| p.parent())
|
||||
{
|
||||
let cand = root.join(&p);
|
||||
if cand.exists() { p = cand; }
|
||||
if cand.exists() {
|
||||
p = cand;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if verbose { crate::runner::trace::log(format!("[using/resolve] dev-file '{}' -> '{}'", value, p.display())); }
|
||||
if verbose {
|
||||
crate::runner::trace::log(format!(
|
||||
"[using/resolve] dev-file '{}' -> '{}'",
|
||||
value,
|
||||
p.display()
|
||||
));
|
||||
}
|
||||
prelude_paths.push(p.to_string_lossy().to_string());
|
||||
}
|
||||
}
|
||||
@ -163,7 +233,51 @@ pub fn resolve_prelude_paths_profiled(
|
||||
code: &str,
|
||||
filename: &str,
|
||||
) -> Result<(String, Vec<String>), String> {
|
||||
collect_using_and_strip(runner, code, filename)
|
||||
// First pass: strip using from the main source and collect direct prelude paths
|
||||
let (cleaned, direct) = collect_using_and_strip(runner, code, filename)?;
|
||||
// When AST using is enabled、recursively collect nested preludes in DFS order
|
||||
let ast_on = std::env::var("NYASH_USING_AST").ok().as_deref() == Some("1");
|
||||
if !ast_on {
|
||||
return Ok((cleaned, direct));
|
||||
}
|
||||
let mut out: Vec<String> = Vec::new();
|
||||
let mut seen: std::collections::HashSet<String> = std::collections::HashSet::new();
|
||||
fn normalize_path(path: &str) -> (String, String) {
|
||||
use std::path::PathBuf;
|
||||
match PathBuf::from(path).canonicalize() {
|
||||
Ok(canon) => {
|
||||
let s = canon.to_string_lossy().to_string();
|
||||
(s.clone(), s)
|
||||
}
|
||||
Err(_) => {
|
||||
// Fall back to the original path representation.
|
||||
(path.to_string(), path.to_string())
|
||||
}
|
||||
}
|
||||
}
|
||||
fn dfs(
|
||||
runner: &NyashRunner,
|
||||
path: &str,
|
||||
out: &mut Vec<String>,
|
||||
seen: &mut std::collections::HashSet<String>,
|
||||
) -> Result<(), String> {
|
||||
let (key, real_path) = normalize_path(path);
|
||||
if !seen.insert(key.clone()) {
|
||||
return Ok(());
|
||||
}
|
||||
let src = std::fs::read_to_string(&real_path)
|
||||
.map_err(|e| format!("using: failed to read '{}': {}", real_path, e))?;
|
||||
let (_cleaned, nested) = collect_using_and_strip(runner, &src, &real_path)?;
|
||||
for n in nested.iter() {
|
||||
dfs(runner, n, out, seen)?;
|
||||
}
|
||||
out.push(real_path);
|
||||
Ok(())
|
||||
}
|
||||
for p in direct.iter() {
|
||||
dfs(runner, p, &mut out, &mut seen)?;
|
||||
}
|
||||
Ok((cleaned, out))
|
||||
}
|
||||
|
||||
/// Pre-expand line-head `@name[: Type] = expr` into `local name[: Type] = expr`.
|
||||
@ -173,21 +287,49 @@ pub fn preexpand_at_local(src: &str) -> String {
|
||||
for line in src.lines() {
|
||||
let bytes = line.as_bytes();
|
||||
let mut i = 0;
|
||||
while i < bytes.len() && (bytes[i] == b' ' || bytes[i] == b'\t') { i += 1; }
|
||||
while i < bytes.len() && (bytes[i] == b' ' || bytes[i] == b'\t') {
|
||||
i += 1;
|
||||
}
|
||||
if i < bytes.len() && bytes[i] == b'@' {
|
||||
// parse identifier
|
||||
let mut j = i + 1;
|
||||
if j < bytes.len() && ((bytes[j] as char).is_ascii_alphabetic() || bytes[j] == b'_') {
|
||||
j += 1;
|
||||
while j < bytes.len() { let c = bytes[j] as char; if c.is_ascii_alphanumeric() || c == '_' { j += 1; } else { break; } }
|
||||
let mut k = j; while k < bytes.len() && (bytes[k] == b' ' || bytes[k] == b'\t') { k += 1; }
|
||||
if k < bytes.len() && bytes[k] == b':' {
|
||||
k += 1; while k < bytes.len() && (bytes[k] == b' ' || bytes[k] == b'\t') { k += 1; }
|
||||
if k < bytes.len() && ((bytes[k] as char).is_ascii_alphabetic() || bytes[k] == b'_') {
|
||||
k += 1; while k < bytes.len() { let c = bytes[k] as char; if c.is_ascii_alphanumeric() || c == '_' { k += 1; } else { break; } }
|
||||
while j < bytes.len() {
|
||||
let c = bytes[j] as char;
|
||||
if c.is_ascii_alphanumeric() || c == '_' {
|
||||
j += 1;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
let mut eqp = k; while eqp < bytes.len() && (bytes[eqp] == b' ' || bytes[eqp] == b'\t') { eqp += 1; }
|
||||
let mut k = j;
|
||||
while k < bytes.len() && (bytes[k] == b' ' || bytes[k] == b'\t') {
|
||||
k += 1;
|
||||
}
|
||||
if k < bytes.len() && bytes[k] == b':' {
|
||||
k += 1;
|
||||
while k < bytes.len() && (bytes[k] == b' ' || bytes[k] == b'\t') {
|
||||
k += 1;
|
||||
}
|
||||
if k < bytes.len()
|
||||
&& ((bytes[k] as char).is_ascii_alphabetic() || bytes[k] == b'_')
|
||||
{
|
||||
k += 1;
|
||||
while k < bytes.len() {
|
||||
let c = bytes[k] as char;
|
||||
if c.is_ascii_alphanumeric() || c == '_' {
|
||||
k += 1;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
let mut eqp = k;
|
||||
while eqp < bytes.len() && (bytes[eqp] == b' ' || bytes[eqp] == b'\t') {
|
||||
eqp += 1;
|
||||
}
|
||||
if eqp < bytes.len() && bytes[eqp] == b'=' {
|
||||
out.push_str(&line[..i]);
|
||||
out.push_str("local ");
|
||||
|
||||
@ -21,15 +21,85 @@ impl NyashRunner {
|
||||
}
|
||||
};
|
||||
|
||||
// Parse to AST
|
||||
let ast = match NyashParser::parse_from_string(&code) {
|
||||
// Using handling (AST prelude merge like common/vm paths)
|
||||
let use_ast = crate::config::env::using_ast_enabled();
|
||||
let mut code_ref: &str = &code;
|
||||
let cleaned_code_owned;
|
||||
let mut prelude_asts: Vec<nyash_rust::ast::ASTNode> = Vec::new();
|
||||
if crate::config::env::enable_using() {
|
||||
match crate::runner::modes::common_util::resolve::resolve_prelude_paths_profiled(
|
||||
self, &code, filename,
|
||||
) {
|
||||
Ok((clean, paths)) => {
|
||||
cleaned_code_owned = clean;
|
||||
code_ref = &cleaned_code_owned;
|
||||
if !paths.is_empty() && !use_ast {
|
||||
eprintln!("❌ using: AST prelude merge is disabled in this profile. Enable NYASH_USING_AST=1 or remove 'using' lines.");
|
||||
std::process::exit(1);
|
||||
}
|
||||
if use_ast {
|
||||
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)) => {
|
||||
match NyashParser::parse_from_string(&clean_src) {
|
||||
Ok(ast) => prelude_asts.push(ast),
|
||||
Err(e) => {
|
||||
eprintln!("❌ Parse error in using prelude {}: {}", prelude_path, e);
|
||||
std::process::exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
eprintln!("❌ {}", e);
|
||||
std::process::exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
eprintln!("❌ Error reading using prelude {}: {}", prelude_path, e);
|
||||
std::process::exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
eprintln!("❌ {}", e);
|
||||
process::exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
// Pre-expand '@name[:T] = expr' sugar at line-head (same as common path)
|
||||
let preexpanded_owned = crate::runner::modes::common_util::resolve::preexpand_at_local(code_ref);
|
||||
code_ref = &preexpanded_owned;
|
||||
|
||||
// Parse to AST (main)
|
||||
let main_ast = match NyashParser::parse_from_string(code_ref) {
|
||||
Ok(ast) => ast,
|
||||
Err(e) => {
|
||||
eprintln!("❌ Parse error: {}", e);
|
||||
process::exit(1);
|
||||
}
|
||||
};
|
||||
// Macro expansion (env-gated)
|
||||
// Merge preludes + main when enabled
|
||||
let ast = if use_ast && !prelude_asts.is_empty() {
|
||||
use nyash_rust::ast::ASTNode;
|
||||
let mut combined: Vec<ASTNode> = Vec::new();
|
||||
for a in prelude_asts {
|
||||
if let ASTNode::Program { statements, .. } = a {
|
||||
combined.extend(statements);
|
||||
}
|
||||
}
|
||||
if let ASTNode::Program { statements, .. } = main_ast.clone() {
|
||||
combined.extend(statements);
|
||||
}
|
||||
ASTNode::Program { statements: combined, span: nyash_rust::ast::Span::unknown() }
|
||||
} else {
|
||||
main_ast
|
||||
};
|
||||
// Macro expansion (env-gated) after merge
|
||||
let ast = crate::r#macro::maybe_expand_and_dump(&ast, false);
|
||||
let ast = crate::runner::modes::macro_child::normalize_core_pass(&ast);
|
||||
|
||||
@ -52,6 +122,14 @@ impl NyashRunner {
|
||||
let injected = inject_method_ids(&mut module);
|
||||
if injected > 0 { crate::cli_v!("[LLVM] method_id injected: {} places", injected); }
|
||||
|
||||
// Dev/Test helper: allow executing via PyVM harness when requested
|
||||
if std::env::var("SMOKES_USE_PYVM").ok().as_deref() == Some("1") {
|
||||
match super::common_util::pyvm::run_pyvm_harness_lib(&module, "llvm-ast") {
|
||||
Ok(code) => { std::process::exit(code); }
|
||||
Err(e) => { eprintln!("❌ PyVM harness error: {}", e); std::process::exit(1); }
|
||||
}
|
||||
}
|
||||
|
||||
// If explicit object path is requested, emit object only
|
||||
if let Ok(_out_path) = std::env::var("NYASH_LLVM_OBJ_OUT") {
|
||||
#[cfg(feature = "llvm-harness")]
|
||||
|
||||
@ -18,95 +18,57 @@ impl NyashRunner {
|
||||
// Read source
|
||||
let code = match fs::read_to_string(filename) {
|
||||
Ok(s) => s,
|
||||
Err(e) => { eprintln!("❌ Error reading file {}: {}", filename, e); process::exit(1); }
|
||||
Err(e) => {
|
||||
eprintln!("❌ Error reading file {}: {}", filename, e);
|
||||
process::exit(1);
|
||||
}
|
||||
};
|
||||
// Using preprocessing with AST-prelude merge (when NYASH_USING_AST=1)
|
||||
let mut code2 = code;
|
||||
let use_ast_prelude = crate::config::env::enable_using()
|
||||
&& crate::config::env::using_ast_enabled();
|
||||
let use_ast_prelude =
|
||||
crate::config::env::enable_using() && crate::config::env::using_ast_enabled();
|
||||
let mut prelude_asts: Vec<nyash_rust::ast::ASTNode> = Vec::new();
|
||||
if crate::config::env::enable_using() {
|
||||
match crate::runner::modes::common_util::resolve::resolve_prelude_paths_profiled(self, &code2, filename) {
|
||||
match crate::runner::modes::common_util::resolve::resolve_prelude_paths_profiled(
|
||||
self, &code2, filename,
|
||||
) {
|
||||
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);
|
||||
}
|
||||
// Normalize initial prelude paths relative to filename or $NYASH_ROOT,
|
||||
// then recursively process prelude files: strip their using-lines and parse cleaned ASTs
|
||||
let mut visited = std::collections::HashSet::<String>::new();
|
||||
let mut stack: Vec<String> = Vec::new();
|
||||
for raw in paths {
|
||||
let mut pb = std::path::PathBuf::from(&raw);
|
||||
if pb.is_relative() {
|
||||
if let Some(dir) = std::path::Path::new(filename).parent() {
|
||||
let cand = dir.join(&pb);
|
||||
if cand.exists() { pb = cand; }
|
||||
}
|
||||
if pb.is_relative() {
|
||||
if let Ok(root) = std::env::var("NYASH_ROOT") {
|
||||
let cand = std::path::Path::new(&root).join(&pb);
|
||||
if cand.exists() { pb = cand; }
|
||||
} else {
|
||||
if let Ok(exe) = std::env::current_exe() {
|
||||
if let Some(root) = exe.parent().and_then(|p| p.parent()).and_then(|p| p.parent()) {
|
||||
let cand = root.join(&pb);
|
||||
if cand.exists() { pb = cand; }
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
eprintln!("❌ {}", e);
|
||||
process::exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
stack.push(pb.to_string_lossy().to_string());
|
||||
}
|
||||
while let Some(mut p) = stack.pop() {
|
||||
if std::path::Path::new(&p).is_relative() {
|
||||
if let Ok(root) = std::env::var("NYASH_ROOT") {
|
||||
let cand = std::path::Path::new(&root).join(&p);
|
||||
p = cand.to_string_lossy().to_string();
|
||||
Err(e) => {
|
||||
eprintln!("❌ Error reading using prelude {}: {}", prelude_path, e);
|
||||
process::exit(1);
|
||||
}
|
||||
}
|
||||
if !visited.insert(p.clone()) { continue; }
|
||||
match std::fs::read_to_string(&p) {
|
||||
Ok(src) => match crate::runner::modes::common_util::resolve::collect_using_and_strip(self, &src, &p) {
|
||||
Ok((clean_src, nested)) => {
|
||||
for np in nested {
|
||||
let mut npp = std::path::PathBuf::from(&np);
|
||||
if npp.is_relative() {
|
||||
if let Some(dir) = std::path::Path::new(&p).parent() {
|
||||
let cand = dir.join(&npp);
|
||||
if cand.exists() { npp = cand; }
|
||||
}
|
||||
if npp.is_relative() {
|
||||
if let Ok(root) = std::env::var("NYASH_ROOT") {
|
||||
let cand = std::path::Path::new(&root).join(&npp);
|
||||
if cand.exists() { npp = cand; }
|
||||
} else {
|
||||
if let Ok(exe) = std::env::current_exe() {
|
||||
if let Some(root) = exe.parent().and_then(|p| p.parent()).and_then(|p| p.parent()) {
|
||||
let cand = root.join(&npp);
|
||||
if cand.exists() { npp = cand; }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
let nps = npp.to_string_lossy().to_string();
|
||||
if !visited.contains(&nps) { stack.push(nps); }
|
||||
}
|
||||
match NyashParser::parse_from_string(&clean_src) {
|
||||
Ok(ast) => prelude_asts.push(ast),
|
||||
Err(e) => { eprintln!("❌ Parse error in using prelude {}: {}", p, e); process::exit(1); }
|
||||
}
|
||||
}
|
||||
Err(e) => { eprintln!("❌ {}", e); process::exit(1); }
|
||||
},
|
||||
Err(e) => { eprintln!("❌ Error reading using prelude {}: {}", p, e); process::exit(1); }
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(e) => { eprintln!("❌ {}", e); process::exit(1); }
|
||||
Err(e) => {
|
||||
eprintln!("❌ {}", e);
|
||||
process::exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
// Dev sugar pre-expand: @name = expr → local name = expr
|
||||
@ -115,20 +77,30 @@ impl NyashRunner {
|
||||
// Parse main code
|
||||
let main_ast = match NyashParser::parse_from_string(&code2) {
|
||||
Ok(ast) => ast,
|
||||
Err(e) => { eprintln!("❌ Parse error: {}", e); process::exit(1); }
|
||||
Err(e) => {
|
||||
eprintln!("❌ Parse error: {}", e);
|
||||
process::exit(1);
|
||||
}
|
||||
};
|
||||
// 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 {
|
||||
if let ASTNode::Program { statements, .. } = a { combined.extend(statements); }
|
||||
if let ASTNode::Program { statements, .. } = a {
|
||||
combined.extend(statements);
|
||||
}
|
||||
}
|
||||
if let ASTNode::Program { statements, .. } = main_ast.clone() {
|
||||
combined.extend(statements);
|
||||
}
|
||||
ASTNode::Program { statements: combined, span: nyash_rust::ast::Span::unknown() }
|
||||
} else { main_ast };
|
||||
ASTNode::Program {
|
||||
statements: combined,
|
||||
span: nyash_rust::ast::Span::unknown(),
|
||||
}
|
||||
} else {
|
||||
main_ast
|
||||
};
|
||||
// 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;
|
||||
@ -136,15 +108,23 @@ impl NyashRunner {
|
||||
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::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),
|
||||
ASTNode::UsingStatement { namespace_name, .. } => {
|
||||
format!("Using({})", namespace_name)
|
||||
}
|
||||
_ => format!("{:?}", st),
|
||||
};
|
||||
eprintln!("[ast] {}: {}", i, kind);
|
||||
@ -162,8 +142,12 @@ impl NyashRunner {
|
||||
use nyash_rust::ast::ASTNode;
|
||||
|
||||
// Collect user-defined (non-static) box declarations at program level.
|
||||
let mut decls: std::collections::HashMap<String, CoreBoxDecl> =
|
||||
// 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> =
|
||||
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 {
|
||||
@ -181,10 +165,10 @@ impl NyashRunner {
|
||||
type_parameters,
|
||||
is_static,
|
||||
..
|
||||
} = st
|
||||
{
|
||||
} = st {
|
||||
if *is_static {
|
||||
continue; // modules/static boxes are not user-instantiable
|
||||
static_names.push(name.clone());
|
||||
continue; // modules/static boxes are not user-instantiable directly
|
||||
}
|
||||
let decl = CoreBoxDecl {
|
||||
name: name.clone(),
|
||||
@ -200,10 +184,18 @@ impl NyashRunner {
|
||||
implements: implements.clone(),
|
||||
type_parameters: type_parameters.clone(),
|
||||
};
|
||||
decls.insert(name.clone(), decl);
|
||||
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
|
||||
@ -215,7 +207,8 @@ impl NyashRunner {
|
||||
&self,
|
||||
name: &str,
|
||||
args: &[Box<dyn crate::box_trait::NyashBox>],
|
||||
) -> Result<Box<dyn crate::box_trait::NyashBox>, RuntimeError> {
|
||||
) -> Result<Box<dyn crate::box_trait::NyashBox>, RuntimeError>
|
||||
{
|
||||
let opt = { self.decls.read().unwrap().get(name).cloned() };
|
||||
let decl = match opt {
|
||||
Some(d) => d,
|
||||
@ -234,35 +227,65 @@ impl NyashRunner {
|
||||
Ok(Box::new(inst))
|
||||
}
|
||||
|
||||
fn box_types(&self) -> Vec<&str> { vec![] }
|
||||
fn box_types(&self) -> Vec<&str> {
|
||||
vec![]
|
||||
}
|
||||
|
||||
fn is_available(&self) -> bool { true }
|
||||
fn is_available(&self) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
fn factory_type(
|
||||
&self,
|
||||
) -> crate::box_factory::FactoryType {
|
||||
fn factory_type(&self) -> crate::box_factory::FactoryType {
|
||||
crate::box_factory::FactoryType::User
|
||||
}
|
||||
}
|
||||
let factory = InlineUserBoxFactory { decls: Arc::new(RwLock::new(decls)) };
|
||||
let factory = InlineUserBoxFactory {
|
||||
decls: Arc::new(RwLock::new(decls)),
|
||||
};
|
||||
crate::runtime::unified_registry::register_user_defined_factory(Arc::new(factory));
|
||||
}
|
||||
}
|
||||
let mut compiler = MirCompiler::with_options(!self.config.no_optimize);
|
||||
let compile = match compiler.compile(ast) {
|
||||
Ok(c) => c,
|
||||
Err(e) => { eprintln!("❌ MIR compilation error: {}", e); process::exit(1); }
|
||||
Err(e) => {
|
||||
eprintln!("❌ MIR compilation error: {}", e);
|
||||
process::exit(1);
|
||||
}
|
||||
};
|
||||
|
||||
// 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);
|
||||
if removed > 0 { crate::cli_v!("[VM-fallback] escape_elide_barriers: removed {} barriers", removed); }
|
||||
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));
|
||||
}
|
||||
|
||||
// Execute via MIR interpreter
|
||||
let mut vm = MirInterpreter::new();
|
||||
// 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); }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if std::env::var("NYASH_DUMP_FUNCS").ok().as_deref() == Some("1") {
|
||||
eprintln!("[vm] functions available:");
|
||||
for k in module_vm.functions.keys() {
|
||||
|
||||
Reference in New Issue
Block a user