From 2720884a20c3d352483649b699106f77ef4f74c1 Mon Sep 17 00:00:00 2001 From: Selfhosting Dev Date: Wed, 17 Sep 2025 11:45:57 +0900 Subject: [PATCH] bridge/json_v0: split expr lowering; add Ternary/Peek lowering + AST; selfhost Peek JSON emit; add selfhost Peek smoke; warning cleanup in lowering/optimizer/verification - Split expr lowering into ; route calls from stmt lowering - Implement ternary/peek lowering (MIR13 PHI-off=Copy, PHI-on=Phi) - Extend JSON v0 AST (ExprV0::{Ternary,Peek}, PeekArmV0) - Selfhost parser_box: emit Peek JSON; add Stage-2 'Peek basic' smoke - Reduce warnings: remove unused imports/vars in several modules - current_task: update plan for legacy VM/Interpreter offboarding --- CURRENT_TASK.md | 25 +- apps/selfhost-compiler/boxes/parser_box.nyash | 80 +++ src/mir/builder/lifecycle.rs | 6 +- src/mir/optimizer_passes/boxfield.rs | 2 +- src/mir/optimizer_passes/diagnostics.rs | 2 +- src/mir/optimizer_passes/intrinsics.rs | 2 +- src/mir/optimizer_passes/normalize.rs | 2 +- src/mir/optimizer_passes/reorder.rs | 2 +- src/mir/passes/dce.rs | 4 +- src/mir/verification.rs | 2 +- src/parser/expressions.rs | 2 +- src/runner/json_v0_bridge/ast.rs | 18 + src/runner/json_v0_bridge/lowering.rs | 486 +----------------- src/runner/json_v0_bridge/lowering/expr.rs | 461 +++++++++++++++++ src/runner/json_v0_bridge/lowering/if_else.rs | 9 +- src/runner/json_v0_bridge/lowering/loop_.rs | 8 +- src/runner/json_v0_bridge/lowering/peek.rs | 84 +++ src/runner/json_v0_bridge/lowering/ternary.rs | 60 +++ .../json_v0_bridge/lowering/try_catch.rs | 3 +- src/runner/selfhost.rs | 2 +- tools/selfhost_stage2_smoke.sh | 21 + 21 files changed, 770 insertions(+), 511 deletions(-) create mode 100644 src/runner/json_v0_bridge/lowering/expr.rs create mode 100644 src/runner/json_v0_bridge/lowering/peek.rs create mode 100644 src/runner/json_v0_bridge/lowering/ternary.rs diff --git a/CURRENT_TASK.md b/CURRENT_TASK.md index 8a9c6176..65f0ee19 100644 --- a/CURRENT_TASK.md +++ b/CURRENT_TASK.md @@ -9,13 +9,17 @@ What Changed (recent) - `mir_no_phi()` default set to true (can disable via `NYASH_MIR_NO_PHI=0`). - Curated LLVM runner defaults to PHI‑off; `--phi-on` enables MIR14 lane. - Added doc: `docs/development/mir/MIR13_MODE.md`; README references it. -- JSON v0 Bridge lowering split (non‑functional) - - Added `src/runner/json_v0_bridge/lowering/{if_else.rs, loop_.rs, try_catch.rs, merge.rs}` and routed calls from `lowering.rs`. +- JSON v0 Bridge lowering refactor + features + - Split helpers: `src/runner/json_v0_bridge/lowering/{if_else.rs, loop_.rs, try_catch.rs, merge.rs}`(既存)に加え、式系を `lowering/expr.rs` に分離(振る舞い不変)。 + - 新規サポート: Ternary/Peek の Lowering を実装し、`expr.rs` から `ternary.rs`/`peek.rs` へ委譲(MIR13 PHI‑off=Copy合流/PHI‑on=Phi 合流)。 + - Self‑host 生成器(Stage‑1 JSON v0)に Peek emit を追加: `apps/selfhost-compiler/boxes/parser_box.nyash`。 + - Selfhost/PyVM スモークを通して E2E 確認(peek/ternary)。 - llvmlite stability for MIR13 - Resolver: forbids cross‑block non‑dominating vmap reuse; for multi‑pred and no declared PHI, synthesizes a localization PHI at block head. - Finalize remains function‑local; `block_end_values` snapshots and placeholder wiring still in place. - Parity runner pragmatics - `tools/pyvm_vs_llvmlite.sh` compares exit code by default; use `CMP_STRICT=1` for stdout+exit. + - Stage‑2 smokes更新: `tools/selfhost_stage2_smoke.sh` に "Peek basic" を追加。 Current Status - Self‑hosting Bridge → PyVM smokes: PASS (Stage‑2 reps: array/string/logic/if/loop). @@ -23,11 +27,16 @@ Current Status - Curated LLVM (PHI‑on experimental): `apps/tests/loop_if_phi.nyash` shows a dominance issue (observed; not blocking, MIR13 recommended). Next (short plan) -1) JSON v0 lowering: split remaining helpers (peek/ternary/expr) without behavior change. -2) PHI‑on lane (optional): investigate `loop_if_phi` dominance by tightening finalize ordering and resolver materialization (low priority). -3) Runner refactor (small PRs): - - `selfhost/{child.rs,json.rs}` split; `modes/common/{io,resolve,exec}.rs` split; reduce `runner/mod.rs` surface. -4) Optimizer/Verifier thin‑hub cleanup (non‑functional): orchestrator minimalization and pass boundaries clarified. +1) Legacy Interpreter/VM offboarding (phase‑A): + - Introduce `vm-legacy` feature (default OFF) to gate old VM execution層。 + - 抽出: JIT が参照する最小型(例: `VMValue`)を薄い共通モジュールへ切替(`vm_types` 等)。 + - `interpreter-legacy`/`vm-legacy` を既定ビルドから外し、ビルド警告を縮小。 +2) Legacy Interpreter/VM offboarding (phase‑B): + - 物理移動: `src/archive/{interpreter_legacy,vm_legacy}/` へ移設(ドキュメント更新)。 +3) PHI‑on lane(任意): `loop_if_phi` 支配関係を finalize/resolve の順序強化で観察(低優先)。 +4) Runner refactor(小PR): + - `selfhost/{child.rs,json.rs}` 分離; `modes/common/{io,resolve,exec}.rs` 分割; `runner/mod.rs`の表面削減。 +5) Optimizer/Verifier thin‑hub cleanup(非機能): orchestrator最小化とパス境界の明確化。 How to Run - PyVM reference smokes: `tools/pyvm_stage2_smoke.sh` @@ -46,4 +55,4 @@ Notes / Policies - Focus is self‑hosting stability. JIT/Cranelift is out of scope (safety fixes only). - PHI generation remains centralized in llvmlite; Bridge/Builder keep PHI‑off by default. - No full tracing GC yet; handles/Arc lifetimes govern object retention. Safepoint/barrier/roots are staging utilities. - + - Legacy Interpreter/VM は段階的にアーカイブへ。日常の意味論確認は PyVM を基準として継続。 diff --git a/apps/selfhost-compiler/boxes/parser_box.nyash b/apps/selfhost-compiler/boxes/parser_box.nyash index 7b5f5031..eaf1099e 100644 --- a/apps/selfhost-compiler/boxes/parser_box.nyash +++ b/apps/selfhost-compiler/boxes/parser_box.nyash @@ -247,6 +247,86 @@ box ParserBox { if me.starts_with_kw(src, j, "true") == 1 { me.gpos_set(j + 4) return "{\"type\":\"Bool\",\"value\":true}" } if me.starts_with_kw(src, j, "false") == 1 { me.gpos_set(j + 5) return "{\"type\":\"Bool\",\"value\":false}" } if me.starts_with_kw(src, j, "null") == 1 { me.gpos_set(j + 4) return "{\"type\":\"Null\"}" } + // Peek expression: peek { "label" => , ..., else => } + if me.starts_with_kw(src, j, "peek") == 1 { + j = j + 4 + j = me.skip_ws(src, j) + // scrutinee expression + local scr = me.parse_expr2(src, j) + j = me.gpos_get() + j = me.skip_ws(src, j) + if src.substring(j, j+1) == "{" { j = j + 1 } // enter arms block + j = me.skip_ws(src, j) + local arms_json = "[" + local first_arm = 1 + local else_json = null + local n = src.length() + local contp = 1 + local guardp = 0 + local maxp = 400000 + loop(contp == 1) { + if guardp > maxp { contp = 0 } else { guardp = guardp + 1 } + j = me.skip_ws(src, j) + if j >= n { contp = 0 } else { + if src.substring(j, j+1) == "}" { + j = j + 1 + contp = 0 + } else { + // else arm or labeled arm + if me.starts_with_kw(src, j, "else") == 1 { + j = j + 4 + j = me.skip_ws(src, j) + if src.substring(j, j+2) == "=>" { j = j + 2 } + j = me.skip_ws(src, j) + // else body may be a block or bare expr + if src.substring(j, j+1) == "{" { + j = j + 1 + j = me.skip_ws(src, j) + else_json = me.parse_expr2(src, j) + j = me.gpos_get() + j = me.skip_ws(src, j) + if src.substring(j, j+1) == "}" { j = j + 1 } + } else { + else_json = me.parse_expr2(src, j) + j = me.gpos_get() + } + // optional separator/newline tolerated; continue until '}' + } else { + // labeled arm: string literal label + if src.substring(j, j+1) != "\"" { + // degrade safely to avoid infinite loop + j = j + 1 + continue + } + local label_raw = me.read_string_lit(src, j) + j = me.gpos_get() + j = me.skip_ws(src, j) + if src.substring(j, j+2) == "=>" { j = j + 2 } + j = me.skip_ws(src, j) + // arm expr: block or bare expr + local expr_json = "{\"type\":\"Int\",\"value\":0}" + if src.substring(j, j+1) == "{" { + j = j + 1 + j = me.skip_ws(src, j) + expr_json = me.parse_expr2(src, j) + j = me.gpos_get() + j = me.skip_ws(src, j) + if src.substring(j, j+1) == "}" { j = j + 1 } + } else { + expr_json = me.parse_expr2(src, j) + j = me.gpos_get() + } + local arm_json = "{\"label\":\"" + me.esc_json(label_raw) + "\",\"expr\":" + expr_json + "}" + if first_arm == 1 { arms_json = arms_json + arm_json first_arm = 0 } else { arms_json = arms_json + "," + arm_json } + } + } + } + } + arms_json = arms_json + "]" + if else_json == null { else_json = "{\"type\":\"Null\"}" } + me.gpos_set(j) + return "{\"type\":\"Peek\",\"scrutinee\":" + scr + ",\"arms\":" + arms_json + ",\"else\":" + else_json + "}" + } local ch = src.substring(j, j+1) // Parenthesized if ch == "(" { diff --git a/src/mir/builder/lifecycle.rs b/src/mir/builder/lifecycle.rs index 8744707e..2589ce76 100644 --- a/src/mir/builder/lifecycle.rs +++ b/src/mir/builder/lifecycle.rs @@ -1,7 +1,4 @@ -use super::{ - BasicBlockIdGenerator, EffectMask, FunctionSignature, MirFunction, MirInstruction, MirModule, - MirType, ValueId, -}; +use super::{EffectMask, FunctionSignature, MirFunction, MirInstruction, MirModule, MirType, ValueId}; use crate::ast::ASTNode; // Lifecycle routines extracted from builder.rs @@ -106,4 +103,3 @@ impl super::MirBuilder { Ok(module) } } - diff --git a/src/mir/optimizer_passes/boxfield.rs b/src/mir/optimizer_passes/boxfield.rs index 289cfe85..0a06d842 100644 --- a/src/mir/optimizer_passes/boxfield.rs +++ b/src/mir/optimizer_passes/boxfield.rs @@ -1,6 +1,6 @@ use crate::mir::optimizer::MirOptimizer; use crate::mir::optimizer_stats::OptimizationStats; -use crate::mir::{EffectMask, MirInstruction as I, MirModule}; +use crate::mir::{MirInstruction as I, MirModule}; /// Optimize BoxField operations (scaffolding) pub fn optimize_boxfield_operations( diff --git a/src/mir/optimizer_passes/diagnostics.rs b/src/mir/optimizer_passes/diagnostics.rs index 4950e931..11b9bfd4 100644 --- a/src/mir/optimizer_passes/diagnostics.rs +++ b/src/mir/optimizer_passes/diagnostics.rs @@ -1,6 +1,6 @@ use crate::mir::optimizer::MirOptimizer; use crate::mir::optimizer_stats::OptimizationStats; -use crate::mir::{function::MirFunction, BasicBlockId, MirInstruction, MirModule, ValueId}; +use crate::mir::{BasicBlockId, MirInstruction, MirModule, ValueId}; /// Diagnostic: detect unlowered is/as/isType/asType after Builder pub fn diagnose_unlowered_type_ops( diff --git a/src/mir/optimizer_passes/intrinsics.rs b/src/mir/optimizer_passes/intrinsics.rs index fa667851..147ce80c 100644 --- a/src/mir/optimizer_passes/intrinsics.rs +++ b/src/mir/optimizer_passes/intrinsics.rs @@ -9,7 +9,7 @@ pub fn optimize_intrinsic_calls( opt: &mut MirOptimizer, module: &mut MirModule, ) -> OptimizationStats { - let mut stats = OptimizationStats::new(); + let stats = OptimizationStats::new(); for (func_name, _function) in &mut module.functions { if opt.debug_enabled() { println!(" ⚡ Intrinsic optimization in function: {}", func_name); diff --git a/src/mir/optimizer_passes/normalize.rs b/src/mir/optimizer_passes/normalize.rs index 25d7e7b8..c4d2459e 100644 --- a/src/mir/optimizer_passes/normalize.rs +++ b/src/mir/optimizer_passes/normalize.rs @@ -1,6 +1,6 @@ use crate::mir::optimizer::MirOptimizer; use crate::mir::optimizer_stats::OptimizationStats; -use crate::mir::{BarrierOp, MirInstruction, MirModule, TypeOpKind, ValueId, WeakRefOp}; +use crate::mir::{BarrierOp, MirModule, TypeOpKind, ValueId, WeakRefOp}; pub fn force_plugin_invoke(_opt: &mut MirOptimizer, module: &mut MirModule) -> OptimizationStats { use crate::mir::MirInstruction as I; diff --git a/src/mir/optimizer_passes/reorder.rs b/src/mir/optimizer_passes/reorder.rs index 46e20a69..f70e8a45 100644 --- a/src/mir/optimizer_passes/reorder.rs +++ b/src/mir/optimizer_passes/reorder.rs @@ -7,7 +7,7 @@ pub fn reorder_pure_instructions( opt: &mut MirOptimizer, module: &mut MirModule, ) -> OptimizationStats { - let mut stats = OptimizationStats::new(); + let stats = OptimizationStats::new(); for (func_name, _function) in &mut module.functions { if opt.debug_enabled() { println!( diff --git a/src/mir/passes/dce.rs b/src/mir/passes/dce.rs index bb966604..b50cbb65 100644 --- a/src/mir/passes/dce.rs +++ b/src/mir/passes/dce.rs @@ -2,7 +2,7 @@ //! //! Extracted from the monolithic optimizer to enable modular pass composition. -use crate::mir::{MirFunction, MirInstruction, MirModule, ValueId}; +use crate::mir::{MirFunction, MirModule, ValueId}; use std::collections::HashSet; /// Eliminate dead code (unused results of pure instructions) across the module. @@ -59,7 +59,7 @@ fn eliminate_dead_code_in_function(function: &mut MirFunction) -> usize { // Remove unused pure instructions let mut eliminated = 0usize; - for (bbid, block) in &mut function.blocks { + for (_bbid, block) in &mut function.blocks { block.instructions.retain(|inst| { if inst.effects().is_pure() { if let Some(dst) = inst.dst_value() { diff --git a/src/mir/verification.rs b/src/mir/verification.rs index 148fc2d0..49cfdd66 100644 --- a/src/mir/verification.rs +++ b/src/mir/verification.rs @@ -7,7 +7,7 @@ use super::{BasicBlockId, MirFunction, MirModule, ValueId}; use crate::debug::log as dlog; use crate::mir::verification_types::VerificationError; -use std::collections::{HashMap, HashSet}; +use std::collections::HashMap; mod awaits; mod barrier; mod legacy; diff --git a/src/parser/expressions.rs b/src/parser/expressions.rs index c31aba20..4c466825 100644 --- a/src/parser/expressions.rs +++ b/src/parser/expressions.rs @@ -7,7 +7,7 @@ use super::common::ParserUtils; use super::{NyashParser, ParseError}; -use crate::ast::{ASTNode, BinaryOperator, LiteralValue, Span, UnaryOperator}; +use crate::ast::{ASTNode, Span, UnaryOperator}; use crate::tokenizer::TokenType; // Debug macros are now imported from the parent module via #[macro_export] diff --git a/src/runner/json_v0_bridge/ast.rs b/src/runner/json_v0_bridge/ast.rs index 3683ed98..b0e7f8d7 100644 --- a/src/runner/json_v0_bridge/ast.rs +++ b/src/runner/json_v0_bridge/ast.rs @@ -57,6 +57,12 @@ pub(super) struct CatchV0 { pub(super) body: Vec, } +#[derive(Debug, Deserialize, Serialize, Clone)] +pub(super) struct PeekArmV0 { + pub(super) label: String, + pub(super) expr: ExprV0, +} + #[derive(Debug, Deserialize, Serialize, Clone)] #[serde(tag = "type")] pub(super) enum ExprV0 { @@ -108,4 +114,16 @@ pub(super) enum ExprV0 { Throw { expr: Box, }, + Ternary { + cond: Box, + then: Box, + #[serde(rename = "else")] + r#else: Box, + }, + Peek { + scrutinee: Box, + arms: Vec, + #[serde(rename = "else")] + r#else: Box, + }, } diff --git a/src/runner/json_v0_bridge/lowering.rs b/src/runner/json_v0_bridge/lowering.rs index 0a1da006..b4017a86 100644 --- a/src/runner/json_v0_bridge/lowering.rs +++ b/src/runner/json_v0_bridge/lowering.rs @@ -1,18 +1,20 @@ -use super::ast::{CatchV0, ExprV0, ProgramV0, StmtV0}; +use super::ast::{ProgramV0, StmtV0}; use crate::mir::{ - BasicBlock, BasicBlockId, BinaryOp, 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::collections::HashSet; // Split out merge/new_block helpers for readability (no behavior change) mod merge; -use merge::{merge_var_maps, merge_values, new_block}; +use merge::{merge_var_maps, new_block}; // Feature splits (gradual extraction) 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 peek; // placeholder (not wired) #[derive(Clone, Copy)] pub(super) struct LoopContext { @@ -39,472 +41,6 @@ impl BridgeEnv { } } -trait VarScope { - fn resolve( - &mut self, - env: &BridgeEnv, - f: &mut MirFunction, - cur_bb: BasicBlockId, - name: &str, - ) -> Result, String>; -} - -struct NoVars; -impl VarScope for NoVars { - fn resolve( - &mut self, - _env: &BridgeEnv, - _f: &mut MirFunction, - _cur_bb: BasicBlockId, - name: &str, - ) -> Result, String> { - Err(format!("undefined variable in this context: {}", name)) - } -} - -struct MapVars<'a> { - vars: &'a mut HashMap, -} -impl<'a> MapVars<'a> { - fn new(vars: &'a mut HashMap) -> Self { - Self { vars } - } -} -impl<'a> VarScope for MapVars<'a> { - fn resolve( - &mut self, - env: &BridgeEnv, - f: &mut MirFunction, - cur_bb: BasicBlockId, - name: &str, - ) -> Result, String> { - if let Some(&vid) = self.vars.get(name) { - return Ok(Some(vid)); - } - if name == "me" { - if env.allow_me_dummy { - let dst = f.next_value_id(); - if let Some(bb) = f.get_block_mut(cur_bb) { - bb.add_instruction(MirInstruction::NewBox { - dst, - box_type: env.me_class.clone(), - args: vec![], - }); - } - self.vars.insert(name.to_string(), dst); - Ok(Some(dst)) - } else { - Err("undefined 'me' outside box context (set NYASH_BRIDGE_ME_DUMMY=1 to inject placeholder)".into()) - } - } else { - Ok(None) - } - } -} - -// moved helpers are imported above - -fn lower_throw( - env: &BridgeEnv, - f: &mut MirFunction, - cur_bb: BasicBlockId, - exception_value: ValueId, -) -> (ValueId, BasicBlockId) { - if env.throw_enabled { - if let Some(bb) = f.get_block_mut(cur_bb) { - bb.set_terminator(MirInstruction::Throw { - exception: exception_value, - effects: EffectMask::PANIC, - }); - } - (exception_value, cur_bb) - } 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), - }); - } - (dst, cur_bb) - } -} - -fn lower_expr_with_scope( - env: &BridgeEnv, - f: &mut MirFunction, - cur_bb: BasicBlockId, - e: &ExprV0, - vars: &mut S, -) -> Result<(ValueId, BasicBlockId), String> { - match e { - ExprV0::Int { value } => { - let ival: i64 = if let Some(n) = value.as_i64() { - n - } else if let Some(s) = value.as_str() { - s.parse().map_err(|_| "invalid int literal")? - } else { - return Err("invalid int literal".into()); - }; - 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(ival), - }); - } - Ok((dst, cur_bb)) - } - ExprV0::Str { value } => { - let dst = f.next_value_id(); - if let Some(bb) = f.get_block_mut(cur_bb) { - bb.add_instruction(MirInstruction::Const { - dst, - value: ConstValue::String(value.clone()), - }); - } - Ok((dst, cur_bb)) - } - ExprV0::Bool { value } => { - let dst = f.next_value_id(); - if let Some(bb) = f.get_block_mut(cur_bb) { - bb.add_instruction(MirInstruction::Const { - dst, - value: ConstValue::Bool(*value), - }); - } - Ok((dst, cur_bb)) - } - ExprV0::Binary { op, lhs, rhs } => { - let (l, cur_after_l) = lower_expr_with_scope(env, f, cur_bb, lhs, vars)?; - let (r, cur_after_r) = lower_expr_with_scope(env, f, cur_after_l, rhs, vars)?; - let bop = match op.as_str() { - "+" => BinaryOp::Add, - "-" => BinaryOp::Sub, - "*" => BinaryOp::Mul, - "/" => BinaryOp::Div, - _ => return Err("unsupported op".into()), - }; - let dst = f.next_value_id(); - if let Some(bb) = f.get_block_mut(cur_after_r) { - bb.add_instruction(MirInstruction::BinOp { - dst, - op: bop, - lhs: l, - rhs: r, - }); - } - Ok((dst, cur_after_r)) - } - ExprV0::Extern { - iface, - method, - args, - } => { - 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) { - bb.add_instruction(MirInstruction::ExternCall { - dst: Some(dst), - iface_name: iface.clone(), - method_name: method.clone(), - args: arg_ids, - effects: EffectMask::IO, - }); - } - Ok((dst, cur2)) - } - ExprV0::Compare { op, lhs, rhs } => { - let (l, cur_after_l) = lower_expr_with_scope(env, f, cur_bb, lhs, vars)?; - let (r, cur_after_r) = lower_expr_with_scope(env, f, cur_after_l, rhs, vars)?; - let cop = match op.as_str() { - "==" => crate::mir::CompareOp::Eq, - "!=" => crate::mir::CompareOp::Ne, - "<" => crate::mir::CompareOp::Lt, - "<=" => crate::mir::CompareOp::Le, - ">" => crate::mir::CompareOp::Gt, - ">=" => crate::mir::CompareOp::Ge, - _ => return Err("unsupported compare op".into()), - }; - let dst = f.next_value_id(); - if let Some(bb) = f.get_block_mut(cur_after_r) { - bb.add_instruction(MirInstruction::Compare { - dst, - op: cop, - lhs: l, - rhs: r, - }); - } - Ok((dst, cur_after_r)) - } - ExprV0::Logical { op, lhs, rhs } => { - let (l, cur_after_l) = lower_expr_with_scope(env, f, cur_bb, lhs, vars)?; - let rhs_bb = new_block(f); - let fall_bb = new_block(f); - let merge_bb = new_block(f); - let is_and = matches!(op.as_str(), "&&" | "and"); - if let Some(bb) = f.get_block_mut(cur_after_l) { - if is_and { - bb.set_terminator(MirInstruction::Branch { - condition: l, - then_bb: rhs_bb, - else_bb: fall_bb, - }); - } else { - bb.set_terminator(MirInstruction::Branch { - condition: l, - then_bb: fall_bb, - else_bb: rhs_bb, - }); - } - } - crate::jit::events::emit_lower( - serde_json::json!({ "id":"shortcircuit","op": if is_and {"and"} else {"or"},"rhs_bb":rhs_bb.0,"fall_bb":fall_bb.0,"merge_bb":merge_bb.0 }), - "shortcircuit", - "", - ); - 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, - }); - bb.set_terminator(MirInstruction::Jump { target: merge_bb }); - } - let (rval, rhs_end) = lower_expr_with_scope(env, f, rhs_bb, rhs, vars)?; - if let Some(bb) = f.get_block_mut(rhs_end) { - if !bb.is_terminated() { - bb.set_terminator(MirInstruction::Jump { target: merge_bb }); - } - } - let out = f.next_value_id(); - if env.mir_no_phi { - if let Some(bb) = f.get_block_mut(fall_bb) { - bb.add_instruction(MirInstruction::Copy { - dst: out, - src: cdst, - }); - } - if let Some(bb) = f.get_block_mut(rhs_end) { - bb.add_instruction(MirInstruction::Copy { - dst: out, - src: rval, - }); - } - } else if let Some(bb) = f.get_block_mut(merge_bb) { - 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)); - } - inputs.sort_by_key(|(bbid, _)| bbid.0); - bb.insert_instruction_after_phis(MirInstruction::Phi { dst: out, inputs }); - } - Ok((out, merge_bb)) - } - ExprV0::Call { name, args } => { - if name == "array.of" { - let arr = f.next_value_id(); - if let Some(bb) = f.get_block_mut(cur_bb) { - bb.add_instruction(MirInstruction::NewBox { - dst: arr, - box_type: "ArrayBox".into(), - args: vec![], - }); - } - let mut cur = cur_bb; - for e in args { - let (v, c) = lower_expr_with_scope(env, f, cur, e, vars)?; - cur = c; - let tmp = f.next_value_id(); - if let Some(bb) = f.get_block_mut(cur) { - bb.add_instruction(MirInstruction::BoxCall { - dst: Some(tmp), - box_val: arr, - method: "push".into(), - method_id: None, - args: vec![v], - effects: EffectMask::READ, - }); - } - } - return Ok((arr, cur)); - } - if name == "map.of" { - let mapv = f.next_value_id(); - if let Some(bb) = f.get_block_mut(cur_bb) { - bb.add_instruction(MirInstruction::NewBox { - dst: mapv, - box_type: "MapBox".into(), - args: vec![], - }); - } - let mut cur = cur_bb; - let mut it = args.iter(); - while let Some(k) = it.next() { - if let Some(v) = it.next() { - let (kv, cur2) = lower_expr_with_scope(env, f, cur, k, vars)?; - cur = cur2; - let (vv, cur3) = lower_expr_with_scope(env, f, cur, v, vars)?; - cur = cur3; - let tmp = f.next_value_id(); - if let Some(bb) = f.get_block_mut(cur) { - bb.add_instruction(MirInstruction::BoxCall { - dst: Some(tmp), - box_val: mapv, - method: "set".into(), - method_id: None, - args: vec![kv, vv], - effects: EffectMask::READ, - }); - } - } else { - break; - } - } - return Ok((mapv, cur)); - } - 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()), - }); - } - let dst = f.next_value_id(); - if let Some(bb) = f.get_block_mut(cur) { - bb.add_instruction(MirInstruction::Call { - dst: Some(dst), - func: fun_val, - args: arg_ids, - effects: EffectMask::READ, - }); - } - 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 (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) { - bb.add_instruction(MirInstruction::ExternCall { - dst: Some(dst), - iface_name: "env.console".into(), - method_name: "log".into(), - args: arg_ids, - effects: EffectMask::READ, - }); - } - return Ok((dst, cur2)); - } - let (recv_v, cur) = lower_expr_with_scope(env, f, cur_bb, recv, vars)?; - let (arg_ids, cur2) = lower_args_with_scope(env, f, cur, args, vars)?; - let dst = f.next_value_id(); - if let Some(bb) = f.get_block_mut(cur2) { - bb.add_instruction(MirInstruction::BoxCall { - dst: Some(dst), - box_val: recv_v, - method: method.clone(), - method_id: None, - args: arg_ids, - effects: EffectMask::READ, - }); - } - Ok((dst, cur2)) - } - ExprV0::New { class, args } => { - 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, - }); - } - Ok((dst, cur)) - } - ExprV0::Var { name } => match vars.resolve(env, f, cur_bb, name)? { - Some(v) => Ok((v, cur_bb)), - None => Err(format!("undefined variable: {}", name)), - }, - ExprV0::Throw { expr } => { - let (exc, cur) = lower_expr_with_scope(env, f, cur_bb, expr, vars)?; - Ok(lower_throw(env, f, cur, exc)) - } - } -} - -fn lower_args_with_scope( - env: &BridgeEnv, - f: &mut MirFunction, - cur_bb: BasicBlockId, - args: &[ExprV0], - scope: &mut S, -) -> Result<(Vec, BasicBlockId), String> { - let mut out = Vec::with_capacity(args.len()); - let mut cur = cur_bb; - for a in args { - let (v, c) = lower_expr_with_scope(env, f, cur, a, scope)?; - out.push(v); - cur = c; - } - Ok((out, cur)) -} - -#[allow(dead_code)] -fn lower_expr( - env: &BridgeEnv, - f: &mut MirFunction, - cur_bb: BasicBlockId, - e: &ExprV0, -) -> Result<(ValueId, BasicBlockId), String> { - let mut scope = NoVars; - lower_expr_with_scope(env, f, cur_bb, e, &mut scope) -} - -pub(super) fn lower_expr_with_vars( - env: &BridgeEnv, - f: &mut MirFunction, - cur_bb: BasicBlockId, - e: &ExprV0, - vars: &mut HashMap, -) -> Result<(ValueId, BasicBlockId), String> { - let mut scope = MapVars::new(vars); - lower_expr_with_scope(env, f, cur_bb, e, &mut scope) -} - -#[allow(dead_code)] -fn lower_args( - env: &BridgeEnv, - f: &mut MirFunction, - cur_bb: BasicBlockId, - args: &[ExprV0], -) -> Result<(Vec, BasicBlockId), String> { - let mut scope = NoVars; - lower_args_with_scope(env, f, cur_bb, args, &mut scope) -} - -pub(super) fn lower_args_with_vars( - env: &BridgeEnv, - f: &mut MirFunction, - cur_bb: BasicBlockId, - args: &[ExprV0], - vars: &mut HashMap, -) -> Result<(Vec, BasicBlockId), String> { - let mut scope = MapVars::new(vars); - lower_args_with_scope(env, f, cur_bb, args, &mut scope) -} pub(super) fn lower_stmt_with_vars( f: &mut MirFunction, @@ -516,7 +52,7 @@ pub(super) fn lower_stmt_with_vars( ) -> Result { match s { StmtV0::Return { expr } => { - let (v, cur) = lower_expr_with_vars(env, f, cur_bb, expr, vars)?; + let (v, cur) = expr::lower_expr_with_vars(env, f, cur_bb, expr, vars)?; if let Some(bb) = f.get_block_mut(cur) { bb.set_terminator(MirInstruction::Return { value: Some(v) }); } @@ -527,7 +63,7 @@ pub(super) fn lower_stmt_with_vars( method, args, } => { - let (arg_ids, cur) = lower_args_with_vars(env, f, cur_bb, args, vars)?; + let (arg_ids, cur) = expr::lower_args_with_vars(env, f, cur_bb, args, vars)?; if let Some(bb) = f.get_block_mut(cur) { bb.add_instruction(MirInstruction::ExternCall { dst: None, @@ -540,11 +76,11 @@ pub(super) fn lower_stmt_with_vars( Ok(cur) } StmtV0::Expr { expr } => { - let (_v, cur) = lower_expr_with_vars(env, f, cur_bb, expr, vars)?; + let (_v, cur) = expr::lower_expr_with_vars(env, f, cur_bb, expr, vars)?; Ok(cur) } StmtV0::Local { name, expr } => { - let (v, cur) = lower_expr_with_vars(env, f, cur_bb, expr, vars)?; + let (v, cur) = expr::lower_expr_with_vars(env, f, cur_bb, expr, vars)?; vars.insert(name.clone(), v); Ok(cur) } diff --git a/src/runner/json_v0_bridge/lowering/expr.rs b/src/runner/json_v0_bridge/lowering/expr.rs new file mode 100644 index 00000000..01a60efc --- /dev/null +++ b/src/runner/json_v0_bridge/lowering/expr.rs @@ -0,0 +1,461 @@ +use super::merge::new_block; +use super::BridgeEnv; +use super::ternary; +use super::peek; +use crate::mir::{ + BasicBlockId, BinaryOp, ConstValue, EffectMask, MirFunction, MirInstruction, ValueId, +}; +use std::collections::HashMap; + +use super::super::ast::ExprV0; + +pub(super) trait VarScope { + fn resolve( + &mut self, + env: &BridgeEnv, + f: &mut MirFunction, + cur_bb: BasicBlockId, + name: &str, + ) -> Result, String>; +} + +pub(super) struct NoVars; +impl VarScope for NoVars { + fn resolve( + &mut self, + _env: &BridgeEnv, + _f: &mut MirFunction, + _cur_bb: BasicBlockId, + name: &str, + ) -> Result, String> { + Err(format!("undefined variable in this context: {}", name)) + } +} + +pub(super) struct MapVars<'a> { + vars: &'a mut HashMap, +} +impl<'a> MapVars<'a> { + fn new(vars: &'a mut HashMap) -> Self { + Self { vars } + } +} +impl<'a> VarScope for MapVars<'a> { + fn resolve( + &mut self, + env: &BridgeEnv, + f: &mut MirFunction, + cur_bb: BasicBlockId, + name: &str, + ) -> Result, String> { + if let Some(&vid) = self.vars.get(name) { + return Ok(Some(vid)); + } + if name == "me" { + if env.allow_me_dummy { + let dst = f.next_value_id(); + if let Some(bb) = f.get_block_mut(cur_bb) { + bb.add_instruction(MirInstruction::NewBox { + dst, + box_type: env.me_class.clone(), + args: vec![], + }); + } + self.vars.insert(name.to_string(), dst); + Ok(Some(dst)) + } else { + Err("undefined 'me' outside box context (set NYASH_BRIDGE_ME_DUMMY=1 to inject placeholder)".into()) + } + } else { + Ok(None) + } + } +} + +fn lower_throw( + env: &BridgeEnv, + f: &mut MirFunction, + cur_bb: BasicBlockId, + exception_value: ValueId, +) -> (ValueId, BasicBlockId) { + if env.throw_enabled { + if let Some(bb) = f.get_block_mut(cur_bb) { + bb.set_terminator(MirInstruction::Throw { + exception: exception_value, + effects: EffectMask::PANIC, + }); + } + (exception_value, cur_bb) + } 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), + }); + } + (dst, cur_bb) + } +} + +pub(super) fn lower_expr_with_scope( + env: &BridgeEnv, + f: &mut MirFunction, + cur_bb: BasicBlockId, + e: &ExprV0, + vars: &mut S, +) -> Result<(ValueId, BasicBlockId), String> { + match e { + ExprV0::Int { value } => { + let ival: i64 = if let Some(n) = value.as_i64() { + n + } else if let Some(s) = value.as_str() { + s.parse().map_err(|_| "invalid int literal")? + } else { + return Err("invalid int literal".into()); + }; + 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(ival), + }); + } + Ok((dst, cur_bb)) + } + ExprV0::Str { value } => { + let dst = f.next_value_id(); + if let Some(bb) = f.get_block_mut(cur_bb) { + bb.add_instruction(MirInstruction::Const { + dst, + value: ConstValue::String(value.clone()), + }); + } + Ok((dst, cur_bb)) + } + ExprV0::Bool { value } => { + let dst = f.next_value_id(); + if let Some(bb) = f.get_block_mut(cur_bb) { + bb.add_instruction(MirInstruction::Const { + dst, + value: ConstValue::Bool(*value), + }); + } + Ok((dst, cur_bb)) + } + ExprV0::Binary { op, lhs, rhs } => { + let (l, cur_after_l) = lower_expr_with_scope(env, f, cur_bb, lhs, vars)?; + let (r, cur_after_r) = lower_expr_with_scope(env, f, cur_after_l, rhs, vars)?; + let bop = match op.as_str() { + "+" => BinaryOp::Add, + "-" => BinaryOp::Sub, + "*" => BinaryOp::Mul, + "/" => BinaryOp::Div, + _ => return Err("unsupported op".into()), + }; + let dst = f.next_value_id(); + if let Some(bb) = f.get_block_mut(cur_after_r) { + bb.add_instruction(MirInstruction::BinOp { + dst, + op: bop, + lhs: l, + rhs: r, + }); + } + Ok((dst, cur_after_r)) + } + ExprV0::Extern { + iface, + method, + args, + } => { + 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) { + bb.add_instruction(MirInstruction::ExternCall { + dst: Some(dst), + iface_name: iface.clone(), + method_name: method.clone(), + args: arg_ids, + effects: EffectMask::IO, + }); + } + Ok((dst, cur2)) + } + ExprV0::Compare { op, lhs, rhs } => { + let (l, cur_after_l) = lower_expr_with_scope(env, f, cur_bb, lhs, vars)?; + let (r, cur_after_r) = lower_expr_with_scope(env, f, cur_after_l, rhs, vars)?; + let cop = match op.as_str() { + "==" => crate::mir::CompareOp::Eq, + "!=" => crate::mir::CompareOp::Ne, + "<" => crate::mir::CompareOp::Lt, + "<=" => crate::mir::CompareOp::Le, + ">" => crate::mir::CompareOp::Gt, + ">=" => crate::mir::CompareOp::Ge, + _ => return Err("unsupported compare op".into()), + }; + let dst = f.next_value_id(); + if let Some(bb) = f.get_block_mut(cur_after_r) { + bb.add_instruction(MirInstruction::Compare { + dst, + op: cop, + lhs: l, + rhs: r, + }); + } + Ok((dst, cur_after_r)) + } + ExprV0::Logical { op, lhs, rhs } => { + let (l, cur_after_l) = lower_expr_with_scope(env, f, cur_bb, lhs, vars)?; + let rhs_bb = new_block(f); + let fall_bb = new_block(f); + let merge_bb = new_block(f); + let is_and = matches!(op.as_str(), "&&" | "and"); + if let Some(bb) = f.get_block_mut(cur_after_l) { + if is_and { + bb.set_terminator(MirInstruction::Branch { + condition: l, + then_bb: rhs_bb, + else_bb: fall_bb, + }); + } else { + bb.set_terminator(MirInstruction::Branch { + condition: l, + then_bb: fall_bb, + else_bb: rhs_bb, + }); + } + } + crate::jit::events::emit_lower( + serde_json::json!({ "id":"shortcircuit","op": if is_and {"and"} else {"or"},"rhs_bb":rhs_bb.0,"fall_bb":fall_bb.0,"merge_bb":merge_bb.0 }), + "shortcircuit", + "", + ); + 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 }); + bb.set_terminator(MirInstruction::Jump { target: merge_bb }); + } + let (rval, rhs_end) = lower_expr_with_scope(env, f, rhs_bb, rhs, vars)?; + if let Some(bb) = f.get_block_mut(rhs_end) { + if !bb.is_terminated() { + bb.set_terminator(MirInstruction::Jump { target: merge_bb }); + } + } + let out = f.next_value_id(); + if env.mir_no_phi { + if let Some(bb) = f.get_block_mut(fall_bb) { + bb.add_instruction(MirInstruction::Copy { dst: out, src: cdst }); + } + if let Some(bb) = f.get_block_mut(rhs_end) { + bb.add_instruction(MirInstruction::Copy { dst: out, src: rval }); + } + } else if let Some(bb) = f.get_block_mut(merge_bb) { + 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)); + } + inputs.sort_by_key(|(bbid, _)| bbid.0); + bb.insert_instruction_after_phis(MirInstruction::Phi { dst: out, inputs }); + } + Ok((out, merge_bb)) + } + ExprV0::Call { name, args } => { + if name == "array.of" { + let arr = f.next_value_id(); + if let Some(bb) = f.get_block_mut(cur_bb) { + bb.add_instruction(MirInstruction::NewBox { + dst: arr, + box_type: "ArrayBox".into(), + args: vec![], + }); + } + let mut cur = cur_bb; + for e in args { + let (v, c) = lower_expr_with_scope(env, f, cur, e, vars)?; + cur = c; + let tmp = f.next_value_id(); + if let Some(bb) = f.get_block_mut(cur) { + bb.add_instruction(MirInstruction::BoxCall { + dst: Some(tmp), + box_val: arr, + method: "push".into(), + method_id: None, + args: vec![v], + effects: EffectMask::READ, + }); + } + } + return Ok((arr, cur)); + } + if name == "map.of" { + let mapv = f.next_value_id(); + if let Some(bb) = f.get_block_mut(cur_bb) { + bb.add_instruction(MirInstruction::NewBox { + dst: mapv, + box_type: "MapBox".into(), + args: vec![], + }); + } + let mut cur = cur_bb; + let mut it = args.iter(); + while let Some(k) = it.next() { + if let Some(v) = it.next() { + let (kv, cur2) = lower_expr_with_scope(env, f, cur, k, vars)?; + cur = cur2; + let (vv, cur3) = lower_expr_with_scope(env, f, cur, v, vars)?; + cur = cur3; + let tmp = f.next_value_id(); + if let Some(bb) = f.get_block_mut(cur) { + bb.add_instruction(MirInstruction::BoxCall { + dst: Some(tmp), + box_val: mapv, + method: "set".into(), + method_id: None, + args: vec![kv, vv], + effects: EffectMask::READ, + }); + } + } else { + break; + } + } + return Ok((mapv, cur)); + } + 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()) }); + } + let dst = f.next_value_id(); + if let Some(bb) = f.get_block_mut(cur) { + bb.add_instruction(MirInstruction::Call { + dst: Some(dst), + func: fun_val, + args: arg_ids, + effects: EffectMask::READ, + }); + } + 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 (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) { + bb.add_instruction(MirInstruction::ExternCall { + dst: Some(dst), + iface_name: "env.console".into(), + method_name: "log".into(), + args: arg_ids, + effects: EffectMask::READ, + }); + } + return Ok((dst, cur2)); + } + let (recv_v, cur) = lower_expr_with_scope(env, f, cur_bb, recv, vars)?; + let (arg_ids, cur2) = lower_args_with_scope(env, f, cur, args, vars)?; + let dst = f.next_value_id(); + if let Some(bb) = f.get_block_mut(cur2) { + bb.add_instruction(MirInstruction::BoxCall { + dst: Some(dst), + box_val: recv_v, + method: method.clone(), + method_id: None, + args: arg_ids, + effects: EffectMask::READ, + }); + } + Ok((dst, cur2)) + } + ExprV0::New { class, args } => { + 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 }); + } + Ok((dst, cur)) + } + ExprV0::Var { name } => match vars.resolve(env, f, cur_bb, name)? { + Some(v) => Ok((v, cur_bb)), + None => Err(format!("undefined variable: {}", name)), + }, + ExprV0::Throw { expr } => { + 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::Peek { scrutinee, arms, r#else } => + peek::lower_peek_expr_with_scope(env, f, cur_bb, scrutinee, arms, r#else, vars), + } +} + +fn lower_args_with_scope( + env: &BridgeEnv, + f: &mut MirFunction, + cur_bb: BasicBlockId, + args: &[ExprV0], + scope: &mut S, +) -> Result<(Vec, BasicBlockId), String> { + let mut out = Vec::with_capacity(args.len()); + let mut cur = cur_bb; + for a in args { + let (v, c) = lower_expr_with_scope(env, f, cur, a, scope)?; + out.push(v); + cur = c; + } + Ok((out, cur)) +} + +#[allow(dead_code)] +fn lower_expr( + env: &BridgeEnv, + f: &mut MirFunction, + cur_bb: BasicBlockId, + e: &ExprV0, +) -> Result<(ValueId, BasicBlockId), String> { + let mut scope = NoVars; + lower_expr_with_scope(env, f, cur_bb, e, &mut scope) +} + +pub(super) fn lower_expr_with_vars( + env: &BridgeEnv, + f: &mut MirFunction, + cur_bb: BasicBlockId, + e: &ExprV0, + vars: &mut HashMap, +) -> Result<(ValueId, BasicBlockId), String> { + let mut scope = MapVars::new(vars); + lower_expr_with_scope(env, f, cur_bb, e, &mut scope) +} + +#[allow(dead_code)] +fn lower_args( + env: &BridgeEnv, + f: &mut MirFunction, + cur_bb: BasicBlockId, + args: &[ExprV0], +) -> Result<(Vec, BasicBlockId), String> { + let mut scope = NoVars; + lower_args_with_scope(env, f, cur_bb, args, &mut scope) +} + +pub(super) fn lower_args_with_vars( + env: &BridgeEnv, + f: &mut MirFunction, + cur_bb: BasicBlockId, + args: &[ExprV0], + vars: &mut HashMap, +) -> Result<(Vec, BasicBlockId), String> { + let mut scope = MapVars::new(vars); + lower_args_with_scope(env, f, cur_bb, args, &mut scope) +} diff --git a/src/runner/json_v0_bridge/lowering/if_else.rs b/src/runner/json_v0_bridge/lowering/if_else.rs index 6c72d6b6..e545f913 100644 --- a/src/runner/json_v0_bridge/lowering/if_else.rs +++ b/src/runner/json_v0_bridge/lowering/if_else.rs @@ -1,7 +1,5 @@ -use super::{ - lower_expr_with_vars, lower_stmt_list_with_vars, merge_var_maps, new_block, BridgeEnv, - LoopContext, -}; +use super::{lower_stmt_list_with_vars, merge_var_maps, new_block, BridgeEnv, LoopContext}; +use super::expr::lower_expr_with_vars; use crate::mir::{BasicBlockId, MirFunction, MirInstruction, ValueId}; use std::collections::HashMap; use super::super::ast::StmtV0; @@ -17,7 +15,7 @@ pub(super) fn lower_if_stmt( loop_stack: &mut Vec, env: &BridgeEnv, ) -> Result { - let (cval, cur) = lower_expr_with_vars(env, f, cur_bb, cond, vars)?; + let (cval, cur) = super::expr::lower_expr_with_vars(env, f, cur_bb, cond, vars)?; let then_bb = new_block(f); let else_bb = new_block(f); let merge_bb = new_block(f); @@ -64,4 +62,3 @@ pub(super) fn lower_if_stmt( ); Ok(merge_bb) } - diff --git a/src/runner/json_v0_bridge/lowering/loop_.rs b/src/runner/json_v0_bridge/lowering/loop_.rs index 518ccf19..eb3dfdbf 100644 --- a/src/runner/json_v0_bridge/lowering/loop_.rs +++ b/src/runner/json_v0_bridge/lowering/loop_.rs @@ -1,6 +1,5 @@ -use super::{ - lower_expr_with_vars, lower_stmt_list_with_vars, new_block, BridgeEnv, LoopContext, -}; +use super::{lower_stmt_list_with_vars, new_block, BridgeEnv, LoopContext}; +use super::expr::lower_expr_with_vars; use crate::mir::{BasicBlockId, MirFunction, MirInstruction, ValueId}; use std::collections::HashMap; use super::super::ast::StmtV0; @@ -46,7 +45,7 @@ pub(super) fn lower_loop_stmt( for (name, &phi) in &phi_map { vars.insert(name.clone(), phi); } - let (cval, _cend) = lower_expr_with_vars(env, f, cond_bb, cond, vars)?; + let (cval, _cend) = super::expr::lower_expr_with_vars(env, f, cond_bb, cond, vars)?; if let Some(bb) = f.get_block_mut(cond_bb) { bb.set_terminator(MirInstruction::Branch { condition: cval, @@ -102,4 +101,3 @@ pub(super) fn lower_loop_stmt( } Ok(exit_bb) } - diff --git a/src/runner/json_v0_bridge/lowering/peek.rs b/src/runner/json_v0_bridge/lowering/peek.rs new file mode 100644 index 00000000..71ceeec1 --- /dev/null +++ b/src/runner/json_v0_bridge/lowering/peek.rs @@ -0,0 +1,84 @@ +//! Peek/expr-block lowering for JSON v0 bridge. + +use super::merge::new_block; +use super::BridgeEnv; +use crate::mir::{BasicBlockId, CompareOp, ConstValue, MirFunction, MirInstruction, ValueId}; +use super::super::ast::{ExprV0, PeekArmV0}; +use super::expr::{lower_expr_with_scope, VarScope}; + +pub(super) fn lower_peek_expr_with_scope( + env: &BridgeEnv, + f: &mut MirFunction, + cur_bb: BasicBlockId, + scrutinee: &ExprV0, + arms: &[PeekArmV0], + else_expr: &ExprV0, + vars: &mut S, +) -> Result<(ValueId, BasicBlockId), String> { + // Evaluate scrutinee + let (scr_val, start_bb) = lower_expr_with_scope(env, f, cur_bb, scrutinee, vars)?; + + // Set up blocks + let dispatch_bb = new_block(f); + if let Some(bb) = f.get_block_mut(start_bb) { + if !bb.is_terminated() { + bb.set_terminator(MirInstruction::Jump { target: dispatch_bb }); + } + } + let else_bb = new_block(f); + let merge_bb = new_block(f); + + // Chain dispatch over arms + let mut cur_dispatch = dispatch_bb; + 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 fall_bb = next_dispatch.unwrap_or(else_bb); + + // Pre-allocate ids to avoid double borrow + let ldst = f.next_value_id(); + 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::Compare { dst: cond, op: CompareOp::Eq, lhs: scr_val, rhs: ldst }); + bb.set_terminator(MirInstruction::Branch { condition: cond, then_bb, else_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() { + bb.set_terminator(MirInstruction::Jump { target: merge_bb }); + } + } + phi_inputs.push((tend, tval)); + + cur_dispatch = fall_bb; + } + + // 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() { + bb.set_terminator(MirInstruction::Jump { target: merge_bb }); + } + } + phi_inputs.push((eend, eval)); + + // Merge result + let out = f.next_value_id(); + if env.mir_no_phi { + for (pred, val) in phi_inputs { + if let Some(bb) = f.get_block_mut(pred) { + bb.add_instruction(MirInstruction::Copy { dst: out, src: val }); + } + } + } else if let Some(bb) = f.get_block_mut(merge_bb) { + let mut inputs = phi_inputs; + inputs.sort_by_key(|(bbid, _)| bbid.0); + bb.insert_instruction_after_phis(MirInstruction::Phi { dst: out, inputs }); + } + Ok((out, merge_bb)) +} diff --git a/src/runner/json_v0_bridge/lowering/ternary.rs b/src/runner/json_v0_bridge/lowering/ternary.rs new file mode 100644 index 00000000..ef6d5bf0 --- /dev/null +++ b/src/runner/json_v0_bridge/lowering/ternary.rs @@ -0,0 +1,60 @@ +//! Ternary lowering (skeleton) +//! +//! NOTE: This module is introduced as part of the helper split. +//! It is not wired yet and should not alter behavior. + +use super::merge::new_block; +use super::BridgeEnv; +use crate::mir::{BasicBlockId, MirFunction, MirInstruction, ValueId}; +use super::super::ast::ExprV0; + +use super::expr::{lower_expr_with_scope, VarScope}; + +#[allow(dead_code)] +pub(super) fn lower_ternary_expr_with_scope( + env: &BridgeEnv, + f: &mut MirFunction, + cur_bb: BasicBlockId, + cond: &ExprV0, + then_e: &ExprV0, + else_e: &ExprV0, + vars: &mut S, +) -> Result<(ValueId, BasicBlockId), String> { + let (cval, cur) = lower_expr_with_scope(env, f, cur_bb, cond, vars)?; + let then_bb = new_block(f); + let else_bb = new_block(f); + let merge_bb = new_block(f); + if let Some(bb) = f.get_block_mut(cur) { + bb.set_terminator(MirInstruction::Branch { + condition: 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() { + bb.set_terminator(MirInstruction::Jump { target: 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() { + bb.set_terminator(MirInstruction::Jump { target: merge_bb }); + } + } + let out = f.next_value_id(); + if env.mir_no_phi { + if let Some(bb) = f.get_block_mut(tend) { + bb.add_instruction(MirInstruction::Copy { dst: out, src: tval }); + } + if let Some(bb) = f.get_block_mut(eend) { + bb.add_instruction(MirInstruction::Copy { dst: out, src: eval }); + } + } else if let Some(bb) = f.get_block_mut(merge_bb) { + let mut inputs = vec![(tend, tval), (eend, eval)]; + inputs.sort_by_key(|(bbid, _)| bbid.0); + bb.insert_instruction_after_phis(MirInstruction::Phi { dst: out, inputs }); + } + Ok((out, merge_bb)) +} diff --git a/src/runner/json_v0_bridge/lowering/try_catch.rs b/src/runner/json_v0_bridge/lowering/try_catch.rs index 8d449658..290ae3b9 100644 --- a/src/runner/json_v0_bridge/lowering/try_catch.rs +++ b/src/runner/json_v0_bridge/lowering/try_catch.rs @@ -90,7 +90,7 @@ pub(super) fn lower_try_stmt( let catch_branch_vars = catch_vars.clone(); use std::collections::HashSet; - let mut branch_vars = vec![(try_end, try_branch_vars), (catch_end, catch_branch_vars)]; + let branch_vars = vec![(try_end, try_branch_vars), (catch_end, catch_branch_vars)]; if let Some(finally_block) = finally_bb { let names: HashSet = { let mut set: HashSet = base_vars.keys().cloned().collect(); @@ -187,4 +187,3 @@ pub(super) fn lower_try_stmt( Ok(exit_bb) } } - diff --git a/src/runner/selfhost.rs b/src/runner/selfhost.rs index da993180..f18d2f6c 100644 --- a/src/runner/selfhost.rs +++ b/src/runner/selfhost.rs @@ -8,7 +8,7 @@ use super::*; -use nyash_rust::{interpreter::NyashInterpreter, parser::NyashParser}; +use nyash_rust::parser::NyashParser; use std::io::Read; use std::process::Stdio; use std::thread::sleep; diff --git a/tools/selfhost_stage2_smoke.sh b/tools/selfhost_stage2_smoke.sh index a683878a..bef9a29a 100644 --- a/tools/selfhost_stage2_smoke.sh +++ b/tools/selfhost_stage2_smoke.sh @@ -141,5 +141,26 @@ else fail "Ternary basic" "exit=$CODE" fi +# K) peek expression → 1 +cat > "$TMP/selfhost_peek_basic.nyash" <<'NY' +local d = "dog" +local v = peek d { + "cat" => { 0 } + "dog" => { 1 } + else => { 0 } +} +return v +NY +set +e +NYASH_USE_NY_COMPILER=1 NYASH_NY_COMPILER_EMIT_ONLY=0 NYASH_VM_USE_PY=${NYASH_VM_USE_PY:-1} \ + "$BIN" --backend vm "$TMP/selfhost_peek_basic.nyash" >/dev/null 2>&1 +CODE=$? +set -e +if [[ "$CODE" -eq 1 ]]; then + pass "Peek basic" +else + fail "Peek basic" "exit=$CODE" +fi + echo "All selfhost Stage-2 smokes PASS" >&2 exit 0