phase: 20.49 COMPLETE; 20.50 Flow+String minimal reps; 20.51 selfhost v0/v1 minimal (Option A/B); hv1-inline binop/unop/copy; docs + run_all + CURRENT_TASK -> 21.0
This commit is contained in:
@ -118,7 +118,7 @@ pub(super) fn run_build_mvp_impl(runner: &NyashRunner, cfg_path: &str) -> Result
|
||||
let app = if !app.is_empty() {
|
||||
app
|
||||
} else {
|
||||
// collect candidates under apps/**/main.nyash
|
||||
// collect candidates under apps/**/main.hako
|
||||
let mut cand: Vec<String> = Vec::new();
|
||||
fn walk(dir: &Path, acc: &mut Vec<String>) {
|
||||
if let Ok(rd) = std::fs::read_dir(dir) {
|
||||
@ -126,7 +126,7 @@ pub(super) fn run_build_mvp_impl(runner: &NyashRunner, cfg_path: &str) -> Result
|
||||
let p = e.path();
|
||||
if p.is_dir() {
|
||||
walk(&p, acc);
|
||||
} else if p.file_name().map(|n| n == "main.nyash").unwrap_or(false) {
|
||||
} else if p.file_name().map(|n| n == "main.hako").unwrap_or(false) {
|
||||
acc.push(p.display().to_string());
|
||||
}
|
||||
}
|
||||
@ -134,7 +134,7 @@ pub(super) fn run_build_mvp_impl(runner: &NyashRunner, cfg_path: &str) -> Result
|
||||
}
|
||||
walk(&cwd.join("apps"), &mut cand);
|
||||
let msg = if cand.is_empty() {
|
||||
"no app specified (--app) and no apps/**/main.nyash found".to_string()
|
||||
"no app specified (--app) and no apps/**/main.hako found".to_string()
|
||||
} else {
|
||||
format!(
|
||||
"no app specified (--app). Candidates:\n - {}",
|
||||
|
||||
@ -39,6 +39,36 @@ pub fn run_json_v0(runner: &NyashRunner, json: &str) -> i32 {
|
||||
}
|
||||
let mut payload = json.to_string();
|
||||
|
||||
// Optional: downconvert/canonicalize even for v1 when requested (dev diagnostics)
|
||||
if crate::config::env::nyvm_v1_downconvert() {
|
||||
if let Ok(j) = crate::runner::modes::common_util::core_bridge::canonicalize_module_json(&payload) {
|
||||
payload = j;
|
||||
}
|
||||
}
|
||||
|
||||
// Prefer v1 bridge when schema_version is present (JSON v1). This must run
|
||||
// before the v0 fast-path because v1 payloads also contain `functions` and
|
||||
// `blocks`, which would otherwise be misrouted to the v0 loader.
|
||||
if payload.contains("\"schema_version\"") {
|
||||
match crate::runner::json_v1_bridge::try_parse_v1_to_module(&payload) {
|
||||
Ok(Some(module)) => {
|
||||
super::json_v0_bridge::maybe_dump_mir(&module);
|
||||
crate::runner::child_env::pre_run_reset_oob_if_strict();
|
||||
let rc = runner.execute_mir_module_quiet_exit(&module);
|
||||
if crate::config::env::oob_strict_fail() && crate::runtime::observe::oob_seen() {
|
||||
eprintln!("[gate-c][oob-strict] Out-of-bounds observed → exit(1)");
|
||||
return 1;
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
Ok(None) => { /* fall through to v0 path */ }
|
||||
Err(e) => {
|
||||
eprintln!("❌ JSON v1 bridge error: {}", e);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Fast-path: accept MIR(JSON v0) directly when it looks like a module (functions/blocks)
|
||||
if payload.contains("\"functions\"") && payload.contains("\"blocks\"") {
|
||||
match super::mir_json_v0::parse_mir_v0_to_module(&payload) {
|
||||
@ -59,15 +89,13 @@ pub fn run_json_v0(runner: &NyashRunner, json: &str) -> i32 {
|
||||
}
|
||||
}
|
||||
|
||||
// Always try the v1 bridge first (Stage‑B Program JSON → MIR module).
|
||||
// This is no‑op when input is already MIR(JSON v0) with functions/blocks.
|
||||
// For non‑v1 input, attempt canonicalization and v1 bridge (Stage‑B program → MIR).
|
||||
if let Ok(j) = crate::runner::modes::common_util::core_bridge::canonicalize_module_json(&payload) {
|
||||
payload = j;
|
||||
}
|
||||
match crate::runner::json_v1_bridge::try_parse_v1_to_module(&payload) {
|
||||
Ok(Some(module)) => {
|
||||
super::json_v0_bridge::maybe_dump_mir(&module);
|
||||
// OOB strict: reset observation flag
|
||||
crate::runner::child_env::pre_run_reset_oob_if_strict();
|
||||
let rc = runner.execute_mir_module_quiet_exit(&module);
|
||||
if crate::config::env::oob_strict_fail() && crate::runtime::observe::oob_seen() {
|
||||
|
||||
@ -93,8 +93,8 @@ pub(super) fn demo_parser_system() {
|
||||
pub(super) fn demo_interpreter_system() {
|
||||
println!("\n🎭 7. Interpreter System:");
|
||||
println!(" ⚠️ Legacy interpreter removed - use VM or LLVM backends instead");
|
||||
println!(" 💡 Try: ./target/release/nyash --backend vm program.nyash");
|
||||
println!(" 💡 Try: ./target/release/nyash --backend llvm program.nyash");
|
||||
println!(" 💡 Try: ./target/release/nyash --backend vm program.hako");
|
||||
println!(" 💡 Try: ./target/release/nyash --backend llvm program.hako");
|
||||
}
|
||||
|
||||
/// Run all demo sections (moved from runner/mod.rs)
|
||||
|
||||
292
src/runner/hv1_inline.rs
Normal file
292
src/runner/hv1_inline.rs
Normal file
@ -0,0 +1,292 @@
|
||||
use serde_json::Value;
|
||||
use std::collections::HashMap;
|
||||
|
||||
/// Minimal hv1 inline executor for tests (no Nyash parser).
|
||||
/// Supports a very small subset needed by canaries:
|
||||
/// - const i64
|
||||
/// - mir_call(Constructor ArrayBox)
|
||||
/// - mir_call(Method ArrayBox.push/size/len/length) with optional per-receiver state
|
||||
/// - ret (by register id)
|
||||
pub fn run_json_v1_inline(json: &str) -> i32 {
|
||||
// Parse JSON
|
||||
let v: Value = match serde_json::from_str(json) {
|
||||
Ok(v) => v,
|
||||
Err(_) => return 1,
|
||||
};
|
||||
// schema_version 1.x required
|
||||
match v.get("schema_version").and_then(Value::as_str) {
|
||||
Some(s) if s.starts_with('1') => {}
|
||||
_ => return 1,
|
||||
}
|
||||
// Fetch first function and build block map
|
||||
let functions = match v.get("functions").and_then(Value::as_array) { Some(a) => a, None => return 1 };
|
||||
let func = match functions.get(0) { Some(f) => f, None => return 1 };
|
||||
let blocks = match func.get("blocks").and_then(Value::as_array) { Some(a) => a, None => return 1 };
|
||||
let mut bmap: HashMap<i64, &Vec<Value>> = HashMap::new();
|
||||
for b in blocks {
|
||||
if let (Some(id), Some(insts)) = (b.get("id").and_then(Value::as_i64), b.get("instructions").and_then(Value::as_array)) {
|
||||
bmap.insert(id, insts);
|
||||
}
|
||||
}
|
||||
|
||||
// Registers and simple method state
|
||||
let mut regs: HashMap<i64, i64> = HashMap::new();
|
||||
let sizestate = crate::config::env::env_bool("HAKO_VM_MIRCALL_SIZESTATE");
|
||||
let per_recv = crate::config::env::env_bool("HAKO_VM_MIRCALL_SIZESTATE_PER_RECV");
|
||||
// Keep Array and Map length states separate to avoid cross‑box interference
|
||||
let mut len_global_arr: i64 = 0;
|
||||
let mut len_by_recv_arr: HashMap<i64, i64> = HashMap::new();
|
||||
let mut len_global_map: i64 = 0;
|
||||
let mut len_by_recv_map: HashMap<i64, i64> = HashMap::new();
|
||||
|
||||
fn is_size_alias(name: &str) -> bool {
|
||||
matches!(name, "size" | "len" | "length")
|
||||
}
|
||||
|
||||
// Simple CFG interpreter with limited ops (const/compare/phi/branch/jump/mir_call/ret)
|
||||
let mut curr: i64 = 0; // assume entry block id 0
|
||||
let mut prev: i64 = -1;
|
||||
let mut steps: i32 = 0;
|
||||
'outer: loop {
|
||||
if steps > 10000 { return 1; }
|
||||
steps += 1;
|
||||
let insts = match bmap.get(&curr) { Some(v) => *v, None => return 1 };
|
||||
let mut ip = 0usize;
|
||||
while ip < insts.len() {
|
||||
let inst = &insts[ip];
|
||||
ip += 1;
|
||||
let op = match inst.get("op").and_then(Value::as_str) { Some(s) => s, None => return 1 };
|
||||
match op {
|
||||
"binop" => {
|
||||
// Minimal integer binops
|
||||
let dst = match inst.get("dst").and_then(Value::as_i64) { Some(d) => d, None => return 1 };
|
||||
let lhs = inst.get("lhs").and_then(Value::as_i64).and_then(|r| regs.get(&r).cloned()).unwrap_or(0);
|
||||
let rhs = inst.get("rhs").and_then(Value::as_i64).and_then(|r| regs.get(&r).cloned()).unwrap_or(0);
|
||||
let opn = inst.get("operation").and_then(Value::as_str).unwrap_or("");
|
||||
let out = match opn {
|
||||
"+" => lhs.wrapping_add(rhs),
|
||||
"-" => lhs.wrapping_sub(rhs),
|
||||
"*" => lhs.wrapping_mul(rhs),
|
||||
"/" => {
|
||||
if rhs == 0 { 0 } else { lhs.wrapping_div(rhs) }
|
||||
}
|
||||
"%" => {
|
||||
if rhs == 0 { 0 } else { lhs.wrapping_rem(rhs) }
|
||||
}
|
||||
"&" => lhs & rhs,
|
||||
"|" => lhs | rhs,
|
||||
"^" => lhs ^ rhs,
|
||||
"<<" => lhs.wrapping_shl((rhs as u32).min(63)),
|
||||
">>" => ((lhs as i64) >> (rhs as u32).min(63)) as i64,
|
||||
_ => 0,
|
||||
};
|
||||
regs.insert(dst, out);
|
||||
}
|
||||
"unop" => {
|
||||
let dst = match inst.get("dst").and_then(Value::as_i64) { Some(d) => d, None => return 1 };
|
||||
let src = inst.get("src").and_then(Value::as_i64).and_then(|r| regs.get(&r).cloned()).unwrap_or(0);
|
||||
let kind = inst.get("kind").and_then(Value::as_str).unwrap_or("");
|
||||
let out = match kind {
|
||||
"neg" => src.wrapping_neg(),
|
||||
"not" => if src == 0 { 1 } else { 0 },
|
||||
"bitnot" => !src,
|
||||
_ => 0,
|
||||
};
|
||||
regs.insert(dst, out);
|
||||
}
|
||||
"copy" => {
|
||||
let dst = match inst.get("dst").and_then(Value::as_i64) { Some(d) => d, None => return 1 };
|
||||
let srcv = inst.get("src").and_then(Value::as_i64).and_then(|r| regs.get(&r).cloned()).unwrap_or(0);
|
||||
regs.insert(dst, srcv);
|
||||
}
|
||||
"typeop" => {
|
||||
// Minimal TypeOp support for PRIMARY reps
|
||||
// Fields: operation ("check"|"is"|"cast"), src (vid), target_type (str), dst (vid)
|
||||
let operation = inst
|
||||
.get("operation")
|
||||
.and_then(Value::as_str)
|
||||
.unwrap_or("")
|
||||
.to_lowercase();
|
||||
let src = match inst.get("src").and_then(Value::as_i64) { Some(s) => s, None => return 1 };
|
||||
let dst = match inst.get("dst").and_then(Value::as_i64) { Some(d) => d, None => return 1 };
|
||||
let target = inst
|
||||
.get("target_type")
|
||||
.and_then(Value::as_str)
|
||||
.unwrap_or("")
|
||||
.to_lowercase();
|
||||
|
||||
let sval = regs.get(&src).cloned();
|
||||
let is_integer = sval.is_some(); // hv1 inline stores i64 only → integer
|
||||
let mut out = 0i64;
|
||||
if operation == "check" || operation == "is" {
|
||||
if target == "i64" || target == "int" || target == "integer" {
|
||||
out = if is_integer { 1 } else { 0 };
|
||||
} else if target == "bool" {
|
||||
// Inline model uses integer registers; treat 0/1 as bool when present
|
||||
out = if let Some(v) = sval { if v == 0 || v == 1 { 1 } else { 0 } } else { 0 };
|
||||
} else if target == "string" {
|
||||
out = 0; // no string registers in inline model
|
||||
} else {
|
||||
out = 0;
|
||||
}
|
||||
regs.insert(dst, out);
|
||||
} else {
|
||||
// cast/as: pass-through (MVP)
|
||||
regs.insert(dst, sval.unwrap_or(0));
|
||||
}
|
||||
}
|
||||
"const" => {
|
||||
let dst = match inst.get("dst").and_then(Value::as_i64) { Some(d) => d, None => return 1 };
|
||||
// Prefer i64 numeric constants; otherwise stub non-numeric to 0 for inline path
|
||||
if let Some(n) = inst.get("value").and_then(|vv| vv.get("value")).and_then(Value::as_i64) {
|
||||
regs.insert(dst, n);
|
||||
} else {
|
||||
regs.insert(dst, 0);
|
||||
}
|
||||
}
|
||||
"compare" => {
|
||||
let dst = match inst.get("dst").and_then(Value::as_i64) { Some(d) => d, None => return 1 };
|
||||
let lhs = inst.get("lhs").and_then(Value::as_i64).and_then(|r| regs.get(&r).cloned()).unwrap_or(0);
|
||||
let rhs = inst.get("rhs").and_then(Value::as_i64).and_then(|r| regs.get(&r).cloned()).unwrap_or(0);
|
||||
let cmp = inst.get("cmp").and_then(Value::as_str).unwrap_or("");
|
||||
let res = match cmp {
|
||||
"Gt" => (lhs > rhs) as i64,
|
||||
"Ge" => (lhs >= rhs) as i64,
|
||||
"Lt" => (lhs < rhs) as i64,
|
||||
"Le" => (lhs <= rhs) as i64,
|
||||
"Eq" => (lhs == rhs) as i64,
|
||||
"Ne" => (lhs != rhs) as i64,
|
||||
_ => 0,
|
||||
};
|
||||
regs.insert(dst, res);
|
||||
}
|
||||
"mir_call" => {
|
||||
// Support both nested shape {"mir_call":{"callee":...}} and flat shape {"callee":...}
|
||||
let mc = inst.get("mir_call");
|
||||
let callee = if let Some(m) = mc { m.get("callee") } else { inst.get("callee") };
|
||||
let Some(callee) = callee else { return 1 };
|
||||
let ctype = callee.get("type").and_then(Value::as_str).unwrap_or("");
|
||||
match ctype {
|
||||
// Constructor: just create and optionally write dst=0
|
||||
"Constructor" => {
|
||||
if let Some(dst) = inst.get("dst").and_then(Value::as_i64) { regs.insert(dst, 0); }
|
||||
continue;
|
||||
}
|
||||
// ArrayBox methods we model inline
|
||||
"Method" => {
|
||||
let bname = callee.get("box_name").and_then(Value::as_str).unwrap_or("");
|
||||
if bname == "ArrayBox" {
|
||||
let method = callee.get("method").and_then(Value::as_str).unwrap_or("");
|
||||
let recv = callee.get("receiver").and_then(Value::as_i64).unwrap_or(-1);
|
||||
if method == "push" {
|
||||
if sizestate {
|
||||
if per_recv {
|
||||
let e = len_by_recv_arr.entry(recv).or_insert(0);
|
||||
*e += 1;
|
||||
} else {
|
||||
len_global_arr += 1;
|
||||
}
|
||||
}
|
||||
if let Some(dst) = inst.get("dst").and_then(Value::as_i64) { regs.insert(dst, 0); }
|
||||
continue;
|
||||
}
|
||||
if is_size_alias(method) {
|
||||
let value = if sizestate {
|
||||
if per_recv { *len_by_recv_arr.get(&recv).unwrap_or(&0) } else { len_global_arr }
|
||||
} else { 0 };
|
||||
if let Some(dst) = inst.get("dst").and_then(Value::as_i64) { regs.insert(dst, value); }
|
||||
continue;
|
||||
}
|
||||
// unsupported method on ArrayBox → stub 0
|
||||
if let Some(dst) = inst.get("dst").and_then(Value::as_i64) { regs.insert(dst, 0); }
|
||||
continue;
|
||||
}
|
||||
if bname == "MapBox" {
|
||||
let method = callee.get("method").and_then(Value::as_str).unwrap_or("");
|
||||
let recv = callee.get("receiver").and_then(Value::as_i64).unwrap_or(-1);
|
||||
if method == "set" {
|
||||
if sizestate {
|
||||
if per_recv {
|
||||
let e = len_by_recv_map.entry(recv).or_insert(0);
|
||||
*e += 1;
|
||||
} else {
|
||||
len_global_map += 1;
|
||||
}
|
||||
}
|
||||
if let Some(dst) = inst.get("dst").and_then(Value::as_i64) { regs.insert(dst, 0); }
|
||||
continue;
|
||||
}
|
||||
if is_size_alias(method) {
|
||||
let value = if sizestate {
|
||||
if per_recv { *len_by_recv_map.get(&recv).unwrap_or(&0) } else { len_global_map }
|
||||
} else { 0 };
|
||||
if let Some(dst) = inst.get("dst").and_then(Value::as_i64) { regs.insert(dst, value); }
|
||||
continue;
|
||||
}
|
||||
// other methods stub 0
|
||||
if let Some(dst) = inst.get("dst").and_then(Value::as_i64) { regs.insert(dst, 0); }
|
||||
continue;
|
||||
}
|
||||
// Other box methods are not modeled; stub if dst present
|
||||
if let Some(dst) = inst.get("dst").and_then(Value::as_i64) { regs.insert(dst, 0); }
|
||||
continue;
|
||||
}
|
||||
// Extern calls: allow stub (return 0) when provider is enabled via env, else still stub to 0
|
||||
"Extern" => {
|
||||
let _provider_on = crate::config::env::env_bool("HAKO_V1_EXTERN_PROVIDER");
|
||||
// For now, always treat extern as stub → write 0 when dst present
|
||||
if let Some(dst) = inst.get("dst").and_then(Value::as_i64) { regs.insert(dst, 0); }
|
||||
continue;
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
// Unsupported callee shape/type → treat as stub if possible, else error
|
||||
if let Some(dst) = inst.get("dst").and_then(Value::as_i64) { regs.insert(dst, 0); continue; }
|
||||
return 1;
|
||||
}
|
||||
"phi" => {
|
||||
let dst = match inst.get("dst").and_then(Value::as_i64) { Some(d) => d, None => return 1 };
|
||||
let mut val: i64 = 0;
|
||||
let mut matched = false;
|
||||
if let Some(incomings) = inst.get("incoming").and_then(Value::as_array) {
|
||||
for pair in incomings {
|
||||
if let Some(arr) = pair.as_array() {
|
||||
if arr.len() >= 2 {
|
||||
// v1 schema (inline minimal): [value_reg, pred_block_id]
|
||||
let r = arr[0].as_i64().unwrap_or(-1);
|
||||
let b = arr[1].as_i64().unwrap_or(-1);
|
||||
if b == prev { val = regs.get(&r).cloned().unwrap_or(0); matched = true; break; }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if !matched { return 1; }
|
||||
regs.insert(dst, val);
|
||||
}
|
||||
"branch" => {
|
||||
let cond = inst.get("cond").and_then(Value::as_i64).and_then(|r| regs.get(&r).cloned()).unwrap_or(0);
|
||||
let then_b = inst.get("then").and_then(Value::as_i64).unwrap_or(curr);
|
||||
let else_b = inst.get("else").and_then(Value::as_i64).unwrap_or(curr);
|
||||
prev = curr;
|
||||
curr = if cond != 0 { then_b } else { else_b };
|
||||
continue 'outer; // switch block
|
||||
}
|
||||
"jump" => {
|
||||
let target = inst.get("target").and_then(Value::as_i64).unwrap_or(curr);
|
||||
prev = curr;
|
||||
curr = target;
|
||||
continue 'outer; // switch block
|
||||
}
|
||||
"ret" => {
|
||||
let vid = match inst.get("value").and_then(Value::as_i64) { Some(v) => v, None => return 0 };
|
||||
let v = *regs.get(&vid).unwrap_or(&0);
|
||||
return (v as i32) & 0xFF;
|
||||
}
|
||||
// ignore others for now (compare/branch not used in hv1 per‑recv canaries)
|
||||
_ => {}
|
||||
}
|
||||
// if we completed a block without control flow, stop
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
@ -35,6 +35,7 @@ mod selfhost;
|
||||
mod tasks;
|
||||
mod trace;
|
||||
mod plugins;
|
||||
pub mod hv1_inline;
|
||||
|
||||
// v2 plugin system imports
|
||||
use nyash_rust::runner_plugin_init;
|
||||
@ -158,7 +159,7 @@ impl NyashRunner {
|
||||
let (target, alias) = if let Some(pos) = s.find(" as ") {
|
||||
(s[..pos].trim().to_string(), Some(s[pos + 4..].trim().to_string()))
|
||||
} else { (s.to_string(), None) };
|
||||
let is_path = target.starts_with('"') || target.starts_with("./") || target.starts_with('/') || target.ends_with(".nyash");
|
||||
let is_path = crate::runner::modes::common_util::resolve::path_util::is_using_target_path_original(&target);
|
||||
if is_path {
|
||||
let path = target.trim_matches('"').to_string();
|
||||
let name = alias.clone().unwrap_or_else(|| {
|
||||
|
||||
@ -47,7 +47,7 @@ impl NyashRunner {
|
||||
|
||||
/// Execute Nyash file with interpreter (common helper)
|
||||
pub(crate) fn execute_nyash_file(&self, filename: &str) {
|
||||
let quiet_pipe = std::env::var("NYASH_JSON_ONLY").ok().as_deref() == Some("1");
|
||||
let quiet_pipe = crate::config::env::env_bool("NYASH_JSON_ONLY");
|
||||
// Ensure runtime and plugins are initialized via unified helper (idempotent)
|
||||
let groups = self.config.as_groups();
|
||||
self.init_runtime_and_plugins(&groups);
|
||||
@ -124,7 +124,7 @@ impl NyashRunner {
|
||||
} else { main_ast };
|
||||
|
||||
// Optional: dump AST statement kinds for quick diagnostics
|
||||
if std::env::var("NYASH_AST_DUMP").ok().as_deref() == Some("1") {
|
||||
if crate::config::env::env_bool("NYASH_AST_DUMP") {
|
||||
use nyash_rust::ast::ASTNode;
|
||||
eprintln!("[ast] dump start");
|
||||
if let ASTNode::Program { statements, .. } = &ast {
|
||||
@ -168,7 +168,7 @@ impl NyashRunner {
|
||||
}
|
||||
let exists = p.exists();
|
||||
if !exists {
|
||||
if std::env::var("NYASH_USING_STRICT").ok().as_deref() == Some("1") {
|
||||
if crate::config::env::env_bool("NYASH_USING_STRICT") {
|
||||
eprintln!(
|
||||
"❌ import: path not found: {} (from {})",
|
||||
p.display(),
|
||||
@ -176,7 +176,7 @@ impl NyashRunner {
|
||||
);
|
||||
process::exit(1);
|
||||
} else if crate::config::env::cli_verbose()
|
||||
|| std::env::var("NYASH_IMPORT_TRACE").ok().as_deref() == Some("1")
|
||||
|| crate::config::env::env_bool("NYASH_IMPORT_TRACE")
|
||||
{
|
||||
eprintln!("[import] path not found (continuing): {}", p.display());
|
||||
}
|
||||
|
||||
@ -15,34 +15,17 @@ pub fn looks_like_hako_code(s: &str) -> bool {
|
||||
|
||||
/// Remove leading `local ` declarations at line head to keep Nyash parser stable
|
||||
pub fn strip_local_decl(s: &str) -> String {
|
||||
let mut out = String::with_capacity(s.len());
|
||||
for line in s.lines() {
|
||||
let leading = line.len() - line.trim_start().len();
|
||||
let (indent, rest) = line.split_at(leading);
|
||||
if rest.starts_with("local ") || rest.starts_with("local\t") {
|
||||
let bytes = rest.as_bytes();
|
||||
let mut i = 5; // after 'local'
|
||||
while i < bytes.len() && (bytes[i] == b' ' || bytes[i] == b'\t') {
|
||||
i += 1;
|
||||
break;
|
||||
}
|
||||
out.push_str(indent);
|
||||
out.push_str(&rest[i..]);
|
||||
out.push('\n');
|
||||
} else {
|
||||
out.push_str(line);
|
||||
out.push('\n');
|
||||
}
|
||||
}
|
||||
out
|
||||
// Stage‑3 パーサでは 'local' を受理できるため、変換は行わず原文を返す
|
||||
s.to_string()
|
||||
}
|
||||
|
||||
/// Policy toggle: fail fast when Hako-like code enters Nyash VM path
|
||||
/// Default: ON (true)
|
||||
pub fn fail_fast_on_hako() -> bool {
|
||||
// Default: OFF(仕様不変=拡張子だけで拒否しない)。
|
||||
// 明示時のみ ON(bring-up やデバッグ用途)。
|
||||
match std::env::var("HAKO_FAIL_FAST_ON_HAKO_IN_NYASH_VM").ok().as_deref() {
|
||||
Some("0") | Some("false") | Some("off") => false,
|
||||
_ => true,
|
||||
Some("1") | Some("true") | Some("on") => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -24,6 +24,7 @@ pub mod seam;
|
||||
pub mod using_resolution;
|
||||
pub mod prelude_manager;
|
||||
pub mod selfhost_pipeline;
|
||||
pub mod path_util;
|
||||
|
||||
// 📦 箱化モジュールの公開にゃ!
|
||||
pub use using_resolution::{
|
||||
|
||||
20
src/runner/modes/common_util/resolve/path_util.rs
Normal file
20
src/runner/modes/common_util/resolve/path_util.rs
Normal file
@ -0,0 +1,20 @@
|
||||
//! path_util — helpers for using target path detection (SSOT)
|
||||
|
||||
/// Determine if an original using target string should be treated as a file path.
|
||||
/// Original means it may still contain surrounding quotes.
|
||||
pub fn is_using_target_path_original(target: &str) -> bool {
|
||||
if target.starts_with('"') {
|
||||
return true;
|
||||
}
|
||||
let t = target.trim_matches('"');
|
||||
is_using_target_path_unquoted(t)
|
||||
}
|
||||
|
||||
/// Determine if an unquoted using target string is a file path.
|
||||
pub fn is_using_target_path_unquoted(target_unquoted: &str) -> bool {
|
||||
target_unquoted.starts_with("./")
|
||||
|| target_unquoted.starts_with('/')
|
||||
|| target_unquoted.ends_with(".hako")
|
||||
|| target_unquoted.ends_with(".nyash")
|
||||
}
|
||||
|
||||
@ -19,9 +19,8 @@ pub fn collect_using_and_strip(
|
||||
}
|
||||
let using_ctx = runner.init_using_context();
|
||||
let prod = crate::config::env::using_is_prod();
|
||||
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 strict = crate::config::env::env_bool("NYASH_USING_STRICT");
|
||||
let verbose = crate::config::env::cli_verbose() || crate::config::env::env_bool("NYASH_RESOLVE_TRACE");
|
||||
let ctx_dir = std::path::Path::new(filename).parent();
|
||||
|
||||
let mut out = String::with_capacity(code.len());
|
||||
@ -61,7 +60,7 @@ pub fn collect_using_and_strip(
|
||||
} else {
|
||||
(rest0.to_string(), None)
|
||||
};
|
||||
// Strip quotes from target for alias/module lookup
|
||||
// Strip quotes from target for alias/module lookup and path detection
|
||||
let target_unquoted = target.trim_matches('"').to_string();
|
||||
|
||||
// Check if this is a known alias or module FIRST before treating as file path
|
||||
@ -73,11 +72,8 @@ pub fn collect_using_and_strip(
|
||||
// Known alias/module - don't treat as file path even if quoted
|
||||
false
|
||||
} else {
|
||||
// Only treat as file path if not a known alias/module
|
||||
target.starts_with("./")
|
||||
|| target.starts_with('/')
|
||||
|| target.ends_with(".nyash")
|
||||
|| target.ends_with(".hako")
|
||||
// SSOT: delegate path pattern check
|
||||
crate::runner::modes::common_util::resolve::path_util::is_using_target_path_unquoted(&target_unquoted)
|
||||
};
|
||||
if is_path {
|
||||
// SSOT: Disallow file-using at top-level; allow only for sources located
|
||||
@ -91,7 +87,7 @@ pub fn collect_using_and_strip(
|
||||
target
|
||||
));
|
||||
}
|
||||
let path = target.trim_matches('"').to_string();
|
||||
let path = target_unquoted.clone();
|
||||
// Resolve relative to current file dir
|
||||
let mut p = std::path::PathBuf::from(&path);
|
||||
if p.is_relative() {
|
||||
@ -197,7 +193,7 @@ pub fn collect_using_and_strip(
|
||||
.file_name()
|
||||
.and_then(|s| s.to_str())
|
||||
.unwrap_or(&pkg_name);
|
||||
base.join(format!("{}.nyash", leaf))
|
||||
base.join(format!("{}.hako", leaf))
|
||||
.to_string_lossy()
|
||||
.to_string()
|
||||
};
|
||||
@ -257,8 +253,8 @@ 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 crate::runner::modes::common_util::resolve::path_util::is_using_target_path_unquoted(&value)
|
||||
{
|
||||
// Resolve relative
|
||||
let mut p = std::path::PathBuf::from(&value);
|
||||
if p.is_relative() {
|
||||
@ -338,7 +334,7 @@ pub fn collect_using_and_strip(
|
||||
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") {
|
||||
if crate::config::env::env_bool("NYASH_RESOLVE_SEAM_DEBUG") {
|
||||
let mut with_marker = String::with_capacity(out.len() + 64);
|
||||
with_marker.push_str("\n/* --- using boundary (AST) --- */\n");
|
||||
with_marker.push_str(&out);
|
||||
@ -364,7 +360,7 @@ pub fn resolve_prelude_paths_profiled(
|
||||
// must be discovered so that their definitions are present at runtime
|
||||
// (e.g., runner_min -> lower_* boxes). Previously this only ran when
|
||||
// NYASH_USING_AST=1, which caused unresolved calls in inline flows.
|
||||
let ast_on = std::env::var("NYASH_USING_AST").ok().as_deref() == Some("1");
|
||||
let ast_on = crate::config::env::env_bool("NYASH_USING_AST");
|
||||
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) {
|
||||
@ -404,14 +400,14 @@ pub fn resolve_prelude_paths_profiled(
|
||||
}
|
||||
// Operator Boxes prelude injection(観測“常時ON”のため)
|
||||
// stringify/compare/add は常に注入(存在時)。その他(bitwise等)は ALL 指定時のみ。
|
||||
let opbox_all = std::env::var("NYASH_OPERATOR_BOX_ALL").ok().as_deref() == Some("1")
|
||||
|| std::env::var("NYASH_BUILDER_OPERATOR_BOX_ALL_CALL").ok().as_deref() == Some("1");
|
||||
let opbox_all = crate::config::env::env_bool("NYASH_OPERATOR_BOX_ALL")
|
||||
|| crate::config::env::env_bool("NYASH_BUILDER_OPERATOR_BOX_ALL_CALL");
|
||||
|
||||
if let Ok(root) = std::env::var("NYASH_ROOT") {
|
||||
let must_have = [
|
||||
"apps/lib/std/operators/stringify.nyash",
|
||||
"apps/lib/std/operators/compare.nyash",
|
||||
"apps/lib/std/operators/add.nyash",
|
||||
"apps/lib/std/operators/stringify.hako",
|
||||
"apps/lib/std/operators/compare.hako",
|
||||
"apps/lib/std/operators/add.hako",
|
||||
];
|
||||
for rel in must_have.iter() {
|
||||
let p = std::path::Path::new(&root).join(rel);
|
||||
@ -427,19 +423,19 @@ pub fn resolve_prelude_paths_profiled(
|
||||
if opbox_all {
|
||||
if let Ok(root) = std::env::var("NYASH_ROOT") {
|
||||
let rels = vec![
|
||||
"apps/lib/std/operators/sub.nyash",
|
||||
"apps/lib/std/operators/mul.nyash",
|
||||
"apps/lib/std/operators/div.nyash",
|
||||
"apps/lib/std/operators/mod.nyash",
|
||||
"apps/lib/std/operators/sub.hako",
|
||||
"apps/lib/std/operators/mul.hako",
|
||||
"apps/lib/std/operators/div.hako",
|
||||
"apps/lib/std/operators/mod.hako",
|
||||
// Shifts / bitwise (parser tokens now supported)
|
||||
"apps/lib/std/operators/shl.nyash",
|
||||
"apps/lib/std/operators/shr.nyash",
|
||||
"apps/lib/std/operators/bitand.nyash",
|
||||
"apps/lib/std/operators/bitor.nyash",
|
||||
"apps/lib/std/operators/bitxor.nyash",
|
||||
"apps/lib/std/operators/neg.nyash",
|
||||
"apps/lib/std/operators/not.nyash",
|
||||
"apps/lib/std/operators/bitnot.nyash",
|
||||
"apps/lib/std/operators/shl.hako",
|
||||
"apps/lib/std/operators/shr.hako",
|
||||
"apps/lib/std/operators/bitand.hako",
|
||||
"apps/lib/std/operators/bitor.hako",
|
||||
"apps/lib/std/operators/bitxor.hako",
|
||||
"apps/lib/std/operators/neg.hako",
|
||||
"apps/lib/std/operators/not.hako",
|
||||
"apps/lib/std/operators/bitnot.hako",
|
||||
];
|
||||
for rel in rels {
|
||||
let p = std::path::Path::new(&root).join(rel);
|
||||
@ -465,7 +461,7 @@ pub fn parse_preludes_to_asts(
|
||||
runner: &NyashRunner,
|
||||
prelude_paths: &[String],
|
||||
) -> Result<Vec<nyash_rust::ast::ASTNode>, String> {
|
||||
let debug = std::env::var("NYASH_STRIP_DEBUG").ok().as_deref() == Some("1");
|
||||
let debug = crate::config::env::env_bool("NYASH_STRIP_DEBUG");
|
||||
if debug {
|
||||
eprintln!("[strip-debug] parse_preludes_to_asts: {} files total", prelude_paths.len());
|
||||
for (idx, p) in prelude_paths.iter().enumerate() {
|
||||
@ -506,7 +502,7 @@ pub fn parse_preludes_to_asts(
|
||||
}
|
||||
Err(e) => {
|
||||
// Always output debug info on parse failure if NYASH_STRIP_DEBUG=1
|
||||
let debug = std::env::var("NYASH_STRIP_DEBUG").ok().as_deref() == Some("1");
|
||||
let debug = crate::config::env::env_bool("NYASH_STRIP_DEBUG");
|
||||
eprintln!("[strip-debug] Parse FAILED for: {} (debug={})", prelude_path, debug);
|
||||
if debug {
|
||||
eprintln!("[strip-debug] Error: {}", e);
|
||||
@ -659,7 +655,7 @@ pub fn merge_prelude_text(
|
||||
source: &str,
|
||||
filename: &str,
|
||||
) -> Result<String, String> {
|
||||
let trace = std::env::var("NYASH_RESOLVE_TRACE").ok().as_deref() == Some("1");
|
||||
let trace = crate::config::env::env_bool("NYASH_RESOLVE_TRACE");
|
||||
|
||||
// First pass: collect and resolve prelude paths
|
||||
let (cleaned_main, prelude_paths) = resolve_prelude_paths_profiled(runner, source, filename)?;
|
||||
@ -737,7 +733,7 @@ pub fn merge_prelude_text(
|
||||
}
|
||||
|
||||
// Add boundary marker if debug mode
|
||||
if std::env::var("NYASH_RESOLVE_SEAM_DEBUG").ok().as_deref() == Some("1") {
|
||||
if crate::config::env::env_bool("NYASH_RESOLVE_SEAM_DEBUG") {
|
||||
merged.push_str("\n/* --- using prelude/main boundary --- */\n\n");
|
||||
}
|
||||
|
||||
|
||||
@ -46,9 +46,9 @@ impl<'a> UsingResolutionBox<'a> {
|
||||
let using_ctx = runner.init_using_context();
|
||||
let config = UsingConfig {
|
||||
prod: crate::config::env::using_is_prod(),
|
||||
strict: std::env::var("NYASH_USING_STRICT").ok().as_deref() == Some("1"),
|
||||
strict: crate::config::env::env_bool("NYASH_USING_STRICT"),
|
||||
verbose: crate::config::env::cli_verbose()
|
||||
|| std::env::var("NYASH_RESOLVE_TRACE").ok().as_deref() == Some("1"),
|
||||
|| crate::config::env::env_bool("NYASH_RESOLVE_TRACE"),
|
||||
allow_file_using: crate::config::env::allow_using_file(),
|
||||
};
|
||||
|
||||
@ -113,10 +113,7 @@ impl<'a> UsingResolutionBox<'a> {
|
||||
let is_path = if is_known_alias_or_module {
|
||||
false
|
||||
} else {
|
||||
target.starts_with("./")
|
||||
|| target.starts_with('/')
|
||||
|| target.ends_with(".nyash")
|
||||
|| target.ends_with(".hako")
|
||||
crate::runner::modes::common_util::resolve::path_util::is_using_target_path_unquoted(&target_unquoted)
|
||||
};
|
||||
|
||||
Some(UsingTarget {
|
||||
|
||||
@ -2,7 +2,7 @@ use std::path::Path;
|
||||
|
||||
/// Run a Nyash program as a child (`nyash --backend vm <program>`) and capture the first JSON v0 line.
|
||||
/// - `exe`: path to nyash executable
|
||||
/// - `program`: path to the Nyash script to run (e.g., apps/selfhost/compiler/compiler.nyash)
|
||||
/// - `program`: path to the Nyash script to run (e.g., apps/selfhost/compiler/compiler.hako)
|
||||
/// - `timeout_ms`: kill child after this duration
|
||||
/// - `extra_args`: additional args to pass after program (e.g., "--", "--read-tmp")
|
||||
/// - `env_remove`: environment variable names to remove for the child
|
||||
|
||||
@ -36,7 +36,7 @@ pub fn run_pyvm_module(module: &MirModule, label: &str) -> Option<i32> {
|
||||
eprintln!("❌ PyVM MIR JSON emit error: {}", e);
|
||||
return None;
|
||||
}
|
||||
if std::env::var("NYASH_CLI_VERBOSE").ok().as_deref() == Some("1") {
|
||||
if crate::config::env::cli_verbose() {
|
||||
eprintln!("[Bridge] using PyVM ({}) → {}", label, mir_json_path.display());
|
||||
}
|
||||
// Select entry (prefer Main.main; top-level main only if allowed)
|
||||
|
||||
@ -4,7 +4,7 @@ use std::{fs, process};
|
||||
|
||||
/// Execute using PyVM only (no Rust VM runtime). Emits MIR(JSON) and invokes tools/pyvm_runner.py.
|
||||
pub fn execute_pyvm_only(runner: &NyashRunner, filename: &str) {
|
||||
if std::env::var("NYASH_PYVM_TRACE").ok().as_deref() == Some("1") { eprintln!("[pyvm] entry"); }
|
||||
if crate::config::env::env_bool("NYASH_PYVM_TRACE") { eprintln!("[pyvm] entry"); }
|
||||
// Read the file
|
||||
let code = match fs::read_to_string(filename) {
|
||||
Ok(content) => content,
|
||||
@ -76,7 +76,7 @@ pub fn execute_pyvm_only(runner: &NyashRunner, filename: &str) {
|
||||
code = normalize_logical_ops(&code);
|
||||
|
||||
// Parse to AST
|
||||
if std::env::var("NYASH_PYVM_DUMP_CODE").ok().as_deref() == Some("1") {
|
||||
if crate::config::env::env_bool("NYASH_PYVM_DUMP_CODE") {
|
||||
eprintln!("[pyvm-code]\n{}", code);
|
||||
}
|
||||
let ast = match NyashParser::parse_from_string(&code) {
|
||||
@ -100,13 +100,13 @@ pub fn execute_pyvm_only(runner: &NyashRunner, filename: &str) {
|
||||
};
|
||||
|
||||
// Optional: VM-only escape analysis elision pass retained for parity with VM path
|
||||
if std::env::var("NYASH_VM_ESCAPE_ANALYSIS").ok().as_deref() == Some("1") {
|
||||
if crate::config::env::env_bool("NYASH_VM_ESCAPE_ANALYSIS") {
|
||||
let removed = nyash_rust::mir::passes::escape::escape_elide_barriers_vm(&mut compile_result.module);
|
||||
if removed > 0 { crate::cli_v!("[PyVM] escape_elide_barriers: removed {} barriers", removed); }
|
||||
}
|
||||
|
||||
// Optional: delegate to Ny selfhost executor (Stage 0 scaffold: no-op)
|
||||
if std::env::var("NYASH_SELFHOST_EXEC").ok().as_deref() == Some("1") {
|
||||
if crate::config::env::env_bool("NYASH_SELFHOST_EXEC") {
|
||||
// Emit MIR JSON to a temp file and invoke Ny runner script.
|
||||
let tmp_dir = std::path::Path::new("tmp");
|
||||
let _ = std::fs::create_dir_all(tmp_dir);
|
||||
@ -117,7 +117,7 @@ pub fn execute_pyvm_only(runner: &NyashRunner, filename: &str) {
|
||||
}
|
||||
// Resolve nyash executable and runner path
|
||||
let exe = std::env::current_exe().unwrap_or_else(|_| std::path::PathBuf::from("target/release/nyash"));
|
||||
let runner = std::path::Path::new("apps/selfhost-runtime/runner.nyash");
|
||||
let runner = std::path::Path::new("apps/selfhost-runtime/runner.hako");
|
||||
if !runner.exists() {
|
||||
eprintln!("❌ Selfhost runner missing: {}", runner.display());
|
||||
process::exit(1);
|
||||
|
||||
@ -19,7 +19,7 @@ impl NyashRunner {
|
||||
// This function is only called after plugin initialization has already occurred.
|
||||
|
||||
// Quiet mode for child pipelines (e.g., selfhost compiler JSON emit)
|
||||
let quiet_pipe = std::env::var("NYASH_JSON_ONLY").ok().as_deref() == Some("1");
|
||||
let quiet_pipe = crate::config::env::env_bool("NYASH_JSON_ONLY");
|
||||
// Enforce plugin-first policy for VM on this branch (deterministic):
|
||||
// - Initialize plugin host if not yet loaded
|
||||
// - Prefer plugin implementations for core boxes
|
||||
@ -35,7 +35,7 @@ impl NyashRunner {
|
||||
.unwrap_or(true)
|
||||
};
|
||||
if need_init {
|
||||
let _ = nyash_rust::runtime::init_global_plugin_host("nyash.toml");
|
||||
// Let init_bid_plugins resolve hakorune.toml/nyash.toml and configure
|
||||
crate::runner_plugin_init::init_bid_plugins();
|
||||
}
|
||||
// Prefer plugin-builtins for core types unless explicitly disabled
|
||||
@ -70,7 +70,7 @@ impl NyashRunner {
|
||||
std::env::set_var("NYASH_PLUGIN_OVERRIDE_TYPES", override_types.join(","));
|
||||
|
||||
// Strict mode: verify providers exist for override types
|
||||
if std::env::var("NYASH_VM_PLUGIN_STRICT").ok().as_deref() == Some("1") {
|
||||
if crate::config::env::env_bool("NYASH_VM_PLUGIN_STRICT") {
|
||||
let v2 = nyash_rust::runtime::get_global_registry();
|
||||
let mut missing: Vec<String> = Vec::new();
|
||||
for t in [
|
||||
@ -158,7 +158,7 @@ impl NyashRunner {
|
||||
let code_ref: &str = &preexpanded_owned;
|
||||
|
||||
// Parse to AST
|
||||
if std::env::var("NYASH_STRIP_DEBUG").ok().as_deref() == Some("1") {
|
||||
if crate::config::env::env_bool("NYASH_STRIP_DEBUG") {
|
||||
eprintln!("[vm-debug] About to parse main source ({} bytes)", code_ref.len());
|
||||
eprintln!("[vm-debug] First 20 lines:");
|
||||
for (idx, line) in code_ref.lines().enumerate().take(20) {
|
||||
@ -170,7 +170,7 @@ impl NyashRunner {
|
||||
Err(e) => {
|
||||
eprintln!("❌ Parse error in main source ({}): {}",
|
||||
cfg.file.as_ref().map(|s| s.as_str()).unwrap_or("<stdin>"), e);
|
||||
if std::env::var("NYASH_STRIP_DEBUG").ok().as_deref() == Some("1") {
|
||||
if crate::config::env::env_bool("NYASH_STRIP_DEBUG") {
|
||||
eprintln!("[vm-debug] Parse failed for main source");
|
||||
eprintln!("[vm-debug] Line 15-25 of source:");
|
||||
for (idx, line) in code_ref.lines().enumerate().skip(14).take(11) {
|
||||
@ -233,14 +233,14 @@ impl NyashRunner {
|
||||
}
|
||||
|
||||
// Optional: dump MIR for diagnostics
|
||||
if std::env::var("NYASH_VM_DUMP_MIR").ok().as_deref() == Some("1") {
|
||||
if crate::config::env::env_bool("NYASH_VM_DUMP_MIR") {
|
||||
let p = nyash_rust::mir::MirPrinter::new();
|
||||
eprintln!("{}", p.print_module(&compile_result.module));
|
||||
}
|
||||
|
||||
// Optional: VM-only escape analysis to elide barriers before execution
|
||||
let mut module_vm = compile_result.module.clone();
|
||||
if std::env::var("NYASH_VM_ESCAPE_ANALYSIS").ok().as_deref() == Some("1") {
|
||||
if crate::config::env::env_bool("NYASH_VM_ESCAPE_ANALYSIS") {
|
||||
let removed = nyash_rust::mir::passes::escape::escape_elide_barriers_vm(&mut module_vm);
|
||||
if removed > 0 { crate::cli_v!("[VM] escape_elide_barriers: removed {} barriers", removed); }
|
||||
}
|
||||
@ -534,10 +534,7 @@ impl NyashRunner {
|
||||
type_parameters: type_parameters.clone(),
|
||||
};
|
||||
if let Ok(mut map) = runtime.box_declarations.write() {
|
||||
if std::env::var("NYASH_BOX_DECL_TRACE")
|
||||
.ok()
|
||||
.as_deref()
|
||||
== Some("1")
|
||||
if crate::config::env::env_bool("NYASH_BOX_DECL_TRACE")
|
||||
{
|
||||
eprintln!("[box-decl] register {}", name);
|
||||
}
|
||||
|
||||
@ -26,32 +26,25 @@ impl NyashRunner {
|
||||
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();
|
||||
// Using preprocessing: 仕様維持のためテキスト・プレリュード統合を既定に(ASTマージは任意)
|
||||
let mut code2 = code.clone();
|
||||
if crate::config::env::enable_using() {
|
||||
// Always perform text-prelude merge when using+AST is enabled to ensure alias/file modules are materialized.
|
||||
if use_ast_prelude {
|
||||
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;
|
||||
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());
|
||||
}
|
||||
Err(e) => { eprintln!("❌ using text merge error: {}", e); process::exit(1); }
|
||||
}
|
||||
} else {
|
||||
match crate::runner::modes::common_util::resolve::resolve_prelude_paths_profiled(self, &code2, filename) {
|
||||
Ok((_clean, paths)) => {
|
||||
if !paths.is_empty() {
|
||||
eprintln!("❌ using: prelude merge is disabled in this profile. Enable NYASH_USING_AST=1 or remove 'using' lines.");
|
||||
process::exit(1);
|
||||
}
|
||||
}
|
||||
Err(e) => { eprintln!("❌ {}", e); process::exit(1); }
|
||||
code2 = merged;
|
||||
}
|
||||
Err(e) => { eprintln!("❌ using text merge error: {}", e); process::exit(1); }
|
||||
}
|
||||
} else {
|
||||
// using disabled: detect and fail fast if present
|
||||
if code.contains("\nusing ") || 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);
|
||||
}
|
||||
}
|
||||
// Dev sugar pre-expand: @name = expr → local name = expr
|
||||
@ -242,7 +235,7 @@ impl NyashRunner {
|
||||
|
||||
// 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") {
|
||||
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!(
|
||||
@ -253,7 +246,7 @@ impl NyashRunner {
|
||||
}
|
||||
|
||||
// Optional: dump MIR for diagnostics (parity with vm path)
|
||||
if std::env::var("NYASH_VM_DUMP_MIR").ok().as_deref() == Some("1") {
|
||||
if crate::config::env::env_bool("NYASH_VM_DUMP_MIR") {
|
||||
let p = crate::mir::MirPrinter::new();
|
||||
eprintln!("{}", p.print_module(&module_vm));
|
||||
}
|
||||
@ -261,7 +254,7 @@ impl NyashRunner {
|
||||
// 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") {
|
||||
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) {
|
||||
|
||||
@ -38,7 +38,13 @@ impl NyashRunner {
|
||||
// Determine output file
|
||||
let groups = self.config.as_groups();
|
||||
let output = groups.output_file.as_deref().unwrap_or_else(|| {
|
||||
if filename.ends_with(".nyash") { filename.strip_suffix(".nyash").unwrap_or(filename) } else { filename }
|
||||
if filename.ends_with(".hako") {
|
||||
filename.strip_suffix(".hako").unwrap_or(filename)
|
||||
} else if filename.ends_with(".nyash") {
|
||||
filename.strip_suffix(".nyash").unwrap_or(filename)
|
||||
} else {
|
||||
filename
|
||||
}
|
||||
});
|
||||
let output_file = format!("{}.wat", output);
|
||||
|
||||
|
||||
@ -98,7 +98,7 @@ pub(super) fn suggest_in_base(base: &str, leaf: &str, out: &mut Vec<String>) {
|
||||
return;
|
||||
}
|
||||
} else if let Some(ext) = path.extension().and_then(|s| s.to_str()) {
|
||||
if ext == "nyash" {
|
||||
if ext == "nyash" || ext == "hako" {
|
||||
if let Some(stem) = path.file_stem().and_then(|s| s.to_str()) {
|
||||
if stem == leaf {
|
||||
out.push(path.to_string_lossy().to_string());
|
||||
@ -134,10 +134,10 @@ pub(super) fn resolve_using_target(
|
||||
if is_path {
|
||||
return Ok(tgt.to_string());
|
||||
}
|
||||
let trace = verbose || std::env::var("NYASH_RESOLVE_TRACE").ok().as_deref() == Some("1");
|
||||
let trace = verbose || crate::config::env::env_bool("NYASH_RESOLVE_TRACE");
|
||||
let idx = super::box_index::get_box_index();
|
||||
let mut strict_effective = strict || idx.plugins_require_prefix_global;
|
||||
if std::env::var("NYASH_PLUGIN_REQUIRE_PREFIX").ok().as_deref() == Some("1") {
|
||||
if crate::config::env::env_bool("NYASH_PLUGIN_REQUIRE_PREFIX") {
|
||||
strict_effective = true;
|
||||
}
|
||||
let meta_for_target = idx.plugin_meta_by_box.get(tgt).cloned();
|
||||
@ -219,7 +219,7 @@ pub(super) fn resolve_using_target(
|
||||
return Ok(out);
|
||||
}
|
||||
PackageKind::Package => {
|
||||
// Compute entry: main or <dir_last>.nyash
|
||||
// Compute entry: main or <dir_last>.hako
|
||||
let base = std::path::Path::new(&pkg.path);
|
||||
let out = if let Some(m) = &pkg.main {
|
||||
if matches!(base.extension().and_then(|s| s.to_str()), Some("nyash") | Some("hako")) {
|
||||
@ -236,7 +236,7 @@ pub(super) fn resolve_using_target(
|
||||
// prefer .hako when package path points to a directory without explicit main
|
||||
let hako = base.join(format!("{}.hako", leaf));
|
||||
if hako.exists() { hako.to_string_lossy().to_string() }
|
||||
else { base.join(format!("{}.nyash", leaf)).to_string_lossy().to_string() }
|
||||
else { base.join(format!("{}.hako", leaf)).to_string_lossy().to_string() }
|
||||
}
|
||||
};
|
||||
if trace {
|
||||
@ -284,19 +284,22 @@ pub(super) fn resolve_using_target(
|
||||
return Ok(out);
|
||||
}
|
||||
// 3) build candidate list: relative then using-paths
|
||||
let rel = tgt.replace('.', "/") + ".nyash";
|
||||
// Prefer .hako, then .nyash(拡張子は等価・.hako優先)
|
||||
let rel_hako = tgt.replace('.', "/") + ".hako";
|
||||
let rel_nyash = tgt.replace('.', "/") + ".nyash";
|
||||
let mut cand: Vec<String> = Vec::new();
|
||||
if let Some(dir) = context_dir {
|
||||
let c = dir.join(&rel);
|
||||
if c.exists() {
|
||||
cand.push(c.to_string_lossy().to_string());
|
||||
}
|
||||
let c1 = dir.join(&rel_hako);
|
||||
if c1.exists() { cand.push(c1.to_string_lossy().to_string()); }
|
||||
let c2 = dir.join(&rel_nyash);
|
||||
if c2.exists() { cand.push(c2.to_string_lossy().to_string()); }
|
||||
}
|
||||
for base in using_paths {
|
||||
let c = std::path::Path::new(base).join(&rel);
|
||||
if c.exists() {
|
||||
cand.push(c.to_string_lossy().to_string());
|
||||
}
|
||||
let p = std::path::Path::new(base);
|
||||
let c1 = p.join(&rel_hako);
|
||||
if c1.exists() { cand.push(c1.to_string_lossy().to_string()); }
|
||||
let c2 = p.join(&rel_nyash);
|
||||
if c2.exists() { cand.push(c2.to_string_lossy().to_string()); }
|
||||
}
|
||||
if cand.is_empty() {
|
||||
// Always emit a concise unresolved note to aid diagnostics in smokes
|
||||
@ -465,7 +468,7 @@ pub(super) fn lint_fields_top(code: &str, strict: bool, verbose: bool) -> Result
|
||||
}
|
||||
return Err(msg);
|
||||
}
|
||||
if verbose || std::env::var("NYASH_RESOLVE_TRACE").ok().as_deref() == Some("1") {
|
||||
if verbose || crate::config::env::env_bool("NYASH_RESOLVE_TRACE") {
|
||||
for (lno, fld, bx) in violations {
|
||||
eprintln!(
|
||||
"[lint] fields-top: line {} in box {} -> {}",
|
||||
|
||||
@ -130,13 +130,13 @@ impl NyashRunner {
|
||||
}
|
||||
}
|
||||
}
|
||||
// Preferred: run Ny selfhost compiler program (apps/selfhost/compiler/compiler.nyash)
|
||||
// Preferred: run Ny selfhost compiler program (apps/selfhost/compiler/compiler.hako)
|
||||
// This avoids inline embedding pitfalls and supports Stage-3 gating via args.
|
||||
{
|
||||
use crate::runner::modes::common_util::selfhost::{child, json};
|
||||
let exe = std::env::current_exe()
|
||||
.unwrap_or_else(|_| std::path::PathBuf::from("target/release/nyash"));
|
||||
let parser_prog = std::path::Path::new("apps/selfhost/compiler/compiler.nyash");
|
||||
let parser_prog = std::path::Path::new("apps/selfhost/compiler/compiler.hako");
|
||||
if parser_prog.exists() {
|
||||
// Build extra args forwarded to child program
|
||||
let mut extra_owned: Vec<String> = Vec::new();
|
||||
@ -384,9 +384,9 @@ impl NyashRunner {
|
||||
_ => esc.push(ch),
|
||||
}
|
||||
}
|
||||
let inline_path = std::path::Path::new("tmp").join("inline_selfhost_emit.nyash");
|
||||
let inline_path = std::path::Path::new("tmp").join("inline_selfhost_emit.hako");
|
||||
let inline_code = format!(
|
||||
"include \"apps/selfhost/compiler/boxes/parser_box.nyash\"\ninclude \"apps/selfhost/compiler/boxes/emitter_box.nyash\"\nstatic box Main {{\n main(args) {{\n local s = \"{}\"\n local p = new ParserBox()\n p.stage3_enable(1)\n local json = p.parse_program2(s)\n local e = new EmitterBox()\n json = e.emit_program(json, \"[]\")\n print(json)\n return 0\n }}\n}}\n",
|
||||
"include \"apps/selfhost/compiler/boxes/parser_box.hako\"\ninclude \"apps/selfhost/compiler/boxes/emitter_box.hako\"\nstatic box Main {{\n main(args) {{\n local s = \"{}\"\n local p = new ParserBox()\n p.stage3_enable(1)\n local json = p.parse_program2(s)\n local e = new EmitterBox()\n json = e.emit_program(json, \"[]\")\n print(json)\n return 0\n }}\n}}\n",
|
||||
esc
|
||||
);
|
||||
if let Err(e) = std::fs::write(&inline_path, inline_code) {
|
||||
|
||||
Reference in New Issue
Block a user