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
This commit is contained in:
Selfhosting Dev
2025-09-17 11:45:57 +09:00
parent d99b941218
commit 2720884a20
21 changed files with 770 additions and 511 deletions

View File

@ -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 PHIoff; `--phi-on` enables MIR14 lane.
- Added doc: `docs/development/mir/MIR13_MODE.md`; README references it.
- JSON v0 Bridge lowering split (nonfunctional)
- 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 PHIoff=Copy合流PHIon=Phi 合流)。
- Selfhost 生成器Stage1 JSON v0に Peek emit を追加: `apps/selfhost-compiler/boxes/parser_box.nyash`
- Selfhost/PyVM スモークを通して E2E 確認peek/ternary
- llvmlite stability for MIR13
- Resolver: forbids crossblock nondominating vmap reuse; for multipred and no declared PHI, synthesizes a localization PHI at block head.
- Finalize remains functionlocal; `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.
- Stage2 smokes更新: `tools/selfhost_stage2_smoke.sh` に "Peek basic" を追加。
Current Status
- Selfhosting Bridge → PyVM smokes: PASS (Stage2 reps: array/string/logic/if/loop).
@ -23,11 +27,16 @@ Current Status
- Curated LLVM (PHIon 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) PHIon 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 thinhub cleanup (nonfunctional): orchestrator minimalization and pass boundaries clarified.
1) Legacy Interpreter/VM offboarding (phaseA):
- Introduce `vm-legacy` feature (default OFF) to gate old VM execution層。
- 抽出: JIT が参照する最小型(例: `VMValue`)を薄い共通モジュールへ切替(`vm_types` 等)。
- `interpreter-legacy`/`vm-legacy` を既定ビルドから外し、ビルド警告を縮小。
2) Legacy Interpreter/VM offboarding (phaseB):
- 物理移動: `src/archive/{interpreter_legacy,vm_legacy}/` へ移設(ドキュメント更新)。
3) PHIon 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 thinhub cleanup非機能: orchestrator最小化とパス境界の明確化。
How to Run
- PyVM reference smokes: `tools/pyvm_stage2_smoke.sh`
@ -46,4 +55,4 @@ Notes / Policies
- Focus is selfhosting stability. JIT/Cranelift is out of scope (safety fixes only).
- PHI generation remains centralized in llvmlite; Bridge/Builder keep PHIoff by default.
- No full tracing GC yet; handles/Arc lifetimes govern object retention. Safepoint/barrier/roots are staging utilities.
- Legacy Interpreter/VM は段階的にアーカイブへ。日常の意味論確認は PyVM を基準として継続。

View File

@ -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 <expr> { "label" => <expr>, ..., else => <expr> }
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 == "(" {

View File

@ -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)
}
}

View File

@ -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(

View File

@ -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(

View File

@ -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);

View File

@ -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;

View File

@ -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!(

View File

@ -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() {

View File

@ -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;

View File

@ -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]

View File

@ -57,6 +57,12 @@ pub(super) struct CatchV0 {
pub(super) body: Vec<StmtV0>,
}
#[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<ExprV0>,
},
Ternary {
cond: Box<ExprV0>,
then: Box<ExprV0>,
#[serde(rename = "else")]
r#else: Box<ExprV0>,
},
Peek {
scrutinee: Box<ExprV0>,
arms: Vec<PeekArmV0>,
#[serde(rename = "else")]
r#else: Box<ExprV0>,
},
}

View File

@ -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<Option<ValueId>, String>;
}
struct NoVars;
impl VarScope for NoVars {
fn resolve(
&mut self,
_env: &BridgeEnv,
_f: &mut MirFunction,
_cur_bb: BasicBlockId,
name: &str,
) -> Result<Option<ValueId>, String> {
Err(format!("undefined variable in this context: {}", name))
}
}
struct MapVars<'a> {
vars: &'a mut HashMap<String, ValueId>,
}
impl<'a> MapVars<'a> {
fn new(vars: &'a mut HashMap<String, ValueId>) -> Self {
Self { vars }
}
}
impl<'a> VarScope for MapVars<'a> {
fn resolve(
&mut self,
env: &BridgeEnv,
f: &mut MirFunction,
cur_bb: BasicBlockId,
name: &str,
) -> Result<Option<ValueId>, 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<S: VarScope>(
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",
"<json_v0>",
);
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<S: VarScope>(
env: &BridgeEnv,
f: &mut MirFunction,
cur_bb: BasicBlockId,
args: &[ExprV0],
scope: &mut S,
) -> Result<(Vec<ValueId>, 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<String, ValueId>,
) -> 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<ValueId>, 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<String, ValueId>,
) -> Result<(Vec<ValueId>, 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<BasicBlockId, String> {
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)
}

View File

@ -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<Option<ValueId>, String>;
}
pub(super) struct NoVars;
impl VarScope for NoVars {
fn resolve(
&mut self,
_env: &BridgeEnv,
_f: &mut MirFunction,
_cur_bb: BasicBlockId,
name: &str,
) -> Result<Option<ValueId>, String> {
Err(format!("undefined variable in this context: {}", name))
}
}
pub(super) struct MapVars<'a> {
vars: &'a mut HashMap<String, ValueId>,
}
impl<'a> MapVars<'a> {
fn new(vars: &'a mut HashMap<String, ValueId>) -> Self {
Self { vars }
}
}
impl<'a> VarScope for MapVars<'a> {
fn resolve(
&mut self,
env: &BridgeEnv,
f: &mut MirFunction,
cur_bb: BasicBlockId,
name: &str,
) -> Result<Option<ValueId>, 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<S: VarScope>(
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",
"<json_v0>",
);
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<S: VarScope>(
env: &BridgeEnv,
f: &mut MirFunction,
cur_bb: BasicBlockId,
args: &[ExprV0],
scope: &mut S,
) -> Result<(Vec<ValueId>, 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<String, ValueId>,
) -> 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<ValueId>, 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<String, ValueId>,
) -> Result<(Vec<ValueId>, BasicBlockId), String> {
let mut scope = MapVars::new(vars);
lower_args_with_scope(env, f, cur_bb, args, &mut scope)
}

View File

@ -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<LoopContext>,
env: &BridgeEnv,
) -> Result<BasicBlockId, String> {
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)
}

View File

@ -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)
}

View File

@ -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<S: VarScope>(
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))
}

View File

@ -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<S: VarScope>(
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))
}

View File

@ -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<String> = {
let mut set: HashSet<String> = base_vars.keys().cloned().collect();
@ -187,4 +187,3 @@ pub(super) fn lower_try_stmt(
Ok(exit_bb)
}
}

View File

@ -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;

View File

@ -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