using(ast): add AST prelude merge mode (NYASH_USING_AST=1); strip-only collector; combine Program ASTs; prep for parser fix. Also keep legacy inlining default.
This commit is contained in:
@ -49,13 +49,36 @@ impl NyashRunner {
|
|||||||
println!("\n🚀 Parsing and executing...\n");
|
println!("\n🚀 Parsing and executing...\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Optional Phase-15: strip `using` lines (gate) for minimal acceptance
|
// Using handling: either strip+inline (legacy) or AST-based prelude merge (when NYASH_USING_AST=1)
|
||||||
|
let use_ast = std::env::var("NYASH_USING_AST").ok().as_deref() == Some("1");
|
||||||
let mut code_ref: &str = &code;
|
let mut code_ref: &str = &code;
|
||||||
let cleaned_code_owned;
|
let cleaned_code_owned;
|
||||||
|
let mut prelude_asts: Vec<nyash_rust::ast::ASTNode> = Vec::new();
|
||||||
if crate::config::env::enable_using() {
|
if crate::config::env::enable_using() {
|
||||||
match crate::runner::modes::common_util::resolve::strip_using_and_register(self, &code, filename) {
|
if use_ast {
|
||||||
Ok(s) => { cleaned_code_owned = s; code_ref = &cleaned_code_owned; }
|
match crate::runner::modes::common_util::resolve::collect_using_and_strip(self, &code, filename) {
|
||||||
Err(e) => { eprintln!("❌ {}", e); std::process::exit(1); }
|
Ok((clean, paths)) => {
|
||||||
|
cleaned_code_owned = clean; code_ref = &cleaned_code_owned;
|
||||||
|
// Parse each prelude file into AST and store
|
||||||
|
for p in paths {
|
||||||
|
match std::fs::read_to_string(&p) {
|
||||||
|
Ok(src) => {
|
||||||
|
match NyashParser::parse_from_string(&src) {
|
||||||
|
Ok(ast) => prelude_asts.push(ast),
|
||||||
|
Err(e) => { eprintln!("❌ Parse error in using prelude {}: {}", p, e); std::process::exit(1); }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(e) => { eprintln!("❌ Error reading using prelude {}: {}", p, e); std::process::exit(1); }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(e) => { eprintln!("❌ {}", e); std::process::exit(1); }
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
match crate::runner::modes::common_util::resolve::strip_using_and_register(self, &code, filename) {
|
||||||
|
Ok(s) => { cleaned_code_owned = s; code_ref = &cleaned_code_owned; }
|
||||||
|
Err(e) => { eprintln!("❌ {}", e); std::process::exit(1); }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Optional dev sugar: @name[:T] = expr → local name[:T] = expr (line-head only)
|
// Optional dev sugar: @name[:T] = expr → local name[:T] = expr (line-head only)
|
||||||
@ -69,10 +92,22 @@ impl NyashRunner {
|
|||||||
// Parse the code with debug fuel limit
|
// Parse the code with debug fuel limit
|
||||||
let groups = self.config.as_groups();
|
let groups = self.config.as_groups();
|
||||||
eprintln!("🔍 DEBUG: Starting parse with fuel: {:?}...", groups.debug.debug_fuel);
|
eprintln!("🔍 DEBUG: Starting parse with fuel: {:?}...", groups.debug.debug_fuel);
|
||||||
let ast = match NyashParser::parse_from_string_with_fuel(code_ref, 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 },
|
Ok(ast) => { eprintln!("🔍 DEBUG: Parse completed, AST created"); 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
|
||||||
|
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 };
|
||||||
|
|
||||||
// Stage-0: import loader (top-level only) — resolve path and register in modules registry
|
// Stage-0: import loader (top-level only) — resolve path and register in modules registry
|
||||||
if let nyash_rust::ast::ASTNode::Program { statements, .. } = &ast {
|
if let nyash_rust::ast::ASTNode::Program { statements, .. } = &ast {
|
||||||
|
|||||||
@ -423,6 +423,86 @@ pub fn strip_using_and_register(
|
|||||||
Ok(preexpand_at_local(&combined))
|
Ok(preexpand_at_local(&combined))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Collect using targets and strip using lines, without inlining.
|
||||||
|
/// Returns (cleaned_source, prelude_paths) where prelude_paths are resolved file paths
|
||||||
|
/// to be parsed separately and AST-merged (when NYASH_USING_AST=1).
|
||||||
|
pub fn collect_using_and_strip(
|
||||||
|
runner: &NyashRunner,
|
||||||
|
code: &str,
|
||||||
|
filename: &str,
|
||||||
|
) -> Result<(String, Vec<String>), String> {
|
||||||
|
if !crate::config::env::enable_using() {
|
||||||
|
return Ok((code.to_string(), Vec::new()));
|
||||||
|
}
|
||||||
|
let using_ctx = runner.init_using_context();
|
||||||
|
let strict = std::env::var("NYASH_USING_STRICT").ok().as_deref() == Some("1");
|
||||||
|
let verbose = crate::config::env::cli_verbose()
|
||||||
|
|| std::env::var("NYASH_RESOLVE_TRACE").ok().as_deref() == Some("1");
|
||||||
|
let ctx_dir = std::path::Path::new(filename).parent();
|
||||||
|
|
||||||
|
let mut out = String::with_capacity(code.len());
|
||||||
|
let mut prelude_paths: Vec<String> = Vec::new();
|
||||||
|
for line in code.lines() {
|
||||||
|
let t = line.trim_start();
|
||||||
|
if t.starts_with("using ") {
|
||||||
|
crate::cli_v!("[using] stripped line: {}", line);
|
||||||
|
let rest0 = t.strip_prefix("using ").unwrap().trim();
|
||||||
|
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");
|
||||||
|
if is_path {
|
||||||
|
let path = target.trim_matches('"').to_string();
|
||||||
|
// 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; } }
|
||||||
|
}
|
||||||
|
prelude_paths.push(p.to_string_lossy().to_string());
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// Resolve namespaces/packages
|
||||||
|
match crate::runner::pipeline::resolve_using_target(
|
||||||
|
&target,
|
||||||
|
false,
|
||||||
|
&using_ctx.pending_modules,
|
||||||
|
&using_ctx.using_paths,
|
||||||
|
&using_ctx.aliases,
|
||||||
|
&using_ctx.packages,
|
||||||
|
ctx_dir,
|
||||||
|
strict,
|
||||||
|
verbose,
|
||||||
|
) {
|
||||||
|
Ok(value) => {
|
||||||
|
// Only file paths are candidates for AST prelude merge
|
||||||
|
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; } }
|
||||||
|
}
|
||||||
|
prelude_paths.push(p.to_string_lossy().to_string());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(e) => return Err(format!("using: {}", e)),
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
out.push_str(line);
|
||||||
|
out.push('\n');
|
||||||
|
}
|
||||||
|
// Optional prelude boundary comment (helps manual inspection; parser ignores comments)
|
||||||
|
if std::env::var("NYASH_RESOLVE_SEAM_DEBUG").ok().as_deref() == Some("1") {
|
||||||
|
let mut with_marker = String::with_capacity(out.len() + 64);
|
||||||
|
with_marker.push_str("\n/* --- using boundary (AST) --- */\n");
|
||||||
|
with_marker.push_str(&out);
|
||||||
|
out = with_marker;
|
||||||
|
}
|
||||||
|
Ok((out, prelude_paths))
|
||||||
|
}
|
||||||
|
|
||||||
/// Pre-expand line-head `@name[: Type] = expr` into `local name[: Type] = expr`.
|
/// Pre-expand line-head `@name[: Type] = expr` into `local name[: Type] = expr`.
|
||||||
/// Minimal, safe, no semantics change. Applies only at line head (after spaces/tabs).
|
/// Minimal, safe, no semantics change. Applies only at line head (after spaces/tabs).
|
||||||
pub fn preexpand_at_local(src: &str) -> String {
|
pub fn preexpand_at_local(src: &str) -> String {
|
||||||
|
|||||||
Reference in New Issue
Block a user