chore: Phase 25.1 完了 - LoopForm v2/Stage1 CLI/環境変数削減 + Phase 26-D からの変更
Phase 25.1 完了成果: - ✅ LoopForm v2 テスト・ドキュメント・コメント完備 - 4ケース(A/B/C/D)完全テストカバレッジ - 最小再現ケース作成(SSAバグ調査用) - SSOT文書作成(loopform_ssot.md) - 全ソースに [LoopForm] コメントタグ追加 - ✅ Stage-1 CLI デバッグ環境構築 - stage1_cli.hako 実装 - stage1_bridge.rs ブリッジ実装 - デバッグツール作成(stage1_debug.sh/stage1_minimal.sh) - アーキテクチャ改善提案文書 - ✅ 環境変数削減計画策定 - 25変数の完全調査・分類 - 6段階削減ロードマップ(25→5、80%削減) - 即時削除可能変数特定(NYASH_CONFIG/NYASH_DEBUG) Phase 26-D からの累積変更: - PHI実装改善(ExitPhiBuilder/HeaderPhiBuilder等) - MIRビルダーリファクタリング - 型伝播・最適化パス改善 - その他約300ファイルの累積変更 🎯 技術的成果: - SSAバグ根本原因特定(条件分岐内loop変数変更) - Region+next_iパターン適用完了(UsingCollectorBox等) - LoopFormパターン文書化・テスト化完了 - セルフホスティング基盤強化 Co-Authored-By: Claude <noreply@anthropic.com> Co-Authored-By: ChatGPT <noreply@openai.com> Co-Authored-By: Task Assistant <task@anthropic.com>
This commit is contained in:
@ -62,15 +62,32 @@ pub(super) fn apply_cli_directives_from_source(
|
||||
if std::env::var("NYASH_ASI_STRICT").ok().as_deref() == Some("1") {
|
||||
// operators to check (suffixes)
|
||||
const OP2: [&str; 6] = ["==", "!=", "<=", ">=", "&&", "||"];
|
||||
const OP1: [&str; 7] = ["+", "-", "*", "/", "%", "<", ">"];
|
||||
const OP1: [&str; 7] = ["+", "-", "*", "/", "%", "<", ">"];
|
||||
for (i, line) in code.lines().enumerate() {
|
||||
let l = line.trim_end();
|
||||
if l.is_empty() { continue; }
|
||||
if l.is_empty() {
|
||||
continue;
|
||||
}
|
||||
let mut bad = false;
|
||||
for op in OP2.iter() { if l.ends_with(op) { bad = true; break; } }
|
||||
if !bad { for op in OP1.iter() { if l.ends_with(op) { bad = true; break; } } }
|
||||
for op in OP2.iter() {
|
||||
if l.ends_with(op) {
|
||||
bad = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if !bad {
|
||||
for op in OP1.iter() {
|
||||
if l.ends_with(op) {
|
||||
bad = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if bad {
|
||||
return Err(format!("Parse error: Strict ASI violation — line {} ends with operator", i + 1));
|
||||
return Err(format!(
|
||||
"Parse error: Strict ASI violation — line {} ends with operator",
|
||||
i + 1
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -100,7 +117,10 @@ pub(super) fn apply_cli_directives_from_source(
|
||||
// find left token end and right token start
|
||||
let (left_ok, right_ok) = (peek_ident_left(l, at), peek_ident_right(l, at + 2));
|
||||
if left_ok && right_ok {
|
||||
return Err(format!("Type error: '==' on boxes — use equals() (line {})", i + 1));
|
||||
return Err(format!(
|
||||
"Type error: '==' on boxes — use equals() (line {})",
|
||||
i + 1
|
||||
));
|
||||
}
|
||||
idx = at + 2;
|
||||
}
|
||||
@ -127,22 +147,37 @@ fn find_plus_operands(line: &str) -> Option<(&str, &str)> {
|
||||
if bytes[i] as char == '+' {
|
||||
// extract left token
|
||||
let mut l = i;
|
||||
while l > 0 && bytes[l - 1].is_ascii_whitespace() { l -= 1; }
|
||||
while l > 0 && bytes[l - 1].is_ascii_whitespace() {
|
||||
l -= 1;
|
||||
}
|
||||
let mut lstart = l;
|
||||
while lstart > 0 {
|
||||
let c = bytes[lstart - 1] as char;
|
||||
if c.is_ascii_alphanumeric() || c == '_' || c == '"' { lstart -= 1; } else { break; }
|
||||
if c.is_ascii_alphanumeric() || c == '_' || c == '"' {
|
||||
lstart -= 1;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
let left = &line[lstart..l];
|
||||
// extract right token
|
||||
let mut r = i + 1;
|
||||
while r < bytes.len() && bytes[r].is_ascii_whitespace() { r += 1; }
|
||||
while r < bytes.len() && bytes[r].is_ascii_whitespace() {
|
||||
r += 1;
|
||||
}
|
||||
let mut rend = r;
|
||||
while rend < bytes.len() {
|
||||
let c = bytes[rend] as char;
|
||||
if c.is_ascii_alphanumeric() || c == '_' || c == '"' { rend += 1; } else { break; }
|
||||
if c.is_ascii_alphanumeric() || c == '_' || c == '"' {
|
||||
rend += 1;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if r <= rend {
|
||||
let right = &line[r..rend];
|
||||
return Some((left, right));
|
||||
}
|
||||
if r <= rend { let right = &line[r..rend]; return Some((left, right)); }
|
||||
return None;
|
||||
}
|
||||
i += 1;
|
||||
@ -153,33 +188,61 @@ fn find_plus_operands(line: &str) -> Option<(&str, &str)> {
|
||||
fn peek_ident_left(s: &str, pos: usize) -> bool {
|
||||
// scan left for first non-space token end, then back to token start
|
||||
let bytes = s.as_bytes();
|
||||
if pos == 0 { return false; }
|
||||
if pos == 0 {
|
||||
return false;
|
||||
}
|
||||
let mut i = pos;
|
||||
// skip spaces
|
||||
while i > 0 && bytes[i - 1].is_ascii_whitespace() { i -= 1; }
|
||||
if i == 0 { return false; }
|
||||
while i > 0 && bytes[i - 1].is_ascii_whitespace() {
|
||||
i -= 1;
|
||||
}
|
||||
if i == 0 {
|
||||
return false;
|
||||
}
|
||||
// now consume identifier chars backwards
|
||||
let mut j = i;
|
||||
while j > 0 {
|
||||
let c = bytes[j - 1] as char;
|
||||
if c.is_ascii_alphanumeric() || c == '_' { j -= 1; } else { break; }
|
||||
if c.is_ascii_alphanumeric() || c == '_' {
|
||||
j -= 1;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if j == i {
|
||||
return false;
|
||||
}
|
||||
if j == i { return false; }
|
||||
// ensure not starting with digit only (avoid numeric literal)
|
||||
let tok = &s[j..i];
|
||||
!tok.chars().next().map(|c| c.is_ascii_digit()).unwrap_or(false)
|
||||
!tok.chars()
|
||||
.next()
|
||||
.map(|c| c.is_ascii_digit())
|
||||
.unwrap_or(false)
|
||||
}
|
||||
fn peek_ident_right(s: &str, pos: usize) -> bool {
|
||||
let bytes = s.as_bytes();
|
||||
let mut i = pos;
|
||||
while i < bytes.len() && bytes[i].is_ascii_whitespace() { i += 1; }
|
||||
if i >= bytes.len() { return false; }
|
||||
while i < bytes.len() && bytes[i].is_ascii_whitespace() {
|
||||
i += 1;
|
||||
}
|
||||
if i >= bytes.len() {
|
||||
return false;
|
||||
}
|
||||
let mut j = i;
|
||||
while j < bytes.len() {
|
||||
let c = bytes[j] as char;
|
||||
if c.is_ascii_alphanumeric() || c == '_' { j += 1; } else { break; }
|
||||
if c.is_ascii_alphanumeric() || c == '_' {
|
||||
j += 1;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if j == i {
|
||||
return false;
|
||||
}
|
||||
if j == i { return false; }
|
||||
let tok = &s[i..j];
|
||||
!tok.chars().next().map(|c| c.is_ascii_digit()).unwrap_or(false)
|
||||
!tok.chars()
|
||||
.next()
|
||||
.map(|c| c.is_ascii_digit())
|
||||
.unwrap_or(false)
|
||||
}
|
||||
|
||||
@ -26,13 +26,18 @@ pub fn run_json_v0(runner: &NyashRunner, json: &str) -> i32 {
|
||||
let looks_like_mir = json.contains("\"functions\"") && json.contains("\"blocks\"");
|
||||
if looks_like_mir {
|
||||
// In-proc prototype (opt-in): HAKO_CORE_DIRECT_INPROC=1 (alias NYASH_CORE_DIRECT_INPROC)
|
||||
let core_direct_inproc = std::env::var("HAKO_CORE_DIRECT_INPROC").ok().as_deref() == Some("1")
|
||||
let core_direct_inproc = std::env::var("HAKO_CORE_DIRECT_INPROC").ok().as_deref()
|
||||
== Some("1")
|
||||
|| std::env::var("NYASH_CORE_DIRECT_INPROC").ok().as_deref() == Some("1");
|
||||
if core_direct_inproc {
|
||||
if let Some(rc) = try_run_core_direct_inproc(runner, json) { return rc; }
|
||||
if let Some(rc) = try_run_core_direct_inproc(runner, json) {
|
||||
return rc;
|
||||
}
|
||||
eprintln!("[core-exec] direct Core (inproc) failed; trying child wrapper");
|
||||
}
|
||||
if let Some(rc) = try_run_core_direct(json) { return rc; }
|
||||
if let Some(rc) = try_run_core_direct(json) {
|
||||
return rc;
|
||||
}
|
||||
eprintln!("[core-exec] direct Core (child) failed; falling back to VM interpreter");
|
||||
}
|
||||
// else: skip direct Core and continue to bridge/VM path
|
||||
@ -41,7 +46,9 @@ pub fn run_json_v0(runner: &NyashRunner, json: &str) -> i32 {
|
||||
|
||||
// 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) {
|
||||
if let Ok(j) =
|
||||
crate::runner::modes::common_util::core_bridge::canonicalize_module_json(&payload)
|
||||
{
|
||||
payload = j;
|
||||
}
|
||||
}
|
||||
@ -90,7 +97,9 @@ pub fn run_json_v0(runner: &NyashRunner, json: &str) -> i32 {
|
||||
}
|
||||
|
||||
// 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) {
|
||||
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) {
|
||||
|
||||
@ -18,7 +18,9 @@ pub(crate) fn execute_file_with_backend(runner: &NyashRunner, filename: &str) {
|
||||
if runner.try_run_selfhost_pipeline(filename) {
|
||||
return;
|
||||
} else {
|
||||
crate::cli_v!("[ny-compiler] fallback to default path (MVP unavailable for this input)");
|
||||
crate::cli_v!(
|
||||
"[ny-compiler] fallback to default path (MVP unavailable for this input)"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -31,7 +33,11 @@ pub(crate) fn execute_file_with_backend(runner: &NyashRunner, filename: &str) {
|
||||
// Try schema v1 first (preferred by emitter)
|
||||
match crate::runner::json_v1_bridge::try_parse_v1_to_module(&text) {
|
||||
Ok(Some(module)) => {
|
||||
crate::cli_v!("[mir-json] schema=v1 executing {} (len={})", path, text.len());
|
||||
crate::cli_v!(
|
||||
"[mir-json] schema=v1 executing {} (len={})",
|
||||
path,
|
||||
text.len()
|
||||
);
|
||||
let rc = runner.execute_mir_module_quiet_exit(&module);
|
||||
std::process::exit(rc);
|
||||
}
|
||||
@ -40,7 +46,11 @@ pub(crate) fn execute_file_with_backend(runner: &NyashRunner, filename: &str) {
|
||||
if text.contains("\"functions\"") && text.contains("\"blocks\"") {
|
||||
match crate::runner::mir_json_v0::parse_mir_v0_to_module(&text) {
|
||||
Ok(module) => {
|
||||
crate::cli_v!("[mir-json] schema=v0 executing {} (len={})", path, text.len());
|
||||
crate::cli_v!(
|
||||
"[mir-json] schema=v0 executing {} (len={})",
|
||||
path,
|
||||
text.len()
|
||||
);
|
||||
let rc = runner.execute_mir_module_quiet_exit(&module);
|
||||
std::process::exit(rc);
|
||||
}
|
||||
@ -77,7 +87,10 @@ pub(crate) fn execute_file_with_backend(runner: &NyashRunner, filename: &str) {
|
||||
};
|
||||
match json_v0_bridge::parse_source_v0_to_module(&code) {
|
||||
Ok(module) => {
|
||||
crate::cli_v!("🚀 Hakorune MIR Interpreter - (parser=ny) Executing file: {} 🚀", filename);
|
||||
crate::cli_v!(
|
||||
"🚀 Hakorune MIR Interpreter - (parser=ny) Executing file: {} 🚀",
|
||||
filename
|
||||
);
|
||||
runner.execute_mir_module(&module);
|
||||
return;
|
||||
}
|
||||
@ -100,10 +113,14 @@ pub(crate) fn execute_file_with_backend(runner: &NyashRunner, filename: &str) {
|
||||
};
|
||||
let ast = match NyashParser::parse_from_string(&code) {
|
||||
Ok(ast) => ast,
|
||||
Err(e) => {
|
||||
eprintln!("❌ Parse error in {}: {}", filename, e);
|
||||
process::exit(1);
|
||||
}
|
||||
Err(e) => {
|
||||
crate::runner::modes::common_util::diag::print_parse_error_with_context(
|
||||
filename,
|
||||
&code,
|
||||
&e,
|
||||
);
|
||||
process::exit(1);
|
||||
}
|
||||
};
|
||||
// Optional macro expansion dump (no-op expansion for now)
|
||||
let ast2 = if crate::r#macro::enabled() {
|
||||
@ -120,16 +137,28 @@ pub(crate) fn execute_file_with_backend(runner: &NyashRunner, filename: &str) {
|
||||
if runner.config.dump_expanded_ast_json {
|
||||
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);
|
||||
}
|
||||
};
|
||||
let ast = match NyashParser::parse_from_string(&code) {
|
||||
Ok(ast) => ast,
|
||||
Err(e) => { eprintln!("❌ Parse error in {}: {}", filename, e); process::exit(1); }
|
||||
Err(e) => {
|
||||
crate::runner::modes::common_util::diag::print_parse_error_with_context(
|
||||
filename,
|
||||
&code,
|
||||
&e,
|
||||
);
|
||||
process::exit(1);
|
||||
}
|
||||
};
|
||||
let expanded = if crate::r#macro::enabled() {
|
||||
let a = crate::r#macro::maybe_expand_and_dump(&ast, false);
|
||||
crate::runner::modes::macro_child::normalize_core_pass(&a)
|
||||
} else { ast };
|
||||
} else {
|
||||
ast
|
||||
};
|
||||
let j = crate::r#macro::ast_json::ast_to_json(&expanded);
|
||||
println!("{}", j.to_string());
|
||||
return;
|
||||
@ -137,7 +166,10 @@ pub(crate) fn execute_file_with_backend(runner: &NyashRunner, filename: &str) {
|
||||
|
||||
// MIR dump/verify
|
||||
if groups.debug.dump_mir || groups.debug.verify_mir {
|
||||
crate::cli_v!("🚀 Hakorune MIR Compiler - Processing file: {} 🚀", filename);
|
||||
crate::cli_v!(
|
||||
"🚀 Hakorune MIR Compiler - Processing file: {} 🚀",
|
||||
filename
|
||||
);
|
||||
runner.execute_mir_mode(filename);
|
||||
return;
|
||||
}
|
||||
@ -171,25 +203,36 @@ pub(crate) fn execute_file_with_backend(runner: &NyashRunner, filename: &str) {
|
||||
// Backend selection
|
||||
match groups.backend.backend.as_str() {
|
||||
"mir" => {
|
||||
crate::cli_v!("🚀 Hakorune MIR Interpreter - Executing file: {} 🚀", filename);
|
||||
crate::cli_v!(
|
||||
"🚀 Hakorune MIR Interpreter - Executing file: {} 🚀",
|
||||
filename
|
||||
);
|
||||
runner.execute_mir_mode(filename);
|
||||
}
|
||||
"vm" => {
|
||||
crate::cli_v!("🚀 Hakorune VM Backend - Executing file: {} 🚀", filename);
|
||||
// Route to primary VM path by default. Fallback is a last resort and must be explicitly enabled.
|
||||
let force_fallback = std::env::var("NYASH_VM_USE_FALLBACK").ok().as_deref() == Some("1");
|
||||
let force_fallback =
|
||||
std::env::var("NYASH_VM_USE_FALLBACK").ok().as_deref() == Some("1");
|
||||
let route_trace = std::env::var("NYASH_VM_ROUTE_TRACE").ok().as_deref() == Some("1");
|
||||
if force_fallback {
|
||||
if route_trace { eprintln!("[vm-route] choose=fallback reason=env:NYASH_VM_USE_FALLBACK=1"); }
|
||||
if route_trace {
|
||||
eprintln!("[vm-route] choose=fallback reason=env:NYASH_VM_USE_FALLBACK=1");
|
||||
}
|
||||
runner.execute_vm_fallback_interpreter(filename);
|
||||
} else {
|
||||
if route_trace { eprintln!("[vm-route] choose=vm"); }
|
||||
if route_trace {
|
||||
eprintln!("[vm-route] choose=vm");
|
||||
}
|
||||
runner.execute_vm_mode(filename);
|
||||
}
|
||||
}
|
||||
#[cfg(feature = "cranelift-jit")]
|
||||
"jit-direct" => {
|
||||
crate::cli_v!("⚡ Hakorune JIT-Direct Backend - Executing file: {} ⚡", filename);
|
||||
crate::cli_v!(
|
||||
"⚡ Hakorune JIT-Direct Backend - Executing file: {} ⚡",
|
||||
filename
|
||||
);
|
||||
#[cfg(feature = "cranelift-jit")]
|
||||
{
|
||||
// Use independent JIT-direct runner method (no VM execute loop)
|
||||
@ -269,10 +312,18 @@ impl NyashRunner {
|
||||
}
|
||||
}
|
||||
// Global fallbacks when signature is missing or imprecise
|
||||
if let Some(ib) = result.as_any().downcast_ref::<IntegerBox>() { return to_rc(ib.value); }
|
||||
if let Some(bb) = result.as_any().downcast_ref::<BoolBox>() { return if bb.value { 1 } else { 0 }; }
|
||||
if let Some(fb) = result.as_any().downcast_ref::<FloatBox>() { return to_rc(fb.value as i64); }
|
||||
if let Some(_sb) = result.as_any().downcast_ref::<StringBox>() { return 0; }
|
||||
if let Some(ib) = result.as_any().downcast_ref::<IntegerBox>() {
|
||||
return to_rc(ib.value);
|
||||
}
|
||||
if let Some(bb) = result.as_any().downcast_ref::<BoolBox>() {
|
||||
return if bb.value { 1 } else { 0 };
|
||||
}
|
||||
if let Some(fb) = result.as_any().downcast_ref::<FloatBox>() {
|
||||
return to_rc(fb.value as i64);
|
||||
}
|
||||
if let Some(_sb) = result.as_any().downcast_ref::<StringBox>() {
|
||||
return 0;
|
||||
}
|
||||
0
|
||||
}
|
||||
Err(_) => 1,
|
||||
|
||||
@ -23,19 +23,31 @@ pub fn run_json_v1_inline(json: &str) -> i32 {
|
||||
_ => 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 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)) {
|
||||
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 mut str_regs: HashMap<i64, String> = HashMap::new(); // Track string values loaded via const
|
||||
let mut str_regs: HashMap<i64, String> = HashMap::new(); // Track string values loaded via const
|
||||
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");
|
||||
let value_state = crate::config::env::env_bool("HAKO_VM_MIRCALL_VALUESTATE");
|
||||
@ -60,323 +72,520 @@ pub fn run_json_v1_inline(json: &str) -> i32 {
|
||||
let mut prev: i64 = -1;
|
||||
let mut steps: i32 = 0;
|
||||
'outer: loop {
|
||||
if steps > 10000 { return 1; }
|
||||
if steps > 10000 {
|
||||
return 1;
|
||||
}
|
||||
steps += 1;
|
||||
let insts = match bmap.get(&curr) { Some(v) => *v, None => return 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
|
||||
if operation == "check" || operation == "is" {
|
||||
let out: i64 = if target == "i64" || target == "int" || target == "integer" {
|
||||
if is_integer { 1 } else { 0 }
|
||||
} else if target == "bool" {
|
||||
// Inline model uses integer registers; treat 0/1 as bool when present
|
||||
if let Some(v) = sval { if v == 0 || v == 1 { 1 } else { 0 } } else { 0 }
|
||||
} else if target == "string" {
|
||||
0 // no string registers in inline model
|
||||
} else {
|
||||
0
|
||||
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);
|
||||
} 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 if let Some(s) = inst.get("value").and_then(|vv| vv.get("value")).and_then(Value::as_str) {
|
||||
// Store string value for Map key tracking
|
||||
str_regs.insert(dst, s.to_string());
|
||||
regs.insert(dst, 0); // Also set numeric register to 0 for compatibility
|
||||
} else {
|
||||
regs.insert(dst, 0);
|
||||
"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);
|
||||
}
|
||||
}
|
||||
"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;
|
||||
}
|
||||
"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
|
||||
if operation == "check" || operation == "is" {
|
||||
let out: i64 = if target == "i64" || target == "int" || target == "integer"
|
||||
{
|
||||
if is_integer {
|
||||
1
|
||||
} else {
|
||||
0
|
||||
}
|
||||
} else if target == "bool" {
|
||||
// Inline model uses integer registers; treat 0/1 as bool when present
|
||||
if let Some(v) = sval {
|
||||
if v == 0 || v == 1 {
|
||||
1
|
||||
} else {
|
||||
0
|
||||
}
|
||||
if let Some(dst) = inst.get("dst").and_then(Value::as_i64) { regs.insert(dst, 0); }
|
||||
continue;
|
||||
} else {
|
||||
0
|
||||
}
|
||||
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;
|
||||
} else if target == "string" {
|
||||
0 // no string registers in inline model
|
||||
} else {
|
||||
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 if let Some(s) = inst
|
||||
.get("value")
|
||||
.and_then(|vv| vv.get("value"))
|
||||
.and_then(Value::as_str)
|
||||
{
|
||||
// Store string value for Map key tracking
|
||||
str_regs.insert(dst, s.to_string());
|
||||
regs.insert(dst, 0); // Also set numeric register to 0 for compatibility
|
||||
} 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);
|
||||
}
|
||||
// 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" {
|
||||
// Extract key from first arg (register containing string value)
|
||||
let args = if let Some(mc) = mc { mc.get("args") } else { inst.get("args") };
|
||||
let key_str = if let Some(args_arr) = args.and_then(Value::as_array) {
|
||||
if let Some(key_reg) = args_arr.get(0).and_then(Value::as_i64) {
|
||||
// Look up string value from str_regs
|
||||
str_regs.get(&key_reg).cloned().unwrap_or_default()
|
||||
} else { String::new() }
|
||||
} else { String::new() };
|
||||
|
||||
if sizestate && !key_str.is_empty() {
|
||||
let is_new_key = if per_recv {
|
||||
let keys = map_keys_by_recv.entry(recv).or_insert_with(std::collections::HashSet::new);
|
||||
keys.insert(key_str.clone())
|
||||
} else {
|
||||
map_keys_global.insert(key_str.clone())
|
||||
};
|
||||
|
||||
// Only increment size if this is a new key
|
||||
if is_new_key {
|
||||
// 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_map.entry(recv).or_insert(0);
|
||||
let e = len_by_recv_arr.entry(recv).or_insert(0);
|
||||
*e += 1;
|
||||
} else {
|
||||
len_global_map += 1;
|
||||
len_global_arr += 1;
|
||||
}
|
||||
}
|
||||
if let Some(dst) = inst.get("dst").and_then(Value::as_i64) {
|
||||
regs.insert(dst, 0);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
// value_state: store the value
|
||||
if value_state && !key_str.is_empty() {
|
||||
if let Some(args_arr) = args.and_then(Value::as_array) {
|
||||
if let Some(val_reg) = args_arr.get(1).and_then(Value::as_i64) {
|
||||
let val = *regs.get(&val_reg).unwrap_or(&0);
|
||||
if per_recv {
|
||||
let vals = map_vals_by_recv.entry(recv).or_insert_with(HashMap::new);
|
||||
vals.insert(key_str.clone(), val);
|
||||
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" {
|
||||
// Extract key from first arg (register containing string value)
|
||||
let args = if let Some(mc) = mc {
|
||||
mc.get("args")
|
||||
} else {
|
||||
inst.get("args")
|
||||
};
|
||||
let key_str =
|
||||
if let Some(args_arr) = args.and_then(Value::as_array) {
|
||||
if let Some(key_reg) =
|
||||
args_arr.get(0).and_then(Value::as_i64)
|
||||
{
|
||||
// Look up string value from str_regs
|
||||
str_regs.get(&key_reg).cloned().unwrap_or_default()
|
||||
} else {
|
||||
map_vals_global.insert(key_str.clone(), val);
|
||||
String::new()
|
||||
}
|
||||
} else {
|
||||
String::new()
|
||||
};
|
||||
|
||||
if sizestate && !key_str.is_empty() {
|
||||
let is_new_key = if per_recv {
|
||||
let keys = map_keys_by_recv
|
||||
.entry(recv)
|
||||
.or_insert_with(std::collections::HashSet::new);
|
||||
keys.insert(key_str.clone())
|
||||
} else {
|
||||
map_keys_global.insert(key_str.clone())
|
||||
};
|
||||
|
||||
// Only increment size if this is a new key
|
||||
if is_new_key {
|
||||
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 method == "get" && value_state {
|
||||
// Extract key and retrieve value
|
||||
let args = if let Some(mc) = mc { mc.get("args") } else { inst.get("args") };
|
||||
let key_str = if let Some(args_arr) = args.and_then(Value::as_array) {
|
||||
if let Some(key_reg) = args_arr.get(0).and_then(Value::as_i64) {
|
||||
str_regs.get(&key_reg).cloned().unwrap_or_default()
|
||||
} else { String::new() }
|
||||
} else { String::new() };
|
||||
|
||||
if !key_str.is_empty() {
|
||||
let val = if per_recv {
|
||||
map_vals_by_recv.get(&recv).and_then(|m| m.get(&key_str)).cloned().unwrap_or(0)
|
||||
} else {
|
||||
*map_vals_global.get(&key_str).unwrap_or(&0)
|
||||
};
|
||||
if let Some(dst) = inst.get("dst").and_then(Value::as_i64) { regs.insert(dst, val); }
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if method == "has" && value_state {
|
||||
// Check if key exists
|
||||
let args = if let Some(mc) = mc { mc.get("args") } else { inst.get("args") };
|
||||
let key_str = if let Some(args_arr) = args.and_then(Value::as_array) {
|
||||
if let Some(key_reg) = args_arr.get(0).and_then(Value::as_i64) {
|
||||
str_regs.get(&key_reg).cloned().unwrap_or_default()
|
||||
} else { String::new() }
|
||||
} else { String::new() };
|
||||
|
||||
let has = if !key_str.is_empty() {
|
||||
if per_recv {
|
||||
map_vals_by_recv.get(&recv).map(|m| m.contains_key(&key_str)).unwrap_or(false)
|
||||
} else {
|
||||
map_vals_global.contains_key(&key_str)
|
||||
// value_state: store the value
|
||||
if value_state && !key_str.is_empty() {
|
||||
if let Some(args_arr) = args.and_then(Value::as_array) {
|
||||
if let Some(val_reg) =
|
||||
args_arr.get(1).and_then(Value::as_i64)
|
||||
{
|
||||
let val = *regs.get(&val_reg).unwrap_or(&0);
|
||||
if per_recv {
|
||||
let vals = map_vals_by_recv
|
||||
.entry(recv)
|
||||
.or_insert_with(HashMap::new);
|
||||
vals.insert(key_str.clone(), val);
|
||||
} else {
|
||||
map_vals_global.insert(key_str.clone(), val);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else { false };
|
||||
|
||||
if let Some(dst) = inst.get("dst").and_then(Value::as_i64) {
|
||||
regs.insert(dst, 0);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if method == "get" && value_state {
|
||||
// Extract key and retrieve value
|
||||
let args = if let Some(mc) = mc {
|
||||
mc.get("args")
|
||||
} else {
|
||||
inst.get("args")
|
||||
};
|
||||
let key_str =
|
||||
if let Some(args_arr) = args.and_then(Value::as_array) {
|
||||
if let Some(key_reg) =
|
||||
args_arr.get(0).and_then(Value::as_i64)
|
||||
{
|
||||
str_regs.get(&key_reg).cloned().unwrap_or_default()
|
||||
} else {
|
||||
String::new()
|
||||
}
|
||||
} else {
|
||||
String::new()
|
||||
};
|
||||
|
||||
if !key_str.is_empty() {
|
||||
let val = if per_recv {
|
||||
map_vals_by_recv
|
||||
.get(&recv)
|
||||
.and_then(|m| m.get(&key_str))
|
||||
.cloned()
|
||||
.unwrap_or(0)
|
||||
} else {
|
||||
*map_vals_global.get(&key_str).unwrap_or(&0)
|
||||
};
|
||||
if let Some(dst) = inst.get("dst").and_then(Value::as_i64) {
|
||||
regs.insert(dst, val);
|
||||
}
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if method == "has" && value_state {
|
||||
// Check if key exists
|
||||
let args = if let Some(mc) = mc {
|
||||
mc.get("args")
|
||||
} else {
|
||||
inst.get("args")
|
||||
};
|
||||
let key_str =
|
||||
if let Some(args_arr) = args.and_then(Value::as_array) {
|
||||
if let Some(key_reg) =
|
||||
args_arr.get(0).and_then(Value::as_i64)
|
||||
{
|
||||
str_regs.get(&key_reg).cloned().unwrap_or_default()
|
||||
} else {
|
||||
String::new()
|
||||
}
|
||||
} else {
|
||||
String::new()
|
||||
};
|
||||
|
||||
let has = if !key_str.is_empty() {
|
||||
if per_recv {
|
||||
map_vals_by_recv
|
||||
.get(&recv)
|
||||
.map(|m| m.contains_key(&key_str))
|
||||
.unwrap_or(false)
|
||||
} else {
|
||||
map_vals_global.contains_key(&key_str)
|
||||
}
|
||||
} else {
|
||||
false
|
||||
};
|
||||
|
||||
if let Some(dst) = inst.get("dst").and_then(Value::as_i64) {
|
||||
regs.insert(dst, if has { 1 } else { 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, if has { 1 } else { 0 });
|
||||
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 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);
|
||||
}
|
||||
// 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); }
|
||||
// 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;
|
||||
}
|
||||
// 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;
|
||||
}
|
||||
_ => {}
|
||||
return 1;
|
||||
}
|
||||
// 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; }
|
||||
"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);
|
||||
}
|
||||
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)
|
||||
_ => {}
|
||||
}
|
||||
"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
|
||||
// if we completed a block without control flow, stop
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -1,23 +1,23 @@
|
||||
use super::ast::{ProgramV0, StmtV0, ExprV0};
|
||||
use super::ast::{ExprV0, ProgramV0, StmtV0};
|
||||
use crate::mir::Callee;
|
||||
use crate::mir::{
|
||||
BasicBlockId, ConstValue, EffectMask, FunctionSignature, MirFunction, MirInstruction, MirModule,
|
||||
MirPrinter, MirType, ValueId,
|
||||
BasicBlockId, ConstValue, EffectMask, FunctionSignature, MirFunction, MirInstruction,
|
||||
MirModule, MirPrinter, MirType, ValueId,
|
||||
};
|
||||
use std::collections::HashMap;
|
||||
use std::cell::RefCell;
|
||||
use std::collections::HashMap;
|
||||
|
||||
// Split out merge/new_block helpers for readability (no behavior change)
|
||||
mod merge;
|
||||
use merge::{merge_var_maps, new_block};
|
||||
// Feature splits (gradual extraction)
|
||||
pub(super) mod expr;
|
||||
pub(super) mod if_else;
|
||||
pub(super) mod loop_;
|
||||
pub(super) mod try_catch;
|
||||
pub(super) mod expr;
|
||||
pub(super) mod ternary; // placeholder (not wired)
|
||||
pub(super) mod match_expr; // placeholder (not wired)
|
||||
pub(super) mod throw_ctx; // thread-local ctx for Result-mode throw routing
|
||||
pub(super) mod ternary; // placeholder (not wired)
|
||||
pub(super) mod throw_ctx;
|
||||
pub(super) mod try_catch; // thread-local ctx for Result-mode throw routing
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub(super) struct LoopContext {
|
||||
@ -77,8 +77,15 @@ pub(super) fn detect_and_push_increment_hint(body: &[StmtV0]) {
|
||||
if vname == name {
|
||||
if let ExprV0::Int { value } = *rhs {
|
||||
if let Some(v) = value.as_i64() {
|
||||
let s = match op.as_str() { "+" => v, "-" => -v, _ => 0 };
|
||||
if s != 0 { hint = Some((name.clone(), s)); break; }
|
||||
let s = match op.as_str() {
|
||||
"+" => v,
|
||||
"-" => -v,
|
||||
_ => 0,
|
||||
};
|
||||
if s != 0 {
|
||||
hint = Some((name.clone(), s));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -149,16 +156,15 @@ impl FunctionDefBuilder {
|
||||
// Phase 25.1m で確立した規則:
|
||||
// - static box 内のメソッドで、box_name が Main 以外 → インスタンスメソッド
|
||||
// - Main.main は特別扱い(エントリポイント)
|
||||
!self.def.box_name.is_empty()
|
||||
&& self.def.box_name != "Main"
|
||||
!self.def.box_name.is_empty() && self.def.box_name != "Main"
|
||||
}
|
||||
|
||||
/// パラメータ ValueId の生成(インスタンスメソッドなら %0 を me 用に予約)
|
||||
fn build_param_ids(&self) -> Vec<ValueId> {
|
||||
let offset = if self.is_instance_method() {
|
||||
0 // %0 = me, params start from %1
|
||||
0 // %0 = me, params start from %1
|
||||
} else {
|
||||
1 // params start from %1 (no implicit receiver)
|
||||
1 // params start from %1 (no implicit receiver)
|
||||
};
|
||||
|
||||
(0..self.def.params.len())
|
||||
@ -251,7 +257,6 @@ fn lower_continue_stmt(f: &mut MirFunction, cur_bb: BasicBlockId, target_bb: Bas
|
||||
// );
|
||||
}
|
||||
|
||||
|
||||
pub(super) fn lower_stmt_with_vars(
|
||||
f: &mut MirFunction,
|
||||
cur_bb: BasicBlockId,
|
||||
@ -322,16 +327,14 @@ pub(super) fn lower_stmt_with_vars(
|
||||
catches,
|
||||
finally,
|
||||
} => {
|
||||
try_catch::lower_try_stmt(
|
||||
f, cur_bb, try_body, catches, finally, vars, loop_stack, env,
|
||||
)
|
||||
try_catch::lower_try_stmt(f, cur_bb, try_body, catches, finally, vars, loop_stack, env)
|
||||
}
|
||||
StmtV0::If { cond, then, r#else } => {
|
||||
if_else::lower_if_stmt(f, cur_bb, cond, then, r#else, vars, loop_stack, env)
|
||||
}
|
||||
StmtV0::Loop { cond, body } => {
|
||||
loop_::lower_loop_stmt(f, cur_bb, cond, body, vars, loop_stack, env)
|
||||
}
|
||||
StmtV0::If { cond, then, r#else } => if_else::lower_if_stmt(
|
||||
f, cur_bb, cond, then, r#else, vars, loop_stack, env,
|
||||
),
|
||||
StmtV0::Loop { cond, body } => loop_::lower_loop_stmt(
|
||||
f, cur_bb, cond, body, vars, loop_stack, env,
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
@ -355,7 +358,10 @@ pub(super) fn lower_stmt_list_with_vars(
|
||||
Ok(cur)
|
||||
}
|
||||
|
||||
pub(super) fn lower_program(prog: ProgramV0, imports: std::collections::HashMap<String, String>) -> Result<MirModule, String> {
|
||||
pub(super) fn lower_program(
|
||||
prog: ProgramV0,
|
||||
imports: std::collections::HashMap<String, String>,
|
||||
) -> Result<MirModule, String> {
|
||||
if prog.body.is_empty() {
|
||||
return Err("empty body".into());
|
||||
}
|
||||
@ -468,7 +474,11 @@ pub(super) fn lower_program(prog: ProgramV0, imports: std::collections::HashMap<
|
||||
// Phase 21.6: Call resolution post-processing
|
||||
// Toggle: HAKO_MIR_BUILDER_CALL_RESOLVE=1
|
||||
// Resolve Call instructions to use qualified function names (e.g., "add" -> "Main.add")
|
||||
if std::env::var("HAKO_MIR_BUILDER_CALL_RESOLVE").ok().as_deref() == Some("1") {
|
||||
if std::env::var("HAKO_MIR_BUILDER_CALL_RESOLVE")
|
||||
.ok()
|
||||
.as_deref()
|
||||
== Some("1")
|
||||
{
|
||||
if !func_map.is_empty() {
|
||||
for (_func_idx, func) in module.functions.iter_mut() {
|
||||
for (_block_id, block) in func.blocks.iter_mut() {
|
||||
@ -476,7 +486,12 @@ pub(super) fn lower_program(prog: ProgramV0, imports: std::collections::HashMap<
|
||||
|
||||
// Find Call instructions and their associated Const values
|
||||
for inst in &block.instructions {
|
||||
if let MirInstruction::Call { func: func_reg, args, .. } = inst {
|
||||
if let MirInstruction::Call {
|
||||
func: func_reg,
|
||||
args,
|
||||
..
|
||||
} = inst
|
||||
{
|
||||
// Look for the Const instruction that defines func_reg
|
||||
for const_inst in &block.instructions {
|
||||
if let MirInstruction::Const { dst, value } = const_inst {
|
||||
@ -486,12 +501,30 @@ pub(super) fn lower_program(prog: ProgramV0, imports: std::collections::HashMap<
|
||||
if let Some(resolved) = func_map.get(name) {
|
||||
let mut new_name = resolved.clone();
|
||||
// Avoid double suffix if already contains '/N'
|
||||
if !resolved.rsplit('/').next().unwrap_or("").chars().all(|c| c.is_ascii_digit()) || !resolved.contains('/') {
|
||||
new_name = format!("{}{}", resolved.clone(), format!("/{}", args.len()));
|
||||
if !resolved
|
||||
.rsplit('/')
|
||||
.next()
|
||||
.unwrap_or("")
|
||||
.chars()
|
||||
.all(|c| c.is_ascii_digit())
|
||||
|| !resolved.contains('/')
|
||||
{
|
||||
new_name = format!(
|
||||
"{}{}",
|
||||
resolved.clone(),
|
||||
format!("/{}", args.len())
|
||||
);
|
||||
}
|
||||
const_replacements.push((*dst, new_name));
|
||||
if std::env::var("HAKO_MIR_BUILDER_DEBUG").ok().as_deref() == Some("1") {
|
||||
eprintln!("[mirbuilder/call:resolve] {} => {}", name, resolved);
|
||||
if std::env::var("HAKO_MIR_BUILDER_DEBUG")
|
||||
.ok()
|
||||
.as_deref()
|
||||
== Some("1")
|
||||
{
|
||||
eprintln!(
|
||||
"[mirbuilder/call:resolve] {} => {}",
|
||||
name, resolved
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -512,15 +545,23 @@ pub(super) fn lower_program(prog: ProgramV0, imports: std::collections::HashMap<
|
||||
}
|
||||
}
|
||||
// Build a map reg -> name after replacements
|
||||
let mut reg_name: std::collections::HashMap<ValueId, String> = std::collections::HashMap::new();
|
||||
let mut reg_name: std::collections::HashMap<ValueId, String> =
|
||||
std::collections::HashMap::new();
|
||||
for inst in &block.instructions {
|
||||
if let MirInstruction::Const { dst, value } = inst {
|
||||
if let ConstValue::String(s) = value { reg_name.insert(*dst, s.clone()); }
|
||||
if let ConstValue::String(s) = value {
|
||||
reg_name.insert(*dst, s.clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
// Upgrade legacy calls to Global callee when name is known
|
||||
for inst in &mut block.instructions {
|
||||
if let MirInstruction::Call { func: func_reg, callee, .. } = inst {
|
||||
if let MirInstruction::Call {
|
||||
func: func_reg,
|
||||
callee,
|
||||
..
|
||||
} = inst
|
||||
{
|
||||
if let Some(name) = reg_name.get(func_reg).cloned() {
|
||||
*callee = Some(Callee::Global(name));
|
||||
}
|
||||
|
||||
@ -1,10 +1,8 @@
|
||||
use super::merge::new_block;
|
||||
use super::BridgeEnv;
|
||||
use super::ternary;
|
||||
use super::match_expr;
|
||||
use crate::mir::{
|
||||
BasicBlockId, ConstValue, EffectMask, MirFunction, MirInstruction, ValueId,
|
||||
};
|
||||
use super::merge::new_block;
|
||||
use super::ternary;
|
||||
use super::BridgeEnv;
|
||||
use crate::mir::{BasicBlockId, ConstValue, EffectMask, MirFunction, MirInstruction, ValueId};
|
||||
use std::collections::HashMap;
|
||||
|
||||
use super::super::ast::ExprV0;
|
||||
@ -140,7 +138,10 @@ fn lower_throw(
|
||||
} else {
|
||||
let dst = f.next_value_id();
|
||||
if let Some(bb) = f.get_block_mut(cur_bb) {
|
||||
bb.add_instruction(MirInstruction::Const { dst, value: ConstValue::Integer(0) });
|
||||
bb.add_instruction(MirInstruction::Const {
|
||||
dst,
|
||||
value: ConstValue::Integer(0),
|
||||
});
|
||||
}
|
||||
(dst, cur_bb)
|
||||
}
|
||||
@ -264,8 +265,15 @@ pub(super) fn lower_expr_with_scope<S: VarScope>(
|
||||
// );
|
||||
let cdst = f.next_value_id();
|
||||
if let Some(bb) = f.get_block_mut(fall_bb) {
|
||||
let cval = if is_and { ConstValue::Bool(false) } else { ConstValue::Bool(true) };
|
||||
bb.add_instruction(MirInstruction::Const { dst: cdst, value: cval });
|
||||
let cval = if is_and {
|
||||
ConstValue::Bool(false)
|
||||
} else {
|
||||
ConstValue::Bool(true)
|
||||
};
|
||||
bb.add_instruction(MirInstruction::Const {
|
||||
dst: cdst,
|
||||
value: cval,
|
||||
});
|
||||
}
|
||||
crate::mir::ssot::cf_common::set_jump(f, fall_bb, merge_bb);
|
||||
let (rval, rhs_end) = lower_expr_with_scope(env, f, rhs_bb, rhs, vars)?;
|
||||
@ -277,7 +285,11 @@ pub(super) fn lower_expr_with_scope<S: VarScope>(
|
||||
let out = f.next_value_id();
|
||||
// フェーズM.2: PHI統一処理(no_phi分岐削除)
|
||||
let mut inputs: Vec<(BasicBlockId, ValueId)> = vec![(fall_bb, cdst)];
|
||||
if rhs_end != fall_bb { inputs.push((rhs_end, rval)); } else { inputs.push((fall_bb, rval)); }
|
||||
if rhs_end != fall_bb {
|
||||
inputs.push((rhs_end, rval));
|
||||
} else {
|
||||
inputs.push((fall_bb, rval));
|
||||
}
|
||||
crate::mir::ssot::cf_common::insert_phi_at_head(f, merge_bb, out, inputs);
|
||||
Ok((out, merge_bb))
|
||||
}
|
||||
@ -346,7 +358,10 @@ pub(super) fn lower_expr_with_scope<S: VarScope>(
|
||||
let (arg_ids, cur) = lower_args_with_scope(env, f, cur_bb, args, vars)?;
|
||||
let fun_val = f.next_value_id();
|
||||
if let Some(bb) = f.get_block_mut(cur) {
|
||||
bb.add_instruction(MirInstruction::Const { dst: fun_val, value: ConstValue::String(name.clone()) });
|
||||
bb.add_instruction(MirInstruction::Const {
|
||||
dst: fun_val,
|
||||
value: ConstValue::String(name.clone()),
|
||||
});
|
||||
}
|
||||
let dst = f.next_value_id();
|
||||
if let Some(bb) = f.get_block_mut(cur) {
|
||||
@ -361,8 +376,10 @@ pub(super) fn lower_expr_with_scope<S: VarScope>(
|
||||
Ok((dst, cur))
|
||||
}
|
||||
ExprV0::Method { recv, method, args } => {
|
||||
let recv_is_console_new = matches!(&**recv, ExprV0::New { class, .. } if class == "ConsoleBox");
|
||||
if recv_is_console_new && (method == "println" || method == "print" || method == "log") {
|
||||
let recv_is_console_new =
|
||||
matches!(&**recv, ExprV0::New { class, .. } if class == "ConsoleBox");
|
||||
if recv_is_console_new && (method == "println" || method == "print" || method == "log")
|
||||
{
|
||||
let (arg_ids, cur2) = lower_args_with_scope(env, f, cur_bb, args, vars)?;
|
||||
let dst = f.next_value_id();
|
||||
if let Some(bb) = f.get_block_mut(cur2) {
|
||||
@ -378,11 +395,16 @@ pub(super) fn lower_expr_with_scope<S: VarScope>(
|
||||
}
|
||||
// Phase 25.1b: Handle env.box_introspect.kind(value) pattern
|
||||
// Pattern: Method { recv: Method { recv: Var("env"), method: "box_introspect" }, method: "kind", args }
|
||||
if let ExprV0::Method { recv: inner_recv, method: inner_method, args: inner_args } = &**recv {
|
||||
if let ExprV0::Method {
|
||||
recv: inner_recv,
|
||||
method: inner_method,
|
||||
args: inner_args,
|
||||
} = &**recv
|
||||
{
|
||||
if matches!(&**inner_recv, ExprV0::Var { name } if name == "env")
|
||||
&& inner_method == "box_introspect"
|
||||
&& inner_args.is_empty() {
|
||||
|
||||
&& inner_args.is_empty()
|
||||
{
|
||||
// Lower args for the final method call
|
||||
let (arg_ids, cur2) = lower_args_with_scope(env, f, cur_bb, args, vars)?;
|
||||
let dst = f.next_value_id();
|
||||
@ -418,7 +440,11 @@ pub(super) fn lower_expr_with_scope<S: VarScope>(
|
||||
let (arg_ids, cur) = lower_args_with_scope(env, f, cur_bb, args, vars)?;
|
||||
let dst = f.next_value_id();
|
||||
if let Some(bb) = f.get_block_mut(cur) {
|
||||
bb.add_instruction(MirInstruction::NewBox { dst, box_type: class.clone(), args: arg_ids });
|
||||
bb.add_instruction(MirInstruction::NewBox {
|
||||
dst,
|
||||
box_type: class.clone(),
|
||||
args: arg_ids,
|
||||
});
|
||||
}
|
||||
Ok((dst, cur))
|
||||
}
|
||||
@ -430,10 +456,14 @@ pub(super) fn lower_expr_with_scope<S: VarScope>(
|
||||
let (exc, cur) = lower_expr_with_scope(env, f, cur_bb, expr, vars)?;
|
||||
Ok(lower_throw(env, f, cur, exc))
|
||||
}
|
||||
ExprV0::Ternary { cond, then, r#else } =>
|
||||
ternary::lower_ternary_expr_with_scope(env, f, cur_bb, cond, then, r#else, vars),
|
||||
ExprV0::Match { scrutinee, arms, r#else } =>
|
||||
match_expr::lower_match_expr_with_scope(env, f, cur_bb, scrutinee, arms, r#else, vars),
|
||||
ExprV0::Ternary { cond, then, r#else } => {
|
||||
ternary::lower_ternary_expr_with_scope(env, f, cur_bb, cond, then, r#else, vars)
|
||||
}
|
||||
ExprV0::Match {
|
||||
scrutinee,
|
||||
arms,
|
||||
r#else,
|
||||
} => match_expr::lower_match_expr_with_scope(env, f, cur_bb, scrutinee, arms, r#else, vars),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
use super::super::ast::ExprV0;
|
||||
use super::super::ast::StmtV0;
|
||||
use super::{lower_stmt_list_with_vars, merge_var_maps, new_block, BridgeEnv, LoopContext};
|
||||
use crate::mir::{BasicBlockId, MirFunction, ValueId};
|
||||
use std::collections::HashMap;
|
||||
use super::super::ast::StmtV0;
|
||||
use super::super::ast::ExprV0;
|
||||
|
||||
pub(super) fn lower_if_stmt(
|
||||
f: &mut MirFunction,
|
||||
@ -24,16 +24,22 @@ pub(super) fn lower_if_stmt(
|
||||
let tend = lower_stmt_list_with_vars(f, then_bb, then_body, &mut then_vars, loop_stack, env)?;
|
||||
let mut then_terminated = false;
|
||||
if let Some(bb) = f.get_block_mut(tend) {
|
||||
if !bb.is_terminated() { crate::mir::ssot::cf_common::set_jump(f, tend, merge_bb); }
|
||||
else { then_terminated = true; }
|
||||
if !bb.is_terminated() {
|
||||
crate::mir::ssot::cf_common::set_jump(f, tend, merge_bb);
|
||||
} else {
|
||||
then_terminated = true;
|
||||
}
|
||||
}
|
||||
let (else_end_pred, else_vars, else_terminated) = if let Some(elses) = else_body {
|
||||
let mut ev = base_vars.clone();
|
||||
let eend = lower_stmt_list_with_vars(f, else_bb, elses, &mut ev, loop_stack, env)?;
|
||||
let mut term = false;
|
||||
if let Some(bb) = f.get_block_mut(eend) {
|
||||
if !bb.is_terminated() { crate::mir::ssot::cf_common::set_jump(f, eend, merge_bb); }
|
||||
else { term = true; }
|
||||
if !bb.is_terminated() {
|
||||
crate::mir::ssot::cf_common::set_jump(f, eend, merge_bb);
|
||||
} else {
|
||||
term = true;
|
||||
}
|
||||
}
|
||||
(eend, ev, term)
|
||||
} else {
|
||||
|
||||
@ -20,14 +20,14 @@
|
||||
* だけを行う。
|
||||
*/
|
||||
|
||||
use super::{lower_stmt_list_with_vars, new_block, BridgeEnv, LoopContext};
|
||||
use crate::mir::{BasicBlockId, MirFunction, MirInstruction, ValueId};
|
||||
use crate::mir::phi_core::loopform_builder::{LoopFormBuilder, LoopFormOps};
|
||||
use crate::mir::phi_core::loop_snapshot_merge::LoopSnapshotMergeBox;
|
||||
use crate::mir::phi_core::phi_input_collector::PhiInputCollector;
|
||||
use std::collections::HashMap;
|
||||
use super::super::ast::StmtV0;
|
||||
use super::super::ast::ExprV0;
|
||||
use super::super::ast::StmtV0;
|
||||
use super::{lower_stmt_list_with_vars, new_block, BridgeEnv, LoopContext};
|
||||
use crate::mir::phi_core::loop_snapshot_merge::LoopSnapshotMergeBox;
|
||||
use crate::mir::phi_core::loopform_builder::{LoopFormBuilder, LoopFormOps};
|
||||
use crate::mir::phi_core::phi_input_collector::PhiInputCollector;
|
||||
use crate::mir::{BasicBlockId, MirFunction, MirInstruction, ValueId};
|
||||
use std::collections::HashMap;
|
||||
|
||||
/// LoopForm v2 用の JSON bridge 実装。
|
||||
///
|
||||
@ -97,7 +97,9 @@ impl LoopFormOps for LoopFormJsonOps<'_> {
|
||||
fn is_parameter(&self, value_id: ValueId) -> bool {
|
||||
// Phase 26-A-4: ValueId から変数名を逆引き
|
||||
// vars マップを逆引きして変数名を取得
|
||||
let name = self.vars.iter()
|
||||
let name = self
|
||||
.vars
|
||||
.iter()
|
||||
.find(|(_, &v)| v == value_id)
|
||||
.map(|(n, _)| n.as_str());
|
||||
|
||||
@ -153,7 +155,11 @@ impl LoopFormOps for LoopFormJsonOps<'_> {
|
||||
) -> Result<(), String> {
|
||||
if let Some(bb) = self.f.get_block_mut(block) {
|
||||
for inst in &mut bb.instructions {
|
||||
if let MirInstruction::Phi { dst, inputs: phi_inputs } = inst {
|
||||
if let MirInstruction::Phi {
|
||||
dst,
|
||||
inputs: phi_inputs,
|
||||
} = inst
|
||||
{
|
||||
if *dst == phi_id {
|
||||
*phi_inputs = inputs;
|
||||
return Ok(());
|
||||
@ -231,7 +237,10 @@ pub(super) fn lower_loop_stmt(
|
||||
|
||||
// DEBUG: Log preheader snapshot
|
||||
if std::env::var("NYASH_LOOPFORM_DEBUG").is_ok() {
|
||||
eprintln!("[loop_/lower] === PREHEADER SNAPSHOT (bb={:?}) ===", preheader_bb);
|
||||
eprintln!(
|
||||
"[loop_/lower] === PREHEADER SNAPSHOT (bb={:?}) ===",
|
||||
preheader_bb
|
||||
);
|
||||
eprintln!("[loop_/lower] Function: {}", f.signature.name);
|
||||
eprintln!("[loop_/lower] base_vars.len() = {}", base_vars.len());
|
||||
let mut sorted: Vec<_> = base_vars.iter().collect();
|
||||
@ -253,8 +262,7 @@ pub(super) fn lower_loop_stmt(
|
||||
loopform.emit_header_phis(&mut ops)?;
|
||||
|
||||
// 3) ループ条件を header ブロックで評価し、body/exit へ分岐
|
||||
let (cval, cend) =
|
||||
super::expr::lower_expr_with_vars(env, ops.f, header_bb, cond, ops.vars)?;
|
||||
let (cval, cend) = super::expr::lower_expr_with_vars(env, ops.f, header_bb, cond, ops.vars)?;
|
||||
crate::mir::ssot::cf_common::set_branch(ops.f, cend, cval, body_bb, exit_bb);
|
||||
|
||||
// 4) ループ本体を lowering(break/continue スナップショットは lowering.rs 側が管理)
|
||||
@ -266,8 +274,7 @@ pub(super) fn lower_loop_stmt(
|
||||
continue_merge_bb: Some(continue_merge_bb),
|
||||
});
|
||||
super::detect_and_push_increment_hint(body);
|
||||
let bend_res =
|
||||
lower_stmt_list_with_vars(ops.f, body_bb, body, &mut body_vars, loop_stack, env);
|
||||
let bend_res = lower_stmt_list_with_vars(ops.f, body_bb, body, &mut body_vars, loop_stack, env);
|
||||
loop_stack.pop();
|
||||
let _ = super::pop_increment_hint();
|
||||
let bend = bend_res?;
|
||||
@ -293,10 +300,13 @@ pub(super) fn lower_loop_stmt(
|
||||
|
||||
// DEBUG: Log writes collection
|
||||
if std::env::var("NYASH_LOOPFORM_DEBUG").is_ok() {
|
||||
let func_name = ops.f.signature.name.clone(); // Clone before borrowing
|
||||
let func_name = ops.f.signature.name.clone(); // Clone before borrowing
|
||||
eprintln!("[loop_/lower] === WRITES COLLECTION (Step 5-1) ===");
|
||||
eprintln!("[loop_/lower] Function: {}", func_name);
|
||||
eprintln!("[loop_/lower] {} variables modified in loop body", writes.len());
|
||||
eprintln!(
|
||||
"[loop_/lower] {} variables modified in loop body",
|
||||
writes.len()
|
||||
);
|
||||
let mut sorted_writes: Vec<_> = writes.iter().collect();
|
||||
sorted_writes.sort();
|
||||
for name in &sorted_writes {
|
||||
@ -330,10 +340,7 @@ pub(super) fn lower_loop_stmt(
|
||||
let mut all_inputs: HashMap<String, Vec<(BasicBlockId, ValueId)>> = HashMap::new();
|
||||
for (bb, snap) in &continue_snaps {
|
||||
for (name, &val) in snap {
|
||||
all_inputs
|
||||
.entry(name.clone())
|
||||
.or_default()
|
||||
.push((*bb, val));
|
||||
all_inputs.entry(name.clone()).or_default().push((*bb, val));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -1,10 +1,10 @@
|
||||
//! Match/expr-block lowering for JSON v0 bridge.
|
||||
|
||||
use super::super::ast::{ExprV0, MatchArmV0};
|
||||
use super::expr::{lower_expr_with_scope, VarScope};
|
||||
use super::merge::new_block;
|
||||
use super::BridgeEnv;
|
||||
use crate::mir::{BasicBlockId, CompareOp, ConstValue, MirFunction, MirInstruction, ValueId};
|
||||
use super::super::ast::{ExprV0, MatchArmV0};
|
||||
use super::expr::{lower_expr_with_scope, VarScope};
|
||||
|
||||
pub(super) fn lower_match_expr_with_scope<S: VarScope>(
|
||||
env: &BridgeEnv,
|
||||
@ -21,7 +21,9 @@ pub(super) fn lower_match_expr_with_scope<S: VarScope>(
|
||||
// Set up blocks
|
||||
let dispatch_bb = new_block(f);
|
||||
if let Some(bb) = f.get_block_mut(start_bb) {
|
||||
if !bb.is_terminated() { crate::mir::ssot::cf_common::set_jump(f, start_bb, dispatch_bb); }
|
||||
if !bb.is_terminated() {
|
||||
crate::mir::ssot::cf_common::set_jump(f, start_bb, dispatch_bb);
|
||||
}
|
||||
}
|
||||
let else_bb = new_block(f);
|
||||
let merge_bb = new_block(f);
|
||||
@ -31,7 +33,11 @@ pub(super) fn lower_match_expr_with_scope<S: VarScope>(
|
||||
let mut phi_inputs: Vec<(BasicBlockId, ValueId)> = Vec::new();
|
||||
for (i, arm) in arms.iter().enumerate() {
|
||||
let then_bb = new_block(f);
|
||||
let next_dispatch = if i + 1 < arms.len() { Some(new_block(f)) } else { None };
|
||||
let next_dispatch = if i + 1 < arms.len() {
|
||||
Some(new_block(f))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
let fall_bb = next_dispatch.unwrap_or(else_bb);
|
||||
|
||||
// Pre-allocate ids to avoid double borrow
|
||||
@ -39,15 +45,27 @@ pub(super) fn lower_match_expr_with_scope<S: VarScope>(
|
||||
let cond = f.next_value_id();
|
||||
if let Some(bb) = f.get_block_mut(cur_dispatch) {
|
||||
// compare scr_val == label
|
||||
bb.add_instruction(MirInstruction::Const { dst: ldst, value: ConstValue::String(arm.label.clone()) });
|
||||
bb.add_instruction(MirInstruction::Const {
|
||||
dst: ldst,
|
||||
value: ConstValue::String(arm.label.clone()),
|
||||
});
|
||||
}
|
||||
crate::mir::ssot::cf_common::emit_compare_func(f, cur_dispatch, cond, CompareOp::Eq, scr_val, ldst);
|
||||
crate::mir::ssot::cf_common::emit_compare_func(
|
||||
f,
|
||||
cur_dispatch,
|
||||
cond,
|
||||
CompareOp::Eq,
|
||||
scr_val,
|
||||
ldst,
|
||||
);
|
||||
crate::mir::ssot::cf_common::set_branch(f, cur_dispatch, cond, then_bb, fall_bb);
|
||||
|
||||
// Then arm body
|
||||
let (tval, tend) = lower_expr_with_scope(env, f, then_bb, &arm.expr, vars)?;
|
||||
if let Some(bb) = f.get_block_mut(tend) {
|
||||
if !bb.is_terminated() { crate::mir::ssot::cf_common::set_jump(f, tend, merge_bb); }
|
||||
if !bb.is_terminated() {
|
||||
crate::mir::ssot::cf_common::set_jump(f, tend, merge_bb);
|
||||
}
|
||||
}
|
||||
phi_inputs.push((tend, tval));
|
||||
|
||||
@ -57,7 +75,9 @@ pub(super) fn lower_match_expr_with_scope<S: VarScope>(
|
||||
// Else body
|
||||
let (eval, eend) = lower_expr_with_scope(env, f, else_bb, else_expr, vars)?;
|
||||
if let Some(bb) = f.get_block_mut(eend) {
|
||||
if !bb.is_terminated() { crate::mir::ssot::cf_common::set_jump(f, eend, merge_bb); }
|
||||
if !bb.is_terminated() {
|
||||
crate::mir::ssot::cf_common::set_jump(f, eend, merge_bb);
|
||||
}
|
||||
}
|
||||
phi_inputs.push((eend, eval));
|
||||
|
||||
|
||||
@ -1,6 +1,4 @@
|
||||
use crate::mir::{
|
||||
BasicBlock, BasicBlockId, MirFunction, MirInstruction, ValueId,
|
||||
};
|
||||
use crate::mir::{BasicBlock, BasicBlockId, MirFunction, MirInstruction, ValueId};
|
||||
|
||||
fn next_block_id(f: &MirFunction) -> BasicBlockId {
|
||||
let mut mx = 0u32;
|
||||
|
||||
@ -3,10 +3,10 @@
|
||||
//! NOTE: This module is introduced as part of the helper split.
|
||||
//! It is not wired yet and should not alter behavior.
|
||||
|
||||
use super::super::ast::ExprV0;
|
||||
use super::merge::new_block;
|
||||
use super::BridgeEnv;
|
||||
use crate::mir::{BasicBlockId, MirFunction, ValueId};
|
||||
use super::super::ast::ExprV0;
|
||||
|
||||
use super::expr::{lower_expr_with_scope, VarScope};
|
||||
|
||||
@ -27,11 +27,15 @@ pub(super) fn lower_ternary_expr_with_scope<S: VarScope>(
|
||||
crate::mir::ssot::cf_common::set_branch(f, cur, cval, then_bb, else_bb);
|
||||
let (tval, tend) = lower_expr_with_scope(env, f, then_bb, then_e, vars)?;
|
||||
if let Some(bb) = f.get_block_mut(tend) {
|
||||
if !bb.is_terminated() { crate::mir::ssot::cf_common::set_jump(f, tend, merge_bb); }
|
||||
if !bb.is_terminated() {
|
||||
crate::mir::ssot::cf_common::set_jump(f, tend, merge_bb);
|
||||
}
|
||||
}
|
||||
let (eval, eend) = lower_expr_with_scope(env, f, else_bb, else_e, vars)?;
|
||||
if let Some(bb) = f.get_block_mut(eend) {
|
||||
if !bb.is_terminated() { crate::mir::ssot::cf_common::set_jump(f, eend, merge_bb); }
|
||||
if !bb.is_terminated() {
|
||||
crate::mir::ssot::cf_common::set_jump(f, eend, merge_bb);
|
||||
}
|
||||
}
|
||||
let out = f.next_value_id();
|
||||
// フェーズM.2: PHI統一処理(no_phi分岐削除)
|
||||
|
||||
@ -13,7 +13,10 @@ pub(super) struct ThrowCtx {
|
||||
|
||||
impl ThrowCtx {
|
||||
fn new(catch_bb: BasicBlockId) -> Self {
|
||||
Self { catch_bb, incoming: Vec::new() }
|
||||
Self {
|
||||
catch_bb,
|
||||
incoming: Vec::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -33,7 +36,11 @@ pub(super) fn is_active() -> bool {
|
||||
|
||||
/// Record a throw from `from_bb` with value `exc_val`. Sets terminator Jump to catch and
|
||||
/// appends predecessor+value to the incoming list. Returns the catch block id if active.
|
||||
pub(super) fn record_throw(f: &mut MirFunction, from_bb: BasicBlockId, exc_val: ValueId) -> Option<BasicBlockId> {
|
||||
pub(super) fn record_throw(
|
||||
f: &mut MirFunction,
|
||||
from_bb: BasicBlockId,
|
||||
exc_val: ValueId,
|
||||
) -> Option<BasicBlockId> {
|
||||
THROW_CTX.with(|slot| {
|
||||
if let Some(ctx) = slot.borrow_mut().as_mut() {
|
||||
let target = ctx.catch_bb;
|
||||
|
||||
@ -1,9 +1,7 @@
|
||||
use super::{
|
||||
lower_stmt_list_with_vars, new_block, BridgeEnv, LoopContext,
|
||||
};
|
||||
use super::super::ast::{CatchV0, StmtV0};
|
||||
use super::{lower_stmt_list_with_vars, new_block, BridgeEnv, LoopContext};
|
||||
use crate::mir::{BasicBlockId, MirFunction, MirInstruction, ValueId};
|
||||
use std::collections::HashMap;
|
||||
use super::super::ast::{StmtV0, CatchV0};
|
||||
|
||||
pub(super) fn lower_try_stmt(
|
||||
f: &mut MirFunction,
|
||||
@ -23,9 +21,23 @@ pub(super) fn lower_try_stmt(
|
||||
if catches.len() > 1 {
|
||||
// Fallback to safe lowering (ignore catches) for multi-catch
|
||||
let mut tmp_vars = vars.clone();
|
||||
let mut next_bb = super::lower_stmt_list_with_vars(f, cur_bb, try_body, &mut tmp_vars, loop_stack, env)?;
|
||||
let mut next_bb = super::lower_stmt_list_with_vars(
|
||||
f,
|
||||
cur_bb,
|
||||
try_body,
|
||||
&mut tmp_vars,
|
||||
loop_stack,
|
||||
env,
|
||||
)?;
|
||||
if !finally.is_empty() {
|
||||
next_bb = super::lower_stmt_list_with_vars(f, next_bb, finally, &mut tmp_vars, loop_stack, env)?;
|
||||
next_bb = super::lower_stmt_list_with_vars(
|
||||
f,
|
||||
next_bb,
|
||||
finally,
|
||||
&mut tmp_vars,
|
||||
loop_stack,
|
||||
env,
|
||||
)?;
|
||||
}
|
||||
*vars = tmp_vars;
|
||||
return Ok(next_bb);
|
||||
@ -34,29 +46,47 @@ pub(super) fn lower_try_stmt(
|
||||
let base_vars = vars.clone();
|
||||
let try_bb = new_block(f);
|
||||
let catch_bb_opt = if has_catch { Some(new_block(f)) } else { None };
|
||||
let finally_bb = if !finally.is_empty() { Some(new_block(f)) } else { None };
|
||||
let finally_bb = if !finally.is_empty() {
|
||||
Some(new_block(f))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
let exit_bb = new_block(f);
|
||||
|
||||
f.set_jump_terminator(cur_bb, try_bb)?;
|
||||
if let Some(succ) = f.get_block_mut(try_bb) { succ.add_predecessor(cur_bb); }
|
||||
if let Some(succ) = f.get_block_mut(try_bb) {
|
||||
succ.add_predecessor(cur_bb);
|
||||
}
|
||||
|
||||
// Install thread-local throw context so nested throw expressions jump to catch_bb
|
||||
if has_catch {
|
||||
let catch_bb = catch_bb_opt.expect("catch_bb must exist when has_catch");
|
||||
if crate::config::env::cli_verbose() {
|
||||
eprintln!("[Bridge] try_result_mode: set ThrowCtx (catch_bb={:?})", catch_bb);
|
||||
eprintln!(
|
||||
"[Bridge] try_result_mode: set ThrowCtx (catch_bb={:?})",
|
||||
catch_bb
|
||||
);
|
||||
}
|
||||
super::throw_ctx::set(catch_bb);
|
||||
} else if crate::config::env::cli_verbose() {
|
||||
eprintln!("[Bridge] try_result_mode: no catch present; ThrowCtx not set");
|
||||
}
|
||||
let mut try_vars = base_vars.clone();
|
||||
let try_end = super::lower_stmt_list_with_vars(f, try_bb, try_body, &mut try_vars, loop_stack, env)?;
|
||||
let try_end =
|
||||
super::lower_stmt_list_with_vars(f, try_bb, try_body, &mut try_vars, loop_stack, env)?;
|
||||
// Take recorded incoming exceptions
|
||||
let incoming_exc = if has_catch { super::throw_ctx::take().map(|c| c.incoming).unwrap_or_default() } else { Vec::new() };
|
||||
let incoming_exc = if has_catch {
|
||||
super::throw_ctx::take()
|
||||
.map(|c| c.incoming)
|
||||
.unwrap_or_default()
|
||||
} else {
|
||||
Vec::new()
|
||||
};
|
||||
let target = finally_bb.unwrap_or(exit_bb);
|
||||
f.set_jump_terminator(try_end, target)?;
|
||||
if let Some(succ) = f.get_block_mut(target) { succ.add_predecessor(try_end); }
|
||||
if let Some(succ) = f.get_block_mut(target) {
|
||||
succ.add_predecessor(try_end);
|
||||
}
|
||||
let try_branch_vars = try_vars.clone();
|
||||
|
||||
// Lower catch block if present and reachable
|
||||
@ -72,7 +102,9 @@ pub(super) fn lower_try_stmt(
|
||||
if let Some(_bb) = f.get_block_mut(catch_bb) {
|
||||
let mut inputs = incoming_exc.clone();
|
||||
inputs.sort_by_key(|(bbid, _)| bbid.0);
|
||||
crate::mir::ssot::cf_common::insert_phi_at_head(f, catch_bb, phi_dst, inputs);
|
||||
crate::mir::ssot::cf_common::insert_phi_at_head(
|
||||
f, catch_bb, phi_dst, inputs,
|
||||
);
|
||||
}
|
||||
catch_vars.insert(param.clone(), phi_dst);
|
||||
}
|
||||
@ -87,7 +119,9 @@ pub(super) fn lower_try_stmt(
|
||||
)?;
|
||||
let target = finally_bb.unwrap_or(exit_bb);
|
||||
f.set_jump_terminator(end, target)?;
|
||||
if let Some(succ) = f.get_block_mut(target) { succ.add_predecessor(end); }
|
||||
if let Some(succ) = f.get_block_mut(target) {
|
||||
succ.add_predecessor(end);
|
||||
}
|
||||
(end, catch_vars)
|
||||
} else {
|
||||
(try_end, base_vars.clone())
|
||||
@ -98,21 +132,30 @@ pub(super) fn lower_try_stmt(
|
||||
if let Some(finally_block) = finally_bb {
|
||||
// Compute merged var map from try_end + catch_end (if has_catch)
|
||||
let branch_vars: Vec<(BasicBlockId, HashMap<String, ValueId>)> = if has_catch {
|
||||
vec![(try_end, try_branch_vars.clone()), (catch_end, catch_branch_vars.clone())]
|
||||
vec![
|
||||
(try_end, try_branch_vars.clone()),
|
||||
(catch_end, catch_branch_vars.clone()),
|
||||
]
|
||||
} else {
|
||||
vec![(try_end, try_branch_vars.clone())]
|
||||
};
|
||||
let mut names: HashSet<String> = base_vars.keys().cloned().collect();
|
||||
for (_, map) in &branch_vars { names.extend(map.keys().cloned()); }
|
||||
for (_, map) in &branch_vars {
|
||||
names.extend(map.keys().cloned());
|
||||
}
|
||||
let mut merged_vars = base_vars.clone();
|
||||
let mut phi_entries: Vec<(ValueId, Vec<(BasicBlockId, ValueId)>)> = Vec::new();
|
||||
for name in names {
|
||||
let mut inputs: Vec<(BasicBlockId, ValueId)> = Vec::new();
|
||||
for (bbid, map) in &branch_vars {
|
||||
if let Some(&v) = map.get(&name) { inputs.push((*bbid, v)); }
|
||||
if let Some(&v) = map.get(&name) {
|
||||
inputs.push((*bbid, v));
|
||||
}
|
||||
}
|
||||
if inputs.is_empty() {
|
||||
if let Some(&b) = base_vars.get(&name) { merged_vars.insert(name.clone(), b); }
|
||||
if let Some(&b) = base_vars.get(&name) {
|
||||
merged_vars.insert(name.clone(), b);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
let uniq: HashSet<ValueId> = inputs.iter().map(|(_, v)| *v).collect();
|
||||
@ -126,12 +169,23 @@ pub(super) fn lower_try_stmt(
|
||||
merged_vars.insert(name.clone(), dst);
|
||||
}
|
||||
if let Some(_bb) = f.get_block_mut(finally_block) {
|
||||
for (dst, inputs) in phi_entries { crate::mir::ssot::cf_common::insert_phi_at_head(f, finally_block, dst, inputs); }
|
||||
for (dst, inputs) in phi_entries {
|
||||
crate::mir::ssot::cf_common::insert_phi_at_head(f, finally_block, dst, inputs);
|
||||
}
|
||||
}
|
||||
let mut finally_vars = merged_vars.clone();
|
||||
let final_end = super::lower_stmt_list_with_vars(f, finally_block, finally, &mut finally_vars, loop_stack, env)?;
|
||||
let final_end = super::lower_stmt_list_with_vars(
|
||||
f,
|
||||
finally_block,
|
||||
finally,
|
||||
&mut finally_vars,
|
||||
loop_stack,
|
||||
env,
|
||||
)?;
|
||||
f.set_jump_terminator(final_end, exit_bb)?;
|
||||
if let Some(succ) = f.get_block_mut(exit_bb) { succ.add_predecessor(final_end); }
|
||||
if let Some(succ) = f.get_block_mut(exit_bb) {
|
||||
succ.add_predecessor(final_end);
|
||||
}
|
||||
*vars = finally_vars;
|
||||
return Ok(exit_bb);
|
||||
} else {
|
||||
@ -142,38 +196,49 @@ pub(super) fn lower_try_stmt(
|
||||
vec![(try_end, try_branch_vars)]
|
||||
};
|
||||
let mut names: HashSet<String> = base_vars.keys().cloned().collect();
|
||||
for (_, map) in &branch_vars { names.extend(map.keys().cloned()); }
|
||||
for (_, map) in &branch_vars {
|
||||
names.extend(map.keys().cloned());
|
||||
}
|
||||
let mut merged_vars = base_vars.clone();
|
||||
let mut phi_entries: Vec<(ValueId, Vec<(BasicBlockId, ValueId)>)> = Vec::new();
|
||||
for name in names {
|
||||
let mut inputs: Vec<(BasicBlockId, ValueId)> = Vec::new();
|
||||
for (bbid, map) in &branch_vars { if let Some(&v) = map.get(&name) { inputs.push((*bbid, v)); } }
|
||||
if inputs.is_empty() { if let Some(&b) = base_vars.get(&name) { merged_vars.insert(name.clone(), b); } continue; }
|
||||
for (bbid, map) in &branch_vars {
|
||||
if let Some(&v) = map.get(&name) {
|
||||
inputs.push((*bbid, v));
|
||||
}
|
||||
}
|
||||
if inputs.is_empty() {
|
||||
if let Some(&b) = base_vars.get(&name) {
|
||||
merged_vars.insert(name.clone(), b);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
let uniq: HashSet<ValueId> = inputs.iter().map(|(_, v)| *v).collect();
|
||||
if uniq.len() == 1 { merged_vars.insert(name.clone(), inputs[0].1); continue; }
|
||||
if uniq.len() == 1 {
|
||||
merged_vars.insert(name.clone(), inputs[0].1);
|
||||
continue;
|
||||
}
|
||||
let dst = f.next_value_id();
|
||||
inputs.sort_by_key(|(bbid, _)| bbid.0);
|
||||
phi_entries.push((dst, inputs));
|
||||
merged_vars.insert(name.clone(), dst);
|
||||
}
|
||||
if let Some(_bb) = f.get_block_mut(exit_bb) {
|
||||
for (dst, inputs) in phi_entries { crate::mir::ssot::cf_common::insert_phi_at_head(f, exit_bb, dst, inputs); }
|
||||
for (dst, inputs) in phi_entries {
|
||||
crate::mir::ssot::cf_common::insert_phi_at_head(f, exit_bb, dst, inputs);
|
||||
}
|
||||
}
|
||||
*vars = merged_vars;
|
||||
return Ok(exit_bb);
|
||||
}
|
||||
} else if !try_enabled || catches.is_empty() || catches.len() > 1 {
|
||||
let mut tmp_vars = vars.clone();
|
||||
let mut next_bb = lower_stmt_list_with_vars(f, cur_bb, try_body, &mut tmp_vars, loop_stack, env)?;
|
||||
let mut next_bb =
|
||||
lower_stmt_list_with_vars(f, cur_bb, try_body, &mut tmp_vars, loop_stack, env)?;
|
||||
if !finally.is_empty() {
|
||||
next_bb = lower_stmt_list_with_vars(
|
||||
f,
|
||||
next_bb,
|
||||
finally,
|
||||
&mut tmp_vars,
|
||||
loop_stack,
|
||||
env,
|
||||
)?;
|
||||
next_bb =
|
||||
lower_stmt_list_with_vars(f, next_bb, finally, &mut tmp_vars, loop_stack, env)?;
|
||||
}
|
||||
*vars = tmp_vars;
|
||||
return Ok(next_bb);
|
||||
@ -260,7 +325,9 @@ pub(super) fn lower_try_stmt(
|
||||
}
|
||||
// フェーズM.2: PHI統一処理(no_phi分岐削除)
|
||||
if let Some(_bb) = f.get_block_mut(finally_block) {
|
||||
for (dst, inputs) in phi_entries { crate::mir::ssot::cf_common::insert_phi_at_head(f, finally_block, dst, inputs); }
|
||||
for (dst, inputs) in phi_entries {
|
||||
crate::mir::ssot::cf_common::insert_phi_at_head(f, finally_block, dst, inputs);
|
||||
}
|
||||
}
|
||||
let mut finally_vars = merged_vars.clone();
|
||||
let final_end = lower_stmt_list_with_vars(
|
||||
@ -309,7 +376,9 @@ pub(super) fn lower_try_stmt(
|
||||
}
|
||||
// フェーズM.2: PHI統一処理(no_phi分岐削除)
|
||||
if let Some(_bb) = f.get_block_mut(exit_bb) {
|
||||
for (dst, inputs) in phi_entries { crate::mir::ssot::cf_common::insert_phi_at_head(f, exit_bb, dst, inputs); }
|
||||
for (dst, inputs) in phi_entries {
|
||||
crate::mir::ssot::cf_common::insert_phi_at_head(f, exit_bb, dst, inputs);
|
||||
}
|
||||
}
|
||||
*vars = merged_vars;
|
||||
Ok(exit_bb)
|
||||
|
||||
@ -10,16 +10,26 @@ pub fn parse_json_v0_to_module(json: &str) -> Result<crate::mir::MirModule, Stri
|
||||
parse_json_v0_to_module_with_imports(json, HashMap::new())
|
||||
}
|
||||
|
||||
pub fn parse_json_v0_to_module_with_imports(json: &str, imports: HashMap<String, String>) -> Result<crate::mir::MirModule, String> {
|
||||
pub fn parse_json_v0_to_module_with_imports(
|
||||
json: &str,
|
||||
imports: HashMap<String, String>,
|
||||
) -> Result<crate::mir::MirModule, String> {
|
||||
let prog: ProgramV0 =
|
||||
serde_json::from_str(json).map_err(|e| format!("invalid JSON v0: {}", e))?;
|
||||
if crate::config::env::cli_verbose() {
|
||||
let first = prog
|
||||
.body
|
||||
.get(1)
|
||||
.map(|s| match s { StmtV0::Try { .. } => "Try", _ => "Other" })
|
||||
.map(|s| match s {
|
||||
StmtV0::Try { .. } => "Try",
|
||||
_ => "Other",
|
||||
})
|
||||
.unwrap_or("<none>");
|
||||
eprintln!("[Bridge] JSON v0: body_len={} first_type={}", prog.body.len(), first);
|
||||
eprintln!(
|
||||
"[Bridge] JSON v0: body_len={} first_type={}",
|
||||
prog.body.len(),
|
||||
first
|
||||
);
|
||||
}
|
||||
if prog.version != 0 || prog.kind != "Program" {
|
||||
return Err("unsupported IR: expected {version:0, kind:\"Program\"}".into());
|
||||
|
||||
@ -1,9 +1,9 @@
|
||||
use super::mir_json::common as mirjson_common;
|
||||
use crate::mir::{
|
||||
function::{FunctionSignature, MirFunction, MirModule},
|
||||
BasicBlock, BasicBlockId, ConstValue, EffectMask, MirInstruction, MirType, ValueId,
|
||||
};
|
||||
use serde_json::Value;
|
||||
use super::mir_json::common as mirjson_common;
|
||||
|
||||
fn parse_effects_from(node: &Value) -> EffectMask {
|
||||
if let Some(arr) = node.get("effects").and_then(Value::as_array) {
|
||||
@ -11,10 +11,18 @@ fn parse_effects_from(node: &Value) -> EffectMask {
|
||||
for e in arr {
|
||||
if let Some(s) = e.as_str() {
|
||||
match s {
|
||||
"write" | "mut" | "WriteHeap" => { m = m.union(EffectMask::WRITE); }
|
||||
"read" | "ReadHeap" => { m = m.union(EffectMask::READ); }
|
||||
"io" | "IO" | "ffi" | "FFI" | "debug" => { m = m.union(EffectMask::IO); }
|
||||
"control" | "Control" => { m = m.union(EffectMask::CONTROL); }
|
||||
"write" | "mut" | "WriteHeap" => {
|
||||
m = m.union(EffectMask::WRITE);
|
||||
}
|
||||
"read" | "ReadHeap" => {
|
||||
m = m.union(EffectMask::READ);
|
||||
}
|
||||
"io" | "IO" | "ffi" | "FFI" | "debug" => {
|
||||
m = m.union(EffectMask::IO);
|
||||
}
|
||||
"control" | "Control" => {
|
||||
m = m.union(EffectMask::CONTROL);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
@ -39,12 +47,7 @@ pub fn try_parse_v1_to_module(json: &str) -> Result<Option<MirModule>, String> {
|
||||
|
||||
let schema = match value.get("schema_version") {
|
||||
Some(Value::String(s)) => s.clone(),
|
||||
Some(other) => {
|
||||
return Err(format!(
|
||||
"expected schema_version string, found {}",
|
||||
other
|
||||
))
|
||||
}
|
||||
Some(other) => return Err(format!("expected schema_version string, found {}", other)),
|
||||
None => return Ok(None),
|
||||
};
|
||||
|
||||
@ -98,7 +101,8 @@ pub fn try_parse_v1_to_module(json: &str) -> Result<Option<MirModule>, String> {
|
||||
let block_id = block
|
||||
.get("id")
|
||||
.and_then(|id| id.as_u64())
|
||||
.ok_or_else(|| format!("function '{}' block missing id", func_name))? as u32;
|
||||
.ok_or_else(|| format!("function '{}' block missing id", func_name))?
|
||||
as u32;
|
||||
let bb_id = BasicBlockId::new(block_id);
|
||||
if mir_fn.get_block(bb_id).is_none() {
|
||||
mir_fn.add_block(BasicBlock::new(bb_id));
|
||||
@ -118,34 +122,23 @@ pub fn try_parse_v1_to_module(json: &str) -> Result<Option<MirModule>, String> {
|
||||
})?;
|
||||
|
||||
for inst in instructions {
|
||||
let op = inst
|
||||
.get("op")
|
||||
.and_then(|o| o.as_str())
|
||||
.ok_or_else(|| {
|
||||
format!(
|
||||
"function '{}' block {} missing op field",
|
||||
func_name, block_id
|
||||
)
|
||||
})?;
|
||||
let op = inst.get("op").and_then(|o| o.as_str()).ok_or_else(|| {
|
||||
format!(
|
||||
"function '{}' block {} missing op field",
|
||||
func_name, block_id
|
||||
)
|
||||
})?;
|
||||
match op {
|
||||
"const" => {
|
||||
let dst = inst
|
||||
.get("dst")
|
||||
.and_then(|d| d.as_u64())
|
||||
.ok_or_else(|| {
|
||||
format!(
|
||||
"const instruction missing dst in function '{}'",
|
||||
func_name
|
||||
)
|
||||
})? as u32;
|
||||
let value_obj = inst
|
||||
.get("value")
|
||||
.ok_or_else(|| {
|
||||
format!(
|
||||
"const instruction missing value in function '{}'",
|
||||
func_name
|
||||
)
|
||||
})?;
|
||||
let dst = inst.get("dst").and_then(|d| d.as_u64()).ok_or_else(|| {
|
||||
format!("const instruction missing dst in function '{}'", func_name)
|
||||
})? as u32;
|
||||
let value_obj = inst.get("value").ok_or_else(|| {
|
||||
format!(
|
||||
"const instruction missing value in function '{}'",
|
||||
func_name
|
||||
)
|
||||
})?;
|
||||
let const_val = mirjson_common::parse_const_value_generic(value_obj)?;
|
||||
block_ref.add_instruction(MirInstruction::Const {
|
||||
dst: ValueId::new(dst),
|
||||
@ -156,24 +149,12 @@ pub fn try_parse_v1_to_module(json: &str) -> Result<Option<MirModule>, String> {
|
||||
}
|
||||
}
|
||||
"copy" => {
|
||||
let dst = inst
|
||||
.get("dst")
|
||||
.and_then(|d| d.as_u64())
|
||||
.ok_or_else(|| {
|
||||
format!(
|
||||
"copy instruction missing dst in function '{}'",
|
||||
func_name
|
||||
)
|
||||
})? as u32;
|
||||
let src = inst
|
||||
.get("src")
|
||||
.and_then(|d| d.as_u64())
|
||||
.ok_or_else(|| {
|
||||
format!(
|
||||
"copy instruction missing src in function '{}'",
|
||||
func_name
|
||||
)
|
||||
})? as u32;
|
||||
let dst = inst.get("dst").and_then(|d| d.as_u64()).ok_or_else(|| {
|
||||
format!("copy instruction missing dst in function '{}'", func_name)
|
||||
})? as u32;
|
||||
let src = inst.get("src").and_then(|d| d.as_u64()).ok_or_else(|| {
|
||||
format!("copy instruction missing src in function '{}'", func_name)
|
||||
})? as u32;
|
||||
block_ref.add_instruction(MirInstruction::Copy {
|
||||
dst: ValueId::new(dst),
|
||||
src: ValueId::new(src),
|
||||
@ -186,10 +167,12 @@ pub fn try_parse_v1_to_module(json: &str) -> Result<Option<MirModule>, String> {
|
||||
let dst = require_u64(inst, "dst", "binop dst")? as u32;
|
||||
let lhs = require_u64(inst, "lhs", "binop lhs")? as u32;
|
||||
let rhs = require_u64(inst, "rhs", "binop rhs")? as u32;
|
||||
let operation = inst
|
||||
.get("operation")
|
||||
.and_then(Value::as_str)
|
||||
.ok_or_else(|| format!("binop operation missing in function '{}'", func_name))?;
|
||||
let operation =
|
||||
inst.get("operation")
|
||||
.and_then(Value::as_str)
|
||||
.ok_or_else(|| {
|
||||
format!("binop operation missing in function '{}'", func_name)
|
||||
})?;
|
||||
let bop = parse_binop(operation)?;
|
||||
block_ref.add_instruction(MirInstruction::BinOp {
|
||||
dst: ValueId::new(dst),
|
||||
@ -206,7 +189,10 @@ pub fn try_parse_v1_to_module(json: &str) -> Result<Option<MirModule>, String> {
|
||||
// Accept both JSON shapes:
|
||||
// - operation: symbolic string ("<", ">=", "==", ...)
|
||||
// - cmp: spelled enum name ("Lt", "Le", "Gt", "Ge", "Eq", "Ne")
|
||||
let op_sym_opt = inst.get("operation").and_then(Value::as_str).map(|s| s.to_string());
|
||||
let op_sym_opt = inst
|
||||
.get("operation")
|
||||
.and_then(Value::as_str)
|
||||
.map(|s| s.to_string());
|
||||
let op_sym = if let Some(sym) = op_sym_opt {
|
||||
sym
|
||||
} else if let Some(name) = inst.get("cmp").and_then(Value::as_str) {
|
||||
@ -257,25 +243,31 @@ pub fn try_parse_v1_to_module(json: &str) -> Result<Option<MirModule>, String> {
|
||||
}
|
||||
"phi" => {
|
||||
let dst = require_u64(inst, "dst", "phi dst")? as u32;
|
||||
let incoming = inst
|
||||
.get("incoming")
|
||||
.and_then(Value::as_array)
|
||||
.ok_or_else(|| format!("phi incoming missing in function '{}'", func_name))?;
|
||||
let incoming =
|
||||
inst.get("incoming")
|
||||
.and_then(Value::as_array)
|
||||
.ok_or_else(|| {
|
||||
format!("phi incoming missing in function '{}'", func_name)
|
||||
})?;
|
||||
let mut pairs = Vec::with_capacity(incoming.len());
|
||||
for entry in incoming {
|
||||
let pair = entry
|
||||
.as_array()
|
||||
.ok_or_else(|| format!("phi incoming entry must be array in function '{}'", func_name))?;
|
||||
let pair = entry.as_array().ok_or_else(|| {
|
||||
format!(
|
||||
"phi incoming entry must be array in function '{}'",
|
||||
func_name
|
||||
)
|
||||
})?;
|
||||
if pair.len() != 2 {
|
||||
return Err("phi incoming entry must have 2 elements".into());
|
||||
}
|
||||
// JSON shape: [pred_block_id, value_id]
|
||||
let pred_bb = pair[0]
|
||||
.as_u64()
|
||||
.ok_or_else(|| "phi incoming predecessor block must be integer".to_string())? as u32;
|
||||
let pred_bb = pair[0].as_u64().ok_or_else(|| {
|
||||
"phi incoming predecessor block must be integer".to_string()
|
||||
})? as u32;
|
||||
let val = pair[1]
|
||||
.as_u64()
|
||||
.ok_or_else(|| "phi incoming value must be integer".to_string())? as u32;
|
||||
.ok_or_else(|| "phi incoming value must be integer".to_string())?
|
||||
as u32;
|
||||
pairs.push((BasicBlockId::new(pred_bb), ValueId::new(val)));
|
||||
}
|
||||
block_ref.add_instruction(MirInstruction::Phi {
|
||||
@ -303,20 +295,30 @@ pub fn try_parse_v1_to_module(json: &str) -> Result<Option<MirModule>, String> {
|
||||
// - flat: { op:"mir_call", callee:{...}, args:[...], effects:[] }
|
||||
// - nested: { op:"mir_call", mir_call:{ callee:{...}, args:[...], effects:[] } }
|
||||
// dst remains at the instruction root level in both forms.
|
||||
let dst_opt = inst.get("dst").and_then(|d| d.as_u64()).map(|v| ValueId::new(v as u32));
|
||||
let effects = if let Some(sub) = inst.get("mir_call") { parse_effects_from(sub) } else { parse_effects_from(inst) };
|
||||
let dst_opt = inst
|
||||
.get("dst")
|
||||
.and_then(|d| d.as_u64())
|
||||
.map(|v| ValueId::new(v as u32));
|
||||
let effects = if let Some(sub) = inst.get("mir_call") {
|
||||
parse_effects_from(sub)
|
||||
} else {
|
||||
parse_effects_from(inst)
|
||||
};
|
||||
// args: support both flat/nested placement
|
||||
let mut argv: Vec<ValueId> = Vec::new();
|
||||
if let Some(arr) = inst
|
||||
.get("args")
|
||||
.and_then(|a| a.as_array())
|
||||
.or_else(|| inst.get("mir_call").and_then(|m| m.get("args").and_then(|a| a.as_array())))
|
||||
if let Some(arr) =
|
||||
inst.get("args").and_then(|a| a.as_array()).or_else(|| {
|
||||
inst.get("mir_call")
|
||||
.and_then(|m| m.get("args").and_then(|a| a.as_array()))
|
||||
})
|
||||
{
|
||||
for a in arr {
|
||||
let id = a.as_u64().ok_or_else(|| format!(
|
||||
"mir_call arg must be integer value id in function '{}'",
|
||||
func_name
|
||||
))? as u32;
|
||||
let id = a.as_u64().ok_or_else(|| {
|
||||
format!(
|
||||
"mir_call arg must be integer value id in function '{}'",
|
||||
func_name
|
||||
)
|
||||
})? as u32;
|
||||
argv.push(ValueId::new(id));
|
||||
}
|
||||
}
|
||||
@ -325,30 +327,37 @@ pub fn try_parse_v1_to_module(json: &str) -> Result<Option<MirModule>, String> {
|
||||
.get("callee")
|
||||
.or_else(|| inst.get("mir_call").and_then(|m| m.get("callee")))
|
||||
.ok_or_else(|| {
|
||||
format!("mir_call missing callee in function '{}'", func_name)
|
||||
})?;
|
||||
let ctype = callee_obj
|
||||
.get("type")
|
||||
.and_then(Value::as_str)
|
||||
.ok_or_else(|| format!("mir_call callee.type missing in function '{}'", func_name))?;
|
||||
format!("mir_call missing callee in function '{}'", func_name)
|
||||
})?;
|
||||
let ctype =
|
||||
callee_obj
|
||||
.get("type")
|
||||
.and_then(Value::as_str)
|
||||
.ok_or_else(|| {
|
||||
format!(
|
||||
"mir_call callee.type missing in function '{}'",
|
||||
func_name
|
||||
)
|
||||
})?;
|
||||
match ctype {
|
||||
"Global" => {
|
||||
let raw_name = callee_obj
|
||||
.get("name")
|
||||
.and_then(Value::as_str)
|
||||
.ok_or_else(|| format!(
|
||||
"mir_call callee Global missing name in function '{}'",
|
||||
func_name
|
||||
))?;
|
||||
.ok_or_else(|| {
|
||||
format!(
|
||||
"mir_call callee Global missing name in function '{}'",
|
||||
func_name
|
||||
)
|
||||
})?;
|
||||
// Map known console aliases to interpreter-accepted names
|
||||
let mapped = match raw_name {
|
||||
"print" => "print".to_string(),
|
||||
"nyash.builtin.print" => "nyash.builtin.print".to_string(),
|
||||
"nyash.console.log" => "nyash.console.log".to_string(),
|
||||
// Accept env.console.* as nyash.console.log (numeric only)
|
||||
"env.console.log" | "env.console.warn" | "env.console.error" => {
|
||||
"nyash.console.log".to_string()
|
||||
}
|
||||
"env.console.log" | "env.console.warn"
|
||||
| "env.console.error" => "nyash.console.log".to_string(),
|
||||
other => {
|
||||
return Err(format!(
|
||||
"unsupported Global callee '{}' in mir_call (Gate-C v1 bridge)",
|
||||
@ -363,7 +372,9 @@ pub fn try_parse_v1_to_module(json: &str) -> Result<Option<MirModule>, String> {
|
||||
args: argv,
|
||||
effects,
|
||||
});
|
||||
if let Some(d) = dst_opt { max_value_id = max_value_id.max(d.as_u32() + 1); }
|
||||
if let Some(d) = dst_opt {
|
||||
max_value_id = max_value_id.max(d.as_u32() + 1);
|
||||
}
|
||||
}
|
||||
"Constructor" => {
|
||||
// new box instance: box_type required
|
||||
@ -375,10 +386,12 @@ pub fn try_parse_v1_to_module(json: &str) -> Result<Option<MirModule>, String> {
|
||||
func_name
|
||||
))?;
|
||||
// dst required for Constructor
|
||||
let dst = dst_opt.ok_or_else(|| format!(
|
||||
"mir_call Constructor requires dst in function '{}'",
|
||||
func_name
|
||||
))?;
|
||||
let dst = dst_opt.ok_or_else(|| {
|
||||
format!(
|
||||
"mir_call Constructor requires dst in function '{}'",
|
||||
func_name
|
||||
)
|
||||
})?;
|
||||
block_ref.add_instruction(MirInstruction::NewBox {
|
||||
dst,
|
||||
box_type: bt.to_string(),
|
||||
@ -391,18 +404,22 @@ pub fn try_parse_v1_to_module(json: &str) -> Result<Option<MirModule>, String> {
|
||||
let method = callee_obj
|
||||
.get("method")
|
||||
.and_then(Value::as_str)
|
||||
.ok_or_else(|| format!(
|
||||
.ok_or_else(|| {
|
||||
format!(
|
||||
"mir_call callee Method missing method in function '{}'",
|
||||
func_name
|
||||
))?
|
||||
)
|
||||
})?
|
||||
.to_string();
|
||||
let recv_id = callee_obj
|
||||
.get("receiver")
|
||||
.and_then(Value::as_u64)
|
||||
.ok_or_else(|| format!(
|
||||
.ok_or_else(|| {
|
||||
format!(
|
||||
"mir_call callee Method missing receiver in function '{}'",
|
||||
func_name
|
||||
))? as u32;
|
||||
)
|
||||
})? as u32;
|
||||
let box_name = callee_obj
|
||||
.get("box_name")
|
||||
.and_then(Value::as_str)
|
||||
@ -422,7 +439,9 @@ pub fn try_parse_v1_to_module(json: &str) -> Result<Option<MirModule>, String> {
|
||||
args: argv,
|
||||
effects,
|
||||
});
|
||||
if let Some(d) = dst_opt { max_value_id = max_value_id.max(d.as_u32() + 1); }
|
||||
if let Some(d) = dst_opt {
|
||||
max_value_id = max_value_id.max(d.as_u32() + 1);
|
||||
}
|
||||
}
|
||||
"Closure" => {
|
||||
// Two shapes are seen in the wild:
|
||||
@ -433,13 +452,17 @@ pub fn try_parse_v1_to_module(json: &str) -> Result<Option<MirModule>, String> {
|
||||
|| callee_obj.get("me_capture").is_some();
|
||||
if has_new_fields {
|
||||
// Closure creation (NewClosure equivalent)
|
||||
let dst = dst_opt.ok_or_else(|| format!(
|
||||
"mir_call Closure requires dst in function '{}'",
|
||||
func_name
|
||||
))?;
|
||||
let dst = dst_opt.ok_or_else(|| {
|
||||
format!(
|
||||
"mir_call Closure requires dst in function '{}'",
|
||||
func_name
|
||||
)
|
||||
})?;
|
||||
// params: array of strings (optional)
|
||||
let mut params: Vec<String> = Vec::new();
|
||||
if let Some(arr) = callee_obj.get("params").and_then(Value::as_array) {
|
||||
if let Some(arr) =
|
||||
callee_obj.get("params").and_then(Value::as_array)
|
||||
{
|
||||
for p in arr {
|
||||
let s = p.as_str().ok_or_else(|| format!(
|
||||
"mir_call Closure params must be strings in function '{}'",
|
||||
@ -450,7 +473,9 @@ pub fn try_parse_v1_to_module(json: &str) -> Result<Option<MirModule>, String> {
|
||||
}
|
||||
// captures: array of [name, id]
|
||||
let mut captures: Vec<(String, ValueId)> = Vec::new();
|
||||
if let Some(arr) = callee_obj.get("captures").and_then(Value::as_array) {
|
||||
if let Some(arr) =
|
||||
callee_obj.get("captures").and_then(Value::as_array)
|
||||
{
|
||||
for e in arr {
|
||||
let pair = e.as_array().ok_or_else(|| format!(
|
||||
"mir_call Closure capture entry must be array in function '{}'",
|
||||
@ -460,11 +485,14 @@ pub fn try_parse_v1_to_module(json: &str) -> Result<Option<MirModule>, String> {
|
||||
return Err("mir_call Closure capture entry must have 2 elements".into());
|
||||
}
|
||||
let name = pair[0].as_str().ok_or_else(|| {
|
||||
"mir_call Closure capture[0] must be string".to_string()
|
||||
"mir_call Closure capture[0] must be string"
|
||||
.to_string()
|
||||
})?;
|
||||
let id = pair[1].as_u64().ok_or_else(|| {
|
||||
"mir_call Closure capture[1] must be integer".to_string()
|
||||
})? as u32;
|
||||
"mir_call Closure capture[1] must be integer"
|
||||
.to_string()
|
||||
})?
|
||||
as u32;
|
||||
captures.push((name.to_string(), ValueId::new(id)));
|
||||
}
|
||||
}
|
||||
@ -487,12 +515,16 @@ pub fn try_parse_v1_to_module(json: &str) -> Result<Option<MirModule>, String> {
|
||||
let fid = callee_obj
|
||||
.get("func")
|
||||
.and_then(Value::as_u64)
|
||||
.ok_or_else(|| format!(
|
||||
.ok_or_else(|| {
|
||||
format!(
|
||||
"mir_call callee Closure missing func in function '{}'",
|
||||
func_name
|
||||
))? as u32;
|
||||
)
|
||||
})? as u32;
|
||||
// Captures array (if present) are appended to argv for minimal parity
|
||||
if let Some(caps) = callee_obj.get("captures").and_then(Value::as_array) {
|
||||
if let Some(caps) =
|
||||
callee_obj.get("captures").and_then(Value::as_array)
|
||||
{
|
||||
for c in caps {
|
||||
let id = c.as_u64().ok_or_else(|| format!(
|
||||
"mir_call Closure capture must be integer in function '{}'",
|
||||
@ -504,21 +536,27 @@ pub fn try_parse_v1_to_module(json: &str) -> Result<Option<MirModule>, String> {
|
||||
block_ref.add_instruction(MirInstruction::Call {
|
||||
dst: dst_opt,
|
||||
func: ValueId::new(0),
|
||||
callee: Some(crate::mir::definitions::Callee::Value(ValueId::new(fid))),
|
||||
callee: Some(crate::mir::definitions::Callee::Value(
|
||||
ValueId::new(fid),
|
||||
)),
|
||||
args: argv,
|
||||
effects,
|
||||
});
|
||||
if let Some(d) = dst_opt { max_value_id = max_value_id.max(d.as_u32() + 1); }
|
||||
if let Some(d) = dst_opt {
|
||||
max_value_id = max_value_id.max(d.as_u32() + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
"Extern" => {
|
||||
let name = callee_obj
|
||||
.get("name")
|
||||
.and_then(Value::as_str)
|
||||
.ok_or_else(|| format!(
|
||||
"mir_call callee Extern missing name in function '{}'",
|
||||
func_name
|
||||
))?
|
||||
.ok_or_else(|| {
|
||||
format!(
|
||||
"mir_call callee Extern missing name in function '{}'",
|
||||
func_name
|
||||
)
|
||||
})?
|
||||
.to_string();
|
||||
block_ref.add_instruction(MirInstruction::Call {
|
||||
dst: dst_opt,
|
||||
@ -527,25 +565,33 @@ pub fn try_parse_v1_to_module(json: &str) -> Result<Option<MirModule>, String> {
|
||||
args: argv,
|
||||
effects: EffectMask::IO,
|
||||
});
|
||||
if let Some(d) = dst_opt { max_value_id = max_value_id.max(d.as_u32() + 1); }
|
||||
if let Some(d) = dst_opt {
|
||||
max_value_id = max_value_id.max(d.as_u32() + 1);
|
||||
}
|
||||
}
|
||||
"Value" => {
|
||||
// dynamic function value id: field 'func' (u64)
|
||||
let fid = callee_obj
|
||||
.get("func")
|
||||
.and_then(Value::as_u64)
|
||||
.ok_or_else(|| format!(
|
||||
"mir_call callee Value missing func in function '{}'",
|
||||
func_name
|
||||
))? as u32;
|
||||
.ok_or_else(|| {
|
||||
format!(
|
||||
"mir_call callee Value missing func in function '{}'",
|
||||
func_name
|
||||
)
|
||||
})? as u32;
|
||||
block_ref.add_instruction(MirInstruction::Call {
|
||||
dst: dst_opt,
|
||||
func: ValueId::new(0),
|
||||
callee: Some(crate::mir::definitions::Callee::Value(ValueId::new(fid))),
|
||||
callee: Some(crate::mir::definitions::Callee::Value(
|
||||
ValueId::new(fid),
|
||||
)),
|
||||
args: argv,
|
||||
effects,
|
||||
});
|
||||
if let Some(d) = dst_opt { max_value_id = max_value_id.max(d.as_u32() + 1); }
|
||||
if let Some(d) = dst_opt {
|
||||
max_value_id = max_value_id.max(d.as_u32() + 1);
|
||||
}
|
||||
}
|
||||
// (no duplicate Closure arm; handled above)
|
||||
other => {
|
||||
@ -646,9 +692,9 @@ fn parse_const_value(value_obj: &Value) -> Result<ConstValue, String> {
|
||||
match box_type.as_str() {
|
||||
// StringBox handle is serialized with raw string payload
|
||||
"StringBox" => {
|
||||
let s = raw_val
|
||||
.as_str()
|
||||
.ok_or_else(|| "StringBox const expects string value".to_string())?;
|
||||
let s = raw_val.as_str().ok_or_else(|| {
|
||||
"StringBox const expects string value".to_string()
|
||||
})?;
|
||||
return Ok(ConstValue::String(s.to_string()));
|
||||
}
|
||||
// Other handle kinds are not yet supported in the bridge
|
||||
@ -667,7 +713,9 @@ fn parse_const_value(value_obj: &Value) -> Result<ConstValue, String> {
|
||||
|
||||
// No explicit type: heuristics
|
||||
match raw_val {
|
||||
Value::Number(n) => Ok(ConstValue::Integer(n.as_i64().ok_or_else(|| "integer expected".to_string())?)),
|
||||
Value::Number(n) => Ok(ConstValue::Integer(
|
||||
n.as_i64().ok_or_else(|| "integer expected".to_string())?,
|
||||
)),
|
||||
Value::Bool(b) => Ok(ConstValue::Bool(b)),
|
||||
Value::String(s) => Ok(ConstValue::String(s)),
|
||||
_ => Err("const value has unsupported type descriptor".to_string()),
|
||||
|
||||
@ -22,17 +22,29 @@ pub fn parse_const_value_generic(value_obj: &Value) -> Result<ConstValue, String
|
||||
|
||||
if let Some(Value::String(s)) = type_desc.as_ref() {
|
||||
return match s.as_str() {
|
||||
"i64" | "int" => raw_val.as_i64().map(ConstValue::Integer).ok_or_else(|| "const value expected integer".to_string()),
|
||||
"f64" | "float" => raw_val.as_f64().map(ConstValue::Float).ok_or_else(|| "const value expected float".to_string()),
|
||||
"i64" | "int" => raw_val
|
||||
.as_i64()
|
||||
.map(ConstValue::Integer)
|
||||
.ok_or_else(|| "const value expected integer".to_string()),
|
||||
"f64" | "float" => raw_val
|
||||
.as_f64()
|
||||
.map(ConstValue::Float)
|
||||
.ok_or_else(|| "const value expected float".to_string()),
|
||||
"i1" | "bool" => Ok(match raw_val {
|
||||
Value::Bool(b) => ConstValue::Bool(b),
|
||||
Value::Number(n) => ConstValue::Bool(n.as_i64().unwrap_or(0) != 0),
|
||||
Value::String(ref s) => ConstValue::Bool(s == "true" || s == "1"),
|
||||
_ => ConstValue::Bool(false),
|
||||
}),
|
||||
"string" | "String" => raw_val.as_str().map(|s| ConstValue::String(s.to_string())).ok_or_else(|| "const value expected string".to_string()),
|
||||
"string" | "String" => raw_val
|
||||
.as_str()
|
||||
.map(|s| ConstValue::String(s.to_string()))
|
||||
.ok_or_else(|| "const value expected string".to_string()),
|
||||
"void" => Ok(ConstValue::Void),
|
||||
other => Err(format!("unsupported const type '{}' in MIR JSON bridge", other)),
|
||||
other => Err(format!(
|
||||
"unsupported const type '{}' in MIR JSON bridge",
|
||||
other
|
||||
)),
|
||||
};
|
||||
}
|
||||
|
||||
@ -45,7 +57,10 @@ pub fn parse_const_value_generic(value_obj: &Value) -> Result<ConstValue, String
|
||||
.as_str()
|
||||
.map(|s| ConstValue::String(s.to_string()))
|
||||
.ok_or_else(|| "StringBox const expects string value".to_string()),
|
||||
other => Err(format!("unsupported const handle type '{}' in MIR JSON bridge", other)),
|
||||
other => Err(format!(
|
||||
"unsupported const handle type '{}' in MIR JSON bridge",
|
||||
other
|
||||
)),
|
||||
};
|
||||
}
|
||||
}
|
||||
@ -54,10 +69,12 @@ pub fn parse_const_value_generic(value_obj: &Value) -> Result<ConstValue, String
|
||||
}
|
||||
|
||||
match raw_val {
|
||||
Value::Number(n) => n.as_i64().map(ConstValue::Integer).ok_or_else(|| "integer expected".to_string()),
|
||||
Value::Number(n) => n
|
||||
.as_i64()
|
||||
.map(ConstValue::Integer)
|
||||
.ok_or_else(|| "integer expected".to_string()),
|
||||
Value::Bool(b) => Ok(ConstValue::Bool(b)),
|
||||
Value::String(s) => Ok(ConstValue::String(s)),
|
||||
_ => Err("const value has unsupported type descriptor".to_string()),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
use serde_json::json;
|
||||
use crate::mir::definitions::call_unified::Callee;
|
||||
use serde_json::json;
|
||||
|
||||
/// Emit MIR JSON for Python harness/PyVM.
|
||||
/// The JSON schema matches tools/llvmlite_harness.py expectations and is
|
||||
@ -64,7 +64,10 @@ fn has_numeric_core_boxcall(root: &serde_json::Value) -> bool {
|
||||
/// - Strict: if NYASH_AOT_NUMERIC_CORE_STRICT=1, return Err to fail fast.
|
||||
#[allow(dead_code)]
|
||||
fn check_numeric_core_invariants(root: &serde_json::Value) -> Result<(), String> {
|
||||
let numeric_on = matches!(std::env::var("NYASH_AOT_NUMERIC_CORE").ok().as_deref(), Some("1"));
|
||||
let numeric_on = matches!(
|
||||
std::env::var("NYASH_AOT_NUMERIC_CORE").ok().as_deref(),
|
||||
Some("1")
|
||||
);
|
||||
if !numeric_on {
|
||||
return Ok(());
|
||||
}
|
||||
@ -74,7 +77,9 @@ fn check_numeric_core_invariants(root: &serde_json::Value) -> Result<(), String>
|
||||
}
|
||||
|
||||
let strict = matches!(
|
||||
std::env::var("NYASH_AOT_NUMERIC_CORE_STRICT").ok().as_deref(),
|
||||
std::env::var("NYASH_AOT_NUMERIC_CORE_STRICT")
|
||||
.ok()
|
||||
.as_deref(),
|
||||
Some("1")
|
||||
);
|
||||
|
||||
@ -118,7 +123,13 @@ fn emit_unified_mir_call(
|
||||
"name": name
|
||||
});
|
||||
}
|
||||
Callee::Method { box_name, method, receiver, certainty, .. } => {
|
||||
Callee::Method {
|
||||
box_name,
|
||||
method,
|
||||
receiver,
|
||||
certainty,
|
||||
..
|
||||
} => {
|
||||
call_obj["mir_call"]["callee"] = json!({
|
||||
"type": "Method",
|
||||
"box_name": box_name,
|
||||
@ -133,8 +144,13 @@ fn emit_unified_mir_call(
|
||||
"box_type": box_type
|
||||
});
|
||||
}
|
||||
Callee::Closure { params, captures, me_capture } => {
|
||||
let captures_json: Vec<_> = captures.iter()
|
||||
Callee::Closure {
|
||||
params,
|
||||
captures,
|
||||
me_capture,
|
||||
} => {
|
||||
let captures_json: Vec<_> = captures
|
||||
.iter()
|
||||
.map(|(name, vid)| json!([name, vid.as_u32()]))
|
||||
.collect();
|
||||
call_obj["mir_call"]["callee"] = json!({
|
||||
@ -175,10 +191,11 @@ pub fn emit_mir_json_for_harness(
|
||||
if let Some(bb) = f.blocks.get(&bid) {
|
||||
let mut insts = Vec::new();
|
||||
// Pre-scan: collect values defined anywhere in this block (to delay use-before-def copies)
|
||||
let mut block_defines: std::collections::HashSet<u32> = std::collections::HashSet::new();
|
||||
let mut block_defines: std::collections::HashSet<u32> =
|
||||
std::collections::HashSet::new();
|
||||
for inst in &bb.instructions {
|
||||
match inst {
|
||||
| I::UnaryOp { dst, .. }
|
||||
I::UnaryOp { dst, .. }
|
||||
| I::Const { dst, .. }
|
||||
| I::BinOp { dst, .. }
|
||||
| I::Compare { dst, .. }
|
||||
@ -193,7 +210,8 @@ pub fn emit_mir_json_for_harness(
|
||||
}
|
||||
}
|
||||
// Track which values have been emitted (to order copies after their sources)
|
||||
let mut emitted_defs: std::collections::HashSet<u32> = std::collections::HashSet::new();
|
||||
let mut emitted_defs: std::collections::HashSet<u32> =
|
||||
std::collections::HashSet::new();
|
||||
// PHI first(オプション)
|
||||
for inst in &bb.instructions {
|
||||
if let I::Copy { dst, src } = inst {
|
||||
@ -202,7 +220,9 @@ pub fn emit_mir_json_for_harness(
|
||||
if block_defines.contains(&s) && !emitted_defs.contains(&s) {
|
||||
// delayed; will be emitted after non-PHI pass
|
||||
} else {
|
||||
insts.push(json!({"op":"copy","dst": dst.as_u32(), "src": src.as_u32()}));
|
||||
insts.push(
|
||||
json!({"op":"copy","dst": dst.as_u32(), "src": src.as_u32()}),
|
||||
);
|
||||
emitted_defs.insert(dst.as_u32());
|
||||
}
|
||||
continue;
|
||||
@ -371,17 +391,27 @@ pub fn emit_mir_json_for_harness(
|
||||
insts.push(obj);
|
||||
}
|
||||
I::Call {
|
||||
dst, func, callee, args, effects, ..
|
||||
dst,
|
||||
func,
|
||||
callee,
|
||||
args,
|
||||
effects,
|
||||
..
|
||||
} => {
|
||||
// Phase 15.5: Unified Call support with environment variable control
|
||||
let use_unified = match std::env::var("NYASH_MIR_UNIFIED_CALL").ok().as_deref().map(|s| s.to_ascii_lowercase()) {
|
||||
let use_unified = match std::env::var("NYASH_MIR_UNIFIED_CALL")
|
||||
.ok()
|
||||
.as_deref()
|
||||
.map(|s| s.to_ascii_lowercase())
|
||||
{
|
||||
Some(s) if s == "0" || s == "false" || s == "off" => false,
|
||||
_ => true,
|
||||
};
|
||||
|
||||
if use_unified && callee.is_some() {
|
||||
// v1: Unified mir_call format
|
||||
let effects_str: Vec<&str> = if effects.is_io() { vec!["IO"] } else { vec![] };
|
||||
let effects_str: Vec<&str> =
|
||||
if effects.is_io() { vec!["IO"] } else { vec![] };
|
||||
let args_u32: Vec<u32> = args.iter().map(|v| v.as_u32()).collect();
|
||||
let unified_call = emit_unified_mir_call(
|
||||
dst.map(|v| v.as_u32()),
|
||||
@ -394,10 +424,13 @@ pub fn emit_mir_json_for_harness(
|
||||
// v0: When unified is OFF but callee exists, emit proper v0 format
|
||||
use nyash_rust::mir::definitions::Callee;
|
||||
match callee.as_ref().unwrap() {
|
||||
Callee::Method { method, receiver, .. } => {
|
||||
Callee::Method {
|
||||
method, receiver, ..
|
||||
} => {
|
||||
// Emit as boxcall for compatibility
|
||||
let box_val = receiver.unwrap_or(*func);
|
||||
let args_a: Vec<_> = args.iter().map(|v| json!(v.as_u32())).collect();
|
||||
let args_a: Vec<_> =
|
||||
args.iter().map(|v| json!(v.as_u32())).collect();
|
||||
let mut obj = json!({
|
||||
"op":"boxcall",
|
||||
"box": box_val.as_u32(),
|
||||
@ -423,17 +456,21 @@ pub fn emit_mir_json_for_harness(
|
||||
obj["dst_type"] = t;
|
||||
}
|
||||
insts.push(obj);
|
||||
if let Some(d) = dst.map(|v| v.as_u32()) { emitted_defs.insert(d); }
|
||||
if let Some(d) = dst.map(|v| v.as_u32()) {
|
||||
emitted_defs.insert(d);
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
// Other callee types: emit generic call
|
||||
let args_a: Vec<_> = args.iter().map(|v| json!(v.as_u32())).collect();
|
||||
let args_a: Vec<_> =
|
||||
args.iter().map(|v| json!(v.as_u32())).collect();
|
||||
insts.push(json!({"op":"call","func": func.as_u32(), "args": args_a, "dst": dst.map(|d| d.as_u32())}));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// v0: Legacy call format (no callee info)
|
||||
let args_a: Vec<_> = args.iter().map(|v| json!(v.as_u32())).collect();
|
||||
let args_a: Vec<_> =
|
||||
args.iter().map(|v| json!(v.as_u32())).collect();
|
||||
insts.push(json!({"op":"call","func": func.as_u32(), "args": args_a, "dst": dst.map(|d| d.as_u32())}));
|
||||
}
|
||||
}
|
||||
@ -494,7 +531,9 @@ pub fn emit_mir_json_for_harness(
|
||||
obj["dst_type"] = t;
|
||||
}
|
||||
insts.push(obj);
|
||||
if let Some(d) = dst.map(|v| v.as_u32()) { emitted_defs.insert(d); }
|
||||
if let Some(d) = dst.map(|v| v.as_u32()) {
|
||||
emitted_defs.insert(d);
|
||||
}
|
||||
}
|
||||
I::NewBox {
|
||||
dst,
|
||||
@ -543,15 +582,19 @@ pub fn emit_mir_json_for_harness(
|
||||
|
||||
// Phase 15.5: JSON v1 schema with environment variable control
|
||||
let use_v1_schema = std::env::var("NYASH_JSON_SCHEMA_V1").unwrap_or_default() == "1"
|
||||
|| match std::env::var("NYASH_MIR_UNIFIED_CALL").ok().as_deref().map(|s| s.to_ascii_lowercase()) {
|
||||
Some(s) if s == "0" || s == "false" || s == "off" => false,
|
||||
_ => true,
|
||||
};
|
||||
|| match std::env::var("NYASH_MIR_UNIFIED_CALL")
|
||||
.ok()
|
||||
.as_deref()
|
||||
.map(|s| s.to_ascii_lowercase())
|
||||
{
|
||||
Some(s) if s == "0" || s == "false" || s == "off" => false,
|
||||
_ => true,
|
||||
};
|
||||
|
||||
let root = if use_v1_schema {
|
||||
create_json_v1_root(json!(funs))
|
||||
} else {
|
||||
json!({"functions": funs}) // v0 legacy format
|
||||
json!({"functions": funs}) // v0 legacy format
|
||||
};
|
||||
|
||||
// NOTE: numeric_core strict validation is applied on the AotPrep output
|
||||
@ -578,7 +621,8 @@ pub fn emit_mir_json_for_harness_bin(
|
||||
if let Some(bb) = f.blocks.get(&bid) {
|
||||
let mut insts = Vec::new();
|
||||
// Pre-scan to collect values defined in this block
|
||||
let mut block_defines: std::collections::HashSet<u32> = std::collections::HashSet::new();
|
||||
let mut block_defines: std::collections::HashSet<u32> =
|
||||
std::collections::HashSet::new();
|
||||
for inst in &bb.instructions {
|
||||
match inst {
|
||||
I::Copy { dst, .. }
|
||||
@ -589,11 +633,14 @@ pub fn emit_mir_json_for_harness_bin(
|
||||
| I::ExternCall { dst: Some(dst), .. }
|
||||
| I::BoxCall { dst: Some(dst), .. }
|
||||
| I::NewBox { dst, .. }
|
||||
| I::Phi { dst, .. } => { block_defines.insert(dst.as_u32()); }
|
||||
| I::Phi { dst, .. } => {
|
||||
block_defines.insert(dst.as_u32());
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
let mut emitted_defs: std::collections::HashSet<u32> = std::collections::HashSet::new();
|
||||
let mut emitted_defs: std::collections::HashSet<u32> =
|
||||
std::collections::HashSet::new();
|
||||
for inst in &bb.instructions {
|
||||
if let I::Phi { dst, inputs } = inst {
|
||||
let incoming: Vec<_> = inputs
|
||||
@ -625,7 +672,8 @@ pub fn emit_mir_json_for_harness_bin(
|
||||
for inst in &bb.instructions {
|
||||
match inst {
|
||||
I::Copy { dst, src } => {
|
||||
let d = dst.as_u32(); let s = src.as_u32();
|
||||
let d = dst.as_u32();
|
||||
let s = src.as_u32();
|
||||
if block_defines.contains(&s) && !emitted_defs.contains(&s) {
|
||||
delayed_copies.push((d, s));
|
||||
} else {
|
||||
@ -708,17 +756,27 @@ pub fn emit_mir_json_for_harness_bin(
|
||||
emitted_defs.insert(dst.as_u32());
|
||||
}
|
||||
I::Call {
|
||||
dst, func, callee, args, effects, ..
|
||||
dst,
|
||||
func,
|
||||
callee,
|
||||
args,
|
||||
effects,
|
||||
..
|
||||
} => {
|
||||
// Phase 15.5: Unified Call support with environment variable control
|
||||
let use_unified = match std::env::var("NYASH_MIR_UNIFIED_CALL").ok().as_deref().map(|s| s.to_ascii_lowercase()) {
|
||||
let use_unified = match std::env::var("NYASH_MIR_UNIFIED_CALL")
|
||||
.ok()
|
||||
.as_deref()
|
||||
.map(|s| s.to_ascii_lowercase())
|
||||
{
|
||||
Some(s) if s == "0" || s == "false" || s == "off" => false,
|
||||
_ => true,
|
||||
};
|
||||
|
||||
if use_unified && callee.is_some() {
|
||||
// v1: Unified mir_call format
|
||||
let effects_str: Vec<&str> = if effects.is_io() { vec!["IO"] } else { vec![] };
|
||||
let effects_str: Vec<&str> =
|
||||
if effects.is_io() { vec!["IO"] } else { vec![] };
|
||||
let args_u32: Vec<u32> = args.iter().map(|v| v.as_u32()).collect();
|
||||
let unified_call = emit_unified_mir_call(
|
||||
dst.map(|v| v.as_u32()),
|
||||
@ -731,10 +789,13 @@ pub fn emit_mir_json_for_harness_bin(
|
||||
// v0: When unified is OFF but callee exists, emit proper v0 format
|
||||
use Callee;
|
||||
match callee.as_ref().unwrap() {
|
||||
Callee::Method { method, receiver, .. } => {
|
||||
Callee::Method {
|
||||
method, receiver, ..
|
||||
} => {
|
||||
// Emit as boxcall for compatibility
|
||||
let box_val = receiver.unwrap_or(*func);
|
||||
let args_a: Vec<_> = args.iter().map(|v| json!(v.as_u32())).collect();
|
||||
let args_a: Vec<_> =
|
||||
args.iter().map(|v| json!(v.as_u32())).collect();
|
||||
let mut obj = json!({
|
||||
"op":"boxcall",
|
||||
"box": box_val.as_u32(),
|
||||
@ -760,17 +821,21 @@ pub fn emit_mir_json_for_harness_bin(
|
||||
obj["dst_type"] = t;
|
||||
}
|
||||
insts.push(obj);
|
||||
if let Some(d) = dst.map(|v| v.as_u32()) { emitted_defs.insert(d); }
|
||||
if let Some(d) = dst.map(|v| v.as_u32()) {
|
||||
emitted_defs.insert(d);
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
// Other callee types: emit generic call
|
||||
let args_a: Vec<_> = args.iter().map(|v| json!(v.as_u32())).collect();
|
||||
let args_a: Vec<_> =
|
||||
args.iter().map(|v| json!(v.as_u32())).collect();
|
||||
insts.push(json!({"op":"call","func": func.as_u32(), "args": args_a, "dst": dst.map(|d| d.as_u32())}));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// v0: Legacy call format (no callee info)
|
||||
let args_a: Vec<_> = args.iter().map(|v| json!(v.as_u32())).collect();
|
||||
let args_a: Vec<_> =
|
||||
args.iter().map(|v| json!(v.as_u32())).collect();
|
||||
insts.push(json!({"op":"call","func": func.as_u32(), "args": args_a, "dst": dst.map(|d| d.as_u32())}));
|
||||
}
|
||||
}
|
||||
@ -792,7 +857,9 @@ pub fn emit_mir_json_for_harness_bin(
|
||||
}
|
||||
}
|
||||
insts.push(obj);
|
||||
if let Some(d) = dst.map(|v| v.as_u32()) { emitted_defs.insert(d); }
|
||||
if let Some(d) = dst.map(|v| v.as_u32()) {
|
||||
emitted_defs.insert(d);
|
||||
}
|
||||
}
|
||||
I::BoxCall {
|
||||
dst,
|
||||
@ -822,7 +889,9 @@ pub fn emit_mir_json_for_harness_bin(
|
||||
obj["dst_type"] = t;
|
||||
}
|
||||
insts.push(obj);
|
||||
if let Some(d) = dst.map(|v| v.as_u32()) { emitted_defs.insert(d); }
|
||||
if let Some(d) = dst.map(|v| v.as_u32()) {
|
||||
emitted_defs.insert(d);
|
||||
}
|
||||
}
|
||||
I::NewBox {
|
||||
dst,
|
||||
@ -850,7 +919,9 @@ pub fn emit_mir_json_for_harness_bin(
|
||||
}
|
||||
}
|
||||
// Append delayed copies after their sources
|
||||
for (d, s) in delayed_copies { insts.push(json!({"op":"copy","dst": d, "src": s})); }
|
||||
for (d, s) in delayed_copies {
|
||||
insts.push(json!({"op":"copy","dst": d, "src": s}));
|
||||
}
|
||||
if let Some(term) = &bb.terminator {
|
||||
match term {
|
||||
I::Return { value } => insts.push(json!({"op":"ret","value": value.map(|v| v.as_u32())})),
|
||||
|
||||
@ -1,9 +1,9 @@
|
||||
use super::mir_json::common as mirjson_common;
|
||||
use crate::mir::{
|
||||
function::{FunctionSignature, MirFunction, MirModule},
|
||||
BasicBlock, BasicBlockId, ConstValue, EffectMask, MirInstruction, MirType, ValueId,
|
||||
};
|
||||
use serde_json::Value;
|
||||
use super::mir_json::common as mirjson_common;
|
||||
|
||||
/// Parse minimal MIR JSON v0 (no schema_version, root has `functions` and each function has `blocks`).
|
||||
/// Supported ops (minimal): const(i64), copy, compare(op/lhs/rhs), branch(cond/then/else), jump(target), phi(dst,incoming), ret(value?).
|
||||
@ -36,7 +36,8 @@ pub fn parse_mir_v0_to_module(json: &str) -> Result<MirModule, String> {
|
||||
.get(0)
|
||||
.and_then(|b| b.get("id"))
|
||||
.and_then(|id| id.as_u64())
|
||||
.ok_or_else(|| format!("function '{}' entry block missing id", func_name))? as u32;
|
||||
.ok_or_else(|| format!("function '{}' entry block missing id", func_name))?
|
||||
as u32;
|
||||
let entry_bb = BasicBlockId::new(entry_id);
|
||||
|
||||
let mut signature = FunctionSignature {
|
||||
@ -52,7 +53,8 @@ pub fn parse_mir_v0_to_module(json: &str) -> Result<MirModule, String> {
|
||||
let block_id = block
|
||||
.get("id")
|
||||
.and_then(|id| id.as_u64())
|
||||
.ok_or_else(|| format!("function '{}' block missing id", func_name))? as u32;
|
||||
.ok_or_else(|| format!("function '{}' block missing id", func_name))?
|
||||
as u32;
|
||||
let bb_id = BasicBlockId::new(block_id);
|
||||
if mir_fn.get_block(bb_id).is_none() {
|
||||
mir_fn.add_block(BasicBlock::new(bb_id));
|
||||
@ -64,95 +66,185 @@ pub fn parse_mir_v0_to_module(json: &str) -> Result<MirModule, String> {
|
||||
let instructions = block
|
||||
.get("instructions")
|
||||
.and_then(|insts| insts.as_array())
|
||||
.ok_or_else(|| format!("function '{}' block {} missing instructions array", func_name, block_id))?;
|
||||
.ok_or_else(|| {
|
||||
format!(
|
||||
"function '{}' block {} missing instructions array",
|
||||
func_name, block_id
|
||||
)
|
||||
})?;
|
||||
|
||||
for inst in instructions {
|
||||
let op = inst
|
||||
.get("op")
|
||||
.and_then(|o| o.as_str())
|
||||
.ok_or_else(|| format!("function '{}' block {} missing op field", func_name, block_id))?;
|
||||
let op = inst.get("op").and_then(|o| o.as_str()).ok_or_else(|| {
|
||||
format!(
|
||||
"function '{}' block {} missing op field",
|
||||
func_name, block_id
|
||||
)
|
||||
})?;
|
||||
match op {
|
||||
"const" => {
|
||||
let dst = require_u64(inst, "dst", "const dst")? as u32;
|
||||
let vobj = inst.get("value").ok_or_else(|| "const missing value".to_string())?;
|
||||
let vobj = inst
|
||||
.get("value")
|
||||
.ok_or_else(|| "const missing value".to_string())?;
|
||||
let val = parse_const_value(vobj)?;
|
||||
block_ref.add_instruction(MirInstruction::Const { dst: ValueId::new(dst), value: val });
|
||||
block_ref.add_instruction(MirInstruction::Const {
|
||||
dst: ValueId::new(dst),
|
||||
value: val,
|
||||
});
|
||||
max_value_id = max_value_id.max(dst + 1);
|
||||
}
|
||||
"copy" => {
|
||||
let dst = require_u64(inst, "dst", "copy dst")? as u32;
|
||||
let src = require_u64(inst, "src", "copy src")? as u32;
|
||||
block_ref.add_instruction(MirInstruction::Copy { dst: ValueId::new(dst), src: ValueId::new(src) });
|
||||
block_ref.add_instruction(MirInstruction::Copy {
|
||||
dst: ValueId::new(dst),
|
||||
src: ValueId::new(src),
|
||||
});
|
||||
max_value_id = max_value_id.max(dst + 1);
|
||||
}
|
||||
"binop" => {
|
||||
let dst = require_u64(inst, "dst", "binop dst")? as u32;
|
||||
let lhs = require_u64(inst, "lhs", "binop lhs")? as u32;
|
||||
let rhs = require_u64(inst, "rhs", "binop rhs")? as u32;
|
||||
let operation = inst.get("operation").and_then(Value::as_str).ok_or_else(|| "binop missing operation".to_string())?;
|
||||
let operation = inst
|
||||
.get("operation")
|
||||
.and_then(Value::as_str)
|
||||
.ok_or_else(|| "binop missing operation".to_string())?;
|
||||
let bop = parse_binop(operation)?;
|
||||
block_ref.add_instruction(MirInstruction::BinOp { dst: ValueId::new(dst), op: bop, lhs: ValueId::new(lhs), rhs: ValueId::new(rhs) });
|
||||
block_ref.add_instruction(MirInstruction::BinOp {
|
||||
dst: ValueId::new(dst),
|
||||
op: bop,
|
||||
lhs: ValueId::new(lhs),
|
||||
rhs: ValueId::new(rhs),
|
||||
});
|
||||
max_value_id = max_value_id.max(dst + 1);
|
||||
}
|
||||
"compare" => {
|
||||
let dst = require_u64(inst, "dst", "compare dst")? as u32;
|
||||
let lhs = require_u64(inst, "lhs", "compare lhs")? as u32;
|
||||
let rhs = require_u64(inst, "rhs", "compare rhs")? as u32;
|
||||
let operation = inst.get("operation").and_then(Value::as_str).unwrap_or("==");
|
||||
let operation = inst
|
||||
.get("operation")
|
||||
.and_then(Value::as_str)
|
||||
.unwrap_or("==");
|
||||
let cop = parse_compare(operation)?;
|
||||
block_ref.add_instruction(MirInstruction::Compare { dst: ValueId::new(dst), op: cop, lhs: ValueId::new(lhs), rhs: ValueId::new(rhs) });
|
||||
block_ref.add_instruction(MirInstruction::Compare {
|
||||
dst: ValueId::new(dst),
|
||||
op: cop,
|
||||
lhs: ValueId::new(lhs),
|
||||
rhs: ValueId::new(rhs),
|
||||
});
|
||||
max_value_id = max_value_id.max(dst + 1);
|
||||
}
|
||||
"branch" => {
|
||||
let cond = require_u64(inst, "cond", "branch cond")? as u32;
|
||||
let then_bb = require_u64(inst, "then", "branch then")? as u32;
|
||||
let else_bb = require_u64(inst, "else", "branch else")? as u32;
|
||||
block_ref.add_instruction(MirInstruction::Branch { condition: ValueId::new(cond), then_bb: BasicBlockId::new(then_bb), else_bb: BasicBlockId::new(else_bb) });
|
||||
block_ref.add_instruction(MirInstruction::Branch {
|
||||
condition: ValueId::new(cond),
|
||||
then_bb: BasicBlockId::new(then_bb),
|
||||
else_bb: BasicBlockId::new(else_bb),
|
||||
});
|
||||
}
|
||||
"jump" => {
|
||||
let target = require_u64(inst, "target", "jump target")? as u32;
|
||||
block_ref.add_instruction(MirInstruction::Jump { target: BasicBlockId::new(target) });
|
||||
block_ref.add_instruction(MirInstruction::Jump {
|
||||
target: BasicBlockId::new(target),
|
||||
});
|
||||
}
|
||||
"phi" => {
|
||||
let dst = require_u64(inst, "dst", "phi dst")? as u32;
|
||||
let incoming = inst.get("incoming").and_then(Value::as_array).ok_or_else(|| "phi incoming missing".to_string())?;
|
||||
let incoming = inst
|
||||
.get("incoming")
|
||||
.and_then(Value::as_array)
|
||||
.ok_or_else(|| "phi incoming missing".to_string())?;
|
||||
let mut pairs = Vec::with_capacity(incoming.len());
|
||||
for entry in incoming {
|
||||
let pair = entry.as_array().ok_or_else(|| "phi incoming entry must be array".to_string())?;
|
||||
if pair.len() != 2 { return Err("phi incoming entry must have 2 elements".into()); }
|
||||
let val = pair[0].as_u64().ok_or_else(|| "phi incoming value must be integer".to_string())? as u32;
|
||||
let bb = pair[1].as_u64().ok_or_else(|| "phi incoming block must be integer".to_string())? as u32;
|
||||
let pair = entry
|
||||
.as_array()
|
||||
.ok_or_else(|| "phi incoming entry must be array".to_string())?;
|
||||
if pair.len() != 2 {
|
||||
return Err("phi incoming entry must have 2 elements".into());
|
||||
}
|
||||
let val = pair[0]
|
||||
.as_u64()
|
||||
.ok_or_else(|| "phi incoming value must be integer".to_string())?
|
||||
as u32;
|
||||
let bb = pair[1]
|
||||
.as_u64()
|
||||
.ok_or_else(|| "phi incoming block must be integer".to_string())?
|
||||
as u32;
|
||||
pairs.push((BasicBlockId::new(bb), ValueId::new(val)));
|
||||
}
|
||||
block_ref.add_instruction(MirInstruction::Phi { dst: ValueId::new(dst), inputs: pairs });
|
||||
block_ref.add_instruction(MirInstruction::Phi {
|
||||
dst: ValueId::new(dst),
|
||||
inputs: pairs,
|
||||
});
|
||||
max_value_id = max_value_id.max(dst + 1);
|
||||
}
|
||||
"ret" => {
|
||||
let value = inst.get("value").and_then(|v| v.as_u64()).map(|v| ValueId::new(v as u32));
|
||||
let value = inst
|
||||
.get("value")
|
||||
.and_then(|v| v.as_u64())
|
||||
.map(|v| ValueId::new(v as u32));
|
||||
block_ref.add_instruction(MirInstruction::Return { value });
|
||||
if let Some(val) = value { signature.return_type = MirType::Integer; max_value_id = max_value_id.max(val.as_u32() + 1); } else { signature.return_type = MirType::Void; }
|
||||
if let Some(val) = value {
|
||||
signature.return_type = MirType::Integer;
|
||||
max_value_id = max_value_id.max(val.as_u32() + 1);
|
||||
} else {
|
||||
signature.return_type = MirType::Void;
|
||||
}
|
||||
}
|
||||
"newbox" => {
|
||||
let dst = require_u64(inst, "dst", "newbox dst")? as u32;
|
||||
let ty = inst.get("type").and_then(Value::as_str).ok_or_else(|| "newbox missing type".to_string())?.to_string();
|
||||
let args_v = inst.get("args").and_then(Value::as_array).cloned().unwrap_or_default();
|
||||
let ty = inst
|
||||
.get("type")
|
||||
.and_then(Value::as_str)
|
||||
.ok_or_else(|| "newbox missing type".to_string())?
|
||||
.to_string();
|
||||
let args_v = inst
|
||||
.get("args")
|
||||
.and_then(Value::as_array)
|
||||
.cloned()
|
||||
.unwrap_or_default();
|
||||
let mut args: Vec<ValueId> = Vec::with_capacity(args_v.len());
|
||||
for a in args_v {
|
||||
let id = a.as_u64().ok_or_else(|| "newbox arg must be integer".to_string())? as u32;
|
||||
let id = a
|
||||
.as_u64()
|
||||
.ok_or_else(|| "newbox arg must be integer".to_string())?
|
||||
as u32;
|
||||
args.push(ValueId::new(id));
|
||||
}
|
||||
block_ref.add_instruction(MirInstruction::NewBox { dst: ValueId::new(dst), box_type: ty, args });
|
||||
block_ref.add_instruction(MirInstruction::NewBox {
|
||||
dst: ValueId::new(dst),
|
||||
box_type: ty,
|
||||
args,
|
||||
});
|
||||
max_value_id = max_value_id.max(dst + 1);
|
||||
}
|
||||
"boxcall" => {
|
||||
// { op:"boxcall", box:<vid>, method:"name", args:[vid...], dst?:<vid> }
|
||||
let box_id = require_u64(inst, "box", "boxcall box")? as u32;
|
||||
let method = inst.get("method").and_then(Value::as_str).ok_or_else(|| "boxcall missing method".to_string())?.to_string();
|
||||
let dst_opt = inst.get("dst").and_then(Value::as_u64).map(|v| ValueId::new(v as u32));
|
||||
let args_v = inst.get("args").and_then(Value::as_array).cloned().unwrap_or_default();
|
||||
let method = inst
|
||||
.get("method")
|
||||
.and_then(Value::as_str)
|
||||
.ok_or_else(|| "boxcall missing method".to_string())?
|
||||
.to_string();
|
||||
let dst_opt = inst
|
||||
.get("dst")
|
||||
.and_then(Value::as_u64)
|
||||
.map(|v| ValueId::new(v as u32));
|
||||
let args_v = inst
|
||||
.get("args")
|
||||
.and_then(Value::as_array)
|
||||
.cloned()
|
||||
.unwrap_or_default();
|
||||
let mut args: Vec<ValueId> = Vec::with_capacity(args_v.len());
|
||||
for a in args_v {
|
||||
let id = a.as_u64().ok_or_else(|| "boxcall arg must be integer".to_string())? as u32;
|
||||
let id = a
|
||||
.as_u64()
|
||||
.ok_or_else(|| "boxcall arg must be integer".to_string())?
|
||||
as u32;
|
||||
args.push(ValueId::new(id));
|
||||
}
|
||||
block_ref.add_instruction(MirInstruction::BoxCall {
|
||||
@ -163,7 +255,9 @@ pub fn parse_mir_v0_to_module(json: &str) -> Result<MirModule, String> {
|
||||
args,
|
||||
effects: EffectMask::READ,
|
||||
});
|
||||
if let Some(dv) = dst_opt { max_value_id = max_value_id.max(dv.as_u32() + 1); }
|
||||
if let Some(dv) = dst_opt {
|
||||
max_value_id = max_value_id.max(dv.as_u32() + 1);
|
||||
}
|
||||
}
|
||||
other => {
|
||||
return Err(format!("unsupported op '{}' in mir_json_v0 loader", other));
|
||||
@ -181,7 +275,9 @@ pub fn parse_mir_v0_to_module(json: &str) -> Result<MirModule, String> {
|
||||
}
|
||||
|
||||
fn require_u64(node: &Value, key: &str, context: &str) -> Result<u64, String> {
|
||||
node.get(key).and_then(Value::as_u64).ok_or_else(|| format!("{} missing field '{}'", context, key))
|
||||
node.get(key)
|
||||
.and_then(Value::as_u64)
|
||||
.ok_or_else(|| format!("{} missing field '{}'", context, key))
|
||||
}
|
||||
|
||||
fn parse_const_value(value_obj: &Value) -> Result<ConstValue, String> {
|
||||
|
||||
@ -23,18 +23,21 @@ mod demos;
|
||||
mod dispatch;
|
||||
pub mod json_v0_bridge;
|
||||
mod json_v1_bridge;
|
||||
pub mod mir_json { pub mod common; }
|
||||
mod mir_json_v0;
|
||||
pub mod mir_json {
|
||||
pub mod common;
|
||||
}
|
||||
pub mod core_executor;
|
||||
pub mod hv1_inline;
|
||||
pub mod mir_json_emit;
|
||||
mod mir_json_v0;
|
||||
pub mod modes;
|
||||
mod pipe_io;
|
||||
pub mod core_executor;
|
||||
mod pipeline;
|
||||
mod plugins;
|
||||
mod selfhost;
|
||||
mod stage1_bridge;
|
||||
mod tasks;
|
||||
mod trace;
|
||||
mod plugins;
|
||||
pub mod hv1_inline;
|
||||
|
||||
// v2 plugin system imports
|
||||
use nyash_rust::runner_plugin_init;
|
||||
@ -89,27 +92,43 @@ impl NyashRunner {
|
||||
return;
|
||||
}
|
||||
let groups = self.config.as_groups();
|
||||
if let Some(code) = self.maybe_run_stage1_cli_stub(&groups) {
|
||||
std::process::exit(code);
|
||||
}
|
||||
// Early: direct MIR JSON execution (no source file). Experimental diagnostics/exec.
|
||||
if let Some(path) = groups.parser.mir_json_file.as_ref() {
|
||||
match std::fs::read_to_string(path) {
|
||||
Ok(text) => {
|
||||
match crate::runner::json_v1_bridge::try_parse_v1_to_module(&text) {
|
||||
Ok(Some(module)) => { let rc = self.execute_mir_module_quiet_exit(&module); std::process::exit(rc); }
|
||||
Ok(None) => {
|
||||
if text.contains("\"functions\"") && text.contains("\"blocks\"") {
|
||||
match crate::runner::mir_json_v0::parse_mir_v0_to_module(&text) {
|
||||
Ok(module) => { let rc = self.execute_mir_module_quiet_exit(&module); std::process::exit(rc); }
|
||||
Err(e) => { eprintln!("❌ MIR JSON v0 parse error: {}", e); std::process::exit(1); }
|
||||
}
|
||||
} else {
|
||||
eprintln!("❌ MIR JSON invalid or unsupported shape: {}", path);
|
||||
std::process::exit(1);
|
||||
}
|
||||
}
|
||||
Err(e) => { eprintln!("❌ MIR JSON parse error (v1): {}", e); std::process::exit(1); }
|
||||
Ok(text) => match crate::runner::json_v1_bridge::try_parse_v1_to_module(&text) {
|
||||
Ok(Some(module)) => {
|
||||
let rc = self.execute_mir_module_quiet_exit(&module);
|
||||
std::process::exit(rc);
|
||||
}
|
||||
Ok(None) => {
|
||||
if text.contains("\"functions\"") && text.contains("\"blocks\"") {
|
||||
match crate::runner::mir_json_v0::parse_mir_v0_to_module(&text) {
|
||||
Ok(module) => {
|
||||
let rc = self.execute_mir_module_quiet_exit(&module);
|
||||
std::process::exit(rc);
|
||||
}
|
||||
Err(e) => {
|
||||
eprintln!("❌ MIR JSON v0 parse error: {}", e);
|
||||
std::process::exit(1);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
eprintln!("❌ MIR JSON invalid or unsupported shape: {}", path);
|
||||
std::process::exit(1);
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
eprintln!("❌ MIR JSON parse error (v1): {}", e);
|
||||
std::process::exit(1);
|
||||
}
|
||||
},
|
||||
Err(e) => {
|
||||
eprintln!("❌ Error reading MIR JSON {}: {}", path, e);
|
||||
std::process::exit(1);
|
||||
}
|
||||
Err(e) => { eprintln!("❌ Error reading MIR JSON {}: {}", path, e); std::process::exit(1); }
|
||||
}
|
||||
}
|
||||
// Early: build
|
||||
@ -123,7 +142,9 @@ impl NyashRunner {
|
||||
// Preprocess usings and directives (includes dep-tree log)
|
||||
self.preprocess_usings_and_directives(&groups);
|
||||
// JSON v0 bridge
|
||||
if self.try_run_json_v0_pipe() { return; }
|
||||
if self.try_run_json_v0_pipe() {
|
||||
return;
|
||||
}
|
||||
// Named task
|
||||
if let Some(task) = groups.run_task.clone() {
|
||||
if let Err(e) = run_named_task(&task) {
|
||||
@ -139,7 +160,9 @@ impl NyashRunner {
|
||||
self.configure_backend(&groups);
|
||||
self.enforce_runtime_jit_policy(&groups);
|
||||
// Benchmark
|
||||
if self.maybe_run_benchmark(&groups) { return; }
|
||||
if self.maybe_run_benchmark(&groups) {
|
||||
return;
|
||||
}
|
||||
// Dispatch
|
||||
if std::env::var("NYASH_VM_ROUTE_TRACE").ok().as_deref() == Some("1") {
|
||||
let backend = &groups.backend.backend;
|
||||
@ -159,15 +182,26 @@ impl NyashRunner {
|
||||
let mut pending_using: Vec<(String, Option<String>)> = Vec::new();
|
||||
for spec in &groups.input.cli_usings {
|
||||
let s = spec.trim();
|
||||
if s.is_empty() { continue; }
|
||||
if s.is_empty() {
|
||||
continue;
|
||||
}
|
||||
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) };
|
||||
(
|
||||
s[..pos].trim().to_string(),
|
||||
Some(s[pos + 4..].trim().to_string()),
|
||||
)
|
||||
} else {
|
||||
(s.to_string(), None)
|
||||
};
|
||||
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(|| {
|
||||
std::path::Path::new(&path).file_stem().and_then(|s| s.to_str()).unwrap_or("module").to_string()
|
||||
std::path::Path::new(&path)
|
||||
.file_stem()
|
||||
.and_then(|s| s.to_str())
|
||||
.unwrap_or("module")
|
||||
.to_string()
|
||||
});
|
||||
pending_using.push((name, Some(path)));
|
||||
} else {
|
||||
@ -190,29 +224,51 @@ impl NyashRunner {
|
||||
root_info = format!(" root='{}'", r);
|
||||
}
|
||||
}
|
||||
crate::cli_v!("[deps] loaded {} bytes from{} {}", bytes, if root_info.is_empty() { "" } else { ":" }, root_info);
|
||||
crate::cli_v!(
|
||||
"[deps] loaded {} bytes from{} {}",
|
||||
bytes,
|
||||
if root_info.is_empty() { "" } else { ":" },
|
||||
root_info
|
||||
);
|
||||
}
|
||||
Err(e) => {
|
||||
crate::cli_v!("[deps] read error: {}", e);
|
||||
}
|
||||
Err(e) => { crate::cli_v!("[deps] read error: {}", e); }
|
||||
}
|
||||
}
|
||||
// If a file is provided, apply script-level directives and late using/env merges
|
||||
if let Some(ref filename) = groups.input.file {
|
||||
if let Ok(code) = fs::read_to_string(filename) {
|
||||
// Apply directives and lint
|
||||
let strict_fields = std::env::var("NYASH_FIELDS_TOP_STRICT").ok().as_deref() == Some("1");
|
||||
if let Err(e) = cli_directives::apply_cli_directives_from_source(&code, strict_fields, groups.debug.cli_verbose) {
|
||||
let strict_fields =
|
||||
std::env::var("NYASH_FIELDS_TOP_STRICT").ok().as_deref() == Some("1");
|
||||
if let Err(e) = cli_directives::apply_cli_directives_from_source(
|
||||
&code,
|
||||
strict_fields,
|
||||
groups.debug.cli_verbose,
|
||||
) {
|
||||
eprintln!("❌ Lint/Directive error: {}", e);
|
||||
std::process::exit(1);
|
||||
}
|
||||
// Late env overrides (paths/modules)
|
||||
if let Ok(paths) = std::env::var("NYASH_USING_PATH") {
|
||||
for p in paths.split(':') { let p = p.trim(); if !p.is_empty() { using_ctx.using_paths.push(p.to_string()); } }
|
||||
for p in paths.split(':') {
|
||||
let p = p.trim();
|
||||
if !p.is_empty() {
|
||||
using_ctx.using_paths.push(p.to_string());
|
||||
}
|
||||
}
|
||||
}
|
||||
if let Ok(mods) = std::env::var("NYASH_MODULES") {
|
||||
for ent in mods.split(',') {
|
||||
if let Some((k, v)) = ent.split_once('=') {
|
||||
let k = k.trim(); let v = v.trim();
|
||||
if !k.is_empty() && !v.is_empty() { using_ctx.pending_modules.push((k.to_string(), v.to_string())); }
|
||||
let k = k.trim();
|
||||
let v = v.trim();
|
||||
if !k.is_empty() && !v.is_empty() {
|
||||
using_ctx
|
||||
.pending_modules
|
||||
.push((k.to_string(), v.to_string()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -226,9 +282,22 @@ impl NyashRunner {
|
||||
let verbose = crate::config::env::cli_verbose();
|
||||
let ctx = std::path::Path::new(filename).parent();
|
||||
for (ns, alias) in pending_using.iter() {
|
||||
let value = match resolve_using_target(ns, false, &using_ctx.pending_modules, &using_ctx.using_paths, &using_ctx.aliases, &using_ctx.packages, ctx, strict, verbose) {
|
||||
let value = match resolve_using_target(
|
||||
ns,
|
||||
false,
|
||||
&using_ctx.pending_modules,
|
||||
&using_ctx.using_paths,
|
||||
&using_ctx.aliases,
|
||||
&using_ctx.packages,
|
||||
ctx,
|
||||
strict,
|
||||
verbose,
|
||||
) {
|
||||
Ok(v) => v,
|
||||
Err(e) => { eprintln!("❌ using: {}", e); std::process::exit(1); }
|
||||
Err(e) => {
|
||||
eprintln!("❌ using: {}", e);
|
||||
std::process::exit(1);
|
||||
}
|
||||
};
|
||||
let sb = nyash_rust::box_trait::StringBox::new(value.clone());
|
||||
nyash_rust::runtime::modules_registry::set(ns.clone(), Box::new(sb));
|
||||
@ -244,8 +313,14 @@ impl NyashRunner {
|
||||
/// Apply early environment toggles that affect CLI behavior and VM stats.
|
||||
/// Side effects: sets `NYASH_CLI_VERBOSE`, `NYASH_GC_MODE` when specified by CLI groups.
|
||||
fn apply_common_env(&self, groups: &crate::cli::CliGroups) {
|
||||
if groups.debug.cli_verbose { std::env::set_var("NYASH_CLI_VERBOSE", "1"); }
|
||||
if let Some(ref m) = groups.gc_mode { if !m.trim().is_empty() { std::env::set_var("NYASH_GC_MODE", m); } }
|
||||
if groups.debug.cli_verbose {
|
||||
std::env::set_var("NYASH_CLI_VERBOSE", "1");
|
||||
}
|
||||
if let Some(ref m) = groups.gc_mode {
|
||||
if !m.trim().is_empty() {
|
||||
std::env::set_var("NYASH_GC_MODE", m);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// init_runtime_and_plugins moved to runner/plugins.rs
|
||||
@ -253,8 +328,12 @@ impl NyashRunner {
|
||||
/// Configure backend knobs (VM) from CLI flags and env.
|
||||
/// JIT configuration removed with JIT/Cranelift archival.
|
||||
fn configure_backend(&self, groups: &crate::cli::CliGroups) {
|
||||
if groups.backend.vm_stats { std::env::set_var("NYASH_VM_STATS", "1"); }
|
||||
if groups.backend.vm_stats_json { std::env::set_var("NYASH_VM_STATS_JSON", "1"); }
|
||||
if groups.backend.vm_stats {
|
||||
std::env::set_var("NYASH_VM_STATS", "1");
|
||||
}
|
||||
if groups.backend.vm_stats_json {
|
||||
std::env::set_var("NYASH_VM_STATS_JSON", "1");
|
||||
}
|
||||
// JIT configuration removed - archived to archive/jit-cranelift/
|
||||
}
|
||||
|
||||
@ -272,7 +351,9 @@ impl NyashRunner {
|
||||
println!("Running {} iterations per test...", groups.iterations);
|
||||
println!();
|
||||
// VM-legacy removed - benchmark mode no longer available
|
||||
eprintln!("❌ Benchmark mode removed with vm-legacy. Use regular execution modes instead.");
|
||||
eprintln!(
|
||||
"❌ Benchmark mode removed with vm-legacy. Use regular execution modes instead."
|
||||
);
|
||||
std::process::exit(1);
|
||||
}
|
||||
false
|
||||
@ -281,7 +362,10 @@ impl NyashRunner {
|
||||
/// Final dispatch to selected execution mode (file/JIT-direct or demos).
|
||||
fn dispatch_entry(&self, groups: &crate::cli::CliGroups) {
|
||||
if let Some(ref filename) = groups.input.file {
|
||||
if groups.backend.jit.direct { self.run_file_jit_direct(filename); return; }
|
||||
if groups.backend.jit.direct {
|
||||
self.run_file_jit_direct(filename);
|
||||
return;
|
||||
}
|
||||
// Optional route trace before delegating to backend dispatcher
|
||||
if std::env::var("NYASH_VM_ROUTE_TRACE").ok().as_deref() == Some("1") {
|
||||
eprintln!(
|
||||
@ -290,7 +374,9 @@ impl NyashRunner {
|
||||
);
|
||||
}
|
||||
self.run_file(filename);
|
||||
} else { demos::run_all_demos(); }
|
||||
} else {
|
||||
demos::run_all_demos();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -114,7 +114,11 @@ impl NyashRunner {
|
||||
ast
|
||||
}
|
||||
Err(e) => {
|
||||
eprintln!("❌ Parse error in {}: {}", filename, e);
|
||||
crate::runner::modes::common_util::diag::print_parse_error_with_context(
|
||||
filename,
|
||||
code_ref,
|
||||
&e,
|
||||
);
|
||||
process::exit(1);
|
||||
}
|
||||
};
|
||||
|
||||
@ -122,15 +122,22 @@ fn methodize_calls(root: &mut Value) -> Result<bool, String> {
|
||||
for inst in insts.iter() {
|
||||
if let Some(obj) = inst.as_object() {
|
||||
if obj.get("op").and_then(Value::as_str) == Some("const") {
|
||||
if let (Some(dst), Some(val)) = (obj.get("dst").and_then(Value::as_i64), obj.get("value")) {
|
||||
if let (Some(dst), Some(val)) =
|
||||
(obj.get("dst").and_then(Value::as_i64), obj.get("value"))
|
||||
{
|
||||
let mut s: Option<String> = None;
|
||||
if let Some(st) = val.as_str() { s = Some(st.to_string()); }
|
||||
else if let Some(vobj) = val.as_object() {
|
||||
if let Some(Value::String(st)) = vobj.get("value") { s = Some(st.clone()); }
|
||||
if let Some(st) = val.as_str() {
|
||||
s = Some(st.to_string());
|
||||
} else if let Some(vobj) = val.as_object() {
|
||||
if let Some(Value::String(st)) = vobj.get("value") {
|
||||
s = Some(st.clone());
|
||||
}
|
||||
}
|
||||
if let Some(name) = s {
|
||||
// Accept only names with dot separator
|
||||
if name.contains('.') { reg_name.insert(dst, name); }
|
||||
if name.contains('.') {
|
||||
reg_name.insert(dst, name);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -138,14 +145,28 @@ fn methodize_calls(root: &mut Value) -> Result<bool, String> {
|
||||
}
|
||||
// Second pass: rewrite calls
|
||||
for inst in insts.iter_mut() {
|
||||
let Some(obj) = inst.as_object_mut() else { continue };
|
||||
if obj.get("op").and_then(Value::as_str) != Some("call") { continue; }
|
||||
let Some(func_reg) = obj.get("func").and_then(Value::as_i64) else { continue };
|
||||
let Some(name) = reg_name.get(&func_reg).cloned() else { continue };
|
||||
let Some(obj) = inst.as_object_mut() else {
|
||||
continue;
|
||||
};
|
||||
if obj.get("op").and_then(Value::as_str) != Some("call") {
|
||||
continue;
|
||||
}
|
||||
let Some(func_reg) = obj.get("func").and_then(Value::as_i64) else {
|
||||
continue;
|
||||
};
|
||||
let Some(name) = reg_name.get(&func_reg).cloned() else {
|
||||
continue;
|
||||
};
|
||||
// Split Box.method[/N]
|
||||
let mut parts = name.split('.');
|
||||
let box_name = match parts.next() { Some(x) => x, None => continue };
|
||||
let rest = match parts.next() { Some(x) => x, None => continue };
|
||||
let box_name = match parts.next() {
|
||||
Some(x) => x,
|
||||
None => continue,
|
||||
};
|
||||
let rest = match parts.next() {
|
||||
Some(x) => x,
|
||||
None => continue,
|
||||
};
|
||||
let method = rest.split('/').next().unwrap_or(rest);
|
||||
|
||||
// Build mir_call object
|
||||
@ -163,7 +184,9 @@ fn methodize_calls(root: &mut Value) -> Result<bool, String> {
|
||||
mir_call.insert("effects".to_string(), Value::Array(vec![]));
|
||||
|
||||
obj.insert("op".to_string(), Value::String("mir_call".into()));
|
||||
if let Some(d) = dst { obj.insert("dst".to_string(), d); }
|
||||
if let Some(d) = dst {
|
||||
obj.insert("dst".to_string(), d);
|
||||
}
|
||||
obj.remove("func");
|
||||
obj.insert("mir_call".to_string(), Value::Object(mir_call));
|
||||
changed = true;
|
||||
|
||||
@ -1,5 +1,8 @@
|
||||
//! Common diagnostics helpers (concise, centralized)
|
||||
|
||||
use crate::parser::ParseError;
|
||||
use crate::tokenizer::TokenizeError;
|
||||
|
||||
/// Whether provider logs should be emitted under current policy.
|
||||
/// quiet_pipe usually reflects NYASH_JSON_ONLY; allowing override with HAKO_PROVIDER_TRACE=1.
|
||||
pub fn provider_log_enabled(quiet_pipe: bool) -> bool {
|
||||
@ -15,14 +18,102 @@ pub fn provider_log_info(msg: &str) {
|
||||
pub fn provider_log_select(box_name: &str, ring: &str, source: &str, caps: Option<&str>) {
|
||||
match caps {
|
||||
Some(c) if !c.is_empty() => {
|
||||
eprintln!("[provider/select:{} ring={} src={} caps={}]", box_name, ring, source, c);
|
||||
eprintln!(
|
||||
"[provider/select:{} ring={} src={} caps={}]",
|
||||
box_name, ring, source, c
|
||||
);
|
||||
}
|
||||
_ => {
|
||||
eprintln!("[provider/select:{} ring={} src={}]", box_name, ring, source);
|
||||
eprintln!(
|
||||
"[provider/select:{} ring={} src={}]",
|
||||
box_name, ring, source
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Emit a Fail-Fast tag for provider fallback/selection errors.
|
||||
pub fn failfast_provider(reason: &str) { eprintln!("[failfast/provider/{}]", reason); }
|
||||
pub fn failfast_provider(reason: &str) {
|
||||
eprintln!("[failfast/provider/{}]", reason);
|
||||
}
|
||||
|
||||
/// Print a parse error with enriched context (source excerpt + caret + origin mapping).
|
||||
pub fn print_parse_error_with_context(filename: &str, src: &str, err: &ParseError) {
|
||||
eprintln!("❌ Parse error in {}: {}", filename, err);
|
||||
|
||||
let (line_opt, col_opt) = extract_line_col(err);
|
||||
if let Some(line) = line_opt {
|
||||
print_source_snippet(filename, src, line, col_opt);
|
||||
|
||||
if let Some((of, ol)) =
|
||||
crate::runner::modes::common_util::resolve::map_merged_line_to_origin(line)
|
||||
{
|
||||
if of != filename {
|
||||
eprintln!(
|
||||
"[parse/context] merged origin: {}:{} (from merged line {})",
|
||||
of, ol, line
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn extract_line_col(err: &ParseError) -> (Option<usize>, Option<usize>) {
|
||||
match err {
|
||||
ParseError::UnexpectedToken { line, .. } => (Some(*line), None),
|
||||
ParseError::UnexpectedEOF => (None, None),
|
||||
ParseError::InvalidExpression { line } => (Some(*line), None),
|
||||
ParseError::InvalidStatement { line } => (Some(*line), None),
|
||||
ParseError::UnsupportedIdentifier { line, .. } => (Some(*line), None),
|
||||
ParseError::CircularDependency { .. } => (None, None),
|
||||
ParseError::InfiniteLoop { line, .. } => (Some(*line), None),
|
||||
ParseError::TransparencySystemRemoved { line, .. } => (Some(*line), None),
|
||||
ParseError::UnsupportedNamespace { line, .. } => (Some(*line), None),
|
||||
ParseError::ExpectedIdentifier { line } => (Some(*line), None),
|
||||
ParseError::TokenizeError(te) => match te {
|
||||
TokenizeError::UnexpectedCharacter { line, column, .. } => {
|
||||
(Some(*line), Some(*column))
|
||||
}
|
||||
TokenizeError::UnterminatedString { line }
|
||||
| TokenizeError::InvalidNumber { line }
|
||||
| TokenizeError::UnterminatedComment { line } => (Some(*line), None),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn print_source_snippet(filename: &str, src: &str, line: usize, col: Option<usize>) {
|
||||
if src.is_empty() {
|
||||
return;
|
||||
}
|
||||
let lines: Vec<&str> = src.lines().collect();
|
||||
if line == 0 || line > lines.len() {
|
||||
return;
|
||||
}
|
||||
let start = line.saturating_sub(2).max(1);
|
||||
let end = (line + 1).min(lines.len());
|
||||
|
||||
eprintln!("[parse/context] in {}", filename);
|
||||
for ln in start..=end {
|
||||
let text = lines[ln - 1];
|
||||
let marker = if ln == line { ">" } else { " " };
|
||||
eprintln!("{} {:6} | {}", marker, ln, text);
|
||||
}
|
||||
|
||||
if let Some(col) = col {
|
||||
if line <= lines.len() {
|
||||
let text = lines[line - 1];
|
||||
let mut underline = String::new();
|
||||
let mut idx = 0usize;
|
||||
for (i, ch) in text.chars().enumerate() {
|
||||
if i + 1 >= col {
|
||||
break;
|
||||
}
|
||||
// Preserve tabs visually; spaces elsewhere
|
||||
underline.push(if ch == '\t' { '\t' } else { ' ' });
|
||||
idx = i;
|
||||
}
|
||||
let pad = " "; // align under " LNNNNN |"
|
||||
eprintln!(" {}{}^", pad, underline);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -52,11 +52,7 @@ pub fn llvmlite_emit_object(
|
||||
// Verify output
|
||||
match std::fs::metadata(out_path) {
|
||||
Ok(meta) if meta.len() > 0 => {
|
||||
crate::cli_v!(
|
||||
"[LLVM] object emitted: {} ({} bytes)",
|
||||
out_path,
|
||||
meta.len()
|
||||
);
|
||||
crate::cli_v!("[LLVM] object emitted: {} ({} bytes)", out_path, meta.len());
|
||||
Ok(())
|
||||
}
|
||||
_ => Err(format!("harness output not found or empty: {}", out_path)),
|
||||
@ -67,7 +63,13 @@ pub fn llvmlite_emit_object(
|
||||
fn resolve_ny_llvmc() -> std::path::PathBuf {
|
||||
std::env::var("NYASH_NY_LLVM_COMPILER")
|
||||
.ok()
|
||||
.and_then(|s| if !s.is_empty() { Some(std::path::PathBuf::from(s)) } else { None })
|
||||
.and_then(|s| {
|
||||
if !s.is_empty() {
|
||||
Some(std::path::PathBuf::from(s))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.or_else(|| which::which("ny-llvmc").ok())
|
||||
.unwrap_or_else(|| std::path::PathBuf::from("target/release/ny-llvmc"))
|
||||
}
|
||||
@ -103,9 +105,24 @@ pub fn ny_llvmc_emit_exe_lib(
|
||||
.arg("exe")
|
||||
.arg("--out")
|
||||
.arg(exe_out);
|
||||
let default_nyrt = std::env::var("NYASH_EMIT_EXE_NYRT") .ok() .or_else(|| std::env::var("NYASH_ROOT").ok().map(|r| format!("{}/target/release", r))) .unwrap_or_else(|| "target/release".to_string());
|
||||
if let Some(dir) = nyrt_dir { cmd.arg("--nyrt").arg(dir); } else { cmd.arg("--nyrt").arg(default_nyrt); }
|
||||
if let Some(flags) = extra_libs { if !flags.trim().is_empty() { cmd.arg("--libs").arg(flags); } }
|
||||
let default_nyrt = std::env::var("NYASH_EMIT_EXE_NYRT")
|
||||
.ok()
|
||||
.or_else(|| {
|
||||
std::env::var("NYASH_ROOT")
|
||||
.ok()
|
||||
.map(|r| format!("{}/target/release", r))
|
||||
})
|
||||
.unwrap_or_else(|| "target/release".to_string());
|
||||
if let Some(dir) = nyrt_dir {
|
||||
cmd.arg("--nyrt").arg(dir);
|
||||
} else {
|
||||
cmd.arg("--nyrt").arg(default_nyrt);
|
||||
}
|
||||
if let Some(flags) = extra_libs {
|
||||
if !flags.trim().is_empty() {
|
||||
cmd.arg("--libs").arg(flags);
|
||||
}
|
||||
}
|
||||
let status = cmd.status().map_err(|e| {
|
||||
let prog_path = std::path::Path::new(cmd.get_program());
|
||||
format!(
|
||||
@ -147,9 +164,24 @@ pub fn ny_llvmc_emit_exe_bin(
|
||||
.arg("exe")
|
||||
.arg("--out")
|
||||
.arg(exe_out);
|
||||
let default_nyrt = std::env::var("NYASH_EMIT_EXE_NYRT") .ok() .or_else(|| std::env::var("NYASH_ROOT").ok().map(|r| format!("{}/target/release", r))) .unwrap_or_else(|| "target/release".to_string());
|
||||
if let Some(dir) = nyrt_dir { cmd.arg("--nyrt").arg(dir); } else { cmd.arg("--nyrt").arg(default_nyrt); }
|
||||
if let Some(flags) = extra_libs { if !flags.trim().is_empty() { cmd.arg("--libs").arg(flags); } }
|
||||
let default_nyrt = std::env::var("NYASH_EMIT_EXE_NYRT")
|
||||
.ok()
|
||||
.or_else(|| {
|
||||
std::env::var("NYASH_ROOT")
|
||||
.ok()
|
||||
.map(|r| format!("{}/target/release", r))
|
||||
})
|
||||
.unwrap_or_else(|| "target/release".to_string());
|
||||
if let Some(dir) = nyrt_dir {
|
||||
cmd.arg("--nyrt").arg(dir);
|
||||
} else {
|
||||
cmd.arg("--nyrt").arg(default_nyrt);
|
||||
}
|
||||
if let Some(flags) = extra_libs {
|
||||
if !flags.trim().is_empty() {
|
||||
cmd.arg("--libs").arg(flags);
|
||||
}
|
||||
}
|
||||
let status = cmd.status().map_err(|e| {
|
||||
let prog_path = std::path::Path::new(cmd.get_program());
|
||||
format!(
|
||||
@ -176,9 +208,11 @@ pub fn run_executable(
|
||||
timeout_ms: u64,
|
||||
) -> Result<(i32, bool, String), String> {
|
||||
let mut cmd = std::process::Command::new(exe_path);
|
||||
for a in args { cmd.arg(a); }
|
||||
let out = super::io::spawn_with_timeout(cmd, timeout_ms)
|
||||
.map_err(|e| format!("spawn exe: {}", e))?;
|
||||
for a in args {
|
||||
cmd.arg(a);
|
||||
}
|
||||
let out =
|
||||
super::io::spawn_with_timeout(cmd, timeout_ms).map_err(|e| format!("spawn exe: {}", e))?;
|
||||
let code = out.exit_code.unwrap_or(1);
|
||||
let stdout_text = String::from_utf8_lossy(&out.stdout).into_owned();
|
||||
Ok((code, out.timed_out, stdout_text))
|
||||
|
||||
@ -22,13 +22,15 @@ pub fn strip_local_decl(s: &str) -> String {
|
||||
for line in s.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;
|
||||
}
|
||||
let mut stripped = false;
|
||||
// Only strip `local ` if it's at the very beginning (i == 0)
|
||||
// Keep `local ` inside blocks (i > 0) to preserve variable declarations
|
||||
if i == 0 && i + 6 <= bytes.len() && &bytes[i..i+6] == b"local " {
|
||||
if i == 0 && i + 6 <= bytes.len() && &bytes[i..i + 6] == b"local " {
|
||||
out.push_str(&line[..i]);
|
||||
out.push_str(&line[i+6..]);
|
||||
out.push_str(&line[i + 6..]);
|
||||
out.push('\n');
|
||||
stripped = true;
|
||||
}
|
||||
@ -45,7 +47,10 @@ pub fn strip_local_decl(s: &str) -> String {
|
||||
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() {
|
||||
match std::env::var("HAKO_FAIL_FAST_ON_HAKO_IN_NYASH_VM")
|
||||
.ok()
|
||||
.as_deref()
|
||||
{
|
||||
Some("1") | Some("true") | Some("on") => true,
|
||||
_ => false,
|
||||
}
|
||||
|
||||
@ -24,7 +24,10 @@ pub fn spawn_with_timeout(mut cmd: Command, timeout_ms: u64) -> std::io::Result<
|
||||
let mut exit_status: Option<std::process::ExitStatus> = None;
|
||||
loop {
|
||||
match child.try_wait()? {
|
||||
Some(status) => { exit_status = Some(status); break },
|
||||
Some(status) => {
|
||||
exit_status = Some(status);
|
||||
break;
|
||||
}
|
||||
None => {
|
||||
if start.elapsed() >= Duration::from_millis(timeout_ms) {
|
||||
let _ = child.kill();
|
||||
@ -46,7 +49,9 @@ pub fn spawn_with_timeout(mut cmd: Command, timeout_ms: u64) -> std::io::Result<
|
||||
}
|
||||
let (status_ok, exit_code) = if let Some(st) = exit_status {
|
||||
(st.success(), st.code())
|
||||
} else { (false, None) };
|
||||
} else {
|
||||
(false, None)
|
||||
};
|
||||
Ok(ChildOutput {
|
||||
stdout: out_buf,
|
||||
stderr: err_buf,
|
||||
|
||||
@ -4,14 +4,14 @@
|
||||
* Minimal extraction to reduce duplication and prepare for full split.
|
||||
*/
|
||||
|
||||
pub mod pyvm;
|
||||
pub mod selfhost_exe;
|
||||
pub mod io;
|
||||
pub mod selfhost;
|
||||
pub mod resolve;
|
||||
pub mod exec;
|
||||
pub mod core_bridge;
|
||||
pub mod diag;
|
||||
pub mod exec;
|
||||
pub mod hako;
|
||||
pub mod io;
|
||||
pub mod plugin_guard;
|
||||
pub mod provider_registry;
|
||||
pub mod diag;
|
||||
pub mod pyvm;
|
||||
pub mod resolve;
|
||||
pub mod selfhost;
|
||||
pub mod selfhost_exe;
|
||||
|
||||
@ -31,8 +31,7 @@ static PROVIDER_FACTORIES: OnceLock<Mutex<HashMap<String, Vec<Arc<dyn ProviderFa
|
||||
|
||||
/// Register a provider factory (called by builtin/dynamic loaders)
|
||||
pub fn register_provider_factory(factory: Arc<dyn ProviderFactory>) {
|
||||
let registry = PROVIDER_FACTORIES
|
||||
.get_or_init(|| Mutex::new(HashMap::new()));
|
||||
let registry = PROVIDER_FACTORIES.get_or_init(|| Mutex::new(HashMap::new()));
|
||||
let mut guard = registry.lock().unwrap();
|
||||
let key = factory.box_name().to_string();
|
||||
guard.entry(key).or_default().push(factory);
|
||||
@ -42,16 +41,23 @@ pub fn register_provider_factory(factory: Arc<dyn ProviderFactory>) {
|
||||
struct CoreRoFileProviderFactory;
|
||||
|
||||
impl ProviderFactory for CoreRoFileProviderFactory {
|
||||
fn box_name(&self) -> &str { "FileBox" }
|
||||
fn create_provider(&self) -> Arc<dyn FileIo> { Arc::new(CoreRoFileIo::new()) }
|
||||
fn is_available(&self) -> bool { true }
|
||||
fn priority(&self) -> i32 { -100 } // ring‑1: lower than any plugin/provider
|
||||
fn box_name(&self) -> &str {
|
||||
"FileBox"
|
||||
}
|
||||
fn create_provider(&self) -> Arc<dyn FileIo> {
|
||||
Arc::new(CoreRoFileIo::new())
|
||||
}
|
||||
fn is_available(&self) -> bool {
|
||||
true
|
||||
}
|
||||
fn priority(&self) -> i32 {
|
||||
-100
|
||||
} // ring‑1: lower than any plugin/provider
|
||||
}
|
||||
|
||||
/// Ensure ring‑1 (core‑ro) provider is present in the registry
|
||||
fn ensure_builtin_file_provider_registered() {
|
||||
let reg = PROVIDER_FACTORIES
|
||||
.get_or_init(|| Mutex::new(HashMap::new()));
|
||||
let reg = PROVIDER_FACTORIES.get_or_init(|| Mutex::new(HashMap::new()));
|
||||
let mut guard = reg.lock().unwrap();
|
||||
let list = guard.entry("FileBox".to_string()).or_default();
|
||||
// keep ring‑1 present for safety; avoid duplicates by checking any core‑ro present by priority
|
||||
@ -63,7 +69,9 @@ fn ensure_builtin_file_provider_registered() {
|
||||
|
||||
/// Backward-compat public readers for existing callers (if any)
|
||||
#[allow(dead_code)]
|
||||
pub fn read_filebox_mode_from_env() -> FileBoxMode { provider_env::filebox_mode_from_env() }
|
||||
pub fn read_filebox_mode_from_env() -> FileBoxMode {
|
||||
provider_env::filebox_mode_from_env()
|
||||
}
|
||||
|
||||
/// Select provider based on mode and registered factories (SSOT)
|
||||
#[allow(dead_code)]
|
||||
@ -93,7 +101,10 @@ pub fn select_file_provider(mode: FileBoxMode) -> Arc<dyn FileIo> {
|
||||
ProviderPolicy::StrictPluginFirst => {
|
||||
if let Some(factory) = factories.first() {
|
||||
if diag::provider_log_enabled(quiet_pipe) {
|
||||
diag::provider_log_info(&format!("FileBox: using registered provider (priority={})", factory.priority()));
|
||||
diag::provider_log_info(&format!(
|
||||
"FileBox: using registered provider (priority={})",
|
||||
factory.priority()
|
||||
));
|
||||
diag::provider_log_select("FileBox", "plugin", "dynamic", None);
|
||||
}
|
||||
return factory.create_provider();
|
||||
@ -111,7 +122,10 @@ pub fn select_file_provider(mode: FileBoxMode) -> Arc<dyn FileIo> {
|
||||
// Fallback to first available (plugin)
|
||||
if let Some(factory) = factories.first() {
|
||||
if diag::provider_log_enabled(quiet_pipe) {
|
||||
diag::provider_log_info(&format!("FileBox: using registered provider (priority={})", factory.priority()));
|
||||
diag::provider_log_info(&format!(
|
||||
"FileBox: using registered provider (priority={})",
|
||||
factory.priority()
|
||||
));
|
||||
diag::provider_log_select("FileBox", "plugin", "dynamic", None);
|
||||
}
|
||||
return factory.create_provider();
|
||||
@ -155,7 +169,10 @@ pub fn select_file_provider(mode: FileBoxMode) -> Arc<dyn FileIo> {
|
||||
|
||||
if let Some(factory) = factories.first() {
|
||||
if diag::provider_log_enabled(quiet_pipe) {
|
||||
diag::provider_log_info(&format!("FileBox: using plugin-only provider (priority={})", factory.priority()));
|
||||
diag::provider_log_info(&format!(
|
||||
"FileBox: using plugin-only provider (priority={})",
|
||||
factory.priority()
|
||||
));
|
||||
diag::provider_log_select("FileBox", "plugin", "dynamic", None);
|
||||
}
|
||||
return factory.create_provider();
|
||||
@ -179,8 +196,8 @@ pub fn select_file_provider(mode: FileBoxMode) -> Arc<dyn FileIo> {
|
||||
#[derive(Clone, Debug)]
|
||||
struct ProviderDescriptor {
|
||||
box_name: &'static str,
|
||||
ring: &'static str, // "0" | "1" | "plugin"
|
||||
source: &'static str, // "static" | "dynamic"
|
||||
ring: &'static str, // "0" | "1" | "plugin"
|
||||
source: &'static str, // "static" | "dynamic"
|
||||
capabilities: &'static [&'static str], // e.g., ["read"]
|
||||
priority: i32,
|
||||
}
|
||||
|
||||
@ -9,18 +9,26 @@ pub fn run_pyvm_harness(module: &crate::mir::MirModule, tag: &str) -> Result<i32
|
||||
if !runner_buf.exists() {
|
||||
if let Ok(root) = std::env::var("NYASH_ROOT") {
|
||||
let alt = std::path::Path::new(&root).join("tools/pyvm_runner.py");
|
||||
if alt.exists() { runner_buf = alt; }
|
||||
if alt.exists() {
|
||||
runner_buf = alt;
|
||||
}
|
||||
}
|
||||
}
|
||||
if !runner_buf.exists() {
|
||||
return Err(format!("PyVM runner not found: tools/pyvm_runner.py (cwd) or $NYASH_ROOT/tools/pyvm_runner.py"));
|
||||
return Err(format!(
|
||||
"PyVM runner not found: tools/pyvm_runner.py (cwd) or $NYASH_ROOT/tools/pyvm_runner.py"
|
||||
));
|
||||
}
|
||||
let tmp_dir = std::path::Path::new("tmp");
|
||||
let _ = std::fs::create_dir_all(tmp_dir);
|
||||
let mir_json_path = tmp_dir.join("nyash_pyvm_mir.json");
|
||||
crate::runner::mir_json_emit::emit_mir_json_for_harness_bin(module, &mir_json_path)
|
||||
.map_err(|e| format!("PyVM MIR JSON emit error: {}", e))?;
|
||||
crate::cli_v!("[ny-compiler] using PyVM ({} ) → {}", tag, mir_json_path.display());
|
||||
crate::cli_v!(
|
||||
"[ny-compiler] using PyVM ({} ) → {}",
|
||||
tag,
|
||||
mir_json_path.display()
|
||||
);
|
||||
// Determine entry function (prefer Main.main; top-level main only if allowed)
|
||||
let allow_top = crate::config::env::entry_allow_toplevel_main();
|
||||
let entry = if module.functions.contains_key("Main.main") {
|
||||
@ -71,18 +79,26 @@ pub fn run_pyvm_harness_lib(module: &nyash_rust::mir::MirModule, tag: &str) -> R
|
||||
if !runner_buf.exists() {
|
||||
if let Ok(root) = std::env::var("NYASH_ROOT") {
|
||||
let alt = std::path::Path::new(&root).join("tools/pyvm_runner.py");
|
||||
if alt.exists() { runner_buf = alt; }
|
||||
if alt.exists() {
|
||||
runner_buf = alt;
|
||||
}
|
||||
}
|
||||
}
|
||||
if !runner_buf.exists() {
|
||||
return Err(format!("PyVM runner not found: tools/pyvm_runner.py (cwd) or $NYASH_ROOT/tools/pyvm_runner.py"));
|
||||
return Err(format!(
|
||||
"PyVM runner not found: tools/pyvm_runner.py (cwd) or $NYASH_ROOT/tools/pyvm_runner.py"
|
||||
));
|
||||
}
|
||||
let tmp_dir = std::path::Path::new("tmp");
|
||||
let _ = std::fs::create_dir_all(tmp_dir);
|
||||
let mir_json_path = tmp_dir.join("nyash_pyvm_mir.json");
|
||||
crate::runner::mir_json_emit::emit_mir_json_for_harness(module, &mir_json_path)
|
||||
.map_err(|e| format!("PyVM MIR JSON emit error: {}", e))?;
|
||||
crate::cli_v!("[Runner] using PyVM ({} ) → {}", tag, mir_json_path.display());
|
||||
crate::cli_v!(
|
||||
"[Runner] using PyVM ({} ) → {}",
|
||||
tag,
|
||||
mir_json_path.display()
|
||||
);
|
||||
// Determine entry function (prefer Main.main; top-level main only if allowed)
|
||||
let allow_top = crate::config::env::entry_allow_toplevel_main();
|
||||
let entry = if module.functions.contains_key("Main.main") {
|
||||
|
||||
@ -1,8 +1,19 @@
|
||||
//! Resolve context — capture per-thread prelude merge context for enriched diagnostics.
|
||||
use std::cell::RefCell;
|
||||
|
||||
/// Line span mapping for merged prelude+main sources.
|
||||
/// Represents that lines [start_line, start_line + line_count) in the merged
|
||||
/// text originate from `file` at local lines [1, line_count].
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct LineSpan {
|
||||
pub file: String,
|
||||
pub start_line: usize,
|
||||
pub line_count: usize,
|
||||
}
|
||||
|
||||
thread_local! {
|
||||
static LAST_MERGED_PRELUDES: RefCell<Vec<String>> = RefCell::new(Vec::new());
|
||||
static LAST_TEXT_MERGE_LINE_SPANS: RefCell<Vec<LineSpan>> = RefCell::new(Vec::new());
|
||||
}
|
||||
|
||||
/// Record the list of prelude file paths used for the last text merge in this thread.
|
||||
@ -23,3 +34,25 @@ pub fn take_last_merged_preludes() -> Vec<String> {
|
||||
LAST_MERGED_PRELUDES.with(|c| std::mem::take(&mut *c.borrow_mut()))
|
||||
}
|
||||
|
||||
/// Record the line-span mapping for the last text merge in this thread.
|
||||
pub fn set_last_text_merge_line_spans(spans: Vec<LineSpan>) {
|
||||
LAST_TEXT_MERGE_LINE_SPANS.with(|c| {
|
||||
*c.borrow_mut() = spans;
|
||||
});
|
||||
}
|
||||
|
||||
/// Try to map a merged (global) line number back to its origin file and local line.
|
||||
pub fn map_merged_line_to_origin(line: usize) -> Option<(String, usize)> {
|
||||
if line == 0 {
|
||||
return None;
|
||||
}
|
||||
LAST_TEXT_MERGE_LINE_SPANS.with(|c| {
|
||||
for span in c.borrow().iter() {
|
||||
if line >= span.start_line && line < span.start_line + span.line_count {
|
||||
let local = line - span.start_line + 1;
|
||||
return Some((span.file.clone(), local));
|
||||
}
|
||||
}
|
||||
None
|
||||
})
|
||||
}
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
/*!
|
||||
* Using resolver utilities — static resolution line (SSOT + AST) 📦
|
||||
*
|
||||
*
|
||||
* 箱化モジュール化で綺麗綺麗になったにゃ!🎉
|
||||
*
|
||||
* Separation of concerns:
|
||||
@ -19,45 +19,29 @@
|
||||
* - seam: seam logging and optional boundary markers (for diagnostics).
|
||||
*/
|
||||
|
||||
pub mod strip;
|
||||
pub mod seam;
|
||||
pub mod using_resolution;
|
||||
pub mod prelude_manager;
|
||||
pub mod selfhost_pipeline;
|
||||
pub mod path_util;
|
||||
pub mod context;
|
||||
pub mod path_util;
|
||||
pub mod prelude_manager;
|
||||
pub mod seam;
|
||||
pub mod selfhost_pipeline;
|
||||
pub mod strip;
|
||||
pub mod using_resolution;
|
||||
|
||||
// 📦 箱化モジュールの公開にゃ!
|
||||
pub use using_resolution::{
|
||||
UsingResolutionBox,
|
||||
UsingTarget,
|
||||
UsingConfig,
|
||||
};
|
||||
pub use using_resolution::{UsingConfig, UsingResolutionBox, UsingTarget};
|
||||
|
||||
pub use prelude_manager::{
|
||||
PreludeManagerBox,
|
||||
MergeStrategy,
|
||||
MergeResult,
|
||||
};
|
||||
pub use prelude_manager::{MergeResult, MergeStrategy, PreludeManagerBox};
|
||||
|
||||
pub use selfhost_pipeline::{
|
||||
SelfhostPipelineBox,
|
||||
CompilationResult,
|
||||
PipelineConfig,
|
||||
};
|
||||
pub use selfhost_pipeline::{CompilationResult, PipelineConfig, SelfhostPipelineBox};
|
||||
|
||||
// 🔧 Legacy functions (preserved for compatibility)
|
||||
pub use strip::{
|
||||
preexpand_at_local,
|
||||
collect_using_and_strip,
|
||||
resolve_prelude_paths_profiled,
|
||||
parse_preludes_to_asts,
|
||||
merge_prelude_asts_with_main,
|
||||
merge_prelude_text,
|
||||
collect_using_and_strip, merge_prelude_asts_with_main, merge_prelude_text,
|
||||
parse_preludes_to_asts, preexpand_at_local, resolve_prelude_paths_profiled,
|
||||
};
|
||||
|
||||
// Expose context helpers for enhanced diagnostics
|
||||
pub use context::{
|
||||
set_last_merged_preludes,
|
||||
clone_last_merged_preludes,
|
||||
clone_last_merged_preludes, map_merged_line_to_origin, set_last_merged_preludes,
|
||||
set_last_text_merge_line_spans, LineSpan,
|
||||
};
|
||||
|
||||
@ -17,4 +17,3 @@ pub fn is_using_target_path_unquoted(target_unquoted: &str) -> bool {
|
||||
|| target_unquoted.ends_with(".hako")
|
||||
|| target_unquoted.ends_with(".nyash")
|
||||
}
|
||||
|
||||
|
||||
@ -1,13 +1,13 @@
|
||||
//! Prelude Manager Box - 綺麗綺麗なプレリュード統合専門家!📦
|
||||
//!
|
||||
//!
|
||||
//! テキストマージとASTマージを分離して、
|
||||
//! 保守性とテスト容易性を向上させるにゃ!
|
||||
|
||||
use crate::runner::NyashRunner;
|
||||
use crate::runner::modes::common_util::resolve::using_resolution::UsingResolutionBox;
|
||||
use crate::runner::NyashRunner;
|
||||
|
||||
/// 📦 PreludeManagerBox - プレリュード統合の専門家!
|
||||
///
|
||||
///
|
||||
/// テキストベースとASTベースの両方の統合を
|
||||
/// 統一インターフェースで提供する箱にゃ!
|
||||
pub struct PreludeManagerBox<'a> {
|
||||
@ -111,11 +111,13 @@ impl<'a> PreludeManagerBox<'a> {
|
||||
fn build_text_merged(
|
||||
&self,
|
||||
source: &str,
|
||||
_filename: &str,
|
||||
filename: &str,
|
||||
prelude_paths: &[String],
|
||||
trace: bool,
|
||||
) -> Result<String, String> {
|
||||
let mut merged = String::new();
|
||||
let mut spans: Vec<crate::runner::modes::common_util::resolve::LineSpan> = Vec::new();
|
||||
let mut current_line: usize = 1;
|
||||
|
||||
// プレリュードをDFS順に追加
|
||||
for (idx, path) in prelude_paths.iter().enumerate() {
|
||||
@ -138,16 +140,44 @@ impl<'a> PreludeManagerBox<'a> {
|
||||
|
||||
merged.push_str(&cleaned);
|
||||
merged.push('\n');
|
||||
let added = cleaned.lines().count();
|
||||
if added > 0 {
|
||||
spans.push(crate::runner::modes::common_util::resolve::LineSpan {
|
||||
file: path.clone(),
|
||||
start_line: current_line,
|
||||
line_count: added,
|
||||
});
|
||||
current_line += added + 1; // +1 for the extra '\n'
|
||||
} else {
|
||||
current_line += 1;
|
||||
}
|
||||
}
|
||||
|
||||
// デバッグモードなら境界マーカーを追加
|
||||
if std::env::var("NYASH_RESOLVE_SEAM_DEBUG").ok().as_deref() == Some("1") {
|
||||
merged.push_str("\n/* --- using prelude/main boundary --- */\n\n");
|
||||
// boundary line(s) are attributed to a synthetic "<boundary>" pseudo-file
|
||||
let boundary_lines = 3usize;
|
||||
spans.push(crate::runner::modes::common_util::resolve::LineSpan {
|
||||
file: "<prelude/main-boundary>".to_string(),
|
||||
start_line: current_line,
|
||||
line_count: boundary_lines,
|
||||
});
|
||||
current_line += boundary_lines;
|
||||
}
|
||||
|
||||
// メインソースを正規化して追加
|
||||
let cleaned_main = self.normalize_text_for_inline(source);
|
||||
merged.push_str(&cleaned_main);
|
||||
let main_lines = cleaned_main.lines().count();
|
||||
if main_lines > 0 {
|
||||
spans.push(crate::runner::modes::common_util::resolve::LineSpan {
|
||||
file: filename.to_string(),
|
||||
start_line: current_line,
|
||||
line_count: main_lines,
|
||||
});
|
||||
current_line += main_lines;
|
||||
}
|
||||
|
||||
if trace {
|
||||
crate::runner::trace::log(format!(
|
||||
@ -158,6 +188,8 @@ impl<'a> PreludeManagerBox<'a> {
|
||||
));
|
||||
}
|
||||
|
||||
crate::runner::modes::common_util::resolve::set_last_text_merge_line_spans(spans);
|
||||
|
||||
Ok(self.normalize_text_for_inline(&merged))
|
||||
}
|
||||
|
||||
@ -169,34 +201,35 @@ impl<'a> PreludeManagerBox<'a> {
|
||||
) -> Result<(String, Vec<String>), String> {
|
||||
// 既存のcollect_using_and_strip関数を呼び出す
|
||||
// TODO: 将来的にはUsingResolutionBox経由に置き換える
|
||||
let (cleaned, prelude_paths, _imports) = crate::runner::modes::common_util::resolve::strip::collect_using_and_strip(
|
||||
&self.runner,
|
||||
code,
|
||||
filename,
|
||||
)?;
|
||||
let (cleaned, prelude_paths, _imports) =
|
||||
crate::runner::modes::common_util::resolve::strip::collect_using_and_strip(
|
||||
&self.runner,
|
||||
code,
|
||||
filename,
|
||||
)?;
|
||||
Ok((cleaned, prelude_paths))
|
||||
}
|
||||
|
||||
/// 🔧 テキストを正規化するにゃ!
|
||||
fn normalize_text_for_inline(&self, s: &str) -> String {
|
||||
let mut out = s.replace("\r\n", "\n").replace("\r", "\n");
|
||||
|
||||
|
||||
// `}` の前の `;` を除去(複数回パス)
|
||||
for _ in 0..2 {
|
||||
let mut tmp = String::with_capacity(out.len());
|
||||
let bytes = out.as_bytes();
|
||||
let mut i = 0usize;
|
||||
|
||||
|
||||
while i < bytes.len() {
|
||||
if bytes[i] == b';' {
|
||||
// 先読みしてスペース/改行をスキップ
|
||||
let mut j = i + 1;
|
||||
while j < bytes.len() {
|
||||
let c = bytes[j];
|
||||
if c == b' ' || c == b'\t' || c == b'\n' {
|
||||
j += 1;
|
||||
} else {
|
||||
break;
|
||||
if c == b' ' || c == b'\t' || c == b'\n' {
|
||||
j += 1;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if j < bytes.len() && bytes[j] == b'}' {
|
||||
@ -210,12 +243,12 @@ impl<'a> PreludeManagerBox<'a> {
|
||||
}
|
||||
out = tmp;
|
||||
}
|
||||
|
||||
|
||||
// ファイル末尾に改行を追加
|
||||
if !out.ends_with('\n') {
|
||||
out.push('\n');
|
||||
if !out.ends_with('\n') {
|
||||
out.push('\n');
|
||||
}
|
||||
|
||||
|
||||
out
|
||||
}
|
||||
|
||||
@ -243,7 +276,7 @@ impl<'a> PreludeManagerBox<'a> {
|
||||
prelude_paths: &[String],
|
||||
) -> Result<MergeResult, String> {
|
||||
let strategy = self.select_strategy(prelude_paths.len());
|
||||
|
||||
|
||||
match strategy {
|
||||
MergeStrategy::Text => self.merge_text(source, filename, prelude_paths),
|
||||
MergeStrategy::Ast => self.merge_ast(source, filename, prelude_paths),
|
||||
|
||||
@ -1,6 +1,8 @@
|
||||
/// Log tail of inlined prelude chunk for seam inspection.
|
||||
pub fn log_inlined_tail(path_key: &str, inlined_text: &str, seam_dbg: bool) {
|
||||
if !seam_dbg { return; }
|
||||
if !seam_dbg {
|
||||
return;
|
||||
}
|
||||
let tail = inlined_text
|
||||
.chars()
|
||||
.rev()
|
||||
@ -18,7 +20,9 @@ pub fn log_inlined_tail(path_key: &str, inlined_text: &str, seam_dbg: bool) {
|
||||
|
||||
/// Log the seam between prelude and body for quick visual diff.
|
||||
pub fn log_prelude_body_seam(prelude_clean: &str, body: &str, seam_dbg: bool) {
|
||||
if !seam_dbg { return; }
|
||||
if !seam_dbg {
|
||||
return;
|
||||
}
|
||||
let tail = prelude_clean
|
||||
.chars()
|
||||
.rev()
|
||||
@ -28,8 +32,14 @@ pub fn log_prelude_body_seam(prelude_clean: &str, body: &str, seam_dbg: bool) {
|
||||
.rev()
|
||||
.collect::<String>();
|
||||
let head = body.chars().take(160).collect::<String>();
|
||||
eprintln!("[using][seam] prelude_tail=<<<{}>>>", tail.replace('\n', "\\n"));
|
||||
eprintln!("[using][seam] body_head =<<<{}>>>", head.replace('\n', "\\n"));
|
||||
eprintln!(
|
||||
"[using][seam] prelude_tail=<<<{}>>>",
|
||||
tail.replace('\n', "\\n")
|
||||
);
|
||||
eprintln!(
|
||||
"[using][seam] body_head =<<<{}>>>",
|
||||
head.replace('\n', "\\n")
|
||||
);
|
||||
}
|
||||
|
||||
// Legacy brace fix function removed (Phase 15 cleanup)
|
||||
|
||||
@ -1,13 +1,15 @@
|
||||
//! Selfhost Pipeline Box - 綺麗綺麗なセルフホストパイプライン専門家!📦
|
||||
//!
|
||||
//!
|
||||
//! セルフホストコンパイルの複雑な処理を箱に閉じ込めて、
|
||||
//! 保守性とテスト容易性を向上させるにゃ!
|
||||
|
||||
use crate::runner::modes::common_util::resolve::prelude_manager::{
|
||||
MergeStrategy, PreludeManagerBox,
|
||||
};
|
||||
use crate::runner::NyashRunner;
|
||||
use crate::runner::modes::common_util::resolve::prelude_manager::{PreludeManagerBox, MergeStrategy};
|
||||
|
||||
/// 📦 SelfhostPipelineBox - セルフホストパイプラインの専門家!
|
||||
///
|
||||
///
|
||||
/// コンパイラーパイプライン全体を管理する箱にゃ!
|
||||
pub struct SelfhostPipelineBox<'a> {
|
||||
runner: &'a NyashRunner,
|
||||
@ -37,7 +39,7 @@ impl<'a> SelfhostPipelineBox<'a> {
|
||||
/// 🌟 新しいSelfhostPipelineBoxを作るにゃ!
|
||||
pub fn new(runner: &'a NyashRunner) -> Self {
|
||||
let prelude_manager = PreludeManagerBox::new(runner);
|
||||
|
||||
|
||||
Self {
|
||||
runner,
|
||||
prelude_manager,
|
||||
@ -66,12 +68,14 @@ impl<'a> SelfhostPipelineBox<'a> {
|
||||
|
||||
// 第1フェーズ:using文解析とプレリュードパス収集
|
||||
let (cleaned_main, prelude_paths) = self.collect_and_resolve_using(code, filename)?;
|
||||
|
||||
|
||||
// 第2フェーズ:プレリュード統合
|
||||
let merge_result = if config.enable_ast_merge {
|
||||
self.prelude_manager.merge_ast(&cleaned_main, filename, &prelude_paths)?
|
||||
self.prelude_manager
|
||||
.merge_ast(&cleaned_main, filename, &prelude_paths)?
|
||||
} else {
|
||||
self.prelude_manager.merge_text(&cleaned_main, filename, &prelude_paths)?
|
||||
self.prelude_manager
|
||||
.merge_text(&cleaned_main, filename, &prelude_paths)?
|
||||
};
|
||||
|
||||
let processing_time = start_time.elapsed().as_millis() as u64;
|
||||
@ -118,9 +122,7 @@ impl<'a> SelfhostPipelineBox<'a> {
|
||||
|
||||
eprintln!(
|
||||
"[selfhost-pipeline] ✅ Completed in {}ms (strategy: {}, preludes: {})",
|
||||
result.processing_time_ms,
|
||||
strategy_str,
|
||||
result.prelude_count
|
||||
result.processing_time_ms, strategy_str, result.prelude_count
|
||||
);
|
||||
}
|
||||
|
||||
@ -146,14 +148,15 @@ impl<'a> SelfhostPipelineBox<'a> {
|
||||
/// 🧪 パイプラインを検証するにゃ!(テスト用)
|
||||
pub fn validate_pipeline(&self, code: &str, filename: &str) -> Result<Vec<String>, String> {
|
||||
let mut issues = Vec::new();
|
||||
|
||||
|
||||
// usingシステムの検証
|
||||
if crate::config::env::enable_using() {
|
||||
// using文があるかチェック
|
||||
let using_count = code.lines()
|
||||
let using_count = code
|
||||
.lines()
|
||||
.filter(|line| line.trim().starts_with("using "))
|
||||
.count();
|
||||
|
||||
|
||||
if using_count > 0 {
|
||||
// プレリュード解決を試みる
|
||||
match crate::runner::modes::common_util::resolve::strip::resolve_prelude_paths_profiled(
|
||||
@ -177,11 +180,7 @@ impl<'a> SelfhostPipelineBox<'a> {
|
||||
}
|
||||
|
||||
/// 📊 パフォーマンスプロファイリングするにゃ!
|
||||
pub fn profile_pipeline(
|
||||
&mut self,
|
||||
_code: &str,
|
||||
_filename: &str,
|
||||
) -> Result<String, String> {
|
||||
pub fn profile_pipeline(&mut self, _code: &str, _filename: &str) -> Result<String, String> {
|
||||
// プロファイル機能を実装(別途)
|
||||
// TODO: プロファイル機能を追加
|
||||
Err("Profiling not yet implemented".to_string())
|
||||
|
||||
@ -14,14 +14,26 @@ pub fn collect_using_and_strip(
|
||||
runner: &NyashRunner,
|
||||
code: &str,
|
||||
filename: &str,
|
||||
) -> Result<(String, Vec<String>, std::collections::HashMap<String, String>), String> {
|
||||
) -> Result<
|
||||
(
|
||||
String,
|
||||
Vec<String>,
|
||||
std::collections::HashMap<String, String>,
|
||||
),
|
||||
String,
|
||||
> {
|
||||
if !crate::config::env::enable_using() {
|
||||
return Ok((code.to_string(), Vec::new(), std::collections::HashMap::new()));
|
||||
return Ok((
|
||||
code.to_string(),
|
||||
Vec::new(),
|
||||
std::collections::HashMap::new(),
|
||||
));
|
||||
}
|
||||
let using_ctx = runner.init_using_context();
|
||||
let prod = crate::config::env::using_is_prod();
|
||||
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 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());
|
||||
@ -30,8 +42,8 @@ pub fn collect_using_and_strip(
|
||||
use std::collections::HashMap;
|
||||
let mut seen_paths: HashMap<String, (String, usize)> = HashMap::new(); // canon_path -> (alias/label, first_line)
|
||||
let mut seen_aliases: HashMap<String, (String, usize)> = HashMap::new(); // alias -> (canon_path, first_line)
|
||||
// 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.
|
||||
// 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 {
|
||||
@ -66,7 +78,10 @@ pub fn collect_using_and_strip(
|
||||
|
||||
// Check if this is a known alias or module FIRST before treating as file path
|
||||
let is_known_alias_or_module = using_ctx.aliases.contains_key(&target_unquoted)
|
||||
|| using_ctx.pending_modules.iter().any(|(k, _)| k == &target_unquoted)
|
||||
|| using_ctx
|
||||
.pending_modules
|
||||
.iter()
|
||||
.any(|(k, _)| k == &target_unquoted)
|
||||
|| using_ctx.packages.contains_key(&target_unquoted);
|
||||
|
||||
let is_path = if is_known_alias_or_module {
|
||||
@ -74,7 +89,9 @@ pub fn collect_using_and_strip(
|
||||
false
|
||||
} else {
|
||||
// SSOT: delegate path pattern check
|
||||
crate::runner::modes::common_util::resolve::path_util::is_using_target_path_unquoted(&target_unquoted)
|
||||
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
|
||||
@ -145,7 +162,13 @@ pub fn collect_using_and_strip(
|
||||
prev_line
|
||||
));
|
||||
} else {
|
||||
seen_paths.insert(canon.clone(), (alias_name.clone().unwrap_or_else(|| "<none>".into()), line_no));
|
||||
seen_paths.insert(
|
||||
canon.clone(),
|
||||
(
|
||||
alias_name.clone().unwrap_or_else(|| "<none>".into()),
|
||||
line_no,
|
||||
),
|
||||
);
|
||||
}
|
||||
if let Some(alias) = alias_name.clone() {
|
||||
if let Some((prev_path, prev_line)) = seen_aliases.get(&alias) {
|
||||
@ -184,7 +207,9 @@ pub fn collect_using_and_strip(
|
||||
strict,
|
||||
verbose,
|
||||
) {
|
||||
if resolved.starts_with("dylib:") { continue; }
|
||||
if resolved.starts_with("dylib:") {
|
||||
continue;
|
||||
}
|
||||
let canon = std::fs::canonicalize(&resolved)
|
||||
.ok()
|
||||
.map(|pb| pb.to_string_lossy().to_string())
|
||||
@ -197,7 +222,10 @@ pub fn collect_using_and_strip(
|
||||
} else {
|
||||
seen_paths.insert(
|
||||
canon.clone(),
|
||||
(alias_name.clone().unwrap_or_else(|| "<none>".into()), line_no),
|
||||
(
|
||||
alias_name.clone().unwrap_or_else(|| "<none>".into()),
|
||||
line_no,
|
||||
),
|
||||
);
|
||||
}
|
||||
if let Some(alias) = alias_name.clone() {
|
||||
@ -217,10 +245,8 @@ pub fn collect_using_and_strip(
|
||||
}
|
||||
|
||||
// 1) modules mapping (name -> path)
|
||||
if let Some((_, mod_path)) = using_ctx
|
||||
.pending_modules
|
||||
.iter()
|
||||
.find(|(n, _)| n == &name)
|
||||
if let Some((_, mod_path)) =
|
||||
using_ctx.pending_modules.iter().find(|(n, _)| n == &name)
|
||||
{
|
||||
let out_path = mod_path.clone();
|
||||
// Duplicate detection (same semantics as packages below)
|
||||
@ -236,7 +262,10 @@ pub fn collect_using_and_strip(
|
||||
} else {
|
||||
seen_paths.insert(
|
||||
canon.clone(),
|
||||
(alias_name.clone().unwrap_or_else(|| "<none>".into()), line_no),
|
||||
(
|
||||
alias_name.clone().unwrap_or_else(|| "<none>".into()),
|
||||
line_no,
|
||||
),
|
||||
);
|
||||
}
|
||||
if let Some(alias) = alias_name.clone() {
|
||||
@ -263,21 +292,25 @@ pub fn collect_using_and_strip(
|
||||
PackageKind::Package => {
|
||||
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")) {
|
||||
if matches!(
|
||||
base.extension().and_then(|s| s.to_str()),
|
||||
Some("nyash") | Some("hako")
|
||||
) {
|
||||
pkg.path.clone()
|
||||
} else {
|
||||
base.join(m).to_string_lossy().to_string()
|
||||
}
|
||||
} else if matches!(base.extension().and_then(|s| s.to_str()), Some("nyash") | Some("hako")) {
|
||||
} else if matches!(
|
||||
base.extension().and_then(|s| s.to_str()),
|
||||
Some("nyash") | Some("hako")
|
||||
) {
|
||||
pkg.path.clone()
|
||||
} else {
|
||||
let leaf = base
|
||||
.file_name()
|
||||
.and_then(|s| s.to_str())
|
||||
.unwrap_or(&name);
|
||||
base.join(format!("{}.hako", leaf))
|
||||
.to_string_lossy()
|
||||
.to_string()
|
||||
let leaf =
|
||||
base.file_name().and_then(|s| s.to_str()).unwrap_or(&name);
|
||||
base.join(format!("{}.hako", leaf))
|
||||
.to_string_lossy()
|
||||
.to_string()
|
||||
};
|
||||
// Duplicate detection for prod package alias resolution
|
||||
let canon = std::fs::canonicalize(&out)
|
||||
@ -294,7 +327,13 @@ pub fn collect_using_and_strip(
|
||||
prev_line
|
||||
));
|
||||
} else {
|
||||
seen_paths.insert(canon.clone(), (alias_name.clone().unwrap_or_else(|| "<none>".into()), line_no));
|
||||
seen_paths.insert(
|
||||
canon.clone(),
|
||||
(
|
||||
alias_name.clone().unwrap_or_else(|| "<none>".into()),
|
||||
line_no,
|
||||
),
|
||||
);
|
||||
}
|
||||
if let Some(alias) = alias_name.clone() {
|
||||
if let Some((prev_path, prev_line)) = seen_aliases.get(&alias) {
|
||||
@ -476,7 +515,8 @@ pub fn resolve_prelude_paths_profiled(
|
||||
}
|
||||
let src = std::fs::read_to_string(&real_path)
|
||||
.map_err(|e| format!("using: failed to read '{}': {}", real_path, e))?;
|
||||
let (_cleaned, nested, _nested_imports) = collect_using_and_strip(runner, &src, &real_path)?;
|
||||
let (_cleaned, nested, _nested_imports) =
|
||||
collect_using_and_strip(runner, &src, &real_path)?;
|
||||
for n in nested.iter() {
|
||||
dfs(runner, n, out, seen)?;
|
||||
}
|
||||
@ -551,7 +591,10 @@ pub fn parse_preludes_to_asts(
|
||||
) -> Result<Vec<nyash_rust::ast::ASTNode>, String> {
|
||||
let debug = crate::config::env::env_bool("NYASH_STRIP_DEBUG");
|
||||
if debug {
|
||||
eprintln!("[strip-debug] parse_preludes_to_asts: {} files total", prelude_paths.len());
|
||||
eprintln!(
|
||||
"[strip-debug] parse_preludes_to_asts: {} files total",
|
||||
prelude_paths.len()
|
||||
);
|
||||
for (idx, p) in prelude_paths.iter().enumerate() {
|
||||
eprintln!("[strip-debug] [{}] {}", idx, p);
|
||||
}
|
||||
@ -559,18 +602,27 @@ pub fn parse_preludes_to_asts(
|
||||
let mut out: Vec<nyash_rust::ast::ASTNode> = Vec::with_capacity(prelude_paths.len());
|
||||
for (idx, prelude_path) in prelude_paths.iter().enumerate() {
|
||||
if debug {
|
||||
eprintln!("[strip-debug] [{}/{}] Processing: {}", idx + 1, prelude_paths.len(), prelude_path);
|
||||
eprintln!(
|
||||
"[strip-debug] [{}/{}] Processing: {}",
|
||||
idx + 1,
|
||||
prelude_paths.len(),
|
||||
prelude_path
|
||||
);
|
||||
}
|
||||
let src = std::fs::read_to_string(prelude_path)
|
||||
.map_err(|e| format!("using: error reading {}: {}", prelude_path, e))?;
|
||||
let (clean_src, _nested, _nested_imports) = collect_using_and_strip(runner, &src, prelude_path)?;
|
||||
let (clean_src, _nested, _nested_imports) =
|
||||
collect_using_and_strip(runner, &src, prelude_path)?;
|
||||
|
||||
// IMPORTANT: Do not attempt to AST-parse .hako preludes here.
|
||||
// .hako is Hakorune surface, not Nyash AST. VM/VM-fallback paths
|
||||
// will route to text-merge when any prelude is .hako.
|
||||
if prelude_path.ends_with(".hako") {
|
||||
if debug {
|
||||
eprintln!("[strip-debug] skip AST parse for .hako prelude: {}", prelude_path);
|
||||
eprintln!(
|
||||
"[strip-debug] skip AST parse for .hako prelude: {}",
|
||||
prelude_path
|
||||
);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
@ -579,22 +631,37 @@ pub fn parse_preludes_to_asts(
|
||||
|
||||
// Debug: dump clean_src if NYASH_STRIP_DEBUG=1
|
||||
if debug {
|
||||
eprintln!("[strip-debug] [{}/{}] About to parse: {}", idx + 1, prelude_paths.len(), prelude_path);
|
||||
eprintln!("[strip-debug] clean_src first 500 chars:\n{}\n---",
|
||||
&clean_src.chars().take(500).collect::<String>());
|
||||
eprintln!(
|
||||
"[strip-debug] [{}/{}] About to parse: {}",
|
||||
idx + 1,
|
||||
prelude_paths.len(),
|
||||
prelude_path
|
||||
);
|
||||
eprintln!(
|
||||
"[strip-debug] clean_src first 500 chars:\n{}\n---",
|
||||
&clean_src.chars().take(500).collect::<String>()
|
||||
);
|
||||
}
|
||||
|
||||
match crate::parser::NyashParser::parse_from_string(&clean_src) {
|
||||
Ok(ast) => {
|
||||
if debug {
|
||||
eprintln!("[strip-debug] [{}/{}] ✅ Parse SUCCESS: {}", idx + 1, prelude_paths.len(), prelude_path);
|
||||
eprintln!(
|
||||
"[strip-debug] [{}/{}] ✅ Parse SUCCESS: {}",
|
||||
idx + 1,
|
||||
prelude_paths.len(),
|
||||
prelude_path
|
||||
);
|
||||
}
|
||||
out.push(ast)
|
||||
}
|
||||
Err(e) => {
|
||||
// Always output debug info on parse failure if NYASH_STRIP_DEBUG=1
|
||||
let debug = crate::config::env::env_bool("NYASH_STRIP_DEBUG");
|
||||
eprintln!("[strip-debug] Parse FAILED for: {} (debug={})", prelude_path, debug);
|
||||
eprintln!(
|
||||
"[strip-debug] Parse FAILED for: {} (debug={})",
|
||||
prelude_path, debug
|
||||
);
|
||||
if debug {
|
||||
eprintln!("[strip-debug] Error: {}", e);
|
||||
let es = format!("{}", e);
|
||||
@ -605,19 +672,31 @@ pub fn parse_preludes_to_asts(
|
||||
if let Some(pos) = es.rfind("line ") {
|
||||
let mut j = pos + 5; // after "line "
|
||||
let bytes = es.as_bytes();
|
||||
let mut n: usize = 0; let mut had = false;
|
||||
let mut n: usize = 0;
|
||||
let mut had = false;
|
||||
while j < bytes.len() {
|
||||
let c = bytes[j];
|
||||
if c >= b'0' && c <= b'9' { n = n * 10 + (c - b'0') as usize; j += 1; had = true; } else { break; }
|
||||
if c >= b'0' && c <= b'9' {
|
||||
n = n * 10 + (c - b'0') as usize;
|
||||
j += 1;
|
||||
had = true;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if had {
|
||||
let ln = if n == 0 { 1 } else { n };
|
||||
let from = ln.saturating_sub(3);
|
||||
let to = std::cmp::min(lines.len(), ln + 3);
|
||||
eprintln!("[strip-debug] Context around line {} ({}..={}):", ln, from.max(1), to);
|
||||
eprintln!(
|
||||
"[strip-debug] Context around line {} ({}..={}):",
|
||||
ln,
|
||||
from.max(1),
|
||||
to
|
||||
);
|
||||
for i in from.max(1)..=to {
|
||||
let mark = if i == ln { ">>" } else { " " };
|
||||
if let Some(line) = lines.get(i-1) {
|
||||
if let Some(line) = lines.get(i - 1) {
|
||||
eprintln!("{} {:4}: {}", mark, i, line);
|
||||
}
|
||||
}
|
||||
@ -640,7 +719,10 @@ pub fn parse_preludes_to_asts(
|
||||
}
|
||||
}
|
||||
if debug {
|
||||
eprintln!("[strip-debug] parse_preludes_to_asts: ✅ All {} files parsed successfully", out.len());
|
||||
eprintln!(
|
||||
"[strip-debug] parse_preludes_to_asts: ✅ All {} files parsed successfully",
|
||||
out.len()
|
||||
);
|
||||
}
|
||||
Ok(out)
|
||||
}
|
||||
@ -663,7 +745,10 @@ pub fn merge_prelude_asts_with_main(
|
||||
if let ASTNode::Program { statements, .. } = main_ast.clone() {
|
||||
let mut all = combined;
|
||||
all.extend(statements);
|
||||
ASTNode::Program { statements: all, span: Span::unknown() }
|
||||
ASTNode::Program {
|
||||
statements: all,
|
||||
span: Span::unknown(),
|
||||
}
|
||||
} else {
|
||||
// Defensive: unexpected shape; preserve main AST unchanged.
|
||||
main_ast.clone()
|
||||
@ -802,6 +887,8 @@ pub fn merge_prelude_text(
|
||||
|
||||
// Build merged text: preludes first, then main source
|
||||
let mut merged = String::new();
|
||||
let mut spans: Vec<crate::runner::modes::common_util::resolve::LineSpan> = Vec::new();
|
||||
let mut current_line: usize = 1;
|
||||
|
||||
// Add preludes in DFS order
|
||||
for (idx, path) in prelude_paths.iter().enumerate() {
|
||||
@ -809,7 +896,8 @@ pub fn merge_prelude_text(
|
||||
.map_err(|e| format!("using: failed to read '{}': {}", path, e))?;
|
||||
|
||||
// Strip using lines from prelude and normalize
|
||||
let (cleaned_raw, _nested, _nested_imports) = collect_using_and_strip(runner, &content, path)?;
|
||||
let (cleaned_raw, _nested, _nested_imports) =
|
||||
collect_using_and_strip(runner, &content, path)?;
|
||||
let mut cleaned = normalize_text_for_inline(&cleaned_raw);
|
||||
// Hako-friendly normalize for preludes: always strip leading `local ` at line head
|
||||
// when the prelude is a .hako (or looks like Hako code). This prevents top-level
|
||||
@ -831,11 +919,30 @@ pub fn merge_prelude_text(
|
||||
|
||||
merged.push_str(&cleaned);
|
||||
merged.push('\n');
|
||||
|
||||
let added = cleaned.lines().count();
|
||||
if added > 0 {
|
||||
spans.push(crate::runner::modes::common_util::resolve::LineSpan {
|
||||
file: path.clone(),
|
||||
start_line: current_line,
|
||||
line_count: added,
|
||||
});
|
||||
current_line += added + 1; // +1 for extra '\n'
|
||||
} else {
|
||||
current_line += 1;
|
||||
}
|
||||
}
|
||||
|
||||
// Add boundary marker if debug mode
|
||||
if crate::config::env::env_bool("NYASH_RESOLVE_SEAM_DEBUG") {
|
||||
merged.push_str("\n/* --- using prelude/main boundary --- */\n\n");
|
||||
let boundary_lines = 3usize;
|
||||
spans.push(crate::runner::modes::common_util::resolve::LineSpan {
|
||||
file: "<prelude/main-boundary>".to_string(),
|
||||
start_line: current_line,
|
||||
line_count: boundary_lines,
|
||||
});
|
||||
current_line += boundary_lines;
|
||||
}
|
||||
|
||||
// Add main source (already cleaned of using lines) and normalize
|
||||
@ -845,9 +952,19 @@ pub fn merge_prelude_text(
|
||||
if filename.ends_with(".hako")
|
||||
|| crate::runner::modes::common_util::hako::looks_like_hako_code(&cleaned_main_norm)
|
||||
{
|
||||
cleaned_main_norm = crate::runner::modes::common_util::hako::strip_local_decl(&cleaned_main_norm);
|
||||
cleaned_main_norm =
|
||||
crate::runner::modes::common_util::hako::strip_local_decl(&cleaned_main_norm);
|
||||
}
|
||||
merged.push_str(&cleaned_main_norm);
|
||||
let main_lines = cleaned_main_norm.lines().count();
|
||||
if main_lines > 0 {
|
||||
spans.push(crate::runner::modes::common_util::resolve::LineSpan {
|
||||
file: filename.to_string(),
|
||||
start_line: current_line,
|
||||
line_count: main_lines,
|
||||
});
|
||||
current_line += main_lines;
|
||||
}
|
||||
|
||||
if trace {
|
||||
crate::runner::trace::log(format!(
|
||||
@ -865,6 +982,8 @@ pub fn merge_prelude_text(
|
||||
}
|
||||
}
|
||||
|
||||
crate::runner::modes::common_util::resolve::set_last_text_merge_line_spans(spans);
|
||||
|
||||
Ok(normalize_text_for_inline(&merged))
|
||||
}
|
||||
|
||||
@ -887,7 +1006,11 @@ fn normalize_text_for_inline(s: &str) -> String {
|
||||
let mut j = i + 1;
|
||||
while j < bytes.len() {
|
||||
let c = bytes[j];
|
||||
if c == b' ' || c == b'\t' || c == b'\n' { j += 1; } else { break; }
|
||||
if c == b' ' || c == b'\t' || c == b'\n' {
|
||||
j += 1;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if j < bytes.len() && bytes[j] == b'}' {
|
||||
// drop ';' (do not advance j here)
|
||||
@ -900,6 +1023,8 @@ fn normalize_text_for_inline(s: &str) -> String {
|
||||
}
|
||||
out = tmp;
|
||||
}
|
||||
if !out.ends_with('\n') { out.push('\n'); }
|
||||
if !out.ends_with('\n') {
|
||||
out.push('\n');
|
||||
}
|
||||
out
|
||||
}
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
//! Using Resolution Box - 綺麗綺麗なusing文解決専門家!📦
|
||||
//!
|
||||
//!
|
||||
//! 巨大な `collect_using_and_strip` 関数を箱に分解して、
|
||||
//! 責務を明確にしてテストしやすくするにゃ!
|
||||
|
||||
@ -8,7 +8,7 @@ use std::collections::HashMap;
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
/// 📦 UsingResolutionBox - using文解決の専門家!
|
||||
///
|
||||
///
|
||||
/// using文の解析、パス解決、重複チェックを一手に引き受ける箱にゃ!
|
||||
pub struct UsingResolutionBox<'a> {
|
||||
runner: &'a NyashRunner,
|
||||
@ -52,9 +52,9 @@ impl<'a> UsingResolutionBox<'a> {
|
||||
|| crate::config::env::env_bool("NYASH_RESOLVE_TRACE"),
|
||||
allow_file_using: crate::config::env::allow_using_file(),
|
||||
};
|
||||
|
||||
|
||||
let ctx_dir = Path::new(filename).parent().map(|p| p.to_path_buf());
|
||||
|
||||
|
||||
// ファイルがパッケージ内にあるかチェック
|
||||
let filename_canon = std::fs::canonicalize(filename).ok();
|
||||
let mut inside_pkg = false;
|
||||
@ -89,11 +89,11 @@ impl<'a> UsingResolutionBox<'a> {
|
||||
}
|
||||
|
||||
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(),
|
||||
@ -105,16 +105,21 @@ impl<'a> UsingResolutionBox<'a> {
|
||||
|
||||
let target_unquoted = target.trim_matches('"').to_string();
|
||||
let using_ctx = self.runner.init_using_context();
|
||||
|
||||
|
||||
// 既知のエイリアスかモジュールかチェック
|
||||
let is_known_alias_or_module = using_ctx.aliases.contains_key(&target_unquoted)
|
||||
|| using_ctx.pending_modules.iter().any(|(k, _)| k == &target_unquoted)
|
||||
|| using_ctx
|
||||
.pending_modules
|
||||
.iter()
|
||||
.any(|(k, _)| k == &target_unquoted)
|
||||
|| using_ctx.packages.contains_key(&target_unquoted);
|
||||
|
||||
let is_path = if is_known_alias_or_module {
|
||||
false
|
||||
} else {
|
||||
crate::runner::modes::common_util::resolve::path_util::is_using_target_path_unquoted(&target_unquoted)
|
||||
crate::runner::modes::common_util::resolve::path_util::is_using_target_path_unquoted(
|
||||
&target_unquoted,
|
||||
)
|
||||
};
|
||||
|
||||
Some(UsingTarget {
|
||||
@ -145,7 +150,7 @@ impl<'a> UsingResolutionBox<'a> {
|
||||
|
||||
let path = target.target.trim_matches('"').to_string();
|
||||
let mut p = PathBuf::from(&path);
|
||||
|
||||
|
||||
// 相対パス解決
|
||||
if p.is_relative() {
|
||||
if let Some(dir) = &self.ctx_dir {
|
||||
@ -154,7 +159,7 @@ impl<'a> UsingResolutionBox<'a> {
|
||||
p = cand;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// NYASH_ROOTも試す
|
||||
if p.is_relative() {
|
||||
if let Ok(root) = std::env::var("NYASH_ROOT") {
|
||||
@ -172,9 +177,13 @@ impl<'a> UsingResolutionBox<'a> {
|
||||
}
|
||||
|
||||
/// 🛡️ 重複チェックするにゃ!
|
||||
pub fn check_duplicates(&mut self, target: &UsingTarget, resolved_path: &str) -> Result<(), String> {
|
||||
let canon_path = std::fs::canonicalize(resolved_path)
|
||||
.unwrap_or_else(|_| PathBuf::from(resolved_path));
|
||||
pub fn check_duplicates(
|
||||
&mut self,
|
||||
target: &UsingTarget,
|
||||
resolved_path: &str,
|
||||
) -> Result<(), String> {
|
||||
let canon_path =
|
||||
std::fs::canonicalize(resolved_path).unwrap_or_else(|_| PathBuf::from(resolved_path));
|
||||
let canon_str = canon_path.to_string_lossy();
|
||||
|
||||
// パスの重複チェック
|
||||
@ -204,10 +213,14 @@ impl<'a> UsingResolutionBox<'a> {
|
||||
|
||||
// 記録
|
||||
let alias_label = target.alias.as_ref().unwrap_or(&target.target).clone();
|
||||
self.seen_paths.insert(canon_str.to_string(), (alias_label.clone(), target.line_no));
|
||||
|
||||
self.seen_paths
|
||||
.insert(canon_str.to_string(), (alias_label.clone(), target.line_no));
|
||||
|
||||
if let Some(ref alias_name) = target.alias {
|
||||
self.seen_aliases.insert(alias_name.clone(), (resolved_path.to_string(), target.line_no));
|
||||
self.seen_aliases.insert(
|
||||
alias_name.clone(),
|
||||
(resolved_path.to_string(), target.line_no),
|
||||
);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
|
||||
@ -37,7 +37,10 @@ pub fn run_ny_program_capture_json(
|
||||
}
|
||||
};
|
||||
if out.timed_out {
|
||||
let head = String::from_utf8_lossy(&out.stdout).chars().take(200).collect::<String>();
|
||||
let head = String::from_utf8_lossy(&out.stdout)
|
||||
.chars()
|
||||
.take(200)
|
||||
.collect::<String>();
|
||||
eprintln!(
|
||||
"[selfhost-child] timeout after {} ms; stdout(head)='{}'",
|
||||
timeout_ms,
|
||||
|
||||
@ -32,12 +32,18 @@ pub fn run_pyvm_module(module: &MirModule, label: &str) -> Option<i32> {
|
||||
let tmp_dir = Path::new("tmp");
|
||||
let _ = std::fs::create_dir_all(tmp_dir);
|
||||
let mir_json_path = tmp_dir.join("nyash_pyvm_mir.json");
|
||||
if let Err(e) = crate::runner::mir_json_emit::emit_mir_json_for_harness_bin(module, &mir_json_path) {
|
||||
if let Err(e) =
|
||||
crate::runner::mir_json_emit::emit_mir_json_for_harness_bin(module, &mir_json_path)
|
||||
{
|
||||
eprintln!("❌ PyVM MIR JSON emit error: {}", e);
|
||||
return None;
|
||||
}
|
||||
if crate::config::env::cli_verbose() {
|
||||
eprintln!("[Bridge] using PyVM ({}) → {}", label, mir_json_path.display());
|
||||
eprintln!(
|
||||
"[Bridge] using PyVM ({}) → {}",
|
||||
label,
|
||||
mir_json_path.display()
|
||||
);
|
||||
}
|
||||
// Select entry (prefer Main.main; top-level main only if allowed)
|
||||
let allow_top = crate::config::env::entry_allow_toplevel_main();
|
||||
|
||||
@ -4,4 +4,3 @@
|
||||
|
||||
pub mod child;
|
||||
pub mod json;
|
||||
|
||||
|
||||
@ -97,13 +97,19 @@ pub fn exe_try_parse_json_v0(filename: &str, timeout_ms: u64) -> Option<crate::m
|
||||
);
|
||||
return None;
|
||||
}
|
||||
let stdout = match String::from_utf8(out_buf) { Ok(s) => s, Err(_) => String::new() };
|
||||
let stdout = match String::from_utf8(out_buf) {
|
||||
Ok(s) => s,
|
||||
Err(_) => String::new(),
|
||||
};
|
||||
let json_line = crate::runner::modes::common_util::selfhost::json::first_json_v0_line(&stdout)
|
||||
.unwrap_or_default();
|
||||
if json_line.is_empty() {
|
||||
if crate::config::env::cli_verbose() {
|
||||
let head: String = stdout.chars().take(200).collect();
|
||||
let errh: String = String::from_utf8_lossy(&err_buf).chars().take(200).collect();
|
||||
let errh: String = String::from_utf8_lossy(&err_buf)
|
||||
.chars()
|
||||
.take(200)
|
||||
.collect();
|
||||
crate::cli_v!(
|
||||
"[ny-compiler] exe produced no JSON; stdout(head)='{}' stderr(head)='{}'",
|
||||
head.replace('\n', "\\n"),
|
||||
|
||||
@ -45,9 +45,14 @@ impl NyashRunner {
|
||||
std::process::exit(1);
|
||||
}
|
||||
if use_ast && !paths.is_empty() {
|
||||
match crate::runner::modes::common_util::resolve::parse_preludes_to_asts(self, &paths) {
|
||||
match crate::runner::modes::common_util::resolve::parse_preludes_to_asts(
|
||||
self, &paths,
|
||||
) {
|
||||
Ok(v) => prelude_asts = v,
|
||||
Err(e) => { eprintln!("❌ {}", e); std::process::exit(1); }
|
||||
Err(e) => {
|
||||
eprintln!("❌ {}", e);
|
||||
std::process::exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -58,16 +63,22 @@ impl NyashRunner {
|
||||
}
|
||||
}
|
||||
// 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);
|
||||
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 in {}: {}", filename, e);
|
||||
crate::runner::modes::common_util::diag::print_parse_error_with_context(
|
||||
filename,
|
||||
code_ref,
|
||||
&e,
|
||||
);
|
||||
// Enhanced context: list merged prelude files if any (from text-merge path)
|
||||
let preludes = crate::runner::modes::common_util::resolve::clone_last_merged_preludes();
|
||||
let preludes =
|
||||
crate::runner::modes::common_util::resolve::clone_last_merged_preludes();
|
||||
if !preludes.is_empty() {
|
||||
eprintln!("[parse/context] merged prelude files ({}):", preludes.len());
|
||||
let show = std::cmp::min(16, preludes.len());
|
||||
@ -83,8 +94,13 @@ impl NyashRunner {
|
||||
};
|
||||
// Merge preludes + main when enabled
|
||||
let ast = if use_ast && !prelude_asts.is_empty() {
|
||||
crate::runner::modes::common_util::resolve::merge_prelude_asts_with_main(prelude_asts, &main_ast)
|
||||
} else { main_ast };
|
||||
crate::runner::modes::common_util::resolve::merge_prelude_asts_with_main(
|
||||
prelude_asts,
|
||||
&main_ast,
|
||||
)
|
||||
} 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);
|
||||
@ -106,13 +122,20 @@ impl NyashRunner {
|
||||
#[allow(unused_mut)]
|
||||
let mut module = compile_result.module.clone();
|
||||
let injected = inject_method_ids(&mut module);
|
||||
if injected > 0 { crate::cli_v!("[LLVM] method_id injected: {} places", injected); }
|
||||
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); }
|
||||
Ok(code) => {
|
||||
std::process::exit(code);
|
||||
}
|
||||
Err(e) => {
|
||||
eprintln!("❌ PyVM harness error: {}", e);
|
||||
std::process::exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -122,7 +145,9 @@ impl NyashRunner {
|
||||
{
|
||||
// Harness path (optional): if NYASH_LLVM_USE_HARNESS=1, try Python/llvmlite first.
|
||||
if crate::config::env::llvm_use_harness() {
|
||||
if let Err(e) = crate::runner::modes::common_util::exec::llvmlite_emit_object(&module, &_out_path, 20_000) {
|
||||
if let Err(e) = crate::runner::modes::common_util::exec::llvmlite_emit_object(
|
||||
&module, &_out_path, 20_000,
|
||||
) {
|
||||
eprintln!("❌ {}", e);
|
||||
process::exit(1);
|
||||
}
|
||||
@ -207,10 +232,16 @@ impl NyashRunner {
|
||||
libs.as_deref(),
|
||||
) {
|
||||
Ok(()) => {
|
||||
match crate::runner::modes::common_util::exec::run_executable(exe_out, &[], 20_000) {
|
||||
match crate::runner::modes::common_util::exec::run_executable(
|
||||
exe_out,
|
||||
&[],
|
||||
20_000,
|
||||
) {
|
||||
Ok((code, _timed_out, stdout_text)) => {
|
||||
// Forward program stdout so parity tests can compare outputs
|
||||
if !stdout_text.is_empty() { print!("{}", stdout_text); }
|
||||
if !stdout_text.is_empty() {
|
||||
print!("{}", stdout_text);
|
||||
}
|
||||
println!("✅ LLVM (harness) execution completed (exit={})", code);
|
||||
std::process::exit(code);
|
||||
}
|
||||
@ -222,7 +253,9 @@ impl NyashRunner {
|
||||
}
|
||||
Err(e) => {
|
||||
eprintln!("❌ ny-llvmc emit-exe error: {}", e);
|
||||
eprintln!(" Hint: build ny-llvmc: cargo build -p nyash-llvm-compiler --release");
|
||||
eprintln!(
|
||||
" Hint: build ny-llvmc: cargo build -p nyash-llvm-compiler --release"
|
||||
);
|
||||
std::process::exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
@ -10,11 +10,17 @@ pub fn run_macro_child(macro_file: &str) {
|
||||
}
|
||||
let value: serde_json::Value = match serde_json::from_str(&input) {
|
||||
Ok(v) => v,
|
||||
Err(_) => { eprintln!("[macro-child] invalid AST JSON v0"); std::process::exit(3); }
|
||||
Err(_) => {
|
||||
eprintln!("[macro-child] invalid AST JSON v0");
|
||||
std::process::exit(3);
|
||||
}
|
||||
};
|
||||
let ast: nyash_rust::ASTNode = match crate::r#macro::ast_json::json_to_ast(&value) {
|
||||
Some(a) => a,
|
||||
None => { eprintln!("[macro-child] unsupported AST JSON v0"); std::process::exit(4); }
|
||||
None => {
|
||||
eprintln!("[macro-child] unsupported AST JSON v0");
|
||||
std::process::exit(4);
|
||||
}
|
||||
};
|
||||
|
||||
// Analyze macro behavior (PoC)
|
||||
@ -29,40 +35,148 @@ pub fn run_macro_child(macro_file: &str) {
|
||||
let m = crate::r#macro::macro_box::UppercasePrintMacro;
|
||||
crate::r#macro::macro_box::MacroBox::expand(&m, &ast)
|
||||
}
|
||||
crate::r#macro::macro_box_ny::MacroBehavior::ArrayPrependZero => transform_array_prepend_zero(&ast),
|
||||
crate::r#macro::macro_box_ny::MacroBehavior::ArrayPrependZero => {
|
||||
transform_array_prepend_zero(&ast)
|
||||
}
|
||||
crate::r#macro::macro_box_ny::MacroBehavior::MapInsertTag => transform_map_insert_tag(&ast),
|
||||
crate::r#macro::macro_box_ny::MacroBehavior::LoopNormalize => transform_loop_normalize(&ast),
|
||||
crate::r#macro::macro_box_ny::MacroBehavior::IfMatchNormalize => transform_peek_match_literal(&ast),
|
||||
crate::r#macro::macro_box_ny::MacroBehavior::ForForeachNormalize => transform_for_foreach(&ast),
|
||||
crate::r#macro::macro_box_ny::MacroBehavior::LoopNormalize => {
|
||||
transform_loop_normalize(&ast)
|
||||
}
|
||||
crate::r#macro::macro_box_ny::MacroBehavior::IfMatchNormalize => {
|
||||
transform_peek_match_literal(&ast)
|
||||
}
|
||||
crate::r#macro::macro_box_ny::MacroBehavior::ForForeachNormalize => {
|
||||
transform_for_foreach(&ast)
|
||||
}
|
||||
crate::r#macro::macro_box_ny::MacroBehavior::EnvTagString => {
|
||||
fn tag(ast: &nyash_rust::ASTNode) -> nyash_rust::ASTNode {
|
||||
use nyash_rust::ast::ASTNode as A;
|
||||
match ast.clone() {
|
||||
A::Literal { value: nyash_rust::ast::LiteralValue::String(s), .. } => {
|
||||
if s == "hello" { A::Literal { value: nyash_rust::ast::LiteralValue::String("hello [ENV]".to_string()), span: nyash_rust::ast::Span::unknown() } } else { ast.clone() }
|
||||
A::Literal {
|
||||
value: nyash_rust::ast::LiteralValue::String(s),
|
||||
..
|
||||
} => {
|
||||
if s == "hello" {
|
||||
A::Literal {
|
||||
value: nyash_rust::ast::LiteralValue::String(
|
||||
"hello [ENV]".to_string(),
|
||||
),
|
||||
span: nyash_rust::ast::Span::unknown(),
|
||||
}
|
||||
} else {
|
||||
ast.clone()
|
||||
}
|
||||
}
|
||||
A::Program { statements, span } => A::Program { statements: statements.iter().map(|n| tag(n)).collect(), span },
|
||||
A::Print { expression, span } => A::Print { expression: Box::new(tag(&expression)), span },
|
||||
A::Return { value, span } => A::Return { value: value.as_ref().map(|v| Box::new(tag(v))), span },
|
||||
A::Assignment { target, value, span } => A::Assignment { target: Box::new(tag(&target)), value: Box::new(tag(&value)), span },
|
||||
A::If { condition, then_body, else_body, span } => A::If { condition: Box::new(tag(&condition)), then_body: then_body.iter().map(|n| tag(n)).collect(), else_body: else_body.map(|v| v.iter().map(|n| tag(n)).collect()), span },
|
||||
A::Loop { condition, body, span } => A::Loop { condition: Box::new(tag(&condition)), body: body.iter().map(|n| tag(n)).collect(), span },
|
||||
A::BinaryOp { operator, left, right, span } => A::BinaryOp { operator, left: Box::new(tag(&left)), right: Box::new(tag(&right)), span },
|
||||
A::UnaryOp { operator, operand, span } => A::UnaryOp { operator, operand: Box::new(tag(&operand)), span },
|
||||
A::MethodCall { object, method, arguments, span } => A::MethodCall { object: Box::new(tag(&object)), method, arguments: arguments.iter().map(|a| tag(a)).collect(), span },
|
||||
A::FunctionCall { name, arguments, span } => A::FunctionCall { name, arguments: arguments.iter().map(|a| tag(a)).collect(), span },
|
||||
A::ArrayLiteral { elements, span } => A::ArrayLiteral { elements: elements.iter().map(|e| tag(e)).collect(), span },
|
||||
A::MapLiteral { entries, span } => A::MapLiteral { entries: entries.iter().map(|(k,v)| (k.clone(), tag(v))).collect(), span },
|
||||
A::Program { statements, span } => A::Program {
|
||||
statements: statements.iter().map(|n| tag(n)).collect(),
|
||||
span,
|
||||
},
|
||||
A::Print { expression, span } => A::Print {
|
||||
expression: Box::new(tag(&expression)),
|
||||
span,
|
||||
},
|
||||
A::Return { value, span } => A::Return {
|
||||
value: value.as_ref().map(|v| Box::new(tag(v))),
|
||||
span,
|
||||
},
|
||||
A::Assignment {
|
||||
target,
|
||||
value,
|
||||
span,
|
||||
} => A::Assignment {
|
||||
target: Box::new(tag(&target)),
|
||||
value: Box::new(tag(&value)),
|
||||
span,
|
||||
},
|
||||
A::If {
|
||||
condition,
|
||||
then_body,
|
||||
else_body,
|
||||
span,
|
||||
} => A::If {
|
||||
condition: Box::new(tag(&condition)),
|
||||
then_body: then_body.iter().map(|n| tag(n)).collect(),
|
||||
else_body: else_body.map(|v| v.iter().map(|n| tag(n)).collect()),
|
||||
span,
|
||||
},
|
||||
A::Loop {
|
||||
condition,
|
||||
body,
|
||||
span,
|
||||
} => A::Loop {
|
||||
condition: Box::new(tag(&condition)),
|
||||
body: body.iter().map(|n| tag(n)).collect(),
|
||||
span,
|
||||
},
|
||||
A::BinaryOp {
|
||||
operator,
|
||||
left,
|
||||
right,
|
||||
span,
|
||||
} => A::BinaryOp {
|
||||
operator,
|
||||
left: Box::new(tag(&left)),
|
||||
right: Box::new(tag(&right)),
|
||||
span,
|
||||
},
|
||||
A::UnaryOp {
|
||||
operator,
|
||||
operand,
|
||||
span,
|
||||
} => A::UnaryOp {
|
||||
operator,
|
||||
operand: Box::new(tag(&operand)),
|
||||
span,
|
||||
},
|
||||
A::MethodCall {
|
||||
object,
|
||||
method,
|
||||
arguments,
|
||||
span,
|
||||
} => A::MethodCall {
|
||||
object: Box::new(tag(&object)),
|
||||
method,
|
||||
arguments: arguments.iter().map(|a| tag(a)).collect(),
|
||||
span,
|
||||
},
|
||||
A::FunctionCall {
|
||||
name,
|
||||
arguments,
|
||||
span,
|
||||
} => A::FunctionCall {
|
||||
name,
|
||||
arguments: arguments.iter().map(|a| tag(a)).collect(),
|
||||
span,
|
||||
},
|
||||
A::ArrayLiteral { elements, span } => A::ArrayLiteral {
|
||||
elements: elements.iter().map(|e| tag(e)).collect(),
|
||||
span,
|
||||
},
|
||||
A::MapLiteral { entries, span } => A::MapLiteral {
|
||||
entries: entries.iter().map(|(k, v)| (k.clone(), tag(v))).collect(),
|
||||
span,
|
||||
},
|
||||
other => other,
|
||||
}
|
||||
}
|
||||
let mut env_on = std::env::var("NYASH_MACRO_CAP_ENV").ok().map(|v| v=="1"||v=="true"||v=="on").unwrap_or(false);
|
||||
let mut env_on = std::env::var("NYASH_MACRO_CAP_ENV")
|
||||
.ok()
|
||||
.map(|v| v == "1" || v == "true" || v == "on")
|
||||
.unwrap_or(false);
|
||||
if let Ok(ctxs) = std::env::var("NYASH_MACRO_CTX_JSON") {
|
||||
if let Ok(v) = serde_json::from_str::<serde_json::Value>(&ctxs) {
|
||||
env_on = v.get("caps").and_then(|c| c.get("env")).and_then(|b| b.as_bool()).unwrap_or(env_on);
|
||||
env_on = v
|
||||
.get("caps")
|
||||
.and_then(|c| c.get("env"))
|
||||
.and_then(|b| b.as_bool())
|
||||
.unwrap_or(env_on);
|
||||
}
|
||||
}
|
||||
if env_on { tag(&ast) } else { ast.clone() }
|
||||
if env_on {
|
||||
tag(&ast)
|
||||
} else {
|
||||
ast.clone()
|
||||
}
|
||||
}
|
||||
};
|
||||
let out_json = crate::r#macro::ast_json::ast_to_json(&out_ast);
|
||||
|
||||
@ -2,9 +2,8 @@
|
||||
* Macro child mode (split modules)
|
||||
*/
|
||||
|
||||
mod transforms;
|
||||
mod entry;
|
||||
mod transforms;
|
||||
|
||||
pub use entry::run_macro_child;
|
||||
pub use transforms::normalize_core_pass;
|
||||
|
||||
|
||||
@ -5,32 +5,110 @@ pub(super) fn transform_array_prepend_zero(ast: &nyash_rust::ASTNode) -> nyash_r
|
||||
let mut new_elems: Vec<A> = Vec::with_capacity(elements.len() + 1);
|
||||
let already_zero = elements
|
||||
.get(0)
|
||||
.and_then(|n| match n { A::Literal { value: LiteralValue::Integer(0), .. } => Some(()), _ => None })
|
||||
.and_then(|n| match n {
|
||||
A::Literal {
|
||||
value: LiteralValue::Integer(0),
|
||||
..
|
||||
} => Some(()),
|
||||
_ => None,
|
||||
})
|
||||
.is_some();
|
||||
if already_zero {
|
||||
for e in elements { new_elems.push(transform_array_prepend_zero(e)); }
|
||||
for e in elements {
|
||||
new_elems.push(transform_array_prepend_zero(e));
|
||||
}
|
||||
} else {
|
||||
new_elems.push(A::Literal { value: LiteralValue::Integer(0), span: Span::unknown() });
|
||||
for e in elements { new_elems.push(transform_array_prepend_zero(e)); }
|
||||
new_elems.push(A::Literal {
|
||||
value: LiteralValue::Integer(0),
|
||||
span: Span::unknown(),
|
||||
});
|
||||
for e in elements {
|
||||
new_elems.push(transform_array_prepend_zero(e));
|
||||
}
|
||||
}
|
||||
A::ArrayLiteral {
|
||||
elements: new_elems,
|
||||
span: Span::unknown(),
|
||||
}
|
||||
A::ArrayLiteral { elements: new_elems, span: Span::unknown() }
|
||||
}
|
||||
A::Program { statements, .. } => A::Program { statements: statements.iter().map(transform_array_prepend_zero).collect(), span: Span::unknown() },
|
||||
A::Print { expression, .. } => A::Print { expression: Box::new(transform_array_prepend_zero(expression)), span: Span::unknown() },
|
||||
A::Return { value, .. } => A::Return { value: value.as_ref().map(|v| Box::new(transform_array_prepend_zero(v))), span: Span::unknown() },
|
||||
A::Assignment { target, value, .. } => A::Assignment { target: Box::new(transform_array_prepend_zero(target)), value: Box::new(transform_array_prepend_zero(value)), span: Span::unknown() },
|
||||
A::If { condition, then_body, else_body, .. } => A::If {
|
||||
condition: Box::new(transform_array_prepend_zero(condition)),
|
||||
then_body: then_body.iter().map(transform_array_prepend_zero).collect(),
|
||||
else_body: else_body.as_ref().map(|v| v.iter().map(transform_array_prepend_zero).collect()),
|
||||
A::Program { statements, .. } => A::Program {
|
||||
statements: statements
|
||||
.iter()
|
||||
.map(transform_array_prepend_zero)
|
||||
.collect(),
|
||||
span: Span::unknown(),
|
||||
},
|
||||
A::Print { expression, .. } => A::Print {
|
||||
expression: Box::new(transform_array_prepend_zero(expression)),
|
||||
span: Span::unknown(),
|
||||
},
|
||||
A::Return { value, .. } => A::Return {
|
||||
value: value
|
||||
.as_ref()
|
||||
.map(|v| Box::new(transform_array_prepend_zero(v))),
|
||||
span: Span::unknown(),
|
||||
},
|
||||
A::Assignment { target, value, .. } => A::Assignment {
|
||||
target: Box::new(transform_array_prepend_zero(target)),
|
||||
value: Box::new(transform_array_prepend_zero(value)),
|
||||
span: Span::unknown(),
|
||||
},
|
||||
A::If {
|
||||
condition,
|
||||
then_body,
|
||||
else_body,
|
||||
..
|
||||
} => A::If {
|
||||
condition: Box::new(transform_array_prepend_zero(condition)),
|
||||
then_body: then_body.iter().map(transform_array_prepend_zero).collect(),
|
||||
else_body: else_body
|
||||
.as_ref()
|
||||
.map(|v| v.iter().map(transform_array_prepend_zero).collect()),
|
||||
span: Span::unknown(),
|
||||
},
|
||||
A::BinaryOp {
|
||||
operator,
|
||||
left,
|
||||
right,
|
||||
..
|
||||
} => A::BinaryOp {
|
||||
operator: operator.clone(),
|
||||
left: Box::new(transform_array_prepend_zero(left)),
|
||||
right: Box::new(transform_array_prepend_zero(right)),
|
||||
span: Span::unknown(),
|
||||
},
|
||||
A::UnaryOp {
|
||||
operator, operand, ..
|
||||
} => A::UnaryOp {
|
||||
operator: operator.clone(),
|
||||
operand: Box::new(transform_array_prepend_zero(operand)),
|
||||
span: Span::unknown(),
|
||||
},
|
||||
A::MethodCall {
|
||||
object,
|
||||
method,
|
||||
arguments,
|
||||
..
|
||||
} => A::MethodCall {
|
||||
object: Box::new(transform_array_prepend_zero(object)),
|
||||
method: method.clone(),
|
||||
arguments: arguments.iter().map(transform_array_prepend_zero).collect(),
|
||||
span: Span::unknown(),
|
||||
},
|
||||
A::FunctionCall {
|
||||
name, arguments, ..
|
||||
} => A::FunctionCall {
|
||||
name: name.clone(),
|
||||
arguments: arguments.iter().map(transform_array_prepend_zero).collect(),
|
||||
span: Span::unknown(),
|
||||
},
|
||||
A::MapLiteral { entries, .. } => A::MapLiteral {
|
||||
entries: entries
|
||||
.iter()
|
||||
.map(|(k, v)| (k.clone(), transform_array_prepend_zero(v)))
|
||||
.collect(),
|
||||
span: Span::unknown(),
|
||||
},
|
||||
A::BinaryOp { operator, left, right, .. } => A::BinaryOp { operator: operator.clone(), left: Box::new(transform_array_prepend_zero(left)), right: Box::new(transform_array_prepend_zero(right)), span: Span::unknown() },
|
||||
A::UnaryOp { operator, operand, .. } => A::UnaryOp { operator: operator.clone(), operand: Box::new(transform_array_prepend_zero(operand)), span: Span::unknown() },
|
||||
A::MethodCall { object, method, arguments, .. } => A::MethodCall { object: Box::new(transform_array_prepend_zero(object)), method: method.clone(), arguments: arguments.iter().map(transform_array_prepend_zero).collect(), span: Span::unknown() },
|
||||
A::FunctionCall { name, arguments, .. } => A::FunctionCall { name: name.clone(), arguments: arguments.iter().map(transform_array_prepend_zero).collect(), span: Span::unknown() },
|
||||
A::MapLiteral { entries, .. } => A::MapLiteral { entries: entries.iter().map(|(k,v)| (k.clone(), transform_array_prepend_zero(v))).collect(), span: Span::unknown() },
|
||||
other => other.clone(),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -1,23 +1,112 @@
|
||||
fn subst_var(node: &nyash_rust::ASTNode, name: &str, replacement: &nyash_rust::ASTNode) -> nyash_rust::ASTNode {
|
||||
fn subst_var(
|
||||
node: &nyash_rust::ASTNode,
|
||||
name: &str,
|
||||
replacement: &nyash_rust::ASTNode,
|
||||
) -> nyash_rust::ASTNode {
|
||||
use nyash_rust::ast::ASTNode as A;
|
||||
match node.clone() {
|
||||
A::Variable { name: n, .. } if n == name => replacement.clone(),
|
||||
A::Program { statements, span } => A::Program { statements: statements.iter().map(|s| subst_var(s, name, replacement)).collect(), span },
|
||||
A::Print { expression, span } => A::Print { expression: Box::new(subst_var(&expression, name, replacement)), span },
|
||||
A::Return { value, span } => A::Return { value: value.as_ref().map(|v| Box::new(subst_var(v, name, replacement))), span },
|
||||
A::Assignment { target, value, span } => A::Assignment { target: Box::new(subst_var(&target, name, replacement)), value: Box::new(subst_var(&value, name, replacement)), span },
|
||||
A::If { condition, then_body, else_body, span } => A::If {
|
||||
condition: Box::new(subst_var(&condition, name, replacement)),
|
||||
then_body: then_body.iter().map(|s| subst_var(s, name, replacement)).collect(),
|
||||
else_body: else_body.map(|v| v.iter().map(|s| subst_var(s, name, replacement)).collect()),
|
||||
A::Program { statements, span } => A::Program {
|
||||
statements: statements
|
||||
.iter()
|
||||
.map(|s| subst_var(s, name, replacement))
|
||||
.collect(),
|
||||
span,
|
||||
},
|
||||
A::Print { expression, span } => A::Print {
|
||||
expression: Box::new(subst_var(&expression, name, replacement)),
|
||||
span,
|
||||
},
|
||||
A::Return { value, span } => A::Return {
|
||||
value: value
|
||||
.as_ref()
|
||||
.map(|v| Box::new(subst_var(v, name, replacement))),
|
||||
span,
|
||||
},
|
||||
A::Assignment {
|
||||
target,
|
||||
value,
|
||||
span,
|
||||
} => A::Assignment {
|
||||
target: Box::new(subst_var(&target, name, replacement)),
|
||||
value: Box::new(subst_var(&value, name, replacement)),
|
||||
span,
|
||||
},
|
||||
A::If {
|
||||
condition,
|
||||
then_body,
|
||||
else_body,
|
||||
span,
|
||||
} => A::If {
|
||||
condition: Box::new(subst_var(&condition, name, replacement)),
|
||||
then_body: then_body
|
||||
.iter()
|
||||
.map(|s| subst_var(s, name, replacement))
|
||||
.collect(),
|
||||
else_body: else_body
|
||||
.map(|v| v.iter().map(|s| subst_var(s, name, replacement)).collect()),
|
||||
span,
|
||||
},
|
||||
A::BinaryOp {
|
||||
operator,
|
||||
left,
|
||||
right,
|
||||
span,
|
||||
} => A::BinaryOp {
|
||||
operator,
|
||||
left: Box::new(subst_var(&left, name, replacement)),
|
||||
right: Box::new(subst_var(&right, name, replacement)),
|
||||
span,
|
||||
},
|
||||
A::UnaryOp {
|
||||
operator,
|
||||
operand,
|
||||
span,
|
||||
} => A::UnaryOp {
|
||||
operator,
|
||||
operand: Box::new(subst_var(&operand, name, replacement)),
|
||||
span,
|
||||
},
|
||||
A::MethodCall {
|
||||
object,
|
||||
method,
|
||||
arguments,
|
||||
span,
|
||||
} => A::MethodCall {
|
||||
object: Box::new(subst_var(&object, name, replacement)),
|
||||
method,
|
||||
arguments: arguments
|
||||
.iter()
|
||||
.map(|a| subst_var(a, name, replacement))
|
||||
.collect(),
|
||||
span,
|
||||
},
|
||||
A::FunctionCall {
|
||||
name: fn_name,
|
||||
arguments,
|
||||
span,
|
||||
} => A::FunctionCall {
|
||||
name: fn_name,
|
||||
arguments: arguments
|
||||
.iter()
|
||||
.map(|a| subst_var(a, name, replacement))
|
||||
.collect(),
|
||||
span,
|
||||
},
|
||||
A::ArrayLiteral { elements, span } => A::ArrayLiteral {
|
||||
elements: elements
|
||||
.iter()
|
||||
.map(|e| subst_var(e, name, replacement))
|
||||
.collect(),
|
||||
span,
|
||||
},
|
||||
A::MapLiteral { entries, span } => A::MapLiteral {
|
||||
entries: entries
|
||||
.iter()
|
||||
.map(|(k, v)| (k.clone(), subst_var(v, name, replacement)))
|
||||
.collect(),
|
||||
span,
|
||||
},
|
||||
A::BinaryOp { operator, left, right, span } => A::BinaryOp { operator, left: Box::new(subst_var(&left, name, replacement)), right: Box::new(subst_var(&right, name, replacement)), span },
|
||||
A::UnaryOp { operator, operand, span } => A::UnaryOp { operator, operand: Box::new(subst_var(&operand, name, replacement)), span },
|
||||
A::MethodCall { object, method, arguments, span } => A::MethodCall { object: Box::new(subst_var(&object, name, replacement)), method, arguments: arguments.iter().map(|a| subst_var(a, name, replacement)).collect(), span },
|
||||
A::FunctionCall { name: fn_name, arguments, span } => A::FunctionCall { name: fn_name, arguments: arguments.iter().map(|a| subst_var(a, name, replacement)).collect(), span },
|
||||
A::ArrayLiteral { elements, span } => A::ArrayLiteral { elements: elements.iter().map(|e| subst_var(e, name, replacement)).collect(), span },
|
||||
A::MapLiteral { entries, span } => A::MapLiteral { entries: entries.iter().map(|(k,v)| (k.clone(), subst_var(v, name, replacement))).collect(), span },
|
||||
other => other,
|
||||
}
|
||||
}
|
||||
@ -28,7 +117,9 @@ pub(super) fn transform_for_foreach(ast: &nyash_rust::ASTNode) -> nyash_rust::AS
|
||||
let mut out: Vec<A> = Vec::new();
|
||||
for st in list.into_iter() {
|
||||
match st.clone() {
|
||||
A::FunctionCall { name, arguments, .. } if (name == "ny_for" || name == "for") && arguments.len() == 4 => {
|
||||
A::FunctionCall {
|
||||
name, arguments, ..
|
||||
} if (name == "ny_for" || name == "for") && arguments.len() == 4 => {
|
||||
let init = arguments[0].clone();
|
||||
let cond = arguments[1].clone();
|
||||
let step = arguments[2].clone();
|
||||
@ -37,38 +128,117 @@ pub(super) fn transform_for_foreach(ast: &nyash_rust::ASTNode) -> nyash_rust::AS
|
||||
if params.is_empty() {
|
||||
match init.clone() {
|
||||
A::Assignment { .. } | A::Local { .. } => out.push(init),
|
||||
A::Lambda { params: p2, body: b2, .. } if p2.is_empty() => { for s in b2 { out.push(transform_for_foreach(&s)); } }
|
||||
A::Lambda {
|
||||
params: p2,
|
||||
body: b2,
|
||||
..
|
||||
} if p2.is_empty() => {
|
||||
for s in b2 {
|
||||
out.push(transform_for_foreach(&s));
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
let mut loop_body: Vec<A> = body.into_iter().map(|n| transform_for_foreach(&n)).collect();
|
||||
let mut loop_body: Vec<A> = body
|
||||
.into_iter()
|
||||
.map(|n| transform_for_foreach(&n))
|
||||
.collect();
|
||||
match step.clone() {
|
||||
A::Assignment { .. } => loop_body.push(step),
|
||||
A::Lambda { params: p2, body: b2, .. } if p2.is_empty() => { for s in b2 { loop_body.push(transform_for_foreach(&s)); } }
|
||||
A::Lambda {
|
||||
params: p2,
|
||||
body: b2,
|
||||
..
|
||||
} if p2.is_empty() => {
|
||||
for s in b2 {
|
||||
loop_body.push(transform_for_foreach(&s));
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
out.push(A::Loop { condition: Box::new(transform_for_foreach(&cond)), body: loop_body, span: Span::unknown() });
|
||||
out.push(A::Loop {
|
||||
condition: Box::new(transform_for_foreach(&cond)),
|
||||
body: loop_body,
|
||||
span: Span::unknown(),
|
||||
});
|
||||
continue;
|
||||
}
|
||||
}
|
||||
out.push(st);
|
||||
}
|
||||
A::FunctionCall { name, arguments, .. } if (name == "ny_foreach" || name == "foreach") && arguments.len() == 3 => {
|
||||
A::FunctionCall {
|
||||
name, arguments, ..
|
||||
} if (name == "ny_foreach" || name == "foreach") && arguments.len() == 3 => {
|
||||
let array = arguments[0].clone();
|
||||
let param_name = match &arguments[1] { A::Variable { name, .. } => name.clone(), _ => "it".to_string() };
|
||||
let param_name = match &arguments[1] {
|
||||
A::Variable { name, .. } => name.clone(),
|
||||
_ => "it".to_string(),
|
||||
};
|
||||
let body_lam = arguments[2].clone();
|
||||
if let A::Lambda { params, body, .. } = body_lam {
|
||||
if params.is_empty() {
|
||||
let iter = A::Variable { name: "__i".to_string(), span: Span::unknown() };
|
||||
let zero = A::Literal { value: LiteralValue::Integer(0), span: Span::unknown() };
|
||||
let one = A::Literal { value: LiteralValue::Integer(1), span: Span::unknown() };
|
||||
let init = A::Local { variables: vec!["__i".to_string()], initial_values: vec![Some(Box::new(zero))], span: Span::unknown() };
|
||||
let len_call = A::MethodCall { object: Box::new(transform_for_foreach(&array)), method: "len".to_string(), arguments: vec![], span: Span::unknown() };
|
||||
let cond = A::BinaryOp { operator: BinaryOperator::Less, left: Box::new(iter.clone()), right: Box::new(len_call), span: Span::unknown() };
|
||||
let get_call = A::MethodCall { object: Box::new(transform_for_foreach(&array)), method: "get".to_string(), arguments: vec![iter.clone()], span: Span::unknown() };
|
||||
let body_stmts: Vec<A> = body.into_iter().map(|s| subst_var(&s, ¶m_name, &get_call)).collect();
|
||||
let step = A::Assignment { target: Box::new(iter.clone()), value: Box::new(A::BinaryOp { operator: BinaryOperator::Add, left: Box::new(iter), right: Box::new(one), span: Span::unknown() }), span: Span::unknown() };
|
||||
let iter = A::Variable {
|
||||
name: "__i".to_string(),
|
||||
span: Span::unknown(),
|
||||
};
|
||||
let zero = A::Literal {
|
||||
value: LiteralValue::Integer(0),
|
||||
span: Span::unknown(),
|
||||
};
|
||||
let one = A::Literal {
|
||||
value: LiteralValue::Integer(1),
|
||||
span: Span::unknown(),
|
||||
};
|
||||
let init = A::Local {
|
||||
variables: vec!["__i".to_string()],
|
||||
initial_values: vec![Some(Box::new(zero))],
|
||||
span: Span::unknown(),
|
||||
};
|
||||
let len_call = A::MethodCall {
|
||||
object: Box::new(transform_for_foreach(&array)),
|
||||
method: "len".to_string(),
|
||||
arguments: vec![],
|
||||
span: Span::unknown(),
|
||||
};
|
||||
let cond = A::BinaryOp {
|
||||
operator: BinaryOperator::Less,
|
||||
left: Box::new(iter.clone()),
|
||||
right: Box::new(len_call),
|
||||
span: Span::unknown(),
|
||||
};
|
||||
let get_call = A::MethodCall {
|
||||
object: Box::new(transform_for_foreach(&array)),
|
||||
method: "get".to_string(),
|
||||
arguments: vec![iter.clone()],
|
||||
span: Span::unknown(),
|
||||
};
|
||||
let body_stmts: Vec<A> = body
|
||||
.into_iter()
|
||||
.map(|s| subst_var(&s, ¶m_name, &get_call))
|
||||
.collect();
|
||||
let step = A::Assignment {
|
||||
target: Box::new(iter.clone()),
|
||||
value: Box::new(A::BinaryOp {
|
||||
operator: BinaryOperator::Add,
|
||||
left: Box::new(iter),
|
||||
right: Box::new(one),
|
||||
span: Span::unknown(),
|
||||
}),
|
||||
span: Span::unknown(),
|
||||
};
|
||||
out.push(init);
|
||||
out.push(A::Loop { condition: Box::new(cond), body: { let mut b = Vec::new(); for s in body_stmts { b.push(transform_for_foreach(&s)); } b.push(step); b }, span: Span::unknown() });
|
||||
out.push(A::Loop {
|
||||
condition: Box::new(cond),
|
||||
body: {
|
||||
let mut b = Vec::new();
|
||||
for s in body_stmts {
|
||||
b.push(transform_for_foreach(&s));
|
||||
}
|
||||
b.push(step);
|
||||
b
|
||||
},
|
||||
span: Span::unknown(),
|
||||
});
|
||||
continue;
|
||||
}
|
||||
}
|
||||
@ -81,18 +251,98 @@ pub(super) fn transform_for_foreach(ast: &nyash_rust::ASTNode) -> nyash_rust::AS
|
||||
}
|
||||
// `A` is already imported above
|
||||
match ast.clone() {
|
||||
A::Program { statements, span } => A::Program { statements: rewrite_stmt_list(statements), span },
|
||||
A::If { condition, then_body, else_body, span } => A::If { condition: Box::new(transform_for_foreach(&condition)), then_body: rewrite_stmt_list(then_body), else_body: else_body.map(rewrite_stmt_list), span },
|
||||
A::Loop { condition, body, span } => A::Loop { condition: Box::new(transform_for_foreach(&condition)), body: rewrite_stmt_list(body), span },
|
||||
A::Print { expression, span } => A::Print { expression: Box::new(transform_for_foreach(&expression)), span },
|
||||
A::Return { value, span } => A::Return { value: value.as_ref().map(|v| Box::new(transform_for_foreach(v))), span },
|
||||
A::Assignment { target, value, span } => A::Assignment { target: Box::new(transform_for_foreach(&target)), value: Box::new(transform_for_foreach(&value)), span },
|
||||
A::BinaryOp { operator, left, right, span } => A::BinaryOp { operator, left: Box::new(transform_for_foreach(&left)), right: Box::new(transform_for_foreach(&right)), span },
|
||||
A::UnaryOp { operator, operand, span } => A::UnaryOp { operator, operand: Box::new(transform_for_foreach(&operand)), span },
|
||||
A::MethodCall { object, method, arguments, span } => A::MethodCall { object: Box::new(transform_for_foreach(&object)), method, arguments: arguments.iter().map(|a| transform_for_foreach(a)).collect(), span },
|
||||
A::FunctionCall { name, arguments, span } => A::FunctionCall { name, arguments: arguments.iter().map(|a| transform_for_foreach(a)).collect(), span },
|
||||
A::ArrayLiteral { elements, span } => A::ArrayLiteral { elements: elements.iter().map(|e| transform_for_foreach(e)).collect(), span },
|
||||
A::MapLiteral { entries, span } => A::MapLiteral { entries: entries.iter().map(|(k,v)| (k.clone(), transform_for_foreach(v))).collect(), span },
|
||||
A::Program { statements, span } => A::Program {
|
||||
statements: rewrite_stmt_list(statements),
|
||||
span,
|
||||
},
|
||||
A::If {
|
||||
condition,
|
||||
then_body,
|
||||
else_body,
|
||||
span,
|
||||
} => A::If {
|
||||
condition: Box::new(transform_for_foreach(&condition)),
|
||||
then_body: rewrite_stmt_list(then_body),
|
||||
else_body: else_body.map(rewrite_stmt_list),
|
||||
span,
|
||||
},
|
||||
A::Loop {
|
||||
condition,
|
||||
body,
|
||||
span,
|
||||
} => A::Loop {
|
||||
condition: Box::new(transform_for_foreach(&condition)),
|
||||
body: rewrite_stmt_list(body),
|
||||
span,
|
||||
},
|
||||
A::Print { expression, span } => A::Print {
|
||||
expression: Box::new(transform_for_foreach(&expression)),
|
||||
span,
|
||||
},
|
||||
A::Return { value, span } => A::Return {
|
||||
value: value.as_ref().map(|v| Box::new(transform_for_foreach(v))),
|
||||
span,
|
||||
},
|
||||
A::Assignment {
|
||||
target,
|
||||
value,
|
||||
span,
|
||||
} => A::Assignment {
|
||||
target: Box::new(transform_for_foreach(&target)),
|
||||
value: Box::new(transform_for_foreach(&value)),
|
||||
span,
|
||||
},
|
||||
A::BinaryOp {
|
||||
operator,
|
||||
left,
|
||||
right,
|
||||
span,
|
||||
} => A::BinaryOp {
|
||||
operator,
|
||||
left: Box::new(transform_for_foreach(&left)),
|
||||
right: Box::new(transform_for_foreach(&right)),
|
||||
span,
|
||||
},
|
||||
A::UnaryOp {
|
||||
operator,
|
||||
operand,
|
||||
span,
|
||||
} => A::UnaryOp {
|
||||
operator,
|
||||
operand: Box::new(transform_for_foreach(&operand)),
|
||||
span,
|
||||
},
|
||||
A::MethodCall {
|
||||
object,
|
||||
method,
|
||||
arguments,
|
||||
span,
|
||||
} => A::MethodCall {
|
||||
object: Box::new(transform_for_foreach(&object)),
|
||||
method,
|
||||
arguments: arguments.iter().map(|a| transform_for_foreach(a)).collect(),
|
||||
span,
|
||||
},
|
||||
A::FunctionCall {
|
||||
name,
|
||||
arguments,
|
||||
span,
|
||||
} => A::FunctionCall {
|
||||
name,
|
||||
arguments: arguments.iter().map(|a| transform_for_foreach(a)).collect(),
|
||||
span,
|
||||
},
|
||||
A::ArrayLiteral { elements, span } => A::ArrayLiteral {
|
||||
elements: elements.iter().map(|e| transform_for_foreach(e)).collect(),
|
||||
span,
|
||||
},
|
||||
A::MapLiteral { entries, span } => A::MapLiteral {
|
||||
entries: entries
|
||||
.iter()
|
||||
.map(|(k, v)| (k.clone(), transform_for_foreach(v)))
|
||||
.collect(),
|
||||
span,
|
||||
},
|
||||
other => other,
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,24 +1,123 @@
|
||||
pub(super) fn transform_if_to_loopform(ast: &nyash_rust::ASTNode) -> nyash_rust::ASTNode {
|
||||
use nyash_rust::ast::{ASTNode as A, Span};
|
||||
match ast.clone() {
|
||||
A::Program { statements, span } => A::Program { statements: statements.into_iter().map(|n| transform_if_to_loopform(&n)).collect(), span },
|
||||
A::If { condition, then_body, else_body, span } => {
|
||||
A::Program { statements, span } => A::Program {
|
||||
statements: statements
|
||||
.into_iter()
|
||||
.map(|n| transform_if_to_loopform(&n))
|
||||
.collect(),
|
||||
span,
|
||||
},
|
||||
A::If {
|
||||
condition,
|
||||
then_body,
|
||||
else_body,
|
||||
span,
|
||||
} => {
|
||||
let cond_t = Box::new(transform_if_to_loopform(&condition));
|
||||
let then_t = then_body.into_iter().map(|n| transform_if_to_loopform(&n)).collect();
|
||||
let else_t = else_body.map(|v| v.into_iter().map(|n| transform_if_to_loopform(&n)).collect());
|
||||
let inner_if = A::If { condition: cond_t, then_body: then_t, else_body: else_t, span: Span::unknown() };
|
||||
let one = A::Literal { value: nyash_rust::ast::LiteralValue::Integer(1), span: Span::unknown() };
|
||||
let loop_body = vec![inner_if, A::Break { span: Span::unknown() }];
|
||||
A::Loop { condition: Box::new(one), body: loop_body, span }
|
||||
let then_t = then_body
|
||||
.into_iter()
|
||||
.map(|n| transform_if_to_loopform(&n))
|
||||
.collect();
|
||||
let else_t = else_body.map(|v| {
|
||||
v.into_iter()
|
||||
.map(|n| transform_if_to_loopform(&n))
|
||||
.collect()
|
||||
});
|
||||
let inner_if = A::If {
|
||||
condition: cond_t,
|
||||
then_body: then_t,
|
||||
else_body: else_t,
|
||||
span: Span::unknown(),
|
||||
};
|
||||
let one = A::Literal {
|
||||
value: nyash_rust::ast::LiteralValue::Integer(1),
|
||||
span: Span::unknown(),
|
||||
};
|
||||
let loop_body = vec![
|
||||
inner_if,
|
||||
A::Break {
|
||||
span: Span::unknown(),
|
||||
},
|
||||
];
|
||||
A::Loop {
|
||||
condition: Box::new(one),
|
||||
body: loop_body,
|
||||
span,
|
||||
}
|
||||
}
|
||||
A::Loop { condition, body, span } => A::Loop { condition: Box::new(transform_if_to_loopform(&condition)), body: body.into_iter().map(|n| transform_if_to_loopform(&n)).collect(), span },
|
||||
A::BinaryOp { operator, left, right, span } => A::BinaryOp { operator, left: Box::new(transform_if_to_loopform(&left)), right: Box::new(transform_if_to_loopform(&right)), span },
|
||||
A::UnaryOp { operator, operand, span } => A::UnaryOp { operator, operand: Box::new(transform_if_to_loopform(&operand)), span },
|
||||
A::MethodCall { object, method, arguments, span } => A::MethodCall { object: Box::new(transform_if_to_loopform(&object)), method, arguments: arguments.into_iter().map(|a| transform_if_to_loopform(&a)).collect(), span },
|
||||
A::FunctionCall { name, arguments, span } => A::FunctionCall { name, arguments: arguments.into_iter().map(|a| transform_if_to_loopform(&a)).collect(), span },
|
||||
A::ArrayLiteral { elements, span } => A::ArrayLiteral { elements: elements.into_iter().map(|e| transform_if_to_loopform(&e)).collect(), span },
|
||||
A::MapLiteral { entries, span } => A::MapLiteral { entries: entries.into_iter().map(|(k, v)| (k, transform_if_to_loopform(&v))).collect(), span },
|
||||
A::Loop {
|
||||
condition,
|
||||
body,
|
||||
span,
|
||||
} => A::Loop {
|
||||
condition: Box::new(transform_if_to_loopform(&condition)),
|
||||
body: body
|
||||
.into_iter()
|
||||
.map(|n| transform_if_to_loopform(&n))
|
||||
.collect(),
|
||||
span,
|
||||
},
|
||||
A::BinaryOp {
|
||||
operator,
|
||||
left,
|
||||
right,
|
||||
span,
|
||||
} => A::BinaryOp {
|
||||
operator,
|
||||
left: Box::new(transform_if_to_loopform(&left)),
|
||||
right: Box::new(transform_if_to_loopform(&right)),
|
||||
span,
|
||||
},
|
||||
A::UnaryOp {
|
||||
operator,
|
||||
operand,
|
||||
span,
|
||||
} => A::UnaryOp {
|
||||
operator,
|
||||
operand: Box::new(transform_if_to_loopform(&operand)),
|
||||
span,
|
||||
},
|
||||
A::MethodCall {
|
||||
object,
|
||||
method,
|
||||
arguments,
|
||||
span,
|
||||
} => A::MethodCall {
|
||||
object: Box::new(transform_if_to_loopform(&object)),
|
||||
method,
|
||||
arguments: arguments
|
||||
.into_iter()
|
||||
.map(|a| transform_if_to_loopform(&a))
|
||||
.collect(),
|
||||
span,
|
||||
},
|
||||
A::FunctionCall {
|
||||
name,
|
||||
arguments,
|
||||
span,
|
||||
} => A::FunctionCall {
|
||||
name,
|
||||
arguments: arguments
|
||||
.into_iter()
|
||||
.map(|a| transform_if_to_loopform(&a))
|
||||
.collect(),
|
||||
span,
|
||||
},
|
||||
A::ArrayLiteral { elements, span } => A::ArrayLiteral {
|
||||
elements: elements
|
||||
.into_iter()
|
||||
.map(|e| transform_if_to_loopform(&e))
|
||||
.collect(),
|
||||
span,
|
||||
},
|
||||
A::MapLiteral { entries, span } => A::MapLiteral {
|
||||
entries: entries
|
||||
.into_iter()
|
||||
.map(|(k, v)| (k, transform_if_to_loopform(&v)))
|
||||
.collect(),
|
||||
span,
|
||||
},
|
||||
other => other,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -2,86 +2,302 @@ pub(super) fn transform_lift_nested_functions(ast: &nyash_rust::ASTNode) -> nyas
|
||||
use nyash_rust::ast::ASTNode as A;
|
||||
use std::sync::atomic::{AtomicUsize, Ordering};
|
||||
static COUNTER: AtomicUsize = AtomicUsize::new(0);
|
||||
fn gensym(base: &str) -> String { let n = COUNTER.fetch_add(1, Ordering::Relaxed); format!("__ny_lifted_{}_{}", base, n) }
|
||||
fn gensym(base: &str) -> String {
|
||||
let n = COUNTER.fetch_add(1, Ordering::Relaxed);
|
||||
format!("__ny_lifted_{}_{}", base, n)
|
||||
}
|
||||
fn collect_locals(n: &A, set: &mut std::collections::HashSet<String>) {
|
||||
match n {
|
||||
A::Local { variables, .. } => { for v in variables { set.insert(v.clone()); } }
|
||||
A::Program { statements, .. } => for s in statements { collect_locals(s, set); },
|
||||
A::FunctionDeclaration { body, .. } => for s in body { collect_locals(s, set); },
|
||||
A::If { then_body, else_body, .. } => { for s in then_body { collect_locals(s, set); } if let Some(b) = else_body { for s in b { collect_locals(s, set); } } }
|
||||
A::Local { variables, .. } => {
|
||||
for v in variables {
|
||||
set.insert(v.clone());
|
||||
}
|
||||
}
|
||||
A::Program { statements, .. } => {
|
||||
for s in statements {
|
||||
collect_locals(s, set);
|
||||
}
|
||||
}
|
||||
A::FunctionDeclaration { body, .. } => {
|
||||
for s in body {
|
||||
collect_locals(s, set);
|
||||
}
|
||||
}
|
||||
A::If {
|
||||
then_body,
|
||||
else_body,
|
||||
..
|
||||
} => {
|
||||
for s in then_body {
|
||||
collect_locals(s, set);
|
||||
}
|
||||
if let Some(b) = else_body {
|
||||
for s in b {
|
||||
collect_locals(s, set);
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
fn collect_vars(n: &A, set: &mut std::collections::HashSet<String>) {
|
||||
match n {
|
||||
A::Variable { name, .. } => { set.insert(name.clone()); }
|
||||
A::Program { statements, .. } => for s in statements { collect_vars(s, set); },
|
||||
A::FunctionDeclaration { body, .. } => for s in body { collect_vars(s, set); },
|
||||
A::If { condition, then_body, else_body, .. } => {
|
||||
collect_vars(condition, set);
|
||||
for s in then_body { collect_vars(s, set); }
|
||||
if let Some(b) = else_body { for s in b { collect_vars(s, set); } }
|
||||
A::Variable { name, .. } => {
|
||||
set.insert(name.clone());
|
||||
}
|
||||
A::Program { statements, .. } => {
|
||||
for s in statements {
|
||||
collect_vars(s, set);
|
||||
}
|
||||
}
|
||||
A::FunctionDeclaration { body, .. } => {
|
||||
for s in body {
|
||||
collect_vars(s, set);
|
||||
}
|
||||
}
|
||||
A::If {
|
||||
condition,
|
||||
then_body,
|
||||
else_body,
|
||||
..
|
||||
} => {
|
||||
collect_vars(condition, set);
|
||||
for s in then_body {
|
||||
collect_vars(s, set);
|
||||
}
|
||||
if let Some(b) = else_body {
|
||||
for s in b {
|
||||
collect_vars(s, set);
|
||||
}
|
||||
}
|
||||
}
|
||||
A::Assignment { target, value, .. } => {
|
||||
collect_vars(target, set);
|
||||
collect_vars(value, set);
|
||||
}
|
||||
A::Return { value, .. } => {
|
||||
if let Some(v) = value {
|
||||
collect_vars(v, set);
|
||||
}
|
||||
}
|
||||
A::Assignment { target, value, .. } => { collect_vars(target, set); collect_vars(value, set); }
|
||||
A::Return { value, .. } => { if let Some(v) = value { collect_vars(v, set); } }
|
||||
A::Print { expression, .. } => collect_vars(expression, set),
|
||||
A::BinaryOp { left, right, .. } => { collect_vars(left, set); collect_vars(right, set); }
|
||||
A::BinaryOp { left, right, .. } => {
|
||||
collect_vars(left, set);
|
||||
collect_vars(right, set);
|
||||
}
|
||||
A::UnaryOp { operand, .. } => collect_vars(operand, set),
|
||||
A::MethodCall { object, arguments, .. } => { collect_vars(object, set); for a in arguments { collect_vars(a, set); } }
|
||||
A::FunctionCall { arguments, .. } => { for a in arguments { collect_vars(a, set); } }
|
||||
A::ArrayLiteral { elements, .. } => { for e in elements { collect_vars(e, set); } }
|
||||
A::MapLiteral { entries, .. } => { for (_,v) in entries { collect_vars(v, set); } }
|
||||
A::MethodCall {
|
||||
object, arguments, ..
|
||||
} => {
|
||||
collect_vars(object, set);
|
||||
for a in arguments {
|
||||
collect_vars(a, set);
|
||||
}
|
||||
}
|
||||
A::FunctionCall { arguments, .. } => {
|
||||
for a in arguments {
|
||||
collect_vars(a, set);
|
||||
}
|
||||
}
|
||||
A::ArrayLiteral { elements, .. } => {
|
||||
for e in elements {
|
||||
collect_vars(e, set);
|
||||
}
|
||||
}
|
||||
A::MapLiteral { entries, .. } => {
|
||||
for (_, v) in entries {
|
||||
collect_vars(v, set);
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
fn rename_calls(n: &A, mapping: &std::collections::HashMap<String, String>) -> A {
|
||||
use nyash_rust::ast::ASTNode as A;
|
||||
match n.clone() {
|
||||
A::FunctionCall { name, arguments, span } => {
|
||||
A::FunctionCall {
|
||||
name,
|
||||
arguments,
|
||||
span,
|
||||
} => {
|
||||
let new_name = mapping.get(&name).cloned().unwrap_or(name);
|
||||
A::FunctionCall { name: new_name, arguments: arguments.into_iter().map(|a| rename_calls(&a, mapping)).collect(), span }
|
||||
A::FunctionCall {
|
||||
name: new_name,
|
||||
arguments: arguments
|
||||
.into_iter()
|
||||
.map(|a| rename_calls(&a, mapping))
|
||||
.collect(),
|
||||
span,
|
||||
}
|
||||
}
|
||||
A::Program { statements, span } => A::Program { statements: statements.into_iter().map(|s| rename_calls(&s, mapping)).collect(), span },
|
||||
A::FunctionDeclaration { name, params, body, is_static, is_override, span } => {
|
||||
A::FunctionDeclaration { name, params, body: body.into_iter().map(|s| rename_calls(&s, mapping)).collect(), is_static, is_override, span }
|
||||
}
|
||||
A::If { condition, then_body, else_body, span } => A::If {
|
||||
condition: Box::new(rename_calls(&condition, mapping)),
|
||||
then_body: then_body.into_iter().map(|s| rename_calls(&s, mapping)).collect(),
|
||||
else_body: else_body.map(|v| v.into_iter().map(|s| rename_calls(&s, mapping)).collect()),
|
||||
A::Program { statements, span } => A::Program {
|
||||
statements: statements
|
||||
.into_iter()
|
||||
.map(|s| rename_calls(&s, mapping))
|
||||
.collect(),
|
||||
span,
|
||||
},
|
||||
A::FunctionDeclaration {
|
||||
name,
|
||||
params,
|
||||
body,
|
||||
is_static,
|
||||
is_override,
|
||||
span,
|
||||
} => A::FunctionDeclaration {
|
||||
name,
|
||||
params,
|
||||
body: body
|
||||
.into_iter()
|
||||
.map(|s| rename_calls(&s, mapping))
|
||||
.collect(),
|
||||
is_static,
|
||||
is_override,
|
||||
span,
|
||||
},
|
||||
A::If {
|
||||
condition,
|
||||
then_body,
|
||||
else_body,
|
||||
span,
|
||||
} => A::If {
|
||||
condition: Box::new(rename_calls(&condition, mapping)),
|
||||
then_body: then_body
|
||||
.into_iter()
|
||||
.map(|s| rename_calls(&s, mapping))
|
||||
.collect(),
|
||||
else_body: else_body
|
||||
.map(|v| v.into_iter().map(|s| rename_calls(&s, mapping)).collect()),
|
||||
span,
|
||||
},
|
||||
A::Assignment {
|
||||
target,
|
||||
value,
|
||||
span,
|
||||
} => A::Assignment {
|
||||
target: Box::new(rename_calls(&target, mapping)),
|
||||
value: Box::new(rename_calls(&value, mapping)),
|
||||
span,
|
||||
},
|
||||
A::Return { value, span } => A::Return {
|
||||
value: value.as_ref().map(|v| Box::new(rename_calls(v, mapping))),
|
||||
span,
|
||||
},
|
||||
A::Print { expression, span } => A::Print {
|
||||
expression: Box::new(rename_calls(&expression, mapping)),
|
||||
span,
|
||||
},
|
||||
A::BinaryOp {
|
||||
operator,
|
||||
left,
|
||||
right,
|
||||
span,
|
||||
} => A::BinaryOp {
|
||||
operator,
|
||||
left: Box::new(rename_calls(&left, mapping)),
|
||||
right: Box::new(rename_calls(&right, mapping)),
|
||||
span,
|
||||
},
|
||||
A::UnaryOp {
|
||||
operator,
|
||||
operand,
|
||||
span,
|
||||
} => A::UnaryOp {
|
||||
operator,
|
||||
operand: Box::new(rename_calls(&operand, mapping)),
|
||||
span,
|
||||
},
|
||||
A::MethodCall {
|
||||
object,
|
||||
method,
|
||||
arguments,
|
||||
span,
|
||||
} => A::MethodCall {
|
||||
object: Box::new(rename_calls(&object, mapping)),
|
||||
method,
|
||||
arguments: arguments
|
||||
.into_iter()
|
||||
.map(|a| rename_calls(&a, mapping))
|
||||
.collect(),
|
||||
span,
|
||||
},
|
||||
A::ArrayLiteral { elements, span } => A::ArrayLiteral {
|
||||
elements: elements
|
||||
.into_iter()
|
||||
.map(|e| rename_calls(&e, mapping))
|
||||
.collect(),
|
||||
span,
|
||||
},
|
||||
A::MapLiteral { entries, span } => A::MapLiteral {
|
||||
entries: entries
|
||||
.into_iter()
|
||||
.map(|(k, v)| (k, rename_calls(&v, mapping)))
|
||||
.collect(),
|
||||
span,
|
||||
},
|
||||
A::Assignment { target, value, span } => A::Assignment { target: Box::new(rename_calls(&target, mapping)), value: Box::new(rename_calls(&value, mapping)), span },
|
||||
A::Return { value, span } => A::Return { value: value.as_ref().map(|v| Box::new(rename_calls(v, mapping))), span },
|
||||
A::Print { expression, span } => A::Print { expression: Box::new(rename_calls(&expression, mapping)), span },
|
||||
A::BinaryOp { operator, left, right, span } => A::BinaryOp { operator, left: Box::new(rename_calls(&left, mapping)), right: Box::new(rename_calls(&right, mapping)), span },
|
||||
A::UnaryOp { operator, operand, span } => A::UnaryOp { operator, operand: Box::new(rename_calls(&operand, mapping)), span },
|
||||
A::MethodCall { object, method, arguments, span } => A::MethodCall { object: Box::new(rename_calls(&object, mapping)), method, arguments: arguments.into_iter().map(|a| rename_calls(&a, mapping)).collect(), span },
|
||||
A::ArrayLiteral { elements, span } => A::ArrayLiteral { elements: elements.into_iter().map(|e| rename_calls(&e, mapping)).collect(), span },
|
||||
A::MapLiteral { entries, span } => A::MapLiteral { entries: entries.into_iter().map(|(k,v)| (k, rename_calls(&v, mapping))).collect(), span },
|
||||
other => other,
|
||||
}
|
||||
}
|
||||
fn lift_in_body(body: Vec<A>, hoisted: &mut Vec<A>, mapping: &mut std::collections::HashMap<String,String>) -> Vec<A> {
|
||||
fn lift_in_body(
|
||||
body: Vec<A>,
|
||||
hoisted: &mut Vec<A>,
|
||||
mapping: &mut std::collections::HashMap<String, String>,
|
||||
) -> Vec<A> {
|
||||
use std::collections::HashSet;
|
||||
let mut out: Vec<A> = Vec::new();
|
||||
for st in body.into_iter() {
|
||||
match st.clone() {
|
||||
A::FunctionDeclaration { name, params, body, is_static, is_override, span } => {
|
||||
A::FunctionDeclaration {
|
||||
name,
|
||||
params,
|
||||
body,
|
||||
is_static,
|
||||
is_override,
|
||||
span,
|
||||
} => {
|
||||
let mut locals: HashSet<String> = HashSet::new();
|
||||
collect_locals(&A::FunctionDeclaration{ name: name.clone(), params: params.clone(), body: body.clone(), is_static, is_override, span }, &mut locals);
|
||||
collect_locals(
|
||||
&A::FunctionDeclaration {
|
||||
name: name.clone(),
|
||||
params: params.clone(),
|
||||
body: body.clone(),
|
||||
is_static,
|
||||
is_override,
|
||||
span,
|
||||
},
|
||||
&mut locals,
|
||||
);
|
||||
let mut used: HashSet<String> = HashSet::new();
|
||||
collect_vars(&A::FunctionDeclaration{ name: name.clone(), params: params.clone(), body: body.clone(), is_static, is_override, span }, &mut used);
|
||||
collect_vars(
|
||||
&A::FunctionDeclaration {
|
||||
name: name.clone(),
|
||||
params: params.clone(),
|
||||
body: body.clone(),
|
||||
is_static,
|
||||
is_override,
|
||||
span,
|
||||
},
|
||||
&mut used,
|
||||
);
|
||||
let params_set: HashSet<String> = params.iter().cloned().collect();
|
||||
let mut extra: HashSet<String> = used.drain().collect();
|
||||
extra.retain(|v| !params_set.contains(v) && !locals.contains(v));
|
||||
if extra.is_empty() {
|
||||
let new_name = gensym(&name);
|
||||
let lifted = A::FunctionDeclaration { name: new_name.clone(), params, body, is_static: true, is_override, span };
|
||||
let lifted = A::FunctionDeclaration {
|
||||
name: new_name.clone(),
|
||||
params,
|
||||
body,
|
||||
is_static: true,
|
||||
is_override,
|
||||
span,
|
||||
};
|
||||
hoisted.push(lifted);
|
||||
mapping.insert(name, new_name);
|
||||
continue;
|
||||
} else { out.push(st); }
|
||||
} else {
|
||||
out.push(st);
|
||||
}
|
||||
}
|
||||
other => out.push(other),
|
||||
}
|
||||
@ -93,30 +309,115 @@ pub(super) fn transform_lift_nested_functions(ast: &nyash_rust::ASTNode) -> nyas
|
||||
match n.clone() {
|
||||
A::Program { statements, span } => {
|
||||
let mut mapping = std::collections::HashMap::new();
|
||||
let stmts2 = lift_in_body(statements.into_iter().map(|s| walk(&s, hoisted)).collect(), hoisted, &mut mapping);
|
||||
A::Program { statements: stmts2, span }
|
||||
let stmts2 = lift_in_body(
|
||||
statements.into_iter().map(|s| walk(&s, hoisted)).collect(),
|
||||
hoisted,
|
||||
&mut mapping,
|
||||
);
|
||||
A::Program {
|
||||
statements: stmts2,
|
||||
span,
|
||||
}
|
||||
}
|
||||
A::FunctionDeclaration { name, params, body, is_static, is_override, span } => {
|
||||
A::FunctionDeclaration {
|
||||
name,
|
||||
params,
|
||||
body,
|
||||
is_static,
|
||||
is_override,
|
||||
span,
|
||||
} => {
|
||||
let mut mapping = std::collections::HashMap::new();
|
||||
let body2: Vec<A> = body.into_iter().map(|s| walk(&s, hoisted)).collect();
|
||||
let body3 = lift_in_body(body2, hoisted, &mut mapping);
|
||||
A::FunctionDeclaration { name, params, body: body3, is_static, is_override, span }
|
||||
A::FunctionDeclaration {
|
||||
name,
|
||||
params,
|
||||
body: body3,
|
||||
is_static,
|
||||
is_override,
|
||||
span,
|
||||
}
|
||||
}
|
||||
A::If { condition, then_body, else_body, span } => A::If {
|
||||
A::If {
|
||||
condition,
|
||||
then_body,
|
||||
else_body,
|
||||
span,
|
||||
} => A::If {
|
||||
condition: Box::new(walk(&condition, hoisted)),
|
||||
then_body: then_body.into_iter().map(|s| walk(&s, hoisted)).collect(),
|
||||
else_body: else_body.map(|v| v.into_iter().map(|s| walk(&s, hoisted)).collect()),
|
||||
span,
|
||||
},
|
||||
A::Assignment { target, value, span } => A::Assignment { target: Box::new(walk(&target, hoisted)), value: Box::new(walk(&value, hoisted)), span },
|
||||
A::Return { value, span } => A::Return { value: value.as_ref().map(|v| Box::new(walk(v, hoisted))), span },
|
||||
A::Print { expression, span } => A::Print { expression: Box::new(walk(&expression, hoisted)), span },
|
||||
A::BinaryOp { operator, left, right, span } => A::BinaryOp { operator, left: Box::new(walk(&left, hoisted)), right: Box::new(walk(&right, hoisted)), span },
|
||||
A::UnaryOp { operator, operand, span } => A::UnaryOp { operator, operand: Box::new(walk(&operand, hoisted)), span },
|
||||
A::MethodCall { object, method, arguments, span } => A::MethodCall { object: Box::new(walk(&object, hoisted)), method, arguments: arguments.into_iter().map(|a| walk(&a, hoisted)).collect(), span },
|
||||
A::FunctionCall { name, arguments, span } => A::FunctionCall { name, arguments: arguments.into_iter().map(|a| walk(&a, hoisted)).collect(), span },
|
||||
A::ArrayLiteral { elements, span } => A::ArrayLiteral { elements: elements.into_iter().map(|e| walk(&e, hoisted)).collect(), span },
|
||||
A::MapLiteral { entries, span } => A::MapLiteral { entries: entries.into_iter().map(|(k,v)| (k, walk(&v, hoisted))).collect(), span },
|
||||
A::Assignment {
|
||||
target,
|
||||
value,
|
||||
span,
|
||||
} => A::Assignment {
|
||||
target: Box::new(walk(&target, hoisted)),
|
||||
value: Box::new(walk(&value, hoisted)),
|
||||
span,
|
||||
},
|
||||
A::Return { value, span } => A::Return {
|
||||
value: value.as_ref().map(|v| Box::new(walk(v, hoisted))),
|
||||
span,
|
||||
},
|
||||
A::Print { expression, span } => A::Print {
|
||||
expression: Box::new(walk(&expression, hoisted)),
|
||||
span,
|
||||
},
|
||||
A::BinaryOp {
|
||||
operator,
|
||||
left,
|
||||
right,
|
||||
span,
|
||||
} => A::BinaryOp {
|
||||
operator,
|
||||
left: Box::new(walk(&left, hoisted)),
|
||||
right: Box::new(walk(&right, hoisted)),
|
||||
span,
|
||||
},
|
||||
A::UnaryOp {
|
||||
operator,
|
||||
operand,
|
||||
span,
|
||||
} => A::UnaryOp {
|
||||
operator,
|
||||
operand: Box::new(walk(&operand, hoisted)),
|
||||
span,
|
||||
},
|
||||
A::MethodCall {
|
||||
object,
|
||||
method,
|
||||
arguments,
|
||||
span,
|
||||
} => A::MethodCall {
|
||||
object: Box::new(walk(&object, hoisted)),
|
||||
method,
|
||||
arguments: arguments.into_iter().map(|a| walk(&a, hoisted)).collect(),
|
||||
span,
|
||||
},
|
||||
A::FunctionCall {
|
||||
name,
|
||||
arguments,
|
||||
span,
|
||||
} => A::FunctionCall {
|
||||
name,
|
||||
arguments: arguments.into_iter().map(|a| walk(&a, hoisted)).collect(),
|
||||
span,
|
||||
},
|
||||
A::ArrayLiteral { elements, span } => A::ArrayLiteral {
|
||||
elements: elements.into_iter().map(|e| walk(&e, hoisted)).collect(),
|
||||
span,
|
||||
},
|
||||
A::MapLiteral { entries, span } => A::MapLiteral {
|
||||
entries: entries
|
||||
.into_iter()
|
||||
.map(|(k, v)| (k, walk(&v, hoisted)))
|
||||
.collect(),
|
||||
span,
|
||||
},
|
||||
other => other,
|
||||
}
|
||||
}
|
||||
@ -125,8 +426,10 @@ pub(super) fn transform_lift_nested_functions(ast: &nyash_rust::ASTNode) -> nyas
|
||||
if let A::Program { statements, span } = out.clone() {
|
||||
let mut ss = statements;
|
||||
ss.extend(hoisted.into_iter());
|
||||
out = A::Program { statements: ss, span };
|
||||
out = A::Program {
|
||||
statements: ss,
|
||||
span,
|
||||
};
|
||||
}
|
||||
out
|
||||
}
|
||||
|
||||
|
||||
@ -1,25 +1,103 @@
|
||||
pub(super) fn transform_loop_normalize(ast: &nyash_rust::ASTNode) -> nyash_rust::ASTNode {
|
||||
use nyash_rust::ast::ASTNode as A;
|
||||
match ast.clone() {
|
||||
A::Program { statements, span } => A::Program { statements: statements.into_iter().map(|n| transform_loop_normalize(&n)).collect(), span },
|
||||
A::If { condition, then_body, else_body, span } => A::If {
|
||||
condition: Box::new(transform_loop_normalize(&condition)),
|
||||
then_body: then_body.into_iter().map(|n| transform_loop_normalize(&n)).collect(),
|
||||
else_body: else_body.map(|v| v.into_iter().map(|n| transform_loop_normalize(&n)).collect()),
|
||||
A::Program { statements, span } => A::Program {
|
||||
statements: statements
|
||||
.into_iter()
|
||||
.map(|n| transform_loop_normalize(&n))
|
||||
.collect(),
|
||||
span,
|
||||
},
|
||||
A::Loop { condition, body, span } => A::Loop {
|
||||
A::If {
|
||||
condition,
|
||||
then_body,
|
||||
else_body,
|
||||
span,
|
||||
} => A::If {
|
||||
condition: Box::new(transform_loop_normalize(&condition)),
|
||||
body: body.into_iter().map(|n| transform_loop_normalize(&n)).collect(),
|
||||
then_body: then_body
|
||||
.into_iter()
|
||||
.map(|n| transform_loop_normalize(&n))
|
||||
.collect(),
|
||||
else_body: else_body.map(|v| {
|
||||
v.into_iter()
|
||||
.map(|n| transform_loop_normalize(&n))
|
||||
.collect()
|
||||
}),
|
||||
span,
|
||||
},
|
||||
A::Loop {
|
||||
condition,
|
||||
body,
|
||||
span,
|
||||
} => A::Loop {
|
||||
condition: Box::new(transform_loop_normalize(&condition)),
|
||||
body: body
|
||||
.into_iter()
|
||||
.map(|n| transform_loop_normalize(&n))
|
||||
.collect(),
|
||||
span,
|
||||
},
|
||||
A::BinaryOp {
|
||||
operator,
|
||||
left,
|
||||
right,
|
||||
span,
|
||||
} => A::BinaryOp {
|
||||
operator,
|
||||
left: Box::new(transform_loop_normalize(&left)),
|
||||
right: Box::new(transform_loop_normalize(&right)),
|
||||
span,
|
||||
},
|
||||
A::UnaryOp {
|
||||
operator,
|
||||
operand,
|
||||
span,
|
||||
} => A::UnaryOp {
|
||||
operator,
|
||||
operand: Box::new(transform_loop_normalize(&operand)),
|
||||
span,
|
||||
},
|
||||
A::MethodCall {
|
||||
object,
|
||||
method,
|
||||
arguments,
|
||||
span,
|
||||
} => A::MethodCall {
|
||||
object: Box::new(transform_loop_normalize(&object)),
|
||||
method,
|
||||
arguments: arguments
|
||||
.into_iter()
|
||||
.map(|a| transform_loop_normalize(&a))
|
||||
.collect(),
|
||||
span,
|
||||
},
|
||||
A::FunctionCall {
|
||||
name,
|
||||
arguments,
|
||||
span,
|
||||
} => A::FunctionCall {
|
||||
name,
|
||||
arguments: arguments
|
||||
.into_iter()
|
||||
.map(|a| transform_loop_normalize(&a))
|
||||
.collect(),
|
||||
span,
|
||||
},
|
||||
A::ArrayLiteral { elements, span } => A::ArrayLiteral {
|
||||
elements: elements
|
||||
.into_iter()
|
||||
.map(|e| transform_loop_normalize(&e))
|
||||
.collect(),
|
||||
span,
|
||||
},
|
||||
A::MapLiteral { entries, span } => A::MapLiteral {
|
||||
entries: entries
|
||||
.into_iter()
|
||||
.map(|(k, v)| (k, transform_loop_normalize(&v)))
|
||||
.collect(),
|
||||
span,
|
||||
},
|
||||
A::BinaryOp { operator, left, right, span } => A::BinaryOp { operator, left: Box::new(transform_loop_normalize(&left)), right: Box::new(transform_loop_normalize(&right)), span },
|
||||
A::UnaryOp { operator, operand, span } => A::UnaryOp { operator, operand: Box::new(transform_loop_normalize(&operand)), span },
|
||||
A::MethodCall { object, method, arguments, span } => A::MethodCall { object: Box::new(transform_loop_normalize(&object)), method, arguments: arguments.into_iter().map(|a| transform_loop_normalize(&a)).collect(), span },
|
||||
A::FunctionCall { name, arguments, span } => A::FunctionCall { name, arguments: arguments.into_iter().map(|a| transform_loop_normalize(&a)).collect(), span },
|
||||
A::ArrayLiteral { elements, span } => A::ArrayLiteral { elements: elements.into_iter().map(|e| transform_loop_normalize(&e)).collect(), span },
|
||||
A::MapLiteral { entries, span } => A::MapLiteral { entries: entries.into_iter().map(|(k,v)| (k, transform_loop_normalize(&v))).collect(), span },
|
||||
other => other,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -5,31 +5,94 @@ pub(super) fn transform_map_insert_tag(ast: &nyash_rust::ASTNode) -> nyash_rust:
|
||||
let mut new_entries: Vec<(String, A)> = Vec::with_capacity(entries.len() + 1);
|
||||
let already_tagged = entries.get(0).map(|(k, _)| k == "__macro").unwrap_or(false);
|
||||
if already_tagged {
|
||||
for (k, v) in entries { new_entries.push((k.clone(), transform_map_insert_tag(v))); }
|
||||
for (k, v) in entries {
|
||||
new_entries.push((k.clone(), transform_map_insert_tag(v)));
|
||||
}
|
||||
} else {
|
||||
new_entries.push((
|
||||
"__macro".to_string(),
|
||||
A::Literal { value: LiteralValue::String("on".to_string()), span: Span::unknown() },
|
||||
A::Literal {
|
||||
value: LiteralValue::String("on".to_string()),
|
||||
span: Span::unknown(),
|
||||
},
|
||||
));
|
||||
for (k, v) in entries { new_entries.push((k.clone(), transform_map_insert_tag(v))); }
|
||||
for (k, v) in entries {
|
||||
new_entries.push((k.clone(), transform_map_insert_tag(v)));
|
||||
}
|
||||
}
|
||||
A::MapLiteral {
|
||||
entries: new_entries,
|
||||
span: Span::unknown(),
|
||||
}
|
||||
A::MapLiteral { entries: new_entries, span: Span::unknown() }
|
||||
}
|
||||
A::Program { statements, .. } => A::Program { statements: statements.iter().map(transform_map_insert_tag).collect(), span: Span::unknown() },
|
||||
A::Print { expression, .. } => A::Print { expression: Box::new(transform_map_insert_tag(expression)), span: Span::unknown() },
|
||||
A::Return { value, .. } => A::Return { value: value.as_ref().map(|v| Box::new(transform_map_insert_tag(v))), span: Span::unknown() },
|
||||
A::Assignment { target, value, .. } => A::Assignment { target: Box::new(transform_map_insert_tag(target)), value: Box::new(transform_map_insert_tag(value)), span: Span::unknown() },
|
||||
A::If { condition, then_body, else_body, .. } => A::If {
|
||||
condition: Box::new(transform_map_insert_tag(condition)),
|
||||
then_body: then_body.iter().map(transform_map_insert_tag).collect(),
|
||||
else_body: else_body.as_ref().map(|v| v.iter().map(transform_map_insert_tag).collect()),
|
||||
A::Program { statements, .. } => A::Program {
|
||||
statements: statements.iter().map(transform_map_insert_tag).collect(),
|
||||
span: Span::unknown(),
|
||||
},
|
||||
A::Print { expression, .. } => A::Print {
|
||||
expression: Box::new(transform_map_insert_tag(expression)),
|
||||
span: Span::unknown(),
|
||||
},
|
||||
A::Return { value, .. } => A::Return {
|
||||
value: value
|
||||
.as_ref()
|
||||
.map(|v| Box::new(transform_map_insert_tag(v))),
|
||||
span: Span::unknown(),
|
||||
},
|
||||
A::Assignment { target, value, .. } => A::Assignment {
|
||||
target: Box::new(transform_map_insert_tag(target)),
|
||||
value: Box::new(transform_map_insert_tag(value)),
|
||||
span: Span::unknown(),
|
||||
},
|
||||
A::If {
|
||||
condition,
|
||||
then_body,
|
||||
else_body,
|
||||
..
|
||||
} => A::If {
|
||||
condition: Box::new(transform_map_insert_tag(condition)),
|
||||
then_body: then_body.iter().map(transform_map_insert_tag).collect(),
|
||||
else_body: else_body
|
||||
.as_ref()
|
||||
.map(|v| v.iter().map(transform_map_insert_tag).collect()),
|
||||
span: Span::unknown(),
|
||||
},
|
||||
A::BinaryOp {
|
||||
operator,
|
||||
left,
|
||||
right,
|
||||
..
|
||||
} => A::BinaryOp {
|
||||
operator: operator.clone(),
|
||||
left: Box::new(transform_map_insert_tag(left)),
|
||||
right: Box::new(transform_map_insert_tag(right)),
|
||||
span: Span::unknown(),
|
||||
},
|
||||
A::UnaryOp {
|
||||
operator, operand, ..
|
||||
} => A::UnaryOp {
|
||||
operator: operator.clone(),
|
||||
operand: Box::new(transform_map_insert_tag(operand)),
|
||||
span: Span::unknown(),
|
||||
},
|
||||
A::MethodCall {
|
||||
object,
|
||||
method,
|
||||
arguments,
|
||||
..
|
||||
} => A::MethodCall {
|
||||
object: Box::new(transform_map_insert_tag(object)),
|
||||
method: method.clone(),
|
||||
arguments: arguments.iter().map(transform_map_insert_tag).collect(),
|
||||
span: Span::unknown(),
|
||||
},
|
||||
A::FunctionCall {
|
||||
name, arguments, ..
|
||||
} => A::FunctionCall {
|
||||
name: name.clone(),
|
||||
arguments: arguments.iter().map(transform_map_insert_tag).collect(),
|
||||
span: Span::unknown(),
|
||||
},
|
||||
A::BinaryOp { operator, left, right, .. } => A::BinaryOp { operator: operator.clone(), left: Box::new(transform_map_insert_tag(left)), right: Box::new(transform_map_insert_tag(right)), span: Span::unknown() },
|
||||
A::UnaryOp { operator, operand, .. } => A::UnaryOp { operator: operator.clone(), operand: Box::new(transform_map_insert_tag(operand)), span: Span::unknown() },
|
||||
A::MethodCall { object, method, arguments, .. } => A::MethodCall { object: Box::new(transform_map_insert_tag(object)), method: method.clone(), arguments: arguments.iter().map(transform_map_insert_tag).collect(), span: Span::unknown() },
|
||||
A::FunctionCall { name, arguments, .. } => A::FunctionCall { name: name.clone(), arguments: arguments.iter().map(transform_map_insert_tag).collect(), span: Span::unknown() },
|
||||
other => other.clone(),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -2,15 +2,15 @@
|
||||
* Macro child transforms — split modules
|
||||
*/
|
||||
|
||||
mod peek;
|
||||
mod array;
|
||||
mod map;
|
||||
mod loops;
|
||||
mod foreach;
|
||||
mod scopebox;
|
||||
mod lift;
|
||||
mod if_to_loopform;
|
||||
mod lift;
|
||||
mod loops;
|
||||
mod map;
|
||||
mod peek;
|
||||
mod postfix;
|
||||
mod scopebox;
|
||||
|
||||
// Re-exported via thin wrappers to keep names stable
|
||||
pub(super) fn transform_peek_match_literal(ast: &nyash_rust::ASTNode) -> nyash_rust::ASTNode {
|
||||
@ -47,15 +47,33 @@ pub fn normalize_core_pass(ast: &nyash_rust::ASTNode) -> nyash_rust::ASTNode {
|
||||
let a1 = transform_for_foreach(ast);
|
||||
let a2 = transform_peek_match_literal(&a1);
|
||||
let a3 = transform_loop_normalize(&a2);
|
||||
let a4 = if std::env::var("NYASH_SCOPEBOX_ENABLE").ok().map(|v| v=="1"||v=="true"||v=="on").unwrap_or(false) {
|
||||
let a4 = if std::env::var("NYASH_SCOPEBOX_ENABLE")
|
||||
.ok()
|
||||
.map(|v| v == "1" || v == "true" || v == "on")
|
||||
.unwrap_or(false)
|
||||
{
|
||||
transform_scopebox_inject(&a3)
|
||||
} else { a3 };
|
||||
} else {
|
||||
a3
|
||||
};
|
||||
let a4b = transform_lift_nested_functions(&a4);
|
||||
let a5 = if std::env::var("NYASH_IF_AS_LOOPFORM").ok().map(|v| v=="1"||v=="true"||v=="on").unwrap_or(false) {
|
||||
let a5 = if std::env::var("NYASH_IF_AS_LOOPFORM")
|
||||
.ok()
|
||||
.map(|v| v == "1" || v == "true" || v == "on")
|
||||
.unwrap_or(false)
|
||||
{
|
||||
transform_if_to_loopform(&a4b)
|
||||
} else { a4b };
|
||||
let a6 = if std::env::var("NYASH_CATCH_NEW").ok().map(|v| v=="1"||v=="true"||v=="on").unwrap_or(false) {
|
||||
} else {
|
||||
a4b
|
||||
};
|
||||
let a6 = if std::env::var("NYASH_CATCH_NEW")
|
||||
.ok()
|
||||
.map(|v| v == "1" || v == "true" || v == "on")
|
||||
.unwrap_or(false)
|
||||
{
|
||||
transform_postfix_handlers(&a5)
|
||||
} else { a5 };
|
||||
} else {
|
||||
a5
|
||||
};
|
||||
a6
|
||||
}
|
||||
|
||||
@ -1,113 +1,329 @@
|
||||
fn map_expr_to_stmt(e: nyash_rust::ASTNode) -> nyash_rust::ASTNode { e }
|
||||
fn map_expr_to_stmt(e: nyash_rust::ASTNode) -> nyash_rust::ASTNode {
|
||||
e
|
||||
}
|
||||
|
||||
fn transform_peek_to_if_expr(peek: &nyash_rust::ASTNode) -> Option<nyash_rust::ASTNode> {
|
||||
use nyash_rust::ast::{ASTNode as A, BinaryOperator, Span};
|
||||
if let A::MatchExpr { scrutinee, arms, else_expr, .. } = peek {
|
||||
if let A::MatchExpr {
|
||||
scrutinee,
|
||||
arms,
|
||||
else_expr,
|
||||
..
|
||||
} = peek
|
||||
{
|
||||
let mut conds_bodies: Vec<(nyash_rust::ast::LiteralValue, A)> = Vec::new();
|
||||
for (lit, body) in arms { conds_bodies.push((lit.clone(), (*body).clone())); }
|
||||
for (lit, body) in arms {
|
||||
conds_bodies.push((lit.clone(), (*body).clone()));
|
||||
}
|
||||
let mut current: A = *(*else_expr).clone();
|
||||
for (lit, body) in conds_bodies.into_iter().rev() {
|
||||
let rhs = A::Literal { value: lit, span: Span::unknown() };
|
||||
let cond = A::BinaryOp { operator: BinaryOperator::Equal, left: scrutinee.clone(), right: Box::new(rhs), span: Span::unknown() };
|
||||
let rhs = A::Literal {
|
||||
value: lit,
|
||||
span: Span::unknown(),
|
||||
};
|
||||
let cond = A::BinaryOp {
|
||||
operator: BinaryOperator::Equal,
|
||||
left: scrutinee.clone(),
|
||||
right: Box::new(rhs),
|
||||
span: Span::unknown(),
|
||||
};
|
||||
let then_body = vec![map_expr_to_stmt(body)];
|
||||
let else_body = Some(vec![map_expr_to_stmt(current)]);
|
||||
current = A::If { condition: Box::new(cond), then_body, else_body, span: Span::unknown() };
|
||||
current = A::If {
|
||||
condition: Box::new(cond),
|
||||
then_body,
|
||||
else_body,
|
||||
span: Span::unknown(),
|
||||
};
|
||||
}
|
||||
Some(current)
|
||||
} else { None }
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
fn transform_peek_to_if_stmt_assign(peek: &nyash_rust::ASTNode, target: &nyash_rust::ASTNode) -> Option<nyash_rust::ASTNode> {
|
||||
fn transform_peek_to_if_stmt_assign(
|
||||
peek: &nyash_rust::ASTNode,
|
||||
target: &nyash_rust::ASTNode,
|
||||
) -> Option<nyash_rust::ASTNode> {
|
||||
use nyash_rust::ast::{ASTNode as A, BinaryOperator, Span};
|
||||
if let A::MatchExpr { scrutinee, arms, else_expr, .. } = peek {
|
||||
if let A::MatchExpr {
|
||||
scrutinee,
|
||||
arms,
|
||||
else_expr,
|
||||
..
|
||||
} = peek
|
||||
{
|
||||
let mut pairs: Vec<(nyash_rust::ast::LiteralValue, A)> = Vec::new();
|
||||
for (lit, body) in arms { pairs.push((lit.clone(), (*body).clone())); }
|
||||
for (lit, body) in arms {
|
||||
pairs.push((lit.clone(), (*body).clone()));
|
||||
}
|
||||
let mut current: A = *(*else_expr).clone();
|
||||
for (lit, body) in pairs.into_iter().rev() {
|
||||
let rhs = A::Literal { value: lit, span: Span::unknown() };
|
||||
let cond = A::BinaryOp { operator: BinaryOperator::Equal, left: scrutinee.clone(), right: Box::new(rhs), span: Span::unknown() };
|
||||
let then_body = vec![A::Assignment { target: Box::new(target.clone()), value: Box::new(body), span: Span::unknown() }];
|
||||
let rhs = A::Literal {
|
||||
value: lit,
|
||||
span: Span::unknown(),
|
||||
};
|
||||
let cond = A::BinaryOp {
|
||||
operator: BinaryOperator::Equal,
|
||||
left: scrutinee.clone(),
|
||||
right: Box::new(rhs),
|
||||
span: Span::unknown(),
|
||||
};
|
||||
let then_body = vec![A::Assignment {
|
||||
target: Box::new(target.clone()),
|
||||
value: Box::new(body),
|
||||
span: Span::unknown(),
|
||||
}];
|
||||
let else_body = Some(vec![map_expr_to_stmt(current)]);
|
||||
current = A::If { condition: Box::new(cond), then_body, else_body, span: Span::unknown() };
|
||||
current = A::If {
|
||||
condition: Box::new(cond),
|
||||
then_body,
|
||||
else_body,
|
||||
span: Span::unknown(),
|
||||
};
|
||||
}
|
||||
Some(current)
|
||||
} else { None }
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
fn transform_peek_to_if_stmt_return(peek: &nyash_rust::ASTNode) -> Option<nyash_rust::ASTNode> {
|
||||
use nyash_rust::ast::{ASTNode as A, BinaryOperator, Span};
|
||||
if let A::MatchExpr { scrutinee, arms, else_expr, .. } = peek {
|
||||
if let A::MatchExpr {
|
||||
scrutinee,
|
||||
arms,
|
||||
else_expr,
|
||||
..
|
||||
} = peek
|
||||
{
|
||||
let mut pairs: Vec<(nyash_rust::ast::LiteralValue, A)> = Vec::new();
|
||||
for (lit, body) in arms { pairs.push((lit.clone(), (*body).clone())); }
|
||||
for (lit, body) in arms {
|
||||
pairs.push((lit.clone(), (*body).clone()));
|
||||
}
|
||||
let mut current: A = *(*else_expr).clone();
|
||||
for (lit, body) in pairs.into_iter().rev() {
|
||||
let rhs = A::Literal { value: lit, span: Span::unknown() };
|
||||
let cond = A::BinaryOp { operator: BinaryOperator::Equal, left: scrutinee.clone(), right: Box::new(rhs), span: Span::unknown() };
|
||||
let then_body = vec![A::Return { value: Some(Box::new(body)), span: Span::unknown() }];
|
||||
let rhs = A::Literal {
|
||||
value: lit,
|
||||
span: Span::unknown(),
|
||||
};
|
||||
let cond = A::BinaryOp {
|
||||
operator: BinaryOperator::Equal,
|
||||
left: scrutinee.clone(),
|
||||
right: Box::new(rhs),
|
||||
span: Span::unknown(),
|
||||
};
|
||||
let then_body = vec![A::Return {
|
||||
value: Some(Box::new(body)),
|
||||
span: Span::unknown(),
|
||||
}];
|
||||
let else_body = Some(vec![map_expr_to_stmt(current)]);
|
||||
current = A::If { condition: Box::new(cond), then_body, else_body, span: Span::unknown() };
|
||||
current = A::If {
|
||||
condition: Box::new(cond),
|
||||
then_body,
|
||||
else_body,
|
||||
span: Span::unknown(),
|
||||
};
|
||||
}
|
||||
Some(current)
|
||||
} else { None }
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
fn transform_peek_to_if_stmt_print(peek: &nyash_rust::ASTNode) -> Option<nyash_rust::ASTNode> {
|
||||
use nyash_rust::ast::{ASTNode as A, BinaryOperator, Span};
|
||||
if let A::MatchExpr { scrutinee, arms, else_expr, .. } = peek {
|
||||
if let A::MatchExpr {
|
||||
scrutinee,
|
||||
arms,
|
||||
else_expr,
|
||||
..
|
||||
} = peek
|
||||
{
|
||||
let mut pairs: Vec<(nyash_rust::ast::LiteralValue, A)> = Vec::new();
|
||||
for (lit, body) in arms { pairs.push((lit.clone(), (*body).clone())); }
|
||||
for (lit, body) in arms {
|
||||
pairs.push((lit.clone(), (*body).clone()));
|
||||
}
|
||||
let mut current: A = *(*else_expr).clone();
|
||||
for (lit, body) in pairs.into_iter().rev() {
|
||||
let rhs = A::Literal { value: lit, span: Span::unknown() };
|
||||
let cond = A::BinaryOp { operator: BinaryOperator::Equal, left: scrutinee.clone(), right: Box::new(rhs), span: Span::unknown() };
|
||||
let then_body = vec![A::Print { expression: Box::new(body), span: Span::unknown() }];
|
||||
let rhs = A::Literal {
|
||||
value: lit,
|
||||
span: Span::unknown(),
|
||||
};
|
||||
let cond = A::BinaryOp {
|
||||
operator: BinaryOperator::Equal,
|
||||
left: scrutinee.clone(),
|
||||
right: Box::new(rhs),
|
||||
span: Span::unknown(),
|
||||
};
|
||||
let then_body = vec![A::Print {
|
||||
expression: Box::new(body),
|
||||
span: Span::unknown(),
|
||||
}];
|
||||
let else_body = Some(vec![map_expr_to_stmt(current)]);
|
||||
current = A::If { condition: Box::new(cond), then_body, else_body, span: Span::unknown() };
|
||||
current = A::If {
|
||||
condition: Box::new(cond),
|
||||
then_body,
|
||||
else_body,
|
||||
span: Span::unknown(),
|
||||
};
|
||||
}
|
||||
Some(current)
|
||||
} else { None }
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn transform_peek_match_literal(ast: &nyash_rust::ASTNode) -> nyash_rust::ASTNode {
|
||||
use nyash_rust::ast::ASTNode as A;
|
||||
match ast.clone() {
|
||||
A::Program { statements, span } => A::Program { statements: statements.into_iter().map(|n| transform_peek_match_literal(&n)).collect(), span },
|
||||
A::If { condition, then_body, else_body, span } => A::If {
|
||||
condition: Box::new(transform_peek_match_literal(&condition)),
|
||||
then_body: then_body.into_iter().map(|n| transform_peek_match_literal(&n)).collect(),
|
||||
else_body: else_body.map(|v| v.into_iter().map(|n| transform_peek_match_literal(&n)).collect()),
|
||||
A::Program { statements, span } => A::Program {
|
||||
statements: statements
|
||||
.into_iter()
|
||||
.map(|n| transform_peek_match_literal(&n))
|
||||
.collect(),
|
||||
span,
|
||||
},
|
||||
A::Loop { condition, body, span } => A::Loop {
|
||||
A::If {
|
||||
condition,
|
||||
then_body,
|
||||
else_body,
|
||||
span,
|
||||
} => A::If {
|
||||
condition: Box::new(transform_peek_match_literal(&condition)),
|
||||
body: body.into_iter().map(|n| transform_peek_match_literal(&n)).collect(),
|
||||
then_body: then_body
|
||||
.into_iter()
|
||||
.map(|n| transform_peek_match_literal(&n))
|
||||
.collect(),
|
||||
else_body: else_body.map(|v| {
|
||||
v.into_iter()
|
||||
.map(|n| transform_peek_match_literal(&n))
|
||||
.collect()
|
||||
}),
|
||||
span,
|
||||
},
|
||||
A::BinaryOp { operator, left, right, span } => A::BinaryOp { operator, left: Box::new(transform_peek_match_literal(&left)), right: Box::new(transform_peek_match_literal(&right)), span },
|
||||
A::UnaryOp { operator, operand, span } => A::UnaryOp { operator, operand: Box::new(transform_peek_match_literal(&operand)), span },
|
||||
A::MethodCall { object, method, arguments, span } => A::MethodCall { object: Box::new(transform_peek_match_literal(&object)), method, arguments: arguments.into_iter().map(|a| transform_peek_match_literal(&a)).collect(), span },
|
||||
A::FunctionCall { name, arguments, span } => {
|
||||
if let Some(if_expr) = transform_peek_to_if_expr(&A::FunctionCall { name: name.clone(), arguments: arguments.clone(), span }) {
|
||||
A::Loop {
|
||||
condition,
|
||||
body,
|
||||
span,
|
||||
} => A::Loop {
|
||||
condition: Box::new(transform_peek_match_literal(&condition)),
|
||||
body: body
|
||||
.into_iter()
|
||||
.map(|n| transform_peek_match_literal(&n))
|
||||
.collect(),
|
||||
span,
|
||||
},
|
||||
A::BinaryOp {
|
||||
operator,
|
||||
left,
|
||||
right,
|
||||
span,
|
||||
} => A::BinaryOp {
|
||||
operator,
|
||||
left: Box::new(transform_peek_match_literal(&left)),
|
||||
right: Box::new(transform_peek_match_literal(&right)),
|
||||
span,
|
||||
},
|
||||
A::UnaryOp {
|
||||
operator,
|
||||
operand,
|
||||
span,
|
||||
} => A::UnaryOp {
|
||||
operator,
|
||||
operand: Box::new(transform_peek_match_literal(&operand)),
|
||||
span,
|
||||
},
|
||||
A::MethodCall {
|
||||
object,
|
||||
method,
|
||||
arguments,
|
||||
span,
|
||||
} => A::MethodCall {
|
||||
object: Box::new(transform_peek_match_literal(&object)),
|
||||
method,
|
||||
arguments: arguments
|
||||
.into_iter()
|
||||
.map(|a| transform_peek_match_literal(&a))
|
||||
.collect(),
|
||||
span,
|
||||
},
|
||||
A::FunctionCall {
|
||||
name,
|
||||
arguments,
|
||||
span,
|
||||
} => {
|
||||
if let Some(if_expr) = transform_peek_to_if_expr(&A::FunctionCall {
|
||||
name: name.clone(),
|
||||
arguments: arguments.clone(),
|
||||
span,
|
||||
}) {
|
||||
if_expr
|
||||
} else { A::FunctionCall { name, arguments: arguments.into_iter().map(|a| transform_peek_match_literal(&a)).collect(), span } }
|
||||
} else {
|
||||
A::FunctionCall {
|
||||
name,
|
||||
arguments: arguments
|
||||
.into_iter()
|
||||
.map(|a| transform_peek_match_literal(&a))
|
||||
.collect(),
|
||||
span,
|
||||
}
|
||||
}
|
||||
}
|
||||
A::ArrayLiteral { elements, span } => A::ArrayLiteral { elements: elements.into_iter().map(|e| transform_peek_match_literal(&e)).collect(), span },
|
||||
A::MapLiteral { entries, span } => A::MapLiteral { entries: entries.into_iter().map(|(k, v)| (k, transform_peek_match_literal(&v))).collect(), span },
|
||||
A::Assignment { target, value, span } => {
|
||||
if let Some(ifstmt) = transform_peek_to_if_stmt_assign(&value, &target) { ifstmt }
|
||||
else { A::Assignment { target, value: Box::new(transform_peek_match_literal(&value)), span } }
|
||||
A::ArrayLiteral { elements, span } => A::ArrayLiteral {
|
||||
elements: elements
|
||||
.into_iter()
|
||||
.map(|e| transform_peek_match_literal(&e))
|
||||
.collect(),
|
||||
span,
|
||||
},
|
||||
A::MapLiteral { entries, span } => A::MapLiteral {
|
||||
entries: entries
|
||||
.into_iter()
|
||||
.map(|(k, v)| (k, transform_peek_match_literal(&v)))
|
||||
.collect(),
|
||||
span,
|
||||
},
|
||||
A::Assignment {
|
||||
target,
|
||||
value,
|
||||
span,
|
||||
} => {
|
||||
if let Some(ifstmt) = transform_peek_to_if_stmt_assign(&value, &target) {
|
||||
ifstmt
|
||||
} else {
|
||||
A::Assignment {
|
||||
target,
|
||||
value: Box::new(transform_peek_match_literal(&value)),
|
||||
span,
|
||||
}
|
||||
}
|
||||
}
|
||||
A::Return { value, span } => {
|
||||
if let Some(v) = &value {
|
||||
if let Some(ifstmt) = transform_peek_to_if_stmt_return(v) { ifstmt }
|
||||
else { A::Return { value: Some(Box::new(transform_peek_match_literal(v))), span } }
|
||||
} else { A::Return { value: None, span } }
|
||||
if let Some(ifstmt) = transform_peek_to_if_stmt_return(v) {
|
||||
ifstmt
|
||||
} else {
|
||||
A::Return {
|
||||
value: Some(Box::new(transform_peek_match_literal(v))),
|
||||
span,
|
||||
}
|
||||
}
|
||||
} else {
|
||||
A::Return { value: None, span }
|
||||
}
|
||||
}
|
||||
A::Print { expression, span } => {
|
||||
if let Some(ifstmt) = transform_peek_to_if_stmt_print(&expression) { ifstmt }
|
||||
else { A::Print { expression: Box::new(transform_peek_match_literal(&expression)), span } }
|
||||
if let Some(ifstmt) = transform_peek_to_if_stmt_print(&expression) {
|
||||
ifstmt
|
||||
} else {
|
||||
A::Print {
|
||||
expression: Box::new(transform_peek_match_literal(&expression)),
|
||||
span,
|
||||
}
|
||||
}
|
||||
}
|
||||
other => other,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -1,14 +1,74 @@
|
||||
pub(super) fn transform_postfix_handlers(ast: &nyash_rust::ASTNode) -> nyash_rust::ASTNode {
|
||||
use nyash_rust::ast::{ASTNode as A, CatchClause, Span};
|
||||
fn map_vec(v: Vec<A>) -> Vec<A> { v.into_iter().map(|n| transform_postfix_handlers(&n)).collect() }
|
||||
fn map_vec(v: Vec<A>) -> Vec<A> {
|
||||
v.into_iter()
|
||||
.map(|n| transform_postfix_handlers(&n))
|
||||
.collect()
|
||||
}
|
||||
match ast.clone() {
|
||||
A::Program { statements, span } => A::Program { statements: map_vec(statements), span },
|
||||
A::If { condition, then_body, else_body, span } => A::If { condition: Box::new(transform_postfix_handlers(&condition)), then_body: map_vec(then_body), else_body: else_body.map(map_vec), span },
|
||||
A::Loop { condition, body, span } => A::Loop { condition: Box::new(transform_postfix_handlers(&condition)), body: map_vec(body), span },
|
||||
A::BinaryOp { operator, left, right, span } => A::BinaryOp { operator, left: Box::new(transform_postfix_handlers(&left)), right: Box::new(transform_postfix_handlers(&right)), span },
|
||||
A::UnaryOp { operator, operand, span } => A::UnaryOp { operator, operand: Box::new(transform_postfix_handlers(&operand)), span },
|
||||
A::MethodCall { object, method, arguments, span } => A::MethodCall { object: Box::new(transform_postfix_handlers(&object)), method, arguments: arguments.into_iter().map(|a| transform_postfix_handlers(&a)).collect(), span },
|
||||
A::FunctionCall { name, arguments, span } => {
|
||||
A::Program { statements, span } => A::Program {
|
||||
statements: map_vec(statements),
|
||||
span,
|
||||
},
|
||||
A::If {
|
||||
condition,
|
||||
then_body,
|
||||
else_body,
|
||||
span,
|
||||
} => A::If {
|
||||
condition: Box::new(transform_postfix_handlers(&condition)),
|
||||
then_body: map_vec(then_body),
|
||||
else_body: else_body.map(map_vec),
|
||||
span,
|
||||
},
|
||||
A::Loop {
|
||||
condition,
|
||||
body,
|
||||
span,
|
||||
} => A::Loop {
|
||||
condition: Box::new(transform_postfix_handlers(&condition)),
|
||||
body: map_vec(body),
|
||||
span,
|
||||
},
|
||||
A::BinaryOp {
|
||||
operator,
|
||||
left,
|
||||
right,
|
||||
span,
|
||||
} => A::BinaryOp {
|
||||
operator,
|
||||
left: Box::new(transform_postfix_handlers(&left)),
|
||||
right: Box::new(transform_postfix_handlers(&right)),
|
||||
span,
|
||||
},
|
||||
A::UnaryOp {
|
||||
operator,
|
||||
operand,
|
||||
span,
|
||||
} => A::UnaryOp {
|
||||
operator,
|
||||
operand: Box::new(transform_postfix_handlers(&operand)),
|
||||
span,
|
||||
},
|
||||
A::MethodCall {
|
||||
object,
|
||||
method,
|
||||
arguments,
|
||||
span,
|
||||
} => A::MethodCall {
|
||||
object: Box::new(transform_postfix_handlers(&object)),
|
||||
method,
|
||||
arguments: arguments
|
||||
.into_iter()
|
||||
.map(|a| transform_postfix_handlers(&a))
|
||||
.collect(),
|
||||
span,
|
||||
},
|
||||
A::FunctionCall {
|
||||
name,
|
||||
arguments,
|
||||
span,
|
||||
} => {
|
||||
let name_l = name.to_ascii_lowercase();
|
||||
if name_l == "postfix_catch" {
|
||||
let mut args = arguments;
|
||||
@ -17,17 +77,32 @@ pub(super) fn transform_postfix_handlers(ast: &nyash_rust::ASTNode) -> nyash_rus
|
||||
let (type_opt, handler) = if args.len() == 1 {
|
||||
(None, args.remove(0))
|
||||
} else if args.len() >= 2 {
|
||||
let ty = match args.remove(0) { A::Literal { value: nyash_rust::ast::LiteralValue::String(s), .. } => Some(s), _ => None };
|
||||
let ty = match args.remove(0) {
|
||||
A::Literal {
|
||||
value: nyash_rust::ast::LiteralValue::String(s),
|
||||
..
|
||||
} => Some(s),
|
||||
_ => None,
|
||||
};
|
||||
(ty, args.remove(0))
|
||||
} else {
|
||||
(None, A::Literal { value: nyash_rust::ast::LiteralValue::Integer(0), span: Span::unknown() })
|
||||
(
|
||||
None,
|
||||
A::Literal {
|
||||
value: nyash_rust::ast::LiteralValue::Integer(0),
|
||||
span: Span::unknown(),
|
||||
},
|
||||
)
|
||||
};
|
||||
if let A::Lambda { params, body, .. } = handler {
|
||||
if params.len() == 1 {
|
||||
let cc = CatchClause {
|
||||
exception_type: type_opt,
|
||||
variable_name: Some(params[0].clone()),
|
||||
body: body.into_iter().map(|n| transform_postfix_handlers(&n)).collect(),
|
||||
body: body
|
||||
.into_iter()
|
||||
.map(|n| transform_postfix_handlers(&n))
|
||||
.collect(),
|
||||
span: Span::unknown(),
|
||||
};
|
||||
return A::TryCatch {
|
||||
@ -39,7 +114,14 @@ pub(super) fn transform_postfix_handlers(ast: &nyash_rust::ASTNode) -> nyash_rus
|
||||
}
|
||||
}
|
||||
}
|
||||
A::FunctionCall { name, arguments: args.into_iter().map(|n| transform_postfix_handlers(&n)).collect(), span }
|
||||
A::FunctionCall {
|
||||
name,
|
||||
arguments: args
|
||||
.into_iter()
|
||||
.map(|n| transform_postfix_handlers(&n))
|
||||
.collect(),
|
||||
span,
|
||||
}
|
||||
} else if name_l == "with_cleanup" {
|
||||
let mut args = arguments;
|
||||
if args.len() >= 2 {
|
||||
@ -50,20 +132,49 @@ pub(super) fn transform_postfix_handlers(ast: &nyash_rust::ASTNode) -> nyash_rus
|
||||
return A::TryCatch {
|
||||
try_body: vec![expr],
|
||||
catch_clauses: vec![],
|
||||
finally_body: Some(body.into_iter().map(|n| transform_postfix_handlers(&n)).collect()),
|
||||
finally_body: Some(
|
||||
body.into_iter()
|
||||
.map(|n| transform_postfix_handlers(&n))
|
||||
.collect(),
|
||||
),
|
||||
span: Span::unknown(),
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
A::FunctionCall { name, arguments: args.into_iter().map(|n| transform_postfix_handlers(&n)).collect(), span }
|
||||
A::FunctionCall {
|
||||
name,
|
||||
arguments: args
|
||||
.into_iter()
|
||||
.map(|n| transform_postfix_handlers(&n))
|
||||
.collect(),
|
||||
span,
|
||||
}
|
||||
} else {
|
||||
A::FunctionCall { name, arguments: arguments.into_iter().map(|n| transform_postfix_handlers(&n)).collect(), span }
|
||||
A::FunctionCall {
|
||||
name,
|
||||
arguments: arguments
|
||||
.into_iter()
|
||||
.map(|n| transform_postfix_handlers(&n))
|
||||
.collect(),
|
||||
span,
|
||||
}
|
||||
}
|
||||
}
|
||||
A::ArrayLiteral { elements, span } => A::ArrayLiteral { elements: elements.into_iter().map(|e| transform_postfix_handlers(&e)).collect(), span },
|
||||
A::MapLiteral { entries, span } => A::MapLiteral { entries: entries.into_iter().map(|(k,v)| (k, transform_postfix_handlers(&v))).collect(), span },
|
||||
A::ArrayLiteral { elements, span } => A::ArrayLiteral {
|
||||
elements: elements
|
||||
.into_iter()
|
||||
.map(|e| transform_postfix_handlers(&e))
|
||||
.collect(),
|
||||
span,
|
||||
},
|
||||
A::MapLiteral { entries, span } => A::MapLiteral {
|
||||
entries: entries
|
||||
.into_iter()
|
||||
.map(|(k, v)| (k, transform_postfix_handlers(&v)))
|
||||
.collect(),
|
||||
span,
|
||||
},
|
||||
other => other,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -1,25 +1,122 @@
|
||||
pub(super) fn transform_scopebox_inject(ast: &nyash_rust::ASTNode) -> nyash_rust::ASTNode {
|
||||
use nyash_rust::ast::ASTNode as A;
|
||||
match ast.clone() {
|
||||
A::Program { statements, span } => A::Program { statements: statements.into_iter().map(|n| transform_scopebox_inject(&n)).collect(), span },
|
||||
A::If { condition, then_body, else_body, span } => {
|
||||
A::Program { statements, span } => A::Program {
|
||||
statements: statements
|
||||
.into_iter()
|
||||
.map(|n| transform_scopebox_inject(&n))
|
||||
.collect(),
|
||||
span,
|
||||
},
|
||||
A::If {
|
||||
condition,
|
||||
then_body,
|
||||
else_body,
|
||||
span,
|
||||
} => {
|
||||
let cond = Box::new(transform_scopebox_inject(&condition));
|
||||
let then_wrapped = vec![A::ScopeBox { body: then_body.into_iter().map(|n| transform_scopebox_inject(&n)).collect(), span: nyash_rust::ast::Span::unknown() }];
|
||||
let else_wrapped = else_body.map(|v| vec![A::ScopeBox { body: v.into_iter().map(|n| transform_scopebox_inject(&n)).collect(), span: nyash_rust::ast::Span::unknown() }]);
|
||||
A::If { condition: cond, then_body: then_wrapped, else_body: else_wrapped, span }
|
||||
let then_wrapped = vec![A::ScopeBox {
|
||||
body: then_body
|
||||
.into_iter()
|
||||
.map(|n| transform_scopebox_inject(&n))
|
||||
.collect(),
|
||||
span: nyash_rust::ast::Span::unknown(),
|
||||
}];
|
||||
let else_wrapped = else_body.map(|v| {
|
||||
vec![A::ScopeBox {
|
||||
body: v
|
||||
.into_iter()
|
||||
.map(|n| transform_scopebox_inject(&n))
|
||||
.collect(),
|
||||
span: nyash_rust::ast::Span::unknown(),
|
||||
}]
|
||||
});
|
||||
A::If {
|
||||
condition: cond,
|
||||
then_body: then_wrapped,
|
||||
else_body: else_wrapped,
|
||||
span,
|
||||
}
|
||||
}
|
||||
A::Loop { condition, body, span } => {
|
||||
A::Loop {
|
||||
condition,
|
||||
body,
|
||||
span,
|
||||
} => {
|
||||
let cond = Box::new(transform_scopebox_inject(&condition));
|
||||
let body_wrapped = vec![A::ScopeBox { body: body.into_iter().map(|n| transform_scopebox_inject(&n)).collect(), span: nyash_rust::ast::Span::unknown() }];
|
||||
A::Loop { condition: cond, body: body_wrapped, span }
|
||||
let body_wrapped = vec![A::ScopeBox {
|
||||
body: body
|
||||
.into_iter()
|
||||
.map(|n| transform_scopebox_inject(&n))
|
||||
.collect(),
|
||||
span: nyash_rust::ast::Span::unknown(),
|
||||
}];
|
||||
A::Loop {
|
||||
condition: cond,
|
||||
body: body_wrapped,
|
||||
span,
|
||||
}
|
||||
}
|
||||
A::BinaryOp { operator, left, right, span } => A::BinaryOp { operator, left: Box::new(transform_scopebox_inject(&left)), right: Box::new(transform_scopebox_inject(&right)), span },
|
||||
A::UnaryOp { operator, operand, span } => A::UnaryOp { operator, operand: Box::new(transform_scopebox_inject(&operand)), span },
|
||||
A::MethodCall { object, method, arguments, span } => A::MethodCall { object: Box::new(transform_scopebox_inject(&object)), method, arguments: arguments.into_iter().map(|a| transform_scopebox_inject(&a)).collect(), span },
|
||||
A::FunctionCall { name, arguments, span } => A::FunctionCall { name, arguments: arguments.into_iter().map(|a| transform_scopebox_inject(&a)).collect(), span },
|
||||
A::ArrayLiteral { elements, span } => A::ArrayLiteral { elements: elements.into_iter().map(|e| transform_scopebox_inject(&e)).collect(), span },
|
||||
A::MapLiteral { entries, span } => A::MapLiteral { entries: entries.into_iter().map(|(k, v)| (k, transform_scopebox_inject(&v))).collect(), span },
|
||||
A::BinaryOp {
|
||||
operator,
|
||||
left,
|
||||
right,
|
||||
span,
|
||||
} => A::BinaryOp {
|
||||
operator,
|
||||
left: Box::new(transform_scopebox_inject(&left)),
|
||||
right: Box::new(transform_scopebox_inject(&right)),
|
||||
span,
|
||||
},
|
||||
A::UnaryOp {
|
||||
operator,
|
||||
operand,
|
||||
span,
|
||||
} => A::UnaryOp {
|
||||
operator,
|
||||
operand: Box::new(transform_scopebox_inject(&operand)),
|
||||
span,
|
||||
},
|
||||
A::MethodCall {
|
||||
object,
|
||||
method,
|
||||
arguments,
|
||||
span,
|
||||
} => A::MethodCall {
|
||||
object: Box::new(transform_scopebox_inject(&object)),
|
||||
method,
|
||||
arguments: arguments
|
||||
.into_iter()
|
||||
.map(|a| transform_scopebox_inject(&a))
|
||||
.collect(),
|
||||
span,
|
||||
},
|
||||
A::FunctionCall {
|
||||
name,
|
||||
arguments,
|
||||
span,
|
||||
} => A::FunctionCall {
|
||||
name,
|
||||
arguments: arguments
|
||||
.into_iter()
|
||||
.map(|a| transform_scopebox_inject(&a))
|
||||
.collect(),
|
||||
span,
|
||||
},
|
||||
A::ArrayLiteral { elements, span } => A::ArrayLiteral {
|
||||
elements: elements
|
||||
.into_iter()
|
||||
.map(|e| transform_scopebox_inject(&e))
|
||||
.collect(),
|
||||
span,
|
||||
},
|
||||
A::MapLiteral { entries, span } => A::MapLiteral {
|
||||
entries: entries
|
||||
.into_iter()
|
||||
.map(|(k, v)| (k, transform_scopebox_inject(&v)))
|
||||
.collect(),
|
||||
span,
|
||||
},
|
||||
other => other,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -21,7 +21,11 @@ impl NyashRunner {
|
||||
let ast = match NyashParser::parse_from_string(&code) {
|
||||
Ok(ast) => ast,
|
||||
Err(e) => {
|
||||
eprintln!("❌ Parse error in {}: {}", filename, e);
|
||||
crate::runner::modes::common_util::diag::print_parse_error_with_context(
|
||||
filename,
|
||||
&code,
|
||||
&e,
|
||||
);
|
||||
process::exit(1);
|
||||
}
|
||||
};
|
||||
@ -71,7 +75,9 @@ impl NyashRunner {
|
||||
// Emit MIR JSON if requested and exit
|
||||
if let Some(path) = groups.emit.emit_mir_json.as_ref() {
|
||||
let p = std::path::Path::new(path);
|
||||
if let Err(e) = crate::runner::mir_json_emit::emit_mir_json_for_harness(&compile_result.module, p) {
|
||||
if let Err(e) =
|
||||
crate::runner::mir_json_emit::emit_mir_json_for_harness(&compile_result.module, p)
|
||||
{
|
||||
eprintln!("❌ MIR JSON emit error: {}", e);
|
||||
std::process::exit(1);
|
||||
}
|
||||
|
||||
@ -15,7 +15,14 @@ impl NyashRunner {
|
||||
// Parse to AST
|
||||
let ast = match NyashParser::parse_from_string(&code) {
|
||||
Ok(ast) => ast,
|
||||
Err(e) => { eprintln!("❌ Parse error in {}: {}", filename, e); process::exit(1); }
|
||||
Err(e) => {
|
||||
crate::runner::modes::common_util::diag::print_parse_error_with_context(
|
||||
filename,
|
||||
&code,
|
||||
&e,
|
||||
);
|
||||
process::exit(1);
|
||||
}
|
||||
};
|
||||
let ast = crate::r#macro::maybe_expand_and_dump(&ast, false);
|
||||
|
||||
|
||||
@ -1,10 +1,10 @@
|
||||
// bench module removed with vm-legacy
|
||||
pub mod llvm;
|
||||
pub mod macro_child;
|
||||
pub mod mir;
|
||||
pub mod pyvm;
|
||||
pub mod vm;
|
||||
pub mod vm_fallback;
|
||||
pub mod pyvm;
|
||||
pub mod macro_child;
|
||||
|
||||
// Shared helpers extracted from common.rs (in progress)
|
||||
pub mod common_util;
|
||||
|
||||
@ -4,7 +4,9 @@ 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 crate::config::env::env_bool("NYASH_PYVM_TRACE") { 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,
|
||||
@ -16,7 +18,9 @@ pub fn execute_pyvm_only(runner: &NyashRunner, filename: &str) {
|
||||
|
||||
// Using handling: AST-prelude collection (legacy inlining removed)
|
||||
let mut code = if crate::config::env::enable_using() {
|
||||
match crate::runner::modes::common_util::resolve::resolve_prelude_paths_profiled(runner, &code, filename) {
|
||||
match crate::runner::modes::common_util::resolve::resolve_prelude_paths_profiled(
|
||||
runner, &code, filename,
|
||||
) {
|
||||
Ok((clean, paths)) => {
|
||||
if !paths.is_empty() && !crate::config::env::using_ast_enabled() {
|
||||
eprintln!("❌ using: AST prelude merge is disabled in this profile. Enable NYASH_USING_AST=1 or remove 'using' lines.");
|
||||
@ -25,9 +29,14 @@ pub fn execute_pyvm_only(runner: &NyashRunner, filename: &str) {
|
||||
// PyVM pipeline currently does not merge prelude ASTs here; rely on main/common path for that.
|
||||
clean
|
||||
}
|
||||
Err(e) => { eprintln!("❌ {}", e); process::exit(1); }
|
||||
Err(e) => {
|
||||
eprintln!("❌ {}", e);
|
||||
process::exit(1);
|
||||
}
|
||||
}
|
||||
} else { code };
|
||||
} else {
|
||||
code
|
||||
};
|
||||
|
||||
// Dev sugar pre-expand: line-head @name[:T] = expr → local name[:T] = expr
|
||||
code = crate::runner::modes::common_util::resolve::preexpand_at_local(&code);
|
||||
@ -42,31 +51,76 @@ pub fn execute_pyvm_only(runner: &NyashRunner, filename: &str) {
|
||||
while let Some(c) = it.next() {
|
||||
if in_line {
|
||||
out.push(c);
|
||||
if c == '\n' { in_line = false; }
|
||||
if c == '\n' {
|
||||
in_line = false;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if in_block {
|
||||
out.push(c);
|
||||
if c == '*' && matches!(it.peek(), Some('/')) { out.push('/'); it.next(); in_block = false; }
|
||||
if c == '*' && matches!(it.peek(), Some('/')) {
|
||||
out.push('/');
|
||||
it.next();
|
||||
in_block = false;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if in_str {
|
||||
out.push(c);
|
||||
if c == '\\' { if let Some(nc) = it.next() { out.push(nc); } continue; }
|
||||
if c == '"' { in_str = false; }
|
||||
if c == '\\' {
|
||||
if let Some(nc) = it.next() {
|
||||
out.push(nc);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if c == '"' {
|
||||
in_str = false;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
match c {
|
||||
'"' => { in_str = true; out.push(c); }
|
||||
'/' => {
|
||||
match it.peek() { Some('/') => { out.push('/'); out.push('/'); it.next(); in_line = true; }, Some('*') => { out.push('/'); out.push('*'); it.next(); in_block = true; }, _ => out.push('/') }
|
||||
'"' => {
|
||||
in_str = true;
|
||||
out.push(c);
|
||||
}
|
||||
'/' => match it.peek() {
|
||||
Some('/') => {
|
||||
out.push('/');
|
||||
out.push('/');
|
||||
it.next();
|
||||
in_line = true;
|
||||
}
|
||||
Some('*') => {
|
||||
out.push('/');
|
||||
out.push('*');
|
||||
it.next();
|
||||
in_block = true;
|
||||
}
|
||||
_ => out.push('/'),
|
||||
},
|
||||
'#' => {
|
||||
in_line = true;
|
||||
out.push('#');
|
||||
}
|
||||
'#' => { in_line = true; out.push('#'); }
|
||||
'|' => {
|
||||
if matches!(it.peek(), Some('|')) { out.push_str(" or "); it.next(); } else if matches!(it.peek(), Some('>')) { out.push('|'); out.push('>'); it.next(); } else { out.push('|'); }
|
||||
if matches!(it.peek(), Some('|')) {
|
||||
out.push_str(" or ");
|
||||
it.next();
|
||||
} else if matches!(it.peek(), Some('>')) {
|
||||
out.push('|');
|
||||
out.push('>');
|
||||
it.next();
|
||||
} else {
|
||||
out.push('|');
|
||||
}
|
||||
}
|
||||
'&' => {
|
||||
if matches!(it.peek(), Some('&')) { out.push_str(" and "); it.next(); } else { out.push('&'); }
|
||||
if matches!(it.peek(), Some('&')) {
|
||||
out.push_str(" and ");
|
||||
it.next();
|
||||
} else {
|
||||
out.push('&');
|
||||
}
|
||||
}
|
||||
_ => out.push(c),
|
||||
}
|
||||
@ -79,13 +133,17 @@ pub fn execute_pyvm_only(runner: &NyashRunner, filename: &str) {
|
||||
if crate::config::env::env_bool("NYASH_PYVM_DUMP_CODE") {
|
||||
eprintln!("[pyvm-code]\n{}", code);
|
||||
}
|
||||
let ast = match NyashParser::parse_from_string(&code) {
|
||||
Ok(ast) => ast,
|
||||
Err(e) => {
|
||||
eprintln!("❌ Parse error in {}: {}", filename, e);
|
||||
process::exit(1);
|
||||
}
|
||||
};
|
||||
let ast = match NyashParser::parse_from_string(&code) {
|
||||
Ok(ast) => ast,
|
||||
Err(e) => {
|
||||
crate::runner::modes::common_util::diag::print_parse_error_with_context(
|
||||
filename,
|
||||
&code,
|
||||
&e,
|
||||
);
|
||||
process::exit(1);
|
||||
}
|
||||
};
|
||||
let ast = crate::r#macro::maybe_expand_and_dump(&ast, false);
|
||||
let ast = crate::runner::modes::macro_child::normalize_core_pass(&ast);
|
||||
|
||||
@ -101,8 +159,11 @@ pub fn execute_pyvm_only(runner: &NyashRunner, filename: &str) {
|
||||
|
||||
// Optional: VM-only escape analysis elision pass retained for parity with VM path
|
||||
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); }
|
||||
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)
|
||||
@ -111,12 +172,16 @@ pub fn execute_pyvm_only(runner: &NyashRunner, filename: &str) {
|
||||
let tmp_dir = std::path::Path::new("tmp");
|
||||
let _ = std::fs::create_dir_all(tmp_dir);
|
||||
let mir_json_path = tmp_dir.join("nyash_selfhost_mir.json");
|
||||
if let Err(e) = crate::runner::mir_json_emit::emit_mir_json_for_harness_bin(&compile_result.module, &mir_json_path) {
|
||||
if let Err(e) = crate::runner::mir_json_emit::emit_mir_json_for_harness_bin(
|
||||
&compile_result.module,
|
||||
&mir_json_path,
|
||||
) {
|
||||
eprintln!("❌ Selfhost MIR JSON emit error: {}", e);
|
||||
process::exit(1);
|
||||
}
|
||||
// Resolve nyash executable and runner path
|
||||
let exe = std::env::current_exe().unwrap_or_else(|_| std::path::PathBuf::from("target/release/nyash"));
|
||||
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.hako");
|
||||
if !runner.exists() {
|
||||
eprintln!("❌ Selfhost runner missing: {}", runner.display());
|
||||
@ -124,7 +189,8 @@ pub fn execute_pyvm_only(runner: &NyashRunner, filename: &str) {
|
||||
}
|
||||
let mut cmd = std::process::Command::new(&exe);
|
||||
crate::runner::child_env::apply_core_wrapper_env(&mut cmd);
|
||||
cmd.arg("--backend").arg("vm")
|
||||
cmd.arg("--backend")
|
||||
.arg("vm")
|
||||
.arg(runner)
|
||||
.arg("--")
|
||||
.arg(mir_json_path.display().to_string());
|
||||
@ -139,14 +205,25 @@ pub fn execute_pyvm_only(runner: &NyashRunner, filename: &str) {
|
||||
// Avoid recursive selfhost delegation inside the child.
|
||||
.env_remove("NYASH_SELFHOST_EXEC")
|
||||
.status()
|
||||
.unwrap_or_else(|e| { eprintln!("❌ spawn selfhost runner failed: {}", e); std::process::exit(1); });
|
||||
.unwrap_or_else(|e| {
|
||||
eprintln!("❌ spawn selfhost runner failed: {}", e);
|
||||
std::process::exit(1);
|
||||
});
|
||||
let code = status.code().unwrap_or(1);
|
||||
process::exit(code);
|
||||
}
|
||||
|
||||
// Delegate to common PyVM harness
|
||||
match crate::runner::modes::common_util::pyvm::run_pyvm_harness_lib(&compile_result.module, "pyvm") {
|
||||
Ok(code) => { process::exit(code); }
|
||||
Err(e) => { eprintln!("❌ PyVM error: {}", e); process::exit(1); }
|
||||
match crate::runner::modes::common_util::pyvm::run_pyvm_harness_lib(
|
||||
&compile_result.module,
|
||||
"pyvm",
|
||||
) {
|
||||
Ok(code) => {
|
||||
process::exit(code);
|
||||
}
|
||||
Err(e) => {
|
||||
eprintln!("❌ PyVM error: {}", e);
|
||||
process::exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,9 +1,5 @@
|
||||
use super::super::NyashRunner;
|
||||
use nyash_rust::{
|
||||
ast::ASTNode,
|
||||
parser::NyashParser,
|
||||
mir::MirCompiler,
|
||||
};
|
||||
use nyash_rust::{ast::ASTNode, mir::MirCompiler, parser::NyashParser};
|
||||
use std::{fs, process};
|
||||
|
||||
impl NyashRunner {
|
||||
@ -71,9 +67,7 @@ impl NyashRunner {
|
||||
// Centralized plugin guard
|
||||
let strict = crate::config::env::env_bool("NYASH_VM_PLUGIN_STRICT");
|
||||
crate::runner::modes::common_util::plugin_guard::check_and_report(
|
||||
strict,
|
||||
quiet_pipe,
|
||||
"vm",
|
||||
strict, quiet_pipe, "vm",
|
||||
);
|
||||
}
|
||||
|
||||
@ -97,17 +91,13 @@ impl NyashRunner {
|
||||
// When using is enabled, resolve preludes/profile; otherwise, keep original code.
|
||||
let mut code_final = if crate::config::env::enable_using() {
|
||||
match crate::runner::modes::common_util::resolve::resolve_prelude_paths_profiled(
|
||||
self,
|
||||
&code,
|
||||
filename,
|
||||
self, &code, filename,
|
||||
) {
|
||||
Ok((_, prelude_paths)) => {
|
||||
if !prelude_paths.is_empty() {
|
||||
// SSOT: always text-merge for VM (includes .hako-safe handling inside)
|
||||
match crate::runner::modes::common_util::resolve::merge_prelude_text(
|
||||
self,
|
||||
&code,
|
||||
filename,
|
||||
self, &code, filename,
|
||||
) {
|
||||
Ok(merged) => {
|
||||
if trace {
|
||||
@ -144,24 +134,18 @@ impl NyashRunner {
|
||||
};
|
||||
|
||||
// Dev sugar pre-expand: @name = expr → local name = expr
|
||||
code_final =
|
||||
crate::runner::modes::common_util::resolve::preexpand_at_local(&code_final);
|
||||
code_final = crate::runner::modes::common_util::resolve::preexpand_at_local(&code_final);
|
||||
|
||||
// Hako-friendly normalize: strip leading `local ` at line head for Nyash parser compatibility.
|
||||
if crate::runner::modes::common_util::hako::looks_like_hako_code(&code_final)
|
||||
|| filename.ends_with(".hako")
|
||||
{
|
||||
code_final =
|
||||
crate::runner::modes::common_util::hako::strip_local_decl(&code_final);
|
||||
code_final = crate::runner::modes::common_util::hako::strip_local_decl(&code_final);
|
||||
}
|
||||
|
||||
// Optional: dump merged Hako source after using/prelude merge and Hako normalization.
|
||||
// Guarded by env; defaultはOFF(Phase 25.1a selfhost builder デバッグ用)。
|
||||
if std::env::var("NYASH_VM_DUMP_MERGED_HAKO")
|
||||
.ok()
|
||||
.as_deref()
|
||||
== Some("1")
|
||||
{
|
||||
if std::env::var("NYASH_VM_DUMP_MERGED_HAKO").ok().as_deref() == Some("1") {
|
||||
let default_path = {
|
||||
let mut tmp = std::env::temp_dir();
|
||||
tmp.push("nyash_merged_vm.hako");
|
||||
@ -176,15 +160,14 @@ impl NyashRunner {
|
||||
if trace {
|
||||
eprintln!("[vm/merged-hako] failed to write {}: {}", path, e);
|
||||
}
|
||||
} else if trace
|
||||
|| crate::config::env::env_bool("NYASH_VM_DUMP_MERGED_HAKO_LOG")
|
||||
{
|
||||
} else if trace || crate::config::env::env_bool("NYASH_VM_DUMP_MERGED_HAKO_LOG") {
|
||||
eprintln!("[vm/merged-hako] dumped merged code to {}", path);
|
||||
}
|
||||
}
|
||||
|
||||
if trace && (std::env::var("NYASH_PARSER_STAGE3").ok() == Some("1".into())
|
||||
|| std::env::var("HAKO_PARSER_STAGE3").ok() == Some("1".into()))
|
||||
if trace
|
||||
&& (std::env::var("NYASH_PARSER_STAGE3").ok() == Some("1".into())
|
||||
|| std::env::var("HAKO_PARSER_STAGE3").ok() == Some("1".into()))
|
||||
{
|
||||
eprintln!("[vm] Stage-3: enabled (env) for {}", filename);
|
||||
}
|
||||
@ -210,9 +193,12 @@ impl NyashRunner {
|
||||
let ast_combined = match NyashParser::parse_from_string(&code_final) {
|
||||
Ok(ast) => ast,
|
||||
Err(e) => {
|
||||
eprintln!("❌ Parse error in {}: {}", filename, e);
|
||||
crate::runner::modes::common_util::diag::print_parse_error_with_context(
|
||||
filename, &code_final, &e,
|
||||
);
|
||||
// Enhanced context: list merged prelude files if any
|
||||
let preludes = crate::runner::modes::common_util::resolve::clone_last_merged_preludes();
|
||||
let preludes =
|
||||
crate::runner::modes::common_util::resolve::clone_last_merged_preludes();
|
||||
if !preludes.is_empty() {
|
||||
eprintln!("[parse/context] merged prelude files ({}):", preludes.len());
|
||||
let show = std::cmp::min(16, preludes.len());
|
||||
@ -297,7 +283,8 @@ impl NyashRunner {
|
||||
type_parameters,
|
||||
is_static,
|
||||
..
|
||||
} = st {
|
||||
} = st
|
||||
{
|
||||
if *is_static {
|
||||
static_names.push(name.clone());
|
||||
// Store static box declaration for VM singleton persistence
|
||||
@ -372,7 +359,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,
|
||||
@ -408,7 +396,9 @@ impl NyashRunner {
|
||||
let factory = InlineUserBoxFactory {
|
||||
decls: Arc::new(RwLock::new(decls)),
|
||||
};
|
||||
crate::runtime::unified_registry::register_user_defined_factory(std::sync::Arc::new(factory));
|
||||
crate::runtime::unified_registry::register_user_defined_factory(
|
||||
std::sync::Arc::new(factory),
|
||||
);
|
||||
}
|
||||
|
||||
// Return static_box_decls for VM registration
|
||||
@ -430,10 +420,7 @@ impl NyashRunner {
|
||||
if crate::config::env::env_bool("NYASH_VM_ESCAPE_ANALYSIS") {
|
||||
let removed = crate::mir::passes::escape::escape_elide_barriers_vm(&mut module_vm);
|
||||
if removed > 0 {
|
||||
crate::cli_v!(
|
||||
"[VM] escape_elide_barriers: removed {} barriers",
|
||||
removed
|
||||
);
|
||||
crate::cli_v!("[VM] escape_elide_barriers: removed {} barriers", removed);
|
||||
}
|
||||
}
|
||||
|
||||
@ -476,13 +463,17 @@ impl NyashRunner {
|
||||
|
||||
match vm.execute_module(&module_vm) {
|
||||
Ok(ret) => {
|
||||
use crate::box_trait::{IntegerBox, BoolBox};
|
||||
use crate::box_trait::{BoolBox, IntegerBox};
|
||||
|
||||
// Extract exit code from return value
|
||||
let exit_code = if let Some(ib) = ret.as_any().downcast_ref::<IntegerBox>() {
|
||||
ib.value as i32
|
||||
} else if let Some(bb) = ret.as_any().downcast_ref::<BoolBox>() {
|
||||
if bb.value { 1 } else { 0 }
|
||||
if bb.value {
|
||||
1
|
||||
} else {
|
||||
0
|
||||
}
|
||||
} else {
|
||||
// For non-integer/bool returns, default to 0 (success)
|
||||
0
|
||||
|
||||
@ -35,16 +35,12 @@ impl NyashRunner {
|
||||
// - merge_prelude_text で text-merge(.hako は AST parse しない)
|
||||
let mut code2 = if crate::config::env::enable_using() {
|
||||
match crate::runner::modes::common_util::resolve::resolve_prelude_paths_profiled(
|
||||
self,
|
||||
&code,
|
||||
filename,
|
||||
self, &code, filename,
|
||||
) {
|
||||
Ok((_, prelude_paths)) => {
|
||||
if !prelude_paths.is_empty() {
|
||||
match crate::runner::modes::common_util::resolve::merge_prelude_text(
|
||||
self,
|
||||
&code,
|
||||
filename,
|
||||
self, &code, filename,
|
||||
) {
|
||||
Ok(merged) => {
|
||||
if trace {
|
||||
@ -90,8 +86,9 @@ impl NyashRunner {
|
||||
code2 = crate::runner::modes::common_util::hako::strip_local_decl(&code2);
|
||||
}
|
||||
|
||||
if trace && (std::env::var("NYASH_PARSER_STAGE3").ok() == Some("1".into())
|
||||
|| std::env::var("HAKO_PARSER_STAGE3").ok() == Some("1".into()))
|
||||
if trace
|
||||
&& (std::env::var("NYASH_PARSER_STAGE3").ok() == Some("1".into())
|
||||
|| std::env::var("HAKO_PARSER_STAGE3").ok() == Some("1".into()))
|
||||
{
|
||||
eprintln!("[vm-fallback] Stage-3: enabled (env) for {}", filename);
|
||||
}
|
||||
@ -102,7 +99,9 @@ impl NyashRunner {
|
||||
let on = crate::runner::modes::common_util::hako::fail_fast_on_hako();
|
||||
if on {
|
||||
let s = code2.as_str();
|
||||
let hako_like = s.contains("static box ") || s.contains("using selfhost.") || s.contains("using hakorune.");
|
||||
let hako_like = s.contains("static box ")
|
||||
|| s.contains("using selfhost.")
|
||||
|| s.contains("using hakorune.");
|
||||
if hako_like {
|
||||
eprintln!(
|
||||
"❌ Hako-like source detected in Nyash VM path. Use Hakorune VM (v1 dispatcher) or Core/LLVM for MIR.\n hint: set HAKO_VERIFY_PRIMARY=hakovm in verify path"
|
||||
@ -116,7 +115,11 @@ impl NyashRunner {
|
||||
let main_ast = match NyashParser::parse_from_string(&code2) {
|
||||
Ok(ast) => ast,
|
||||
Err(e) => {
|
||||
eprintln!("❌ Parse error in {}: {}", filename, e);
|
||||
crate::runner::modes::common_util::diag::print_parse_error_with_context(
|
||||
filename,
|
||||
&code2,
|
||||
&e,
|
||||
);
|
||||
process::exit(1);
|
||||
}
|
||||
};
|
||||
@ -186,7 +189,8 @@ impl NyashRunner {
|
||||
type_parameters,
|
||||
is_static,
|
||||
..
|
||||
} = st {
|
||||
} = st
|
||||
{
|
||||
if *is_static {
|
||||
static_names.push(name.clone());
|
||||
continue; // modules/static boxes are not user-instantiable directly
|
||||
@ -308,7 +312,9 @@ impl NyashRunner {
|
||||
if let Err(errors) = verifier.verify_function(func) {
|
||||
if !errors.is_empty() {
|
||||
eprintln!("[vm-verify] function: {}", name);
|
||||
for er in errors { eprintln!(" • {}", er); }
|
||||
for er in errors {
|
||||
eprintln!(" • {}", er);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -321,13 +327,17 @@ impl NyashRunner {
|
||||
}
|
||||
match vm.execute_module(&module_vm) {
|
||||
Ok(ret) => {
|
||||
use crate::box_trait::{IntegerBox, BoolBox};
|
||||
use crate::box_trait::{BoolBox, IntegerBox};
|
||||
|
||||
// Extract exit code from return value
|
||||
let exit_code = if let Some(ib) = ret.as_any().downcast_ref::<IntegerBox>() {
|
||||
ib.value as i32
|
||||
} else if let Some(bb) = ret.as_any().downcast_ref::<BoolBox>() {
|
||||
if bb.value { 1 } else { 0 }
|
||||
if bb.value {
|
||||
1
|
||||
} else {
|
||||
0
|
||||
}
|
||||
} else {
|
||||
// For non-integer/bool returns, default to 0 (success)
|
||||
0
|
||||
@ -355,21 +365,54 @@ impl NyashRunner {
|
||||
instance_v2::InstanceBox,
|
||||
mir::MirCompiler,
|
||||
};
|
||||
use std::sync::{Arc, RwLock};
|
||||
use std::process;
|
||||
use std::sync::{Arc, RwLock};
|
||||
|
||||
// Macro expand (if enabled)
|
||||
let ast = crate::r#macro::maybe_expand_and_dump(&ast, false);
|
||||
// Minimal user-defined Box support (inline factory)
|
||||
{
|
||||
use nyash_rust::ast::ASTNode;
|
||||
let mut nonstatic_decls: std::collections::HashMap<String, CoreBoxDecl> = std::collections::HashMap::new();
|
||||
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 { name, fields, public_fields, private_fields, methods, constructors, init_fields, weak_fields, is_interface, extends, implements, type_parameters, is_static, .. } = st {
|
||||
if *is_static { static_names.push(name.clone()); continue; }
|
||||
let decl = CoreBoxDecl { name: name.clone(), fields: fields.clone(), public_fields: public_fields.clone(), private_fields: private_fields.clone(), methods: methods.clone(), constructors: constructors.clone(), init_fields: init_fields.clone(), weak_fields: weak_fields.clone(), is_interface: *is_interface, extends: extends.clone(), implements: implements.clone(), type_parameters: type_parameters.clone() };
|
||||
if let ASTNode::BoxDeclaration {
|
||||
name,
|
||||
fields,
|
||||
public_fields,
|
||||
private_fields,
|
||||
methods,
|
||||
constructors,
|
||||
init_fields,
|
||||
weak_fields,
|
||||
is_interface,
|
||||
extends,
|
||||
implements,
|
||||
type_parameters,
|
||||
is_static,
|
||||
..
|
||||
} = st
|
||||
{
|
||||
if *is_static {
|
||||
static_names.push(name.clone());
|
||||
continue;
|
||||
}
|
||||
let decl = CoreBoxDecl {
|
||||
name: name.clone(),
|
||||
fields: fields.clone(),
|
||||
public_fields: public_fields.clone(),
|
||||
private_fields: private_fields.clone(),
|
||||
methods: methods.clone(),
|
||||
constructors: constructors.clone(),
|
||||
init_fields: init_fields.clone(),
|
||||
weak_fields: weak_fields.clone(),
|
||||
is_interface: *is_interface,
|
||||
extends: extends.clone(),
|
||||
implements: implements.clone(),
|
||||
type_parameters: type_parameters.clone(),
|
||||
};
|
||||
nonstatic_decls.insert(name.clone(), decl);
|
||||
}
|
||||
}
|
||||
@ -390,7 +433,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,
|
||||
@ -409,8 +453,12 @@ impl NyashRunner {
|
||||
Ok(Box::new(inst))
|
||||
}
|
||||
|
||||
fn box_types(&self) -> Vec<&str> { vec![] }
|
||||
fn is_available(&self) -> bool { true }
|
||||
fn box_types(&self) -> Vec<&str> {
|
||||
vec![]
|
||||
}
|
||||
fn is_available(&self) -> bool {
|
||||
true
|
||||
}
|
||||
fn factory_type(&self) -> crate::box_factory::FactoryType {
|
||||
crate::box_factory::FactoryType::User
|
||||
}
|
||||
@ -425,7 +473,10 @@ impl NyashRunner {
|
||||
let mut compiler = MirCompiler::with_options(!self.config.no_optimize);
|
||||
let module = match compiler.compile(ast) {
|
||||
Ok(r) => r.module,
|
||||
Err(e) => { eprintln!("❌ MIR compilation error: {}", e); process::exit(1); }
|
||||
Err(e) => {
|
||||
eprintln!("❌ MIR compilation error: {}", e);
|
||||
process::exit(1);
|
||||
}
|
||||
};
|
||||
let mut interp = MirInterpreter::new();
|
||||
match interp.execute_module(&module) {
|
||||
@ -434,7 +485,11 @@ impl NyashRunner {
|
||||
let rc = if let Some(ib) = result.as_any().downcast_ref::<IntegerBox>() {
|
||||
ib.value as i32
|
||||
} else if let Some(bb) = result.as_any().downcast_ref::<BoolBox>() {
|
||||
if bb.value { 1 } else { 0 }
|
||||
if bb.value {
|
||||
1
|
||||
} else {
|
||||
0
|
||||
}
|
||||
} else {
|
||||
0
|
||||
};
|
||||
|
||||
@ -17,7 +17,14 @@ impl NyashRunner {
|
||||
// Parse to AST
|
||||
let ast = match NyashParser::parse_from_string(&code) {
|
||||
Ok(ast) => ast,
|
||||
Err(e) => { eprintln!("❌ Parse error in {}: {}", filename, e); process::exit(1); }
|
||||
Err(e) => {
|
||||
crate::runner::modes::common_util::diag::print_parse_error_with_context(
|
||||
filename,
|
||||
&code,
|
||||
&e,
|
||||
);
|
||||
process::exit(1);
|
||||
}
|
||||
};
|
||||
let ast = crate::r#macro::maybe_expand_and_dump(&ast, false);
|
||||
|
||||
|
||||
@ -41,7 +41,8 @@ impl NyashRunner {
|
||||
match super::json_v0_bridge::parse_json_v0_to_module(&json) {
|
||||
Ok(module) => {
|
||||
let p = std::path::Path::new(out);
|
||||
if let Err(e) = super::mir_json_emit::emit_mir_json_for_harness_bin(&module, p) {
|
||||
if let Err(e) = super::mir_json_emit::emit_mir_json_for_harness_bin(&module, p)
|
||||
{
|
||||
eprintln!("❌ Program→MIR emit error: {}", e);
|
||||
std::process::exit(1);
|
||||
}
|
||||
@ -61,16 +62,21 @@ impl NyashRunner {
|
||||
let runner = std::path::Path::new("tools/pyvm_runner.py");
|
||||
if runner.exists() {
|
||||
// We need a MIR module for PyVM: try v1 bridge first, then v0 parse
|
||||
if let Ok(Some(module)) = crate::runner::json_v1_bridge::try_parse_v1_to_module(&json) {
|
||||
if let Ok(Some(module)) =
|
||||
crate::runner::json_v1_bridge::try_parse_v1_to_module(&json)
|
||||
{
|
||||
super::json_v0_bridge::maybe_dump_mir(&module);
|
||||
let tmp_dir = std::path::Path::new("tmp");
|
||||
let _ = std::fs::create_dir_all(tmp_dir);
|
||||
let mir_json_path = tmp_dir.join("nyash_pyvm_mir.json");
|
||||
if let Err(e) = super::mir_json_emit::emit_mir_json_for_harness_bin(&module, &mir_json_path) {
|
||||
if let Err(e) = super::mir_json_emit::emit_mir_json_for_harness_bin(
|
||||
&module,
|
||||
&mir_json_path,
|
||||
) {
|
||||
eprintln!("❌ PyVM MIR JSON emit error: {}", e);
|
||||
std::process::exit(1);
|
||||
}
|
||||
crate::cli_v!("[Bridge] using PyVM (pipe) → {}", mir_json_path.display());
|
||||
crate::cli_v!("[Bridge] using PyVM (pipe) → {}", mir_json_path.display());
|
||||
// Determine entry function
|
||||
let allow_top = crate::config::env::entry_allow_toplevel_main();
|
||||
let entry = if module.functions.contains_key("Main.main") {
|
||||
@ -80,7 +86,9 @@ impl NyashRunner {
|
||||
} else if module.functions.contains_key("main") {
|
||||
eprintln!("[entry] Warning: using top-level 'main' without explicit allow; set NYASH_ENTRY_ALLOW_TOPLEVEL_MAIN=1 to silence.");
|
||||
"main"
|
||||
} else { "Main.main" };
|
||||
} else {
|
||||
"Main.main"
|
||||
};
|
||||
let mut cmd = std::process::Command::new(py3);
|
||||
crate::runner::child_env::apply_core_wrapper_env(&mut cmd);
|
||||
let status = cmd
|
||||
@ -95,7 +103,9 @@ impl NyashRunner {
|
||||
.map_err(|e| format!("spawn pyvm: {}", e))
|
||||
.unwrap();
|
||||
let code = status.code().unwrap_or(1);
|
||||
if !status.success() { crate::cli_v!("❌ PyVM (pipe) failed (status={})", code); }
|
||||
if !status.success() {
|
||||
crate::cli_v!("❌ PyVM (pipe) failed (status={})", code);
|
||||
}
|
||||
std::process::exit(code);
|
||||
}
|
||||
// v0 fallback for PyVM
|
||||
@ -104,11 +114,17 @@ impl NyashRunner {
|
||||
let tmp_dir = std::path::Path::new("tmp");
|
||||
let _ = std::fs::create_dir_all(tmp_dir);
|
||||
let mir_json_path = tmp_dir.join("nyash_pyvm_mir.json");
|
||||
if let Err(e) = super::mir_json_emit::emit_mir_json_for_harness_bin(&module, &mir_json_path) {
|
||||
if let Err(e) = super::mir_json_emit::emit_mir_json_for_harness_bin(
|
||||
&module,
|
||||
&mir_json_path,
|
||||
) {
|
||||
eprintln!("❌ PyVM MIR JSON emit error: {}", e);
|
||||
std::process::exit(1);
|
||||
}
|
||||
crate::cli_v!("[Bridge] using PyVM (pipe, v0) → {}", mir_json_path.display());
|
||||
crate::cli_v!(
|
||||
"[Bridge] using PyVM (pipe, v0) → {}",
|
||||
mir_json_path.display()
|
||||
);
|
||||
let mut cmd = std::process::Command::new(py3);
|
||||
crate::runner::child_env::apply_core_wrapper_env(&mut cmd);
|
||||
let status = cmd
|
||||
@ -121,7 +137,9 @@ impl NyashRunner {
|
||||
.map_err(|e| format!("spawn pyvm: {}", e))
|
||||
.unwrap();
|
||||
let code = status.code().unwrap_or(1);
|
||||
if !status.success() { crate::cli_v!("❌ PyVM (pipe,v0) failed (status={})", code); }
|
||||
if !status.success() {
|
||||
crate::cli_v!("❌ PyVM (pipe,v0) failed (status={})", code);
|
||||
}
|
||||
std::process::exit(code);
|
||||
}
|
||||
Err(e) => {
|
||||
|
||||
@ -8,9 +8,9 @@
|
||||
*/
|
||||
|
||||
use super::*;
|
||||
use std::collections::HashMap;
|
||||
use crate::using::spec::{UsingPackage, PackageKind};
|
||||
use crate::using::spec::{PackageKind, UsingPackage};
|
||||
use crate::using::ssot_bridge::{call_using_resolve_ssot, SsotCtx};
|
||||
use std::collections::HashMap;
|
||||
|
||||
/// Using/module resolution context accumulated from config/env/nyash.toml
|
||||
pub(super) struct UsingContext {
|
||||
@ -149,13 +149,18 @@ pub(super) fn resolve_using_target(
|
||||
}
|
||||
// Phase 22.1: Thin SSOT hook (future wiring). No behavior change for now.
|
||||
if std::env::var("HAKO_USING_SSOT").ok().as_deref() == Some("1")
|
||||
&& std::env::var("HAKO_USING_SSOT_INVOKING")
|
||||
.ok()
|
||||
.as_deref()
|
||||
!= Some("1")
|
||||
&& std::env::var("HAKO_USING_SSOT_INVOKING").ok().as_deref() != Some("1")
|
||||
{
|
||||
if let Some(ssot_res) = try_resolve_using_target_ssot(
|
||||
tgt, is_path, modules, using_paths, aliases, packages, context_dir, strict, verbose,
|
||||
tgt,
|
||||
is_path,
|
||||
modules,
|
||||
using_paths,
|
||||
aliases,
|
||||
packages,
|
||||
context_dir,
|
||||
strict,
|
||||
verbose,
|
||||
) {
|
||||
return Ok(ssot_res);
|
||||
}
|
||||
@ -220,7 +225,9 @@ pub(super) fn resolve_using_target(
|
||||
let mut cur = tgt.to_string();
|
||||
let mut depth = 0usize;
|
||||
while let Some(next) = aliases.get(&cur).cloned() {
|
||||
if trace { crate::runner::trace::log(format!("[using/resolve] alias '{}' -> '{}'", cur, next)); }
|
||||
if trace {
|
||||
crate::runner::trace::log(format!("[using/resolve] alias '{}' -> '{}'", cur, next));
|
||||
}
|
||||
if !seen.insert(cur.clone()) {
|
||||
return Err(format!("alias cycle detected at '{}'", cur));
|
||||
}
|
||||
@ -230,10 +237,22 @@ pub(super) fn resolve_using_target(
|
||||
return Err(format!("alias resolution too deep starting at '{}'", tgt));
|
||||
}
|
||||
// Continue while next is also an alias; break when concrete
|
||||
if !aliases.contains_key(&cur) { break; }
|
||||
if !aliases.contains_key(&cur) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
// Recurse once into final target to materialize path/token
|
||||
let rec = resolve_using_target(&cur, false, modules, using_paths, aliases, packages, context_dir, strict, verbose)?;
|
||||
let rec = resolve_using_target(
|
||||
&cur,
|
||||
false,
|
||||
modules,
|
||||
using_paths,
|
||||
aliases,
|
||||
packages,
|
||||
context_dir,
|
||||
strict,
|
||||
verbose,
|
||||
)?;
|
||||
crate::runner::box_index::cache_put(&key, rec.clone());
|
||||
return Ok(rec);
|
||||
}
|
||||
@ -244,7 +263,10 @@ pub(super) fn resolve_using_target(
|
||||
// Return a marker token to avoid inlining attempts; loader will consume later stages
|
||||
let out = format!("dylib:{}", pkg.path);
|
||||
if trace {
|
||||
crate::runner::trace::log(format!("[using/resolve] dylib '{}' -> '{}'", tgt, out));
|
||||
crate::runner::trace::log(format!(
|
||||
"[using/resolve] dylib '{}' -> '{}'",
|
||||
tgt, out
|
||||
));
|
||||
}
|
||||
crate::runner::box_index::cache_put(&key, out.clone());
|
||||
return Ok(out);
|
||||
@ -253,25 +275,39 @@ pub(super) fn resolve_using_target(
|
||||
// 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")) {
|
||||
if matches!(
|
||||
base.extension().and_then(|s| s.to_str()),
|
||||
Some("nyash") | Some("hako")
|
||||
) {
|
||||
// path is a file; ignore main and use as-is
|
||||
pkg.path.clone()
|
||||
} else {
|
||||
base.join(m).to_string_lossy().to_string()
|
||||
}
|
||||
} else {
|
||||
if matches!(base.extension().and_then(|s| s.to_str()), Some("nyash") | Some("hako")) {
|
||||
if matches!(
|
||||
base.extension().and_then(|s| s.to_str()),
|
||||
Some("nyash") | Some("hako")
|
||||
) {
|
||||
pkg.path.clone()
|
||||
} else {
|
||||
let leaf = base.file_name().and_then(|s| s.to_str()).unwrap_or(tgt);
|
||||
// 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!("{}.hako", leaf)).to_string_lossy().to_string() }
|
||||
if hako.exists() {
|
||||
hako.to_string_lossy().to_string()
|
||||
} else {
|
||||
base.join(format!("{}.hako", leaf))
|
||||
.to_string_lossy()
|
||||
.to_string()
|
||||
}
|
||||
}
|
||||
};
|
||||
if trace {
|
||||
crate::runner::trace::log(format!("[using/resolve] package '{}' -> '{}'", tgt, out));
|
||||
crate::runner::trace::log(format!(
|
||||
"[using/resolve] package '{}' -> '{}'",
|
||||
tgt, out
|
||||
));
|
||||
}
|
||||
crate::runner::box_index::cache_put(&key, out.clone());
|
||||
return Ok(out);
|
||||
@ -321,8 +357,11 @@ pub(super) fn resolve_using_target(
|
||||
}
|
||||
Err(e) => {
|
||||
// Maintain previous behavior: return original name and log when unresolved
|
||||
if trace { crate::runner::trace::log(format!("[using] unresolved '{}' ({})", tgt, e)); }
|
||||
else { eprintln!("[using] not found: '{}'", tgt); }
|
||||
if trace {
|
||||
crate::runner::trace::log(format!("[using] unresolved '{}' ({})", tgt, e));
|
||||
} else {
|
||||
eprintln!("[using] not found: '{}'", tgt);
|
||||
}
|
||||
Ok(tgt.to_string())
|
||||
}
|
||||
}
|
||||
@ -349,7 +388,11 @@ fn try_resolve_using_target_ssot(
|
||||
map.insert(k.clone(), v.clone());
|
||||
}
|
||||
let cwd_str = context_dir.and_then(|p| p.to_str()).map(|s| s.to_string());
|
||||
let ctx = SsotCtx { modules: map, using_paths: using_paths.to_vec(), cwd: cwd_str };
|
||||
let ctx = SsotCtx {
|
||||
modules: map,
|
||||
using_paths: using_paths.to_vec(),
|
||||
cwd: cwd_str,
|
||||
};
|
||||
if let Some(hit) = call_using_resolve_ssot(tgt, &ctx) {
|
||||
if trace {
|
||||
crate::runner::trace::log(format!("[using/ssot] '{}' -> '{}'", tgt, hit));
|
||||
@ -412,9 +455,21 @@ fn try_resolve_using_target_ssot(
|
||||
let prev = std::env::var("HAKO_USING_SSOT_INVOKING").ok();
|
||||
std::env::set_var("HAKO_USING_SSOT_INVOKING", "1");
|
||||
let res = resolve_using_target(
|
||||
tgt, is_path, modules, using_paths, aliases, packages, context_dir, strict, verbose,
|
||||
tgt,
|
||||
is_path,
|
||||
modules,
|
||||
using_paths,
|
||||
aliases,
|
||||
packages,
|
||||
context_dir,
|
||||
strict,
|
||||
verbose,
|
||||
);
|
||||
if let Some(val) = prev { std::env::set_var("HAKO_USING_SSOT_INVOKING", val); } else { let _ = std::env::remove_var("HAKO_USING_SSOT_INVOKING"); }
|
||||
if let Some(val) = prev {
|
||||
std::env::set_var("HAKO_USING_SSOT_INVOKING", val);
|
||||
} else {
|
||||
let _ = std::env::remove_var("HAKO_USING_SSOT_INVOKING");
|
||||
}
|
||||
res.ok()
|
||||
}
|
||||
|
||||
|
||||
@ -45,22 +45,49 @@ impl NyashRunner {
|
||||
}
|
||||
|
||||
// Optional Ny script plugins loader (best-effort)
|
||||
if groups.load_ny_plugins || std::env::var("NYASH_LOAD_NY_PLUGINS").ok().as_deref() == Some("1") {
|
||||
if groups.load_ny_plugins
|
||||
|| std::env::var("NYASH_LOAD_NY_PLUGINS").ok().as_deref() == Some("1")
|
||||
{
|
||||
if let Ok(text) = std::fs::read_to_string("nyash.toml") {
|
||||
if let Ok(doc) = toml::from_str::<toml::Value>(&text) {
|
||||
if let Some(np) = doc.get("ny_plugins") {
|
||||
let mut list: Vec<String> = Vec::new();
|
||||
if let Some(arr) = np.as_array() { for v in arr { if let Some(s) = v.as_str() { list.push(s.to_string()); } } }
|
||||
else if let Some(tbl) = np.as_table() { for (_k, v) in tbl { if let Some(s) = v.as_str() { list.push(s.to_string()); } else if let Some(arr) = v.as_array() { for e in arr { if let Some(s) = e.as_str() { list.push(s.to_string()); } } } } }
|
||||
if let Some(arr) = np.as_array() {
|
||||
for v in arr {
|
||||
if let Some(s) = v.as_str() {
|
||||
list.push(s.to_string());
|
||||
}
|
||||
}
|
||||
} else if let Some(tbl) = np.as_table() {
|
||||
for (_k, v) in tbl {
|
||||
if let Some(s) = v.as_str() {
|
||||
list.push(s.to_string());
|
||||
} else if let Some(arr) = v.as_array() {
|
||||
for e in arr {
|
||||
if let Some(s) = e.as_str() {
|
||||
list.push(s.to_string());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if !list.is_empty() {
|
||||
let list_only = std::env::var("NYASH_NY_PLUGINS_LIST_ONLY").ok().as_deref() == Some("1");
|
||||
let list_only =
|
||||
std::env::var("NYASH_NY_PLUGINS_LIST_ONLY").ok().as_deref()
|
||||
== Some("1");
|
||||
println!("🧩 Ny script plugins ({}):", list.len());
|
||||
for p in list {
|
||||
if list_only { println!(" • {}", p); continue; }
|
||||
if list_only {
|
||||
println!(" • {}", p);
|
||||
continue;
|
||||
}
|
||||
match std::fs::read_to_string(&p) {
|
||||
Ok(_code) => {
|
||||
// Legacy interpreter removed - ny_plugins execution disabled
|
||||
println!("[ny_plugins] {}: SKIP (legacy interpreter removed)", p);
|
||||
println!(
|
||||
"[ny_plugins] {}: SKIP (legacy interpreter removed)",
|
||||
p
|
||||
);
|
||||
}
|
||||
Err(e) => println!("[ny_plugins] {}: FAIL (read: {})", p, e),
|
||||
}
|
||||
@ -74,7 +101,10 @@ impl NyashRunner {
|
||||
// Provider verify (受け口): env で warn/strict のみ動作(未設定時は無処理)
|
||||
match crate::runtime::provider_verify::verify_from_env() {
|
||||
Ok(()) => {}
|
||||
Err(e) => { eprintln!("❌ {}", e); std::process::exit(1); }
|
||||
Err(e) => {
|
||||
eprintln!("❌ {}", e);
|
||||
std::process::exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
// Provider Lock — lock after registry and plugins are initialized (受け口)
|
||||
|
||||
@ -54,7 +54,9 @@ impl NyashRunner {
|
||||
let using_ast = crate::config::env::using_ast_enabled();
|
||||
if using_ast {
|
||||
// Text-based merge: faster for inline/selfhost execution
|
||||
match crate::runner::modes::common_util::resolve::merge_prelude_text(self, &code, filename) {
|
||||
match crate::runner::modes::common_util::resolve::merge_prelude_text(
|
||||
self, &code, filename,
|
||||
) {
|
||||
Ok(merged) => {
|
||||
code_ref = std::borrow::Cow::Owned(merged);
|
||||
}
|
||||
@ -65,7 +67,9 @@ impl NyashRunner {
|
||||
}
|
||||
} else {
|
||||
// Legacy: strip only (no prelude merge)
|
||||
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)) => {
|
||||
if !paths.is_empty() {
|
||||
eprintln!("[ny-compiler] using: AST prelude merge is disabled in this profile. Enable NYASH_USING_AST=1 or remove 'using' lines.");
|
||||
@ -73,14 +77,18 @@ impl NyashRunner {
|
||||
}
|
||||
code_ref = std::borrow::Cow::Owned(clean);
|
||||
}
|
||||
Err(e) => { eprintln!("[ny-compiler] {}", e); return false; }
|
||||
Err(e) => {
|
||||
eprintln!("[ny-compiler] {}", e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Promote dev sugar to standard: pre-expand line-head '@name[:T] = expr' to 'local name[:T] = expr'
|
||||
{
|
||||
let expanded = crate::runner::modes::common_util::resolve::preexpand_at_local(code_ref.as_ref());
|
||||
let expanded =
|
||||
crate::runner::modes::common_util::resolve::preexpand_at_local(code_ref.as_ref());
|
||||
code_ref = std::borrow::Cow::Owned(expanded);
|
||||
}
|
||||
|
||||
@ -98,47 +106,56 @@ impl NyashRunner {
|
||||
{
|
||||
let preenv = std::env::var("NYASH_MACRO_SELFHOST_PRE_EXPAND")
|
||||
.ok()
|
||||
.or_else(|| if crate::r#macro::enabled() { Some("auto".to_string()) } else { None });
|
||||
.or_else(|| {
|
||||
if crate::r#macro::enabled() {
|
||||
Some("auto".to_string())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
});
|
||||
let do_pre = match preenv.as_deref() {
|
||||
Some("1") => true,
|
||||
Some("auto") => crate::r#macro::enabled() && crate::config::env::vm_use_py(),
|
||||
_ => false,
|
||||
};
|
||||
if do_pre && crate::r#macro::enabled() {
|
||||
crate::cli_v!("[ny-compiler] selfhost macro pre-expand: engaging (mode={:?})", preenv);
|
||||
match NyashParser::parse_from_string(code_ref.as_ref()) {
|
||||
Ok(ast0) => {
|
||||
let ast = crate::r#macro::maybe_expand_and_dump(&ast0, false);
|
||||
// Compile to MIR and execute (respect VM/PyVM policy similar to vm mode)
|
||||
let mut mir_compiler = MirCompiler::with_options(true);
|
||||
match mir_compiler.compile(ast) {
|
||||
Ok(result) => {
|
||||
let prefer_pyvm = crate::config::env::vm_use_py();
|
||||
if prefer_pyvm {
|
||||
if let Ok(code) = crate::runner::modes::common_util::pyvm::run_pyvm_harness_lib(&result.module, "selfhost-preexpand") {
|
||||
crate::cli_v!(
|
||||
"[ny-compiler] selfhost macro pre-expand: engaging (mode={:?})",
|
||||
preenv
|
||||
);
|
||||
match NyashParser::parse_from_string(code_ref.as_ref()) {
|
||||
Ok(ast0) => {
|
||||
let ast = crate::r#macro::maybe_expand_and_dump(&ast0, false);
|
||||
// Compile to MIR and execute (respect VM/PyVM policy similar to vm mode)
|
||||
let mut mir_compiler = MirCompiler::with_options(true);
|
||||
match mir_compiler.compile(ast) {
|
||||
Ok(result) => {
|
||||
let prefer_pyvm = crate::config::env::vm_use_py();
|
||||
if prefer_pyvm {
|
||||
if let Ok(code) = crate::runner::modes::common_util::pyvm::run_pyvm_harness_lib(&result.module, "selfhost-preexpand") {
|
||||
println!("Result: {}", code);
|
||||
std::process::exit(code);
|
||||
} else {
|
||||
eprintln!("❌ PyVM error (selfhost-preexpand)");
|
||||
std::process::exit(1);
|
||||
}
|
||||
} else {
|
||||
// For now, only PyVM path is supported in pre-expand mode; fall back otherwise.
|
||||
crate::cli_v!("[ny-compiler] pre-expand path requires NYASH_VM_USE_PY=1; falling back to default selfhost");
|
||||
} else {
|
||||
// For now, only PyVM path is supported in pre-expand mode; fall back otherwise.
|
||||
crate::cli_v!("[ny-compiler] pre-expand path requires NYASH_VM_USE_PY=1; falling back to default selfhost");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
eprintln!("[ny-compiler] pre-expand compile error: {}", e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
eprintln!("[ny-compiler] pre-expand compile error: {}", e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
eprintln!("[ny-compiler] pre-expand parse error: {}", e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
eprintln!("[ny-compiler] pre-expand parse error: {}", e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
let tmp_path = tmp_dir.join("ny_parser_input.ny");
|
||||
@ -194,7 +211,9 @@ impl NyashRunner {
|
||||
.collect();
|
||||
if !items.is_empty() {
|
||||
extra_owned.push("--".to_string());
|
||||
for it in items { extra_owned.push(it); }
|
||||
for it in items {
|
||||
extra_owned.push(it);
|
||||
}
|
||||
}
|
||||
}
|
||||
let extra: Vec<&str> = extra_owned.iter().map(|s| s.as_str()).collect();
|
||||
@ -204,10 +223,7 @@ impl NyashRunner {
|
||||
parser_prog,
|
||||
timeout_ms,
|
||||
&extra,
|
||||
&[
|
||||
"NYASH_USE_NY_COMPILER",
|
||||
"NYASH_CLI_VERBOSE",
|
||||
],
|
||||
&["NYASH_USE_NY_COMPILER", "NYASH_CLI_VERBOSE"],
|
||||
&[
|
||||
("NYASH_JSON_ONLY", "1"),
|
||||
("NYASH_DISABLE_PLUGINS", "1"),
|
||||
@ -220,9 +236,9 @@ impl NyashRunner {
|
||||
match json::parse_json_v0_line(&line) {
|
||||
Ok(module) => {
|
||||
if crate::config::env::cli_verbose() {
|
||||
if crate::config::env::cli_verbose() {
|
||||
super::json_v0_bridge::maybe_dump_mir(&module);
|
||||
}
|
||||
if crate::config::env::cli_verbose() {
|
||||
super::json_v0_bridge::maybe_dump_mir(&module);
|
||||
}
|
||||
}
|
||||
let emit_only = crate::config::env::ny_compiler_emit_only();
|
||||
if emit_only {
|
||||
@ -230,14 +246,14 @@ impl NyashRunner {
|
||||
}
|
||||
// Prefer PyVM path when requested
|
||||
if crate::config::env::vm_use_py() {
|
||||
if let Some(code) = crate::runner::modes::common_util::selfhost::json::run_pyvm_module(&module, "selfhost") {
|
||||
if let Some(code) = crate::runner::modes::common_util::selfhost::json::run_pyvm_module(&module, "selfhost") {
|
||||
println!("Result: {}", code);
|
||||
std::process::exit(code);
|
||||
}
|
||||
}
|
||||
self.execute_mir_module(&module);
|
||||
return true;
|
||||
}
|
||||
self.execute_mir_module(&module);
|
||||
return true;
|
||||
}
|
||||
Err(e) => {
|
||||
eprintln!("[ny-compiler] json parse error (child): {}", e);
|
||||
}
|
||||
@ -260,10 +276,14 @@ impl NyashRunner {
|
||||
.ok()
|
||||
.and_then(|s| s.parse().ok())
|
||||
.unwrap_or(60000); // Phase 25.1b: Increased to 60000ms (60s) for consistency
|
||||
let out = match super::modes::common_util::io::spawn_with_timeout(cmd, timeout_ms) {
|
||||
Ok(o) => o,
|
||||
Err(e) => { eprintln!("[ny-compiler] python harness failed: {}", e); return false; }
|
||||
};
|
||||
let out =
|
||||
match super::modes::common_util::io::spawn_with_timeout(cmd, timeout_ms) {
|
||||
Ok(o) => o,
|
||||
Err(e) => {
|
||||
eprintln!("[ny-compiler] python harness failed: {}", e);
|
||||
return false;
|
||||
}
|
||||
};
|
||||
if !out.timed_out {
|
||||
if let Ok(s) = String::from_utf8(out.stdout) {
|
||||
if let Some(line) = crate::runner::modes::common_util::selfhost::json::first_json_v0_line(&s) {
|
||||
@ -340,14 +360,18 @@ impl NyashRunner {
|
||||
.ok()
|
||||
.and_then(|s| s.parse().ok())
|
||||
.unwrap_or(2000);
|
||||
if let Some(module) = super::modes::common_util::selfhost_exe::exe_try_parse_json_v0(filename, timeout_ms) {
|
||||
if let Some(module) = super::modes::common_util::selfhost_exe::exe_try_parse_json_v0(
|
||||
filename, timeout_ms,
|
||||
) {
|
||||
if crate::config::env::cli_verbose() {
|
||||
super::json_v0_bridge::maybe_dump_mir(&module);
|
||||
}
|
||||
let emit_only = std::env::var("NYASH_NY_COMPILER_EMIT_ONLY")
|
||||
.unwrap_or_else(|_| "1".to_string())
|
||||
== "1";
|
||||
if emit_only { return false; }
|
||||
if emit_only {
|
||||
return false;
|
||||
}
|
||||
// Prefer PyVM when requested (reference semantics)
|
||||
if std::env::var("NYASH_VM_USE_PY").ok().as_deref() == Some("1") {
|
||||
if let Ok(py3) = which::which("python3") {
|
||||
@ -356,20 +380,40 @@ impl NyashRunner {
|
||||
let tmp_dir = std::path::Path::new("tmp");
|
||||
let _ = std::fs::create_dir_all(tmp_dir);
|
||||
let mir_json_path = tmp_dir.join("nyash_pyvm_mir.json");
|
||||
if let Err(e) = crate::runner::mir_json_emit::emit_mir_json_for_harness_bin(&module, &mir_json_path) {
|
||||
if let Err(e) =
|
||||
crate::runner::mir_json_emit::emit_mir_json_for_harness_bin(
|
||||
&module,
|
||||
&mir_json_path,
|
||||
)
|
||||
{
|
||||
eprintln!("❌ PyVM MIR JSON emit error: {}", e);
|
||||
process::exit(1);
|
||||
}
|
||||
crate::cli_v!("[Bridge] using PyVM (selfhost) → {}", mir_json_path.display());
|
||||
crate::cli_v!(
|
||||
"[Bridge] using PyVM (selfhost) → {}",
|
||||
mir_json_path.display()
|
||||
);
|
||||
let allow_top = crate::config::env::entry_allow_toplevel_main();
|
||||
let entry = if module.functions.contains_key("Main.main") { "Main.main" }
|
||||
else if allow_top && module.functions.contains_key("main") { "main" }
|
||||
else if module.functions.contains_key("main") { eprintln!("[entry] Warning: using top-level 'main' without explicit allow; set NYASH_ENTRY_ALLOW_TOPLEVEL_MAIN=1 to silence."); "main" }
|
||||
else { "Main.main" };
|
||||
let entry = if module.functions.contains_key("Main.main") {
|
||||
"Main.main"
|
||||
} else if allow_top && module.functions.contains_key("main") {
|
||||
"main"
|
||||
} else if module.functions.contains_key("main") {
|
||||
eprintln!("[entry] Warning: using top-level 'main' without explicit allow; set NYASH_ENTRY_ALLOW_TOPLEVEL_MAIN=1 to silence.");
|
||||
"main"
|
||||
} else {
|
||||
"Main.main"
|
||||
};
|
||||
let mut cmd = std::process::Command::new(py3);
|
||||
crate::runner::child_env::apply_core_wrapper_env(&mut cmd);
|
||||
let status = cmd
|
||||
.args(["tools/pyvm_runner.py", "--in", &mir_json_path.display().to_string(), "--entry", entry])
|
||||
.args([
|
||||
"tools/pyvm_runner.py",
|
||||
"--in",
|
||||
&mir_json_path.display().to_string(),
|
||||
"--entry",
|
||||
entry,
|
||||
])
|
||||
.status()
|
||||
.map_err(|e| format!("spawn pyvm: {}", e))
|
||||
.unwrap();
|
||||
@ -382,11 +426,13 @@ impl NyashRunner {
|
||||
crate::runner::child_env::pre_run_reset_oob_if_strict();
|
||||
crate::runner::child_env::pre_run_reset_oob_if_strict();
|
||||
self.execute_mir_module(&module);
|
||||
if crate::config::env::oob_strict_fail() && crate::runtime::observe::oob_seen() {
|
||||
if crate::config::env::oob_strict_fail() && crate::runtime::observe::oob_seen()
|
||||
{
|
||||
eprintln!("[selfhost][oob-strict] Out-of-bounds observed → exit(1)");
|
||||
std::process::exit(1);
|
||||
}
|
||||
if crate::config::env::oob_strict_fail() && crate::runtime::observe::oob_seen() {
|
||||
if crate::config::env::oob_strict_fail() && crate::runtime::observe::oob_seen()
|
||||
{
|
||||
eprintln!("[selfhost][oob-strict] Out-of-bounds observed → exit(1)");
|
||||
std::process::exit(1);
|
||||
}
|
||||
@ -419,28 +465,37 @@ impl NyashRunner {
|
||||
if emit_only {
|
||||
return false;
|
||||
}
|
||||
// Phase-15 policy: when NYASH_VM_USE_PY=1, prefer PyVM as reference executor
|
||||
// regardless of BoxCall presence to ensure semantics parity (e.g., PHI merges).
|
||||
let prefer_pyvm = std::env::var("NYASH_VM_USE_PY").ok().as_deref() == Some("1");
|
||||
// Backward compatibility: if not preferring PyVM explicitly, still auto-enable when BoxCalls exist.
|
||||
let needs_pyvm = !prefer_pyvm
|
||||
&& module.functions.values().any(|f| {
|
||||
f.blocks.values().any(|bb| {
|
||||
bb.instructions.iter().any(|inst| {
|
||||
matches!(inst, crate::mir::MirInstruction::BoxCall { .. })
|
||||
// Phase-15 policy: when NYASH_VM_USE_PY=1, prefer PyVM as reference executor
|
||||
// regardless of BoxCall presence to ensure semantics parity (e.g., PHI merges).
|
||||
let prefer_pyvm = std::env::var("NYASH_VM_USE_PY").ok().as_deref() == Some("1");
|
||||
// Backward compatibility: if not preferring PyVM explicitly, still auto-enable when BoxCalls exist.
|
||||
let needs_pyvm = !prefer_pyvm
|
||||
&& module.functions.values().any(|f| {
|
||||
f.blocks.values().any(|bb| {
|
||||
bb.instructions.iter().any(|inst| {
|
||||
matches!(inst, crate::mir::MirInstruction::BoxCall { .. })
|
||||
})
|
||||
})
|
||||
})
|
||||
});
|
||||
});
|
||||
if prefer_pyvm || needs_pyvm {
|
||||
let label = if prefer_pyvm { "selfhost" } else { "selfhost-fallback" };
|
||||
if let Some(code) = crate::runner::modes::common_util::selfhost::json::run_pyvm_module(&module, label) {
|
||||
let label = if prefer_pyvm {
|
||||
"selfhost"
|
||||
} else {
|
||||
"selfhost-fallback"
|
||||
};
|
||||
if let Some(code) =
|
||||
crate::runner::modes::common_util::selfhost::json::run_pyvm_module(
|
||||
&module, label,
|
||||
)
|
||||
{
|
||||
println!("Result: {}", code);
|
||||
std::process::exit(code);
|
||||
}
|
||||
}
|
||||
crate::runner::child_env::pre_run_reset_oob_if_strict();
|
||||
self.execute_mir_module(&module);
|
||||
if crate::config::env::oob_strict_fail() && crate::runtime::observe::oob_seen() {
|
||||
if crate::config::env::oob_strict_fail() && crate::runtime::observe::oob_seen()
|
||||
{
|
||||
eprintln!("[selfhost][oob-strict] Out-of-bounds observed → exit(1)");
|
||||
std::process::exit(1);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user