2025-09-05 13:29:17 +09:00
|
|
|
|
use crate::mir::{
|
2025-09-16 23:49:36 +09:00
|
|
|
|
BasicBlockId, BinaryOp, ConstValue, EffectMask, FunctionSignature, MirFunction, MirInstruction,
|
|
|
|
|
|
MirModule, MirPrinter, MirType, ValueId,
|
2025-09-05 13:29:17 +09:00
|
|
|
|
};
|
2025-09-16 23:49:36 +09:00
|
|
|
|
use serde::{Deserialize, Serialize};
|
2025-09-05 13:29:17 +09:00
|
|
|
|
|
|
|
|
|
|
#[derive(Debug, Deserialize, Serialize)]
|
|
|
|
|
|
struct ProgramV0 {
|
|
|
|
|
|
version: i32,
|
|
|
|
|
|
kind: String,
|
|
|
|
|
|
body: Vec<StmtV0>,
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-09-16 23:49:36 +09:00
|
|
|
|
#[derive(Debug, Deserialize, Serialize, Clone)]
|
2025-09-05 13:29:17 +09:00
|
|
|
|
#[serde(tag = "type")]
|
|
|
|
|
|
enum StmtV0 {
|
2025-09-16 23:49:36 +09:00
|
|
|
|
Return {
|
|
|
|
|
|
expr: ExprV0,
|
|
|
|
|
|
},
|
|
|
|
|
|
Extern {
|
|
|
|
|
|
iface: String,
|
|
|
|
|
|
method: String,
|
|
|
|
|
|
args: Vec<ExprV0>,
|
|
|
|
|
|
},
|
2025-09-14 23:22:05 +09:00
|
|
|
|
// Optional: expression statement (side effects only)
|
2025-09-16 23:49:36 +09:00
|
|
|
|
Expr {
|
|
|
|
|
|
expr: ExprV0,
|
|
|
|
|
|
},
|
2025-09-14 23:22:05 +09:00
|
|
|
|
// Optional: local binding (Stage-2)
|
2025-09-16 23:49:36 +09:00
|
|
|
|
Local {
|
|
|
|
|
|
name: String,
|
|
|
|
|
|
expr: ExprV0,
|
|
|
|
|
|
},
|
2025-09-14 23:22:05 +09:00
|
|
|
|
// Optional: if/else (Stage-2)
|
2025-09-16 23:49:36 +09:00
|
|
|
|
If {
|
|
|
|
|
|
cond: ExprV0,
|
|
|
|
|
|
then: Vec<StmtV0>,
|
|
|
|
|
|
#[serde(rename = "else", default)]
|
|
|
|
|
|
r#else: Option<Vec<StmtV0>>,
|
|
|
|
|
|
},
|
2025-09-14 23:22:05 +09:00
|
|
|
|
// Optional: loop (Stage-2)
|
2025-09-16 23:49:36 +09:00
|
|
|
|
Loop {
|
|
|
|
|
|
cond: ExprV0,
|
|
|
|
|
|
body: Vec<StmtV0>,
|
|
|
|
|
|
},
|
2025-09-16 18:33:59 +09:00
|
|
|
|
Break,
|
|
|
|
|
|
Continue,
|
2025-09-16 23:49:36 +09:00
|
|
|
|
Try {
|
|
|
|
|
|
#[serde(rename = "try")]
|
|
|
|
|
|
try_body: Vec<StmtV0>,
|
|
|
|
|
|
#[serde(default)]
|
|
|
|
|
|
catches: Vec<CatchV0>,
|
|
|
|
|
|
#[serde(default)]
|
|
|
|
|
|
finally: Vec<StmtV0>,
|
|
|
|
|
|
},
|
2025-09-16 18:33:59 +09:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#[derive(Debug, Deserialize, Serialize, Clone, Default)]
|
|
|
|
|
|
struct CatchV0 {
|
2025-09-16 23:49:36 +09:00
|
|
|
|
#[serde(rename = "param", default)]
|
2025-09-16 18:33:59 +09:00
|
|
|
|
param: Option<String>,
|
2025-09-16 23:49:36 +09:00
|
|
|
|
#[serde(rename = "typeHint", default)]
|
2025-09-16 18:33:59 +09:00
|
|
|
|
type_hint: Option<String>,
|
|
|
|
|
|
#[serde(default)]
|
|
|
|
|
|
body: Vec<StmtV0>,
|
2025-09-05 13:29:17 +09:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#[derive(Debug, Deserialize, Serialize, Clone)]
|
|
|
|
|
|
#[serde(tag = "type")]
|
|
|
|
|
|
enum ExprV0 {
|
2025-09-16 23:49:36 +09:00
|
|
|
|
Int {
|
|
|
|
|
|
value: serde_json::Value,
|
|
|
|
|
|
},
|
|
|
|
|
|
Str {
|
|
|
|
|
|
value: String,
|
|
|
|
|
|
},
|
|
|
|
|
|
Bool {
|
|
|
|
|
|
value: bool,
|
|
|
|
|
|
},
|
|
|
|
|
|
Binary {
|
|
|
|
|
|
op: String,
|
|
|
|
|
|
lhs: Box<ExprV0>,
|
|
|
|
|
|
rhs: Box<ExprV0>,
|
|
|
|
|
|
},
|
|
|
|
|
|
Extern {
|
|
|
|
|
|
iface: String,
|
|
|
|
|
|
method: String,
|
|
|
|
|
|
args: Vec<ExprV0>,
|
|
|
|
|
|
},
|
|
|
|
|
|
Compare {
|
|
|
|
|
|
op: String,
|
|
|
|
|
|
lhs: Box<ExprV0>,
|
|
|
|
|
|
rhs: Box<ExprV0>,
|
|
|
|
|
|
},
|
|
|
|
|
|
Logical {
|
|
|
|
|
|
op: String,
|
|
|
|
|
|
lhs: Box<ExprV0>,
|
|
|
|
|
|
rhs: Box<ExprV0>,
|
|
|
|
|
|
}, // short-circuit: &&, || (or: "and"/"or")
|
2025-09-14 23:22:05 +09:00
|
|
|
|
// Stage-2 additions (optional):
|
2025-09-16 23:49:36 +09:00
|
|
|
|
Call {
|
|
|
|
|
|
name: String,
|
|
|
|
|
|
args: Vec<ExprV0>,
|
|
|
|
|
|
},
|
|
|
|
|
|
Method {
|
|
|
|
|
|
recv: Box<ExprV0>,
|
|
|
|
|
|
method: String,
|
|
|
|
|
|
args: Vec<ExprV0>,
|
|
|
|
|
|
},
|
|
|
|
|
|
New {
|
|
|
|
|
|
class: String,
|
|
|
|
|
|
args: Vec<ExprV0>,
|
|
|
|
|
|
},
|
|
|
|
|
|
Var {
|
|
|
|
|
|
name: String,
|
|
|
|
|
|
},
|
|
|
|
|
|
Throw {
|
|
|
|
|
|
expr: Box<ExprV0>,
|
|
|
|
|
|
},
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#[derive(Clone, Copy)]
|
|
|
|
|
|
struct LoopContext {
|
|
|
|
|
|
cond_bb: BasicBlockId,
|
|
|
|
|
|
exit_bb: BasicBlockId,
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
fn lower_throw(
|
|
|
|
|
|
f: &mut MirFunction,
|
|
|
|
|
|
cur_bb: BasicBlockId,
|
|
|
|
|
|
exception_value: ValueId,
|
|
|
|
|
|
) -> (ValueId, BasicBlockId) {
|
|
|
|
|
|
if std::env::var("NYASH_BRIDGE_THROW_ENABLE").ok().as_deref() == Some("1") {
|
|
|
|
|
|
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)
|
|
|
|
|
|
}
|
2025-09-05 13:29:17 +09:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
pub fn parse_json_v0_to_module(json: &str) -> Result<MirModule, String> {
|
2025-09-16 23:49:36 +09:00
|
|
|
|
let prog: ProgramV0 =
|
|
|
|
|
|
serde_json::from_str(json).map_err(|e| format!("invalid JSON v0: {}", e))?;
|
2025-09-05 13:29:17 +09:00
|
|
|
|
if prog.version != 0 || prog.kind != "Program" {
|
|
|
|
|
|
return Err("unsupported IR: expected {version:0, kind:\"Program\"}".into());
|
|
|
|
|
|
}
|
|
|
|
|
|
// Create module and main function
|
|
|
|
|
|
let mut module = MirModule::new("ny_json_v0".into());
|
2025-09-16 23:49:36 +09:00
|
|
|
|
let sig = FunctionSignature {
|
|
|
|
|
|
name: "main".into(),
|
|
|
|
|
|
params: vec![],
|
|
|
|
|
|
return_type: MirType::Integer,
|
|
|
|
|
|
effects: EffectMask::PURE,
|
|
|
|
|
|
};
|
2025-09-05 13:29:17 +09:00
|
|
|
|
let entry = BasicBlockId::new(0);
|
|
|
|
|
|
let mut f = MirFunction::new(sig, entry);
|
2025-09-16 23:49:36 +09:00
|
|
|
|
|
|
|
|
|
|
if prog.body.is_empty() {
|
|
|
|
|
|
return Err("empty body".into());
|
|
|
|
|
|
}
|
2025-09-07 07:36:15 +09:00
|
|
|
|
|
2025-09-14 23:22:05 +09:00
|
|
|
|
// Variable map for simple locals (Stage-2; currently minimal)
|
2025-09-16 23:49:36 +09:00
|
|
|
|
let mut var_map: std::collections::HashMap<String, crate::mir::ValueId> =
|
|
|
|
|
|
std::collections::HashMap::new();
|
|
|
|
|
|
let mut loop_stack: Vec<LoopContext> = Vec::new();
|
2025-09-14 23:22:05 +09:00
|
|
|
|
let start_bb = f.entry_block;
|
2025-09-16 23:49:36 +09:00
|
|
|
|
let end_bb =
|
|
|
|
|
|
lower_stmt_list_with_vars(&mut f, start_bb, &prog.body, &mut var_map, &mut loop_stack)?;
|
2025-09-14 23:22:05 +09:00
|
|
|
|
// Ensure function terminates: add `ret 0` to last un-terminated block (prefer end_bb else entry)
|
2025-09-16 23:49:36 +09:00
|
|
|
|
let need_default_ret = f.blocks.iter().any(|(_k, b)| !b.is_terminated());
|
2025-09-14 23:22:05 +09:00
|
|
|
|
if need_default_ret {
|
|
|
|
|
|
let target_bb = end_bb;
|
2025-09-07 07:36:15 +09:00
|
|
|
|
let dst_id = f.next_value_id();
|
2025-09-14 23:22:05 +09:00
|
|
|
|
if let Some(bb) = f.get_block_mut(target_bb) {
|
|
|
|
|
|
if !bb.is_terminated() {
|
2025-09-16 23:49:36 +09:00
|
|
|
|
bb.add_instruction(MirInstruction::Const {
|
|
|
|
|
|
dst: dst_id,
|
|
|
|
|
|
value: ConstValue::Integer(0),
|
|
|
|
|
|
});
|
|
|
|
|
|
bb.set_terminator(MirInstruction::Return {
|
|
|
|
|
|
value: Some(dst_id),
|
|
|
|
|
|
});
|
2025-09-14 23:22:05 +09:00
|
|
|
|
}
|
2025-09-07 07:36:15 +09:00
|
|
|
|
}
|
2025-09-05 13:29:17 +09:00
|
|
|
|
}
|
2025-09-07 07:36:15 +09:00
|
|
|
|
// Keep return type unknown to allow dynamic display (VM/Interpreter)
|
|
|
|
|
|
f.signature.return_type = MirType::Unknown;
|
2025-09-05 13:29:17 +09:00
|
|
|
|
module.add_function(f);
|
|
|
|
|
|
Ok(module)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-09-07 07:36:15 +09:00
|
|
|
|
fn next_block_id(f: &MirFunction) -> BasicBlockId {
|
|
|
|
|
|
let mut mx = 0u32;
|
2025-09-16 23:49:36 +09:00
|
|
|
|
for k in f.blocks.keys() {
|
|
|
|
|
|
if k.0 >= mx {
|
|
|
|
|
|
mx = k.0 + 1;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2025-09-07 07:36:15 +09:00
|
|
|
|
BasicBlockId::new(mx)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-09-16 23:49:36 +09:00
|
|
|
|
fn lower_expr(
|
|
|
|
|
|
f: &mut MirFunction,
|
|
|
|
|
|
cur_bb: BasicBlockId,
|
|
|
|
|
|
e: &ExprV0,
|
|
|
|
|
|
) -> Result<(crate::mir::ValueId, BasicBlockId), String> {
|
2025-09-05 13:29:17 +09:00
|
|
|
|
match e {
|
|
|
|
|
|
ExprV0::Int { value } => {
|
|
|
|
|
|
// Accept number or stringified digits
|
|
|
|
|
|
let ival: i64 = if let Some(n) = value.as_i64() {
|
|
|
|
|
|
n
|
2025-09-16 23:49:36 +09:00
|
|
|
|
} else if let Some(s) = value.as_str() {
|
|
|
|
|
|
s.parse().map_err(|_| "invalid int literal")?
|
|
|
|
|
|
} else {
|
2025-09-05 13:29:17 +09:00
|
|
|
|
return Err("invalid int literal".into());
|
|
|
|
|
|
};
|
|
|
|
|
|
let dst = f.next_value_id();
|
2025-09-07 07:36:15 +09:00
|
|
|
|
if let Some(bb) = f.get_block_mut(cur_bb) {
|
2025-09-16 23:49:36 +09:00
|
|
|
|
bb.add_instruction(MirInstruction::Const {
|
|
|
|
|
|
dst,
|
|
|
|
|
|
value: ConstValue::Integer(ival),
|
|
|
|
|
|
});
|
2025-09-05 13:29:17 +09:00
|
|
|
|
}
|
2025-09-07 07:36:15 +09:00
|
|
|
|
Ok((dst, cur_bb))
|
|
|
|
|
|
}
|
|
|
|
|
|
ExprV0::Str { value } => {
|
|
|
|
|
|
let dst = f.next_value_id();
|
|
|
|
|
|
if let Some(bb) = f.get_block_mut(cur_bb) {
|
2025-09-16 23:49:36 +09:00
|
|
|
|
bb.add_instruction(MirInstruction::Const {
|
|
|
|
|
|
dst,
|
|
|
|
|
|
value: ConstValue::String(value.clone()),
|
|
|
|
|
|
});
|
2025-09-07 07:36:15 +09:00
|
|
|
|
}
|
|
|
|
|
|
Ok((dst, cur_bb))
|
|
|
|
|
|
}
|
|
|
|
|
|
ExprV0::Bool { value } => {
|
|
|
|
|
|
let dst = f.next_value_id();
|
|
|
|
|
|
if let Some(bb) = f.get_block_mut(cur_bb) {
|
2025-09-16 23:49:36 +09:00
|
|
|
|
bb.add_instruction(MirInstruction::Const {
|
|
|
|
|
|
dst,
|
|
|
|
|
|
value: ConstValue::Bool(*value),
|
|
|
|
|
|
});
|
2025-09-07 07:36:15 +09:00
|
|
|
|
}
|
|
|
|
|
|
Ok((dst, cur_bb))
|
2025-09-05 13:29:17 +09:00
|
|
|
|
}
|
|
|
|
|
|
ExprV0::Binary { op, lhs, rhs } => {
|
2025-09-07 07:36:15 +09:00
|
|
|
|
let (l, cur_after_l) = lower_expr(f, cur_bb, lhs)?;
|
|
|
|
|
|
let (r, cur_after_r) = lower_expr(f, cur_after_l, rhs)?;
|
2025-09-16 23:49:36 +09:00
|
|
|
|
let bop = match op.as_str() {
|
|
|
|
|
|
"+" => BinaryOp::Add,
|
|
|
|
|
|
"-" => BinaryOp::Sub,
|
|
|
|
|
|
"*" => BinaryOp::Mul,
|
|
|
|
|
|
"/" => BinaryOp::Div,
|
|
|
|
|
|
_ => return Err("unsupported op".into()),
|
|
|
|
|
|
};
|
2025-09-05 13:29:17 +09:00
|
|
|
|
let dst = f.next_value_id();
|
2025-09-07 07:36:15 +09:00
|
|
|
|
if let Some(bb) = f.get_block_mut(cur_after_r) {
|
2025-09-16 23:49:36 +09:00
|
|
|
|
bb.add_instruction(MirInstruction::BinOp {
|
|
|
|
|
|
dst,
|
|
|
|
|
|
op: bop,
|
|
|
|
|
|
lhs: l,
|
|
|
|
|
|
rhs: r,
|
|
|
|
|
|
});
|
2025-09-05 13:29:17 +09:00
|
|
|
|
}
|
2025-09-07 07:36:15 +09:00
|
|
|
|
Ok((dst, cur_after_r))
|
2025-09-05 13:29:17 +09:00
|
|
|
|
}
|
2025-09-16 23:49:36 +09:00
|
|
|
|
ExprV0::Extern {
|
|
|
|
|
|
iface,
|
|
|
|
|
|
method,
|
|
|
|
|
|
args,
|
|
|
|
|
|
} => {
|
2025-09-07 07:36:15 +09:00
|
|
|
|
let (arg_ids, cur2) = lower_args(f, cur_bb, args)?;
|
|
|
|
|
|
let dst = f.next_value_id();
|
|
|
|
|
|
if let Some(bb) = f.get_block_mut(cur2) {
|
2025-09-16 23:49:36 +09:00
|
|
|
|
bb.add_instruction(MirInstruction::ExternCall {
|
|
|
|
|
|
dst: Some(dst),
|
|
|
|
|
|
iface_name: iface.clone(),
|
|
|
|
|
|
method_name: method.clone(),
|
|
|
|
|
|
args: arg_ids,
|
|
|
|
|
|
effects: EffectMask::IO,
|
|
|
|
|
|
});
|
2025-09-07 07:36:15 +09:00
|
|
|
|
}
|
|
|
|
|
|
Ok((dst, cur2))
|
|
|
|
|
|
}
|
|
|
|
|
|
ExprV0::Compare { op, lhs, rhs } => {
|
|
|
|
|
|
let (l, cur_after_l) = lower_expr(f, cur_bb, lhs)?;
|
|
|
|
|
|
let (r, cur_after_r) = lower_expr(f, cur_after_l, rhs)?;
|
|
|
|
|
|
let cop = match op.as_str() {
|
|
|
|
|
|
"==" => crate::mir::CompareOp::Eq,
|
|
|
|
|
|
"!=" => crate::mir::CompareOp::Ne,
|
2025-09-16 23:49:36 +09:00
|
|
|
|
"<" => crate::mir::CompareOp::Lt,
|
2025-09-07 07:36:15 +09:00
|
|
|
|
"<=" => crate::mir::CompareOp::Le,
|
2025-09-16 23:49:36 +09:00
|
|
|
|
">" => crate::mir::CompareOp::Gt,
|
2025-09-07 07:36:15 +09:00
|
|
|
|
">=" => 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) {
|
2025-09-16 23:49:36 +09:00
|
|
|
|
bb.add_instruction(MirInstruction::Compare {
|
|
|
|
|
|
dst,
|
|
|
|
|
|
op: cop,
|
|
|
|
|
|
lhs: l,
|
|
|
|
|
|
rhs: r,
|
|
|
|
|
|
});
|
2025-09-07 07:36:15 +09:00
|
|
|
|
}
|
|
|
|
|
|
Ok((dst, cur_after_r))
|
|
|
|
|
|
}
|
|
|
|
|
|
ExprV0::Logical { op, lhs, rhs } => {
|
2025-09-16 23:49:36 +09:00
|
|
|
|
// Short-circuit boolean logic with branches (+phi or edge-copy)
|
2025-09-07 07:36:15 +09:00
|
|
|
|
let (l, cur_after_l) = lower_expr(f, cur_bb, lhs)?;
|
|
|
|
|
|
let rhs_bb = next_block_id(f);
|
|
|
|
|
|
let fall_bb = BasicBlockId::new(rhs_bb.0 + 1);
|
|
|
|
|
|
let merge_bb = BasicBlockId::new(rhs_bb.0 + 2);
|
|
|
|
|
|
f.add_block(crate::mir::BasicBlock::new(rhs_bb));
|
|
|
|
|
|
f.add_block(crate::mir::BasicBlock::new(fall_bb));
|
|
|
|
|
|
f.add_block(crate::mir::BasicBlock::new(merge_bb));
|
|
|
|
|
|
// Branch depending on op
|
|
|
|
|
|
let is_and = matches!(op.as_str(), "&&" | "and");
|
|
|
|
|
|
if let Some(bb) = f.get_block_mut(cur_after_l) {
|
|
|
|
|
|
if is_and {
|
2025-09-16 23:49:36 +09:00
|
|
|
|
bb.set_terminator(MirInstruction::Branch {
|
|
|
|
|
|
condition: l,
|
|
|
|
|
|
then_bb: rhs_bb,
|
|
|
|
|
|
else_bb: fall_bb,
|
|
|
|
|
|
});
|
2025-09-07 07:36:15 +09:00
|
|
|
|
} else {
|
|
|
|
|
|
// OR: if lhs true, go to fall_bb (true path), else evaluate rhs
|
2025-09-16 23:49:36 +09:00
|
|
|
|
bb.set_terminator(MirInstruction::Branch {
|
|
|
|
|
|
condition: l,
|
|
|
|
|
|
then_bb: fall_bb,
|
|
|
|
|
|
else_bb: rhs_bb,
|
|
|
|
|
|
});
|
2025-09-07 07:36:15 +09:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
// Telemetry: note short-circuit lowering
|
|
|
|
|
|
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",
|
2025-09-16 23:49:36 +09:00
|
|
|
|
"<json_v0>",
|
2025-09-07 07:36:15 +09:00
|
|
|
|
);
|
2025-09-16 23:49:36 +09:00
|
|
|
|
if std::env::var("NYASH_CLI_VERBOSE").ok().as_deref() == Some("1") {
|
|
|
|
|
|
eprintln!(
|
|
|
|
|
|
"[bridge/logical] op={} rhs_bb={} fall_bb={} merge_bb={}",
|
|
|
|
|
|
if is_and { "and" } else { "or" },
|
|
|
|
|
|
rhs_bb.0,
|
|
|
|
|
|
fall_bb.0,
|
|
|
|
|
|
merge_bb.0
|
|
|
|
|
|
);
|
|
|
|
|
|
}
|
2025-09-07 07:36:15 +09:00
|
|
|
|
// false/true constant in fall_bb depending on op
|
|
|
|
|
|
let cdst = f.next_value_id();
|
|
|
|
|
|
if let Some(bb) = f.get_block_mut(fall_bb) {
|
2025-09-16 23:49:36 +09:00
|
|
|
|
let cval = if is_and {
|
|
|
|
|
|
ConstValue::Bool(false)
|
|
|
|
|
|
} else {
|
|
|
|
|
|
ConstValue::Bool(true)
|
|
|
|
|
|
};
|
|
|
|
|
|
bb.add_instruction(MirInstruction::Const {
|
|
|
|
|
|
dst: cdst,
|
|
|
|
|
|
value: cval,
|
|
|
|
|
|
});
|
2025-09-07 07:36:15 +09:00
|
|
|
|
bb.set_terminator(MirInstruction::Jump { target: merge_bb });
|
|
|
|
|
|
}
|
2025-09-15 18:44:49 +09:00
|
|
|
|
// evaluate rhs starting at rhs_bb and ensure the terminal block jumps to merge
|
|
|
|
|
|
let (rval, rhs_end) = lower_expr(f, rhs_bb, rhs)?;
|
|
|
|
|
|
if let Some(bb) = f.get_block_mut(rhs_end) {
|
2025-09-16 23:49:36 +09:00
|
|
|
|
if !bb.is_terminated() {
|
|
|
|
|
|
bb.set_terminator(MirInstruction::Jump { target: merge_bb });
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
if std::env::var("NYASH_CLI_VERBOSE").ok().as_deref() == Some("1") {
|
|
|
|
|
|
eprintln!(
|
|
|
|
|
|
"[bridge/logical] rhs_end={} jump->merge_bb={}",
|
|
|
|
|
|
rhs_end.0, merge_bb.0
|
|
|
|
|
|
);
|
2025-09-07 07:36:15 +09:00
|
|
|
|
}
|
2025-09-16 23:49:36 +09:00
|
|
|
|
// Merge: PHI または edge-copy で合流値を定義
|
|
|
|
|
|
let no_phi = crate::config::env::mir_no_phi();
|
2025-09-07 07:36:15 +09:00
|
|
|
|
let out = f.next_value_id();
|
2025-09-16 23:49:36 +09:00
|
|
|
|
if no_phi {
|
|
|
|
|
|
// Edge copies in predecessors
|
|
|
|
|
|
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) {
|
2025-09-16 18:33:59 +09:00
|
|
|
|
let mut inputs: Vec<(BasicBlockId, ValueId)> = vec![(fall_bb, cdst)];
|
|
|
|
|
|
if rhs_end != fall_bb {
|
|
|
|
|
|
inputs.push((rhs_end, rval));
|
|
|
|
|
|
} else {
|
|
|
|
|
|
// Degenerate case: RHS ended in fall_bb (e.g., constant expression).
|
|
|
|
|
|
// Reuse the constant to keep PHI well-formed.
|
|
|
|
|
|
inputs.push((fall_bb, rval));
|
|
|
|
|
|
}
|
|
|
|
|
|
inputs.sort_by_key(|(bbid, _)| bbid.0);
|
|
|
|
|
|
bb.insert_instruction_after_phis(MirInstruction::Phi { dst: out, inputs });
|
2025-09-07 07:36:15 +09:00
|
|
|
|
}
|
|
|
|
|
|
Ok((out, merge_bb))
|
|
|
|
|
|
}
|
2025-09-14 23:22:05 +09:00
|
|
|
|
ExprV0::Call { name, args } => {
|
2025-09-16 16:28:25 +09:00
|
|
|
|
// Special: array literal lowering — Call{name:"array.of", args:[...]} → new ArrayBox(); push(...); result=array
|
|
|
|
|
|
if name == "array.of" {
|
|
|
|
|
|
// Create array first
|
|
|
|
|
|
let arr = f.next_value_id();
|
|
|
|
|
|
if let Some(bb) = f.get_block_mut(cur_bb) {
|
2025-09-16 23:49:36 +09:00
|
|
|
|
bb.add_instruction(MirInstruction::NewBox {
|
|
|
|
|
|
dst: arr,
|
|
|
|
|
|
box_type: "ArrayBox".into(),
|
|
|
|
|
|
args: vec![],
|
|
|
|
|
|
});
|
2025-09-16 16:28:25 +09:00
|
|
|
|
}
|
|
|
|
|
|
// For each element: eval then push
|
|
|
|
|
|
let mut cur = cur_bb;
|
|
|
|
|
|
for e in args {
|
2025-09-16 23:49:36 +09:00
|
|
|
|
let (v, c) = lower_expr(f, cur, e)?;
|
|
|
|
|
|
cur = c;
|
2025-09-16 16:28:25 +09:00
|
|
|
|
let tmp = f.next_value_id();
|
|
|
|
|
|
if let Some(bb) = f.get_block_mut(cur) {
|
2025-09-16 23:49:36 +09:00
|
|
|
|
bb.add_instruction(MirInstruction::BoxCall {
|
|
|
|
|
|
dst: Some(tmp),
|
|
|
|
|
|
box_val: arr,
|
|
|
|
|
|
method: "push".into(),
|
|
|
|
|
|
method_id: None,
|
|
|
|
|
|
args: vec![v],
|
|
|
|
|
|
effects: EffectMask::READ,
|
|
|
|
|
|
});
|
2025-09-16 16:28:25 +09:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
return Ok((arr, cur));
|
|
|
|
|
|
}
|
|
|
|
|
|
// Special: map literal lowering — Call{name:"map.of", args:[k1, v1, k2, v2, ...]} → new MapBox(); set(k,v)...; result=map
|
|
|
|
|
|
if name == "map.of" {
|
|
|
|
|
|
let mapv = f.next_value_id();
|
|
|
|
|
|
if let Some(bb) = f.get_block_mut(cur_bb) {
|
2025-09-16 23:49:36 +09:00
|
|
|
|
bb.add_instruction(MirInstruction::NewBox {
|
|
|
|
|
|
dst: mapv,
|
|
|
|
|
|
box_type: "MapBox".into(),
|
|
|
|
|
|
args: vec![],
|
|
|
|
|
|
});
|
2025-09-16 16:28:25 +09:00
|
|
|
|
}
|
|
|
|
|
|
let mut cur = cur_bb;
|
|
|
|
|
|
let mut it = args.iter();
|
|
|
|
|
|
while let Some(k) = it.next() {
|
|
|
|
|
|
if let Some(v) = it.next() {
|
2025-09-16 23:49:36 +09:00
|
|
|
|
let (kv, cur2) = lower_expr(f, cur, k)?;
|
|
|
|
|
|
cur = cur2;
|
|
|
|
|
|
let (vv, cur3) = lower_expr(f, cur, v)?;
|
|
|
|
|
|
cur = cur3;
|
2025-09-16 16:28:25 +09:00
|
|
|
|
let tmp = f.next_value_id();
|
|
|
|
|
|
if let Some(bb) = f.get_block_mut(cur) {
|
2025-09-16 23:49:36 +09:00
|
|
|
|
bb.add_instruction(MirInstruction::BoxCall {
|
|
|
|
|
|
dst: Some(tmp),
|
|
|
|
|
|
box_val: mapv,
|
|
|
|
|
|
method: "set".into(),
|
|
|
|
|
|
method_id: None,
|
|
|
|
|
|
args: vec![kv, vv],
|
|
|
|
|
|
effects: EffectMask::READ,
|
|
|
|
|
|
});
|
2025-09-16 16:28:25 +09:00
|
|
|
|
}
|
2025-09-16 23:49:36 +09:00
|
|
|
|
} else {
|
|
|
|
|
|
break;
|
|
|
|
|
|
}
|
2025-09-16 16:28:25 +09:00
|
|
|
|
}
|
|
|
|
|
|
return Ok((mapv, cur));
|
|
|
|
|
|
}
|
|
|
|
|
|
// Fallback: treat as normal dynamic call
|
2025-09-14 23:22:05 +09:00
|
|
|
|
let (arg_ids, cur) = lower_args(f, cur_bb, args)?;
|
|
|
|
|
|
let fun_val = f.next_value_id();
|
|
|
|
|
|
if let Some(bb) = f.get_block_mut(cur) {
|
2025-09-16 23:49:36 +09:00
|
|
|
|
bb.add_instruction(MirInstruction::Const {
|
|
|
|
|
|
dst: fun_val,
|
|
|
|
|
|
value: ConstValue::String(name.clone()),
|
|
|
|
|
|
});
|
2025-09-14 23:22:05 +09:00
|
|
|
|
}
|
|
|
|
|
|
let dst = f.next_value_id();
|
|
|
|
|
|
if let Some(bb) = f.get_block_mut(cur) {
|
2025-09-16 23:49:36 +09:00
|
|
|
|
bb.add_instruction(MirInstruction::Call {
|
|
|
|
|
|
dst: Some(dst),
|
|
|
|
|
|
func: fun_val,
|
|
|
|
|
|
args: arg_ids,
|
|
|
|
|
|
effects: EffectMask::READ,
|
|
|
|
|
|
});
|
2025-09-14 23:22:05 +09:00
|
|
|
|
}
|
|
|
|
|
|
Ok((dst, cur))
|
|
|
|
|
|
}
|
|
|
|
|
|
ExprV0::Method { recv, method, args } => {
|
2025-09-15 18:44:49 +09:00
|
|
|
|
// Heuristic: new ConsoleBox().println(x) → externcall env.console.log(x)
|
2025-09-16 23:49:36 +09:00
|
|
|
|
let recv_is_console_new =
|
|
|
|
|
|
matches!(&**recv, ExprV0::New { class, .. } if class == "ConsoleBox");
|
|
|
|
|
|
if recv_is_console_new && (method == "println" || method == "print" || method == "log")
|
|
|
|
|
|
{
|
2025-09-15 18:44:49 +09:00
|
|
|
|
let (arg_ids, cur2) = lower_args(f, cur_bb, args)?;
|
|
|
|
|
|
let dst = f.next_value_id();
|
|
|
|
|
|
if let Some(bb) = f.get_block_mut(cur2) {
|
2025-09-16 23:49:36 +09:00
|
|
|
|
bb.add_instruction(MirInstruction::ExternCall {
|
|
|
|
|
|
dst: Some(dst),
|
|
|
|
|
|
iface_name: "env.console".into(),
|
|
|
|
|
|
method_name: "log".into(),
|
|
|
|
|
|
args: arg_ids,
|
|
|
|
|
|
effects: EffectMask::READ,
|
|
|
|
|
|
});
|
2025-09-15 18:44:49 +09:00
|
|
|
|
}
|
|
|
|
|
|
return Ok((dst, cur2));
|
|
|
|
|
|
}
|
2025-09-14 23:22:05 +09:00
|
|
|
|
let (recv_v, cur) = lower_expr(f, cur_bb, recv)?;
|
|
|
|
|
|
let (arg_ids, cur2) = lower_args(f, cur, args)?;
|
|
|
|
|
|
let dst = f.next_value_id();
|
|
|
|
|
|
if let Some(bb) = f.get_block_mut(cur2) {
|
2025-09-16 23:49:36 +09:00
|
|
|
|
bb.add_instruction(MirInstruction::BoxCall {
|
|
|
|
|
|
dst: Some(dst),
|
|
|
|
|
|
box_val: recv_v,
|
|
|
|
|
|
method: method.clone(),
|
|
|
|
|
|
method_id: None,
|
|
|
|
|
|
args: arg_ids,
|
|
|
|
|
|
effects: EffectMask::READ,
|
|
|
|
|
|
});
|
2025-09-14 23:22:05 +09:00
|
|
|
|
}
|
|
|
|
|
|
Ok((dst, cur2))
|
|
|
|
|
|
}
|
|
|
|
|
|
ExprV0::New { class, args } => {
|
|
|
|
|
|
let (arg_ids, cur) = lower_args(f, cur_bb, args)?;
|
|
|
|
|
|
let dst = f.next_value_id();
|
|
|
|
|
|
if let Some(bb) = f.get_block_mut(cur) {
|
2025-09-16 23:49:36 +09:00
|
|
|
|
bb.add_instruction(MirInstruction::NewBox {
|
|
|
|
|
|
dst,
|
|
|
|
|
|
box_type: class.clone(),
|
|
|
|
|
|
args: arg_ids,
|
|
|
|
|
|
});
|
2025-09-14 23:22:05 +09:00
|
|
|
|
}
|
|
|
|
|
|
Ok((dst, cur))
|
|
|
|
|
|
}
|
|
|
|
|
|
ExprV0::Var { name } => Err(format!("undefined variable in this context: {}", name)),
|
2025-09-16 18:33:59 +09:00
|
|
|
|
ExprV0::Throw { expr } => {
|
2025-09-16 23:49:36 +09:00
|
|
|
|
let (exc, cur) = lower_expr(f, cur_bb, expr)?;
|
|
|
|
|
|
let (dst, cur) = lower_throw(f, cur, exc);
|
2025-09-16 18:33:59 +09:00
|
|
|
|
Ok((dst, cur))
|
|
|
|
|
|
}
|
2025-09-14 23:22:05 +09:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
fn lower_expr_with_vars(
|
|
|
|
|
|
f: &mut MirFunction,
|
|
|
|
|
|
cur_bb: BasicBlockId,
|
|
|
|
|
|
e: &ExprV0,
|
|
|
|
|
|
vars: &mut std::collections::HashMap<String, crate::mir::ValueId>,
|
|
|
|
|
|
) -> Result<(crate::mir::ValueId, BasicBlockId), String> {
|
|
|
|
|
|
match e {
|
|
|
|
|
|
ExprV0::Var { name } => {
|
|
|
|
|
|
if let Some(&vid) = vars.get(name) {
|
2025-09-15 01:21:37 +09:00
|
|
|
|
return Ok((vid, cur_bb));
|
2025-09-14 23:22:05 +09:00
|
|
|
|
}
|
2025-09-15 01:21:37 +09:00
|
|
|
|
if name == "me" {
|
|
|
|
|
|
// Optional gate: allow a dummy 'me' instance for Stage-2 JSON smoke
|
|
|
|
|
|
if std::env::var("NYASH_BRIDGE_ME_DUMMY").ok().as_deref() == Some("1") {
|
2025-09-16 23:49:36 +09:00
|
|
|
|
let class = std::env::var("NYASH_BRIDGE_ME_CLASS")
|
|
|
|
|
|
.unwrap_or_else(|_| "Main".to_string());
|
2025-09-15 01:21:37 +09:00
|
|
|
|
let dst = f.next_value_id();
|
|
|
|
|
|
if let Some(bb) = f.get_block_mut(cur_bb) {
|
2025-09-16 23:49:36 +09:00
|
|
|
|
bb.add_instruction(MirInstruction::NewBox {
|
|
|
|
|
|
dst,
|
|
|
|
|
|
box_type: class,
|
|
|
|
|
|
args: vec![],
|
|
|
|
|
|
});
|
2025-09-15 01:21:37 +09:00
|
|
|
|
}
|
|
|
|
|
|
vars.insert("me".to_string(), dst);
|
|
|
|
|
|
return Ok((dst, cur_bb));
|
|
|
|
|
|
} else {
|
|
|
|
|
|
return Err("undefined 'me' outside box context (set NYASH_BRIDGE_ME_DUMMY=1 to inject placeholder)".into());
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
Err(format!("undefined variable: {}", name))
|
2025-09-14 23:22:05 +09:00
|
|
|
|
}
|
2025-09-16 18:33:59 +09:00
|
|
|
|
ExprV0::Throw { expr } => {
|
2025-09-16 23:49:36 +09:00
|
|
|
|
let (exc, cur) = lower_expr_with_vars(f, cur_bb, expr, vars)?;
|
|
|
|
|
|
let (dst, cur) = lower_throw(f, cur, exc);
|
2025-09-16 18:33:59 +09:00
|
|
|
|
Ok((dst, cur))
|
|
|
|
|
|
}
|
2025-09-14 23:22:05 +09:00
|
|
|
|
ExprV0::Call { name, args } => {
|
2025-09-16 16:28:25 +09:00
|
|
|
|
// Special: array literal lowering in vars context
|
|
|
|
|
|
if name == "array.of" {
|
|
|
|
|
|
let arr = f.next_value_id();
|
|
|
|
|
|
if let Some(bb) = f.get_block_mut(cur_bb) {
|
2025-09-16 23:49:36 +09:00
|
|
|
|
bb.add_instruction(MirInstruction::NewBox {
|
|
|
|
|
|
dst: arr,
|
|
|
|
|
|
box_type: "ArrayBox".into(),
|
|
|
|
|
|
args: vec![],
|
|
|
|
|
|
});
|
2025-09-16 16:28:25 +09:00
|
|
|
|
}
|
|
|
|
|
|
let mut cur = cur_bb;
|
|
|
|
|
|
for e in args {
|
2025-09-16 23:49:36 +09:00
|
|
|
|
let (v, c) = lower_expr_with_vars(f, cur, e, vars)?;
|
|
|
|
|
|
cur = c;
|
2025-09-16 16:28:25 +09:00
|
|
|
|
let tmp = f.next_value_id();
|
|
|
|
|
|
if let Some(bb) = f.get_block_mut(cur) {
|
2025-09-16 23:49:36 +09:00
|
|
|
|
bb.add_instruction(MirInstruction::BoxCall {
|
|
|
|
|
|
dst: Some(tmp),
|
|
|
|
|
|
box_val: arr,
|
|
|
|
|
|
method: "push".into(),
|
|
|
|
|
|
method_id: None,
|
|
|
|
|
|
args: vec![v],
|
|
|
|
|
|
effects: EffectMask::READ,
|
|
|
|
|
|
});
|
2025-09-16 16:28:25 +09:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
return Ok((arr, cur));
|
|
|
|
|
|
}
|
|
|
|
|
|
// Special: map literal lowering in vars context
|
|
|
|
|
|
if name == "map.of" {
|
|
|
|
|
|
let mapv = f.next_value_id();
|
|
|
|
|
|
if let Some(bb) = f.get_block_mut(cur_bb) {
|
2025-09-16 23:49:36 +09:00
|
|
|
|
bb.add_instruction(MirInstruction::NewBox {
|
|
|
|
|
|
dst: mapv,
|
|
|
|
|
|
box_type: "MapBox".into(),
|
|
|
|
|
|
args: vec![],
|
|
|
|
|
|
});
|
2025-09-16 16:28:25 +09:00
|
|
|
|
}
|
|
|
|
|
|
let mut cur = cur_bb;
|
|
|
|
|
|
let mut it = args.iter();
|
|
|
|
|
|
while let Some(k) = it.next() {
|
|
|
|
|
|
if let Some(v) = it.next() {
|
2025-09-16 23:49:36 +09:00
|
|
|
|
let (kv, cur2) = lower_expr_with_vars(f, cur, k, vars)?;
|
|
|
|
|
|
cur = cur2;
|
|
|
|
|
|
let (vv, cur3) = lower_expr_with_vars(f, cur, v, vars)?;
|
|
|
|
|
|
cur = cur3;
|
2025-09-16 16:28:25 +09:00
|
|
|
|
let tmp = f.next_value_id();
|
|
|
|
|
|
if let Some(bb) = f.get_block_mut(cur) {
|
2025-09-16 23:49:36 +09:00
|
|
|
|
bb.add_instruction(MirInstruction::BoxCall {
|
|
|
|
|
|
dst: Some(tmp),
|
|
|
|
|
|
box_val: mapv,
|
|
|
|
|
|
method: "set".into(),
|
|
|
|
|
|
method_id: None,
|
|
|
|
|
|
args: vec![kv, vv],
|
|
|
|
|
|
effects: EffectMask::READ,
|
|
|
|
|
|
});
|
2025-09-16 16:28:25 +09:00
|
|
|
|
}
|
2025-09-16 23:49:36 +09:00
|
|
|
|
} else {
|
|
|
|
|
|
break;
|
|
|
|
|
|
}
|
2025-09-16 16:28:25 +09:00
|
|
|
|
}
|
|
|
|
|
|
return Ok((mapv, cur));
|
|
|
|
|
|
}
|
2025-09-14 23:22:05 +09:00
|
|
|
|
// Lower args
|
|
|
|
|
|
let (arg_ids, cur) = lower_args_with_vars(f, cur_bb, args, vars)?;
|
|
|
|
|
|
// Encode as: const fun_name; call
|
|
|
|
|
|
let fun_val = f.next_value_id();
|
|
|
|
|
|
if let Some(bb) = f.get_block_mut(cur) {
|
2025-09-16 23:49:36 +09:00
|
|
|
|
bb.add_instruction(MirInstruction::Const {
|
|
|
|
|
|
dst: fun_val,
|
|
|
|
|
|
value: ConstValue::String(name.clone()),
|
|
|
|
|
|
});
|
2025-09-14 23:22:05 +09:00
|
|
|
|
}
|
|
|
|
|
|
let dst = f.next_value_id();
|
|
|
|
|
|
if let Some(bb) = f.get_block_mut(cur) {
|
2025-09-16 23:49:36 +09:00
|
|
|
|
bb.add_instruction(MirInstruction::Call {
|
|
|
|
|
|
dst: Some(dst),
|
|
|
|
|
|
func: fun_val,
|
|
|
|
|
|
args: arg_ids,
|
|
|
|
|
|
effects: EffectMask::READ,
|
|
|
|
|
|
});
|
2025-09-14 23:22:05 +09:00
|
|
|
|
}
|
|
|
|
|
|
Ok((dst, cur))
|
|
|
|
|
|
}
|
|
|
|
|
|
ExprV0::Method { recv, method, args } => {
|
2025-09-16 23:49:36 +09:00
|
|
|
|
let recv_is_console_new =
|
|
|
|
|
|
matches!(&**recv, ExprV0::New { class, .. } if class == "ConsoleBox");
|
|
|
|
|
|
if recv_is_console_new && (method == "println" || method == "print" || method == "log")
|
|
|
|
|
|
{
|
2025-09-15 18:44:49 +09:00
|
|
|
|
let (arg_ids, cur2) = lower_args_with_vars(f, cur_bb, args, vars)?;
|
|
|
|
|
|
let dst = f.next_value_id();
|
|
|
|
|
|
if let Some(bb) = f.get_block_mut(cur2) {
|
2025-09-16 23:49:36 +09:00
|
|
|
|
bb.add_instruction(MirInstruction::ExternCall {
|
|
|
|
|
|
dst: Some(dst),
|
|
|
|
|
|
iface_name: "env.console".into(),
|
|
|
|
|
|
method_name: "log".into(),
|
|
|
|
|
|
args: arg_ids,
|
|
|
|
|
|
effects: EffectMask::READ,
|
|
|
|
|
|
});
|
2025-09-15 18:44:49 +09:00
|
|
|
|
}
|
|
|
|
|
|
return Ok((dst, cur2));
|
|
|
|
|
|
}
|
2025-09-14 23:22:05 +09:00
|
|
|
|
let (recv_v, cur) = lower_expr_with_vars(f, cur_bb, recv, vars)?;
|
|
|
|
|
|
let (arg_ids, cur2) = lower_args_with_vars(f, cur, args, vars)?;
|
|
|
|
|
|
let dst = f.next_value_id();
|
|
|
|
|
|
if let Some(bb) = f.get_block_mut(cur2) {
|
2025-09-16 23:49:36 +09:00
|
|
|
|
bb.add_instruction(MirInstruction::BoxCall {
|
|
|
|
|
|
dst: Some(dst),
|
|
|
|
|
|
box_val: recv_v,
|
|
|
|
|
|
method: method.clone(),
|
|
|
|
|
|
method_id: None,
|
|
|
|
|
|
args: arg_ids,
|
|
|
|
|
|
effects: EffectMask::READ,
|
|
|
|
|
|
});
|
2025-09-14 23:22:05 +09:00
|
|
|
|
}
|
|
|
|
|
|
Ok((dst, cur2))
|
|
|
|
|
|
}
|
|
|
|
|
|
ExprV0::New { class, args } => {
|
|
|
|
|
|
let (arg_ids, cur) = lower_args_with_vars(f, cur_bb, args, vars)?;
|
|
|
|
|
|
let dst = f.next_value_id();
|
|
|
|
|
|
if let Some(bb) = f.get_block_mut(cur) {
|
2025-09-16 23:49:36 +09:00
|
|
|
|
bb.add_instruction(MirInstruction::NewBox {
|
|
|
|
|
|
dst,
|
|
|
|
|
|
box_type: class.clone(),
|
|
|
|
|
|
args: arg_ids,
|
|
|
|
|
|
});
|
2025-09-14 23:22:05 +09:00
|
|
|
|
}
|
|
|
|
|
|
Ok((dst, cur))
|
|
|
|
|
|
}
|
|
|
|
|
|
ExprV0::Binary { op, lhs, rhs } => {
|
|
|
|
|
|
let (l, cur_after_l) = lower_expr_with_vars(f, cur_bb, lhs, vars)?;
|
|
|
|
|
|
let (r, cur_after_r) = lower_expr_with_vars(f, cur_after_l, rhs, vars)?;
|
2025-09-16 23:49:36 +09:00
|
|
|
|
let bop = match op.as_str() {
|
|
|
|
|
|
"+" => BinaryOp::Add,
|
|
|
|
|
|
"-" => BinaryOp::Sub,
|
|
|
|
|
|
"*" => BinaryOp::Mul,
|
|
|
|
|
|
"/" => BinaryOp::Div,
|
|
|
|
|
|
_ => return Err("unsupported op".into()),
|
|
|
|
|
|
};
|
2025-09-14 23:22:05 +09:00
|
|
|
|
let dst = f.next_value_id();
|
|
|
|
|
|
if let Some(bb) = f.get_block_mut(cur_after_r) {
|
2025-09-16 23:49:36 +09:00
|
|
|
|
bb.add_instruction(MirInstruction::BinOp {
|
|
|
|
|
|
dst,
|
|
|
|
|
|
op: bop,
|
|
|
|
|
|
lhs: l,
|
|
|
|
|
|
rhs: r,
|
|
|
|
|
|
});
|
2025-09-14 23:22:05 +09:00
|
|
|
|
}
|
|
|
|
|
|
Ok((dst, cur_after_r))
|
|
|
|
|
|
}
|
|
|
|
|
|
ExprV0::Compare { op, lhs, rhs } => {
|
|
|
|
|
|
let (l, cur_after_l) = lower_expr_with_vars(f, cur_bb, lhs, vars)?;
|
|
|
|
|
|
let (r, cur_after_r) = lower_expr_with_vars(f, cur_after_l, rhs, vars)?;
|
|
|
|
|
|
let cop = match op.as_str() {
|
|
|
|
|
|
"==" => crate::mir::CompareOp::Eq,
|
|
|
|
|
|
"!=" => crate::mir::CompareOp::Ne,
|
2025-09-16 23:49:36 +09:00
|
|
|
|
"<" => crate::mir::CompareOp::Lt,
|
2025-09-14 23:22:05 +09:00
|
|
|
|
"<=" => crate::mir::CompareOp::Le,
|
2025-09-16 23:49:36 +09:00
|
|
|
|
">" => crate::mir::CompareOp::Gt,
|
2025-09-14 23:22:05 +09:00
|
|
|
|
">=" => 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) {
|
2025-09-16 23:49:36 +09:00
|
|
|
|
bb.add_instruction(MirInstruction::Compare {
|
|
|
|
|
|
dst,
|
|
|
|
|
|
op: cop,
|
|
|
|
|
|
lhs: l,
|
|
|
|
|
|
rhs: r,
|
|
|
|
|
|
});
|
2025-09-14 23:22:05 +09:00
|
|
|
|
}
|
|
|
|
|
|
Ok((dst, cur_after_r))
|
|
|
|
|
|
}
|
|
|
|
|
|
ExprV0::Logical { op, lhs, rhs } => {
|
|
|
|
|
|
let (l, cur_after_l) = lower_expr_with_vars(f, cur_bb, lhs, vars)?;
|
|
|
|
|
|
let rhs_bb = next_block_id(f);
|
|
|
|
|
|
let fall_bb = BasicBlockId::new(rhs_bb.0 + 1);
|
|
|
|
|
|
let merge_bb = BasicBlockId::new(rhs_bb.0 + 2);
|
|
|
|
|
|
f.add_block(crate::mir::BasicBlock::new(rhs_bb));
|
|
|
|
|
|
f.add_block(crate::mir::BasicBlock::new(fall_bb));
|
|
|
|
|
|
f.add_block(crate::mir::BasicBlock::new(merge_bb));
|
|
|
|
|
|
let is_and = matches!(op.as_str(), "&&" | "and");
|
|
|
|
|
|
if let Some(bb) = f.get_block_mut(cur_after_l) {
|
|
|
|
|
|
if is_and {
|
2025-09-16 23:49:36 +09:00
|
|
|
|
bb.set_terminator(MirInstruction::Branch {
|
|
|
|
|
|
condition: l,
|
|
|
|
|
|
then_bb: rhs_bb,
|
|
|
|
|
|
else_bb: fall_bb,
|
|
|
|
|
|
});
|
2025-09-14 23:22:05 +09:00
|
|
|
|
} else {
|
2025-09-16 23:49:36 +09:00
|
|
|
|
bb.set_terminator(MirInstruction::Branch {
|
|
|
|
|
|
condition: l,
|
|
|
|
|
|
then_bb: fall_bb,
|
|
|
|
|
|
else_bb: rhs_bb,
|
|
|
|
|
|
});
|
2025-09-14 23:22:05 +09:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
let cdst = f.next_value_id();
|
|
|
|
|
|
if let Some(bb) = f.get_block_mut(fall_bb) {
|
2025-09-16 23:49:36 +09:00
|
|
|
|
let cval = if is_and {
|
|
|
|
|
|
ConstValue::Bool(false)
|
|
|
|
|
|
} else {
|
|
|
|
|
|
ConstValue::Bool(true)
|
|
|
|
|
|
};
|
|
|
|
|
|
bb.add_instruction(MirInstruction::Const {
|
|
|
|
|
|
dst: cdst,
|
|
|
|
|
|
value: cval,
|
|
|
|
|
|
});
|
2025-09-14 23:22:05 +09:00
|
|
|
|
bb.set_terminator(MirInstruction::Jump { target: merge_bb });
|
|
|
|
|
|
}
|
2025-09-15 18:44:49 +09:00
|
|
|
|
let (rval, rhs_end) = lower_expr_with_vars(f, rhs_bb, rhs, vars)?;
|
2025-09-16 23:49:36 +09:00
|
|
|
|
if let Some(bb) = f.get_block_mut(rhs_end) {
|
|
|
|
|
|
if !bb.is_terminated() {
|
|
|
|
|
|
bb.set_terminator(MirInstruction::Jump { target: merge_bb });
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2025-09-14 23:22:05 +09:00
|
|
|
|
let out = f.next_value_id();
|
2025-09-16 23:49:36 +09:00
|
|
|
|
if let Some(bb) = f.get_block_mut(merge_bb) {
|
|
|
|
|
|
bb.insert_instruction_after_phis(MirInstruction::Phi {
|
|
|
|
|
|
dst: out,
|
|
|
|
|
|
inputs: vec![(rhs_end, rval), (fall_bb, cdst)],
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
2025-09-14 23:22:05 +09:00
|
|
|
|
Ok((out, merge_bb))
|
|
|
|
|
|
}
|
|
|
|
|
|
_ => lower_expr(f, cur_bb, e),
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
fn lower_stmt_with_vars(
|
|
|
|
|
|
f: &mut MirFunction,
|
|
|
|
|
|
cur_bb: BasicBlockId,
|
|
|
|
|
|
s: &StmtV0,
|
|
|
|
|
|
vars: &mut std::collections::HashMap<String, crate::mir::ValueId>,
|
2025-09-16 23:49:36 +09:00
|
|
|
|
loop_stack: &mut Vec<LoopContext>,
|
2025-09-14 23:22:05 +09:00
|
|
|
|
) -> Result<BasicBlockId, String> {
|
|
|
|
|
|
match s {
|
|
|
|
|
|
StmtV0::Return { expr } => {
|
|
|
|
|
|
let (v, cur) = lower_expr_with_vars(f, cur_bb, expr, vars)?;
|
2025-09-16 23:49:36 +09:00
|
|
|
|
if let Some(bb) = f.get_block_mut(cur) {
|
|
|
|
|
|
bb.set_terminator(MirInstruction::Return { value: Some(v) });
|
|
|
|
|
|
}
|
2025-09-14 23:22:05 +09:00
|
|
|
|
Ok(cur)
|
|
|
|
|
|
}
|
2025-09-16 23:49:36 +09:00
|
|
|
|
StmtV0::Extern {
|
|
|
|
|
|
iface,
|
|
|
|
|
|
method,
|
|
|
|
|
|
args,
|
|
|
|
|
|
} => {
|
2025-09-14 23:22:05 +09:00
|
|
|
|
let (arg_ids, cur) = lower_args_with_vars(f, cur_bb, args, vars)?;
|
2025-09-16 23:49:36 +09:00
|
|
|
|
if let Some(bb) = f.get_block_mut(cur) {
|
|
|
|
|
|
bb.add_instruction(MirInstruction::ExternCall {
|
|
|
|
|
|
dst: None,
|
|
|
|
|
|
iface_name: iface.clone(),
|
|
|
|
|
|
method_name: method.clone(),
|
|
|
|
|
|
args: arg_ids,
|
|
|
|
|
|
effects: EffectMask::IO,
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
2025-09-14 23:22:05 +09:00
|
|
|
|
Ok(cur)
|
|
|
|
|
|
}
|
|
|
|
|
|
StmtV0::Expr { expr } => {
|
2025-09-16 23:49:36 +09:00
|
|
|
|
let (_v, cur) = lower_expr_with_vars(f, cur_bb, expr, vars)?;
|
|
|
|
|
|
Ok(cur)
|
2025-09-14 23:22:05 +09:00
|
|
|
|
}
|
|
|
|
|
|
StmtV0::Local { name, expr } => {
|
2025-09-16 23:49:36 +09:00
|
|
|
|
let (v, cur) = lower_expr_with_vars(f, cur_bb, expr, vars)?;
|
|
|
|
|
|
vars.insert(name.clone(), v);
|
|
|
|
|
|
Ok(cur)
|
2025-09-14 23:22:05 +09:00
|
|
|
|
}
|
2025-09-16 18:33:59 +09:00
|
|
|
|
StmtV0::Break => {
|
2025-09-16 23:49:36 +09:00
|
|
|
|
if let Some(ctx) = loop_stack.last().copied() {
|
|
|
|
|
|
if let Some(bb) = f.get_block_mut(cur_bb) {
|
|
|
|
|
|
bb.set_terminator(MirInstruction::Jump {
|
|
|
|
|
|
target: ctx.exit_bb,
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
crate::jit::events::emit_lower(
|
|
|
|
|
|
serde_json::json!({
|
|
|
|
|
|
"id": "loop_break",
|
|
|
|
|
|
"exit_bb": ctx.exit_bb.0,
|
|
|
|
|
|
"decision": "lower",
|
|
|
|
|
|
}),
|
|
|
|
|
|
"loop",
|
|
|
|
|
|
"<json_v0>",
|
|
|
|
|
|
);
|
|
|
|
|
|
} else if std::env::var("NYASH_CLI_VERBOSE").ok().as_deref() == Some("1") {
|
|
|
|
|
|
eprintln!("[bridge/break] ignoring break outside loop context");
|
|
|
|
|
|
}
|
2025-09-16 18:33:59 +09:00
|
|
|
|
Ok(cur_bb)
|
|
|
|
|
|
}
|
|
|
|
|
|
StmtV0::Continue => {
|
2025-09-16 23:49:36 +09:00
|
|
|
|
if let Some(ctx) = loop_stack.last().copied() {
|
|
|
|
|
|
if let Some(bb) = f.get_block_mut(cur_bb) {
|
|
|
|
|
|
bb.set_terminator(MirInstruction::Jump {
|
|
|
|
|
|
target: ctx.cond_bb,
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
crate::jit::events::emit_lower(
|
|
|
|
|
|
serde_json::json!({
|
|
|
|
|
|
"id": "loop_continue",
|
|
|
|
|
|
"cond_bb": ctx.cond_bb.0,
|
|
|
|
|
|
"decision": "lower",
|
|
|
|
|
|
}),
|
|
|
|
|
|
"loop",
|
|
|
|
|
|
"<json_v0>",
|
|
|
|
|
|
);
|
|
|
|
|
|
} else if std::env::var("NYASH_CLI_VERBOSE").ok().as_deref() == Some("1") {
|
|
|
|
|
|
eprintln!("[bridge/continue] ignoring continue outside loop context");
|
|
|
|
|
|
}
|
2025-09-16 18:33:59 +09:00
|
|
|
|
Ok(cur_bb)
|
|
|
|
|
|
}
|
2025-09-16 23:49:36 +09:00
|
|
|
|
StmtV0::Try {
|
|
|
|
|
|
try_body,
|
|
|
|
|
|
catches,
|
|
|
|
|
|
finally,
|
|
|
|
|
|
} => {
|
|
|
|
|
|
let try_enabled = std::env::var("NYASH_BRIDGE_TRY_ENABLE").ok().as_deref() == Some("1");
|
|
|
|
|
|
|
|
|
|
|
|
if !try_enabled || catches.is_empty() || catches.len() > 1 {
|
|
|
|
|
|
let mut tmp_vars = vars.clone();
|
|
|
|
|
|
let mut next_bb =
|
|
|
|
|
|
lower_stmt_list_with_vars(f, cur_bb, try_body, &mut tmp_vars, loop_stack)?;
|
|
|
|
|
|
if !finally.is_empty() {
|
|
|
|
|
|
next_bb =
|
|
|
|
|
|
lower_stmt_list_with_vars(f, next_bb, finally, &mut tmp_vars, loop_stack)?;
|
|
|
|
|
|
}
|
|
|
|
|
|
*vars = tmp_vars;
|
|
|
|
|
|
return Ok(next_bb);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
let base_vars = vars.clone();
|
|
|
|
|
|
let try_bb = next_block_id(f);
|
|
|
|
|
|
f.add_block(crate::mir::BasicBlock::new(try_bb));
|
|
|
|
|
|
|
|
|
|
|
|
let catch_clause = &catches[0];
|
|
|
|
|
|
let catch_bb = next_block_id(f);
|
|
|
|
|
|
f.add_block(crate::mir::BasicBlock::new(catch_bb));
|
|
|
|
|
|
|
|
|
|
|
|
let finally_bb = if !finally.is_empty() {
|
|
|
|
|
|
let id = next_block_id(f);
|
|
|
|
|
|
f.add_block(crate::mir::BasicBlock::new(id));
|
|
|
|
|
|
Some(id)
|
|
|
|
|
|
} else {
|
|
|
|
|
|
None
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
let exit_bb = next_block_id(f);
|
|
|
|
|
|
f.add_block(crate::mir::BasicBlock::new(exit_bb));
|
|
|
|
|
|
|
|
|
|
|
|
let handler_target = finally_bb.unwrap_or(exit_bb);
|
|
|
|
|
|
|
|
|
|
|
|
let exception_value = f.next_value_id();
|
|
|
|
|
|
if let Some(bb) = f.get_block_mut(cur_bb) {
|
|
|
|
|
|
bb.add_instruction(MirInstruction::Catch {
|
|
|
|
|
|
exception_type: catch_clause.type_hint.clone(),
|
|
|
|
|
|
exception_value,
|
|
|
|
|
|
handler_bb: catch_bb,
|
|
|
|
|
|
});
|
|
|
|
|
|
bb.set_terminator(MirInstruction::Jump { target: try_bb });
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
let mut try_vars = vars.clone();
|
|
|
|
|
|
let try_end =
|
|
|
|
|
|
lower_stmt_list_with_vars(f, try_bb, try_body, &mut try_vars, loop_stack)?;
|
|
|
|
|
|
if let Some(bb) = f.get_block_mut(try_end) {
|
|
|
|
|
|
if !bb.is_terminated() {
|
|
|
|
|
|
bb.set_terminator(MirInstruction::Jump {
|
|
|
|
|
|
target: handler_target,
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
let try_branch_vars = try_vars.clone();
|
|
|
|
|
|
|
|
|
|
|
|
let mut catch_vars = base_vars.clone();
|
|
|
|
|
|
if let Some(param) = &catch_clause.param {
|
|
|
|
|
|
catch_vars.insert(param.clone(), exception_value);
|
|
|
|
|
|
}
|
|
|
|
|
|
let catch_end = lower_stmt_list_with_vars(
|
|
|
|
|
|
f,
|
|
|
|
|
|
catch_bb,
|
|
|
|
|
|
&catch_clause.body,
|
|
|
|
|
|
&mut catch_vars,
|
|
|
|
|
|
loop_stack,
|
|
|
|
|
|
)?;
|
|
|
|
|
|
if let Some(param) = &catch_clause.param {
|
|
|
|
|
|
catch_vars.remove(param);
|
|
|
|
|
|
}
|
|
|
|
|
|
if let Some(bb) = f.get_block_mut(catch_end) {
|
|
|
|
|
|
if !bb.is_terminated() {
|
|
|
|
|
|
bb.set_terminator(MirInstruction::Jump {
|
|
|
|
|
|
target: handler_target,
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
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 merge_target = handler_target;
|
|
|
|
|
|
|
|
|
|
|
|
if let Some(finally_block) = finally_bb {
|
|
|
|
|
|
// ensure finally block exists before inserting phi
|
|
|
|
|
|
let names: HashSet<String> = {
|
|
|
|
|
|
let mut set: HashSet<String> = base_vars.keys().cloned().collect();
|
|
|
|
|
|
for (_, map) in &branch_vars {
|
|
|
|
|
|
set.extend(map.keys().cloned());
|
|
|
|
|
|
}
|
|
|
|
|
|
set
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
let mut merged_vars = base_vars.clone();
|
|
|
|
|
|
let mut phi_entries: Vec<(ValueId, Vec<(BasicBlockId, ValueId)>)> = Vec::new();
|
|
|
|
|
|
for name in names {
|
|
|
|
|
|
let mut inputs: Vec<(BasicBlockId, ValueId)> = Vec::new();
|
|
|
|
|
|
for (bbid, map) in &branch_vars {
|
|
|
|
|
|
if let Some(&val) = map.get(&name) {
|
|
|
|
|
|
inputs.push((*bbid, val));
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
if inputs.is_empty() {
|
|
|
|
|
|
if let Some(&base_val) = base_vars.get(&name) {
|
|
|
|
|
|
merged_vars.insert(name.clone(), base_val);
|
|
|
|
|
|
}
|
|
|
|
|
|
continue;
|
|
|
|
|
|
}
|
|
|
|
|
|
let unique: HashSet<ValueId> = inputs.iter().map(|(_, v)| *v).collect();
|
|
|
|
|
|
if unique.len() == 1 {
|
|
|
|
|
|
merged_vars.insert(name.clone(), inputs[0].1);
|
|
|
|
|
|
continue;
|
|
|
|
|
|
}
|
|
|
|
|
|
let dst = f.next_value_id();
|
|
|
|
|
|
inputs.sort_by_key(|(bbid, _)| bbid.0);
|
|
|
|
|
|
phi_entries.push((dst, inputs));
|
|
|
|
|
|
merged_vars.insert(name.clone(), dst);
|
|
|
|
|
|
}
|
|
|
|
|
|
if let Some(bb) = f.get_block_mut(finally_block) {
|
|
|
|
|
|
for (dst, inputs) in phi_entries {
|
|
|
|
|
|
bb.insert_instruction_after_phis(MirInstruction::Phi { dst, inputs });
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
let mut finally_vars = merged_vars.clone();
|
|
|
|
|
|
let final_end = lower_stmt_list_with_vars(
|
|
|
|
|
|
f,
|
|
|
|
|
|
finally_block,
|
|
|
|
|
|
finally,
|
|
|
|
|
|
&mut finally_vars,
|
|
|
|
|
|
loop_stack,
|
|
|
|
|
|
)?;
|
|
|
|
|
|
if let Some(bb) = f.get_block_mut(final_end) {
|
|
|
|
|
|
if !bb.is_terminated() {
|
|
|
|
|
|
bb.set_terminator(MirInstruction::Jump { target: exit_bb });
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
*vars = finally_vars;
|
|
|
|
|
|
Ok(exit_bb)
|
|
|
|
|
|
} else {
|
|
|
|
|
|
let names: HashSet<String> = {
|
|
|
|
|
|
let mut set: HashSet<String> = base_vars.keys().cloned().collect();
|
|
|
|
|
|
for (_, map) in &branch_vars {
|
|
|
|
|
|
set.extend(map.keys().cloned());
|
|
|
|
|
|
}
|
|
|
|
|
|
set
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
let mut merged_vars = base_vars.clone();
|
|
|
|
|
|
let mut phi_entries: Vec<(ValueId, Vec<(BasicBlockId, ValueId)>)> = Vec::new();
|
|
|
|
|
|
for name in names {
|
|
|
|
|
|
let mut inputs: Vec<(BasicBlockId, ValueId)> = Vec::new();
|
|
|
|
|
|
for (bbid, map) in &branch_vars {
|
|
|
|
|
|
if let Some(&val) = map.get(&name) {
|
|
|
|
|
|
inputs.push((*bbid, val));
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
if inputs.is_empty() {
|
|
|
|
|
|
if let Some(&base_val) = base_vars.get(&name) {
|
|
|
|
|
|
merged_vars.insert(name.clone(), base_val);
|
|
|
|
|
|
}
|
|
|
|
|
|
continue;
|
|
|
|
|
|
}
|
|
|
|
|
|
let unique: HashSet<ValueId> = inputs.iter().map(|(_, v)| *v).collect();
|
|
|
|
|
|
if unique.len() == 1 {
|
|
|
|
|
|
merged_vars.insert(name.clone(), inputs[0].1);
|
|
|
|
|
|
continue;
|
|
|
|
|
|
}
|
|
|
|
|
|
let dst = f.next_value_id();
|
|
|
|
|
|
inputs.sort_by_key(|(bbid, _)| bbid.0);
|
|
|
|
|
|
phi_entries.push((dst, inputs));
|
|
|
|
|
|
merged_vars.insert(name.clone(), dst);
|
|
|
|
|
|
}
|
|
|
|
|
|
if let Some(bb) = f.get_block_mut(exit_bb) {
|
|
|
|
|
|
for (dst, inputs) in phi_entries {
|
|
|
|
|
|
bb.insert_instruction_after_phis(MirInstruction::Phi { dst, inputs });
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
*vars = merged_vars;
|
|
|
|
|
|
Ok(exit_bb)
|
|
|
|
|
|
}
|
2025-09-16 18:33:59 +09:00
|
|
|
|
}
|
2025-09-14 23:22:05 +09:00
|
|
|
|
StmtV0::If { cond, then, r#else } => {
|
|
|
|
|
|
// Lower condition first
|
|
|
|
|
|
let (cval, cur) = lower_expr_with_vars(f, cur_bb, cond, vars)?;
|
|
|
|
|
|
// Create then/else/merge blocks
|
|
|
|
|
|
let then_bb = next_block_id(f);
|
|
|
|
|
|
let else_bb = BasicBlockId::new(then_bb.0 + 1);
|
|
|
|
|
|
let merge_bb = BasicBlockId::new(then_bb.0 + 2);
|
|
|
|
|
|
f.add_block(crate::mir::BasicBlock::new(then_bb));
|
|
|
|
|
|
f.add_block(crate::mir::BasicBlock::new(else_bb));
|
|
|
|
|
|
f.add_block(crate::mir::BasicBlock::new(merge_bb));
|
|
|
|
|
|
// Branch to then/else
|
|
|
|
|
|
if let Some(bb) = f.get_block_mut(cur) {
|
2025-09-16 23:49:36 +09:00
|
|
|
|
bb.set_terminator(MirInstruction::Branch {
|
|
|
|
|
|
condition: cval,
|
|
|
|
|
|
then_bb,
|
|
|
|
|
|
else_bb,
|
|
|
|
|
|
});
|
2025-09-14 23:22:05 +09:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Clone current vars as branch-local maps
|
|
|
|
|
|
let base_vars = vars.clone();
|
|
|
|
|
|
let mut then_vars = base_vars.clone();
|
2025-09-16 23:49:36 +09:00
|
|
|
|
let tend = lower_stmt_list_with_vars(f, then_bb, then, &mut then_vars, loop_stack)?;
|
2025-09-14 23:22:05 +09:00
|
|
|
|
if let Some(bb) = f.get_block_mut(tend) {
|
2025-09-16 23:49:36 +09:00
|
|
|
|
if !bb.is_terminated() {
|
|
|
|
|
|
bb.set_terminator(MirInstruction::Jump { target: merge_bb });
|
|
|
|
|
|
}
|
2025-09-14 23:22:05 +09:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
let (else_end_pred, else_vars) = if let Some(elses) = r#else {
|
|
|
|
|
|
let mut ev = base_vars.clone();
|
2025-09-16 23:49:36 +09:00
|
|
|
|
let eend = lower_stmt_list_with_vars(f, else_bb, elses, &mut ev, loop_stack)?;
|
2025-09-14 23:22:05 +09:00
|
|
|
|
if let Some(bb) = f.get_block_mut(eend) {
|
2025-09-16 23:49:36 +09:00
|
|
|
|
if !bb.is_terminated() {
|
|
|
|
|
|
bb.set_terminator(MirInstruction::Jump { target: merge_bb });
|
|
|
|
|
|
}
|
2025-09-14 23:22:05 +09:00
|
|
|
|
}
|
|
|
|
|
|
(eend, ev)
|
|
|
|
|
|
} else {
|
|
|
|
|
|
// No else: empty path falls through with base vars
|
|
|
|
|
|
if let Some(bb) = f.get_block_mut(else_bb) {
|
|
|
|
|
|
bb.set_terminator(MirInstruction::Jump { target: merge_bb });
|
|
|
|
|
|
}
|
|
|
|
|
|
(else_bb, base_vars.clone())
|
|
|
|
|
|
};
|
|
|
|
|
|
|
2025-09-16 23:49:36 +09:00
|
|
|
|
// Merge at then/else predecessors(PHI or edge-copy)
|
2025-09-14 23:22:05 +09:00
|
|
|
|
use std::collections::HashSet;
|
2025-09-16 23:49:36 +09:00
|
|
|
|
let no_phi = crate::config::env::mir_no_phi();
|
2025-09-14 23:22:05 +09:00
|
|
|
|
let mut names: HashSet<String> = base_vars.keys().cloned().collect();
|
|
|
|
|
|
for k in then_vars.keys() { names.insert(k.clone()); }
|
|
|
|
|
|
for k in else_vars.keys() { names.insert(k.clone()); }
|
|
|
|
|
|
|
|
|
|
|
|
for name in names {
|
|
|
|
|
|
let tv = then_vars.get(&name).copied();
|
|
|
|
|
|
let ev = else_vars.get(&name).copied();
|
|
|
|
|
|
let exists_base = base_vars.contains_key(&name);
|
|
|
|
|
|
match (tv, ev, exists_base) {
|
2025-09-16 23:49:36 +09:00
|
|
|
|
(Some(tval), Some(eval), _) => {
|
|
|
|
|
|
let merged = if tval == eval { tval } else {
|
2025-09-14 23:22:05 +09:00
|
|
|
|
let dst = f.next_value_id();
|
2025-09-16 23:49:36 +09:00
|
|
|
|
if no_phi {
|
|
|
|
|
|
if let Some(bb) = f.get_block_mut(tend) { bb.add_instruction(MirInstruction::Copy { dst, src: tval }); }
|
|
|
|
|
|
if let Some(bb) = f.get_block_mut(else_end_pred) { bb.add_instruction(MirInstruction::Copy { dst, src: eval }); }
|
|
|
|
|
|
} else if let Some(bb) = f.get_block_mut(merge_bb) {
|
2025-09-14 23:22:05 +09:00
|
|
|
|
bb.insert_instruction_after_phis(MirInstruction::Phi { dst, inputs: vec![(tend, tval), (else_end_pred, eval)] });
|
|
|
|
|
|
}
|
|
|
|
|
|
dst
|
|
|
|
|
|
};
|
|
|
|
|
|
vars.insert(name, merged);
|
|
|
|
|
|
}
|
|
|
|
|
|
(Some(tval), None, true) => {
|
|
|
|
|
|
if let Some(&bval) = base_vars.get(&name) {
|
|
|
|
|
|
let merged = if tval == bval { tval } else {
|
|
|
|
|
|
let dst = f.next_value_id();
|
2025-09-16 23:49:36 +09:00
|
|
|
|
if no_phi {
|
|
|
|
|
|
if let Some(bb) = f.get_block_mut(tend) { bb.add_instruction(MirInstruction::Copy { dst, src: tval }); }
|
|
|
|
|
|
if let Some(bb) = f.get_block_mut(else_end_pred) { bb.add_instruction(MirInstruction::Copy { dst, src: bval }); }
|
|
|
|
|
|
} else if let Some(bb) = f.get_block_mut(merge_bb) {
|
2025-09-14 23:22:05 +09:00
|
|
|
|
bb.insert_instruction_after_phis(MirInstruction::Phi { dst, inputs: vec![(tend, tval), (else_end_pred, bval)] });
|
|
|
|
|
|
}
|
|
|
|
|
|
dst
|
|
|
|
|
|
};
|
|
|
|
|
|
vars.insert(name, merged);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
(None, Some(eval), true) => {
|
|
|
|
|
|
if let Some(&bval) = base_vars.get(&name) {
|
|
|
|
|
|
let merged = if eval == bval { eval } else {
|
|
|
|
|
|
let dst = f.next_value_id();
|
2025-09-16 23:49:36 +09:00
|
|
|
|
if no_phi {
|
|
|
|
|
|
if let Some(bb) = f.get_block_mut(tend) { bb.add_instruction(MirInstruction::Copy { dst, src: bval }); }
|
|
|
|
|
|
if let Some(bb) = f.get_block_mut(else_end_pred) { bb.add_instruction(MirInstruction::Copy { dst, src: eval }); }
|
|
|
|
|
|
} else if let Some(bb) = f.get_block_mut(merge_bb) {
|
2025-09-14 23:22:05 +09:00
|
|
|
|
bb.insert_instruction_after_phis(MirInstruction::Phi { dst, inputs: vec![(tend, bval), (else_end_pred, eval)] });
|
|
|
|
|
|
}
|
|
|
|
|
|
dst
|
|
|
|
|
|
};
|
|
|
|
|
|
vars.insert(name, merged);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
_ => {}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
Ok(merge_bb)
|
|
|
|
|
|
}
|
|
|
|
|
|
StmtV0::Loop { cond, body } => {
|
|
|
|
|
|
// Create loop blocks
|
|
|
|
|
|
let cond_bb = next_block_id(f);
|
|
|
|
|
|
let body_bb = BasicBlockId::new(cond_bb.0 + 1);
|
|
|
|
|
|
let exit_bb = BasicBlockId::new(cond_bb.0 + 2);
|
|
|
|
|
|
f.add_block(crate::mir::BasicBlock::new(cond_bb));
|
|
|
|
|
|
f.add_block(crate::mir::BasicBlock::new(body_bb));
|
|
|
|
|
|
f.add_block(crate::mir::BasicBlock::new(exit_bb));
|
|
|
|
|
|
|
|
|
|
|
|
// Preheader jump into cond
|
|
|
|
|
|
if let Some(bb) = f.get_block_mut(cur_bb) {
|
2025-09-16 23:49:36 +09:00
|
|
|
|
if !bb.is_terminated() {
|
|
|
|
|
|
bb.add_instruction(MirInstruction::Jump { target: cond_bb });
|
|
|
|
|
|
}
|
2025-09-14 23:22:05 +09:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-09-16 23:49:36 +09:00
|
|
|
|
// Snapshot base vars and set up merged ids for loop-carried vars
|
|
|
|
|
|
let no_phi = crate::config::env::mir_no_phi();
|
2025-09-14 23:22:05 +09:00
|
|
|
|
let base_vars = vars.clone();
|
|
|
|
|
|
let orig_names: Vec<String> = base_vars.keys().cloned().collect();
|
2025-09-16 23:49:36 +09:00
|
|
|
|
let mut phi_map: std::collections::HashMap<String, crate::mir::ValueId> =
|
|
|
|
|
|
std::collections::HashMap::new();
|
2025-09-14 23:22:05 +09:00
|
|
|
|
for name in &orig_names {
|
|
|
|
|
|
if let Some(&bval) = base_vars.get(name) {
|
|
|
|
|
|
let dst = f.next_value_id();
|
2025-09-16 23:49:36 +09:00
|
|
|
|
if no_phi {
|
|
|
|
|
|
// Preheader edge-copy(cur_bb -> cond)
|
|
|
|
|
|
if let Some(bb) = f.get_block_mut(cur_bb) {
|
|
|
|
|
|
bb.add_instruction(MirInstruction::Copy { dst, src: bval });
|
|
|
|
|
|
}
|
|
|
|
|
|
} else if let Some(bb) = f.get_block_mut(cond_bb) {
|
|
|
|
|
|
// Initial incoming from preheader via PHI
|
2025-09-14 23:22:05 +09:00
|
|
|
|
bb.insert_instruction_after_phis(MirInstruction::Phi { dst, inputs: vec![(cur_bb, bval)] });
|
|
|
|
|
|
}
|
|
|
|
|
|
phi_map.insert(name.clone(), dst);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
// Redirect current vars to PHIs for use in cond/body
|
2025-09-16 23:49:36 +09:00
|
|
|
|
for (name, &phi) in &phi_map {
|
|
|
|
|
|
vars.insert(name.clone(), phi);
|
|
|
|
|
|
}
|
2025-09-14 23:22:05 +09:00
|
|
|
|
|
|
|
|
|
|
// Lower condition using phi-backed vars
|
|
|
|
|
|
let (cval, _cend) = lower_expr_with_vars(f, cond_bb, cond, vars)?;
|
|
|
|
|
|
if let Some(bb) = f.get_block_mut(cond_bb) {
|
2025-09-16 23:49:36 +09:00
|
|
|
|
bb.set_terminator(MirInstruction::Branch {
|
|
|
|
|
|
condition: cval,
|
|
|
|
|
|
then_bb: body_bb,
|
|
|
|
|
|
else_bb: exit_bb,
|
|
|
|
|
|
});
|
2025-09-14 23:22:05 +09:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Lower body; record end block and body-out vars
|
|
|
|
|
|
let mut body_vars = vars.clone();
|
2025-09-16 23:49:36 +09:00
|
|
|
|
loop_stack.push(LoopContext { cond_bb, exit_bb });
|
|
|
|
|
|
let bend_res = lower_stmt_list_with_vars(f, body_bb, body, &mut body_vars, loop_stack);
|
|
|
|
|
|
loop_stack.pop();
|
|
|
|
|
|
let bend = bend_res?;
|
2025-09-14 23:22:05 +09:00
|
|
|
|
if let Some(bb) = f.get_block_mut(bend) {
|
2025-09-16 23:49:36 +09:00
|
|
|
|
if !bb.is_terminated() {
|
|
|
|
|
|
bb.set_terminator(MirInstruction::Jump { target: cond_bb });
|
|
|
|
|
|
}
|
2025-09-14 23:22:05 +09:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-09-16 23:49:36 +09:00
|
|
|
|
// Wire second incoming from latch (body end)
|
|
|
|
|
|
let backedge_to_cond = matches!(
|
|
|
|
|
|
f.blocks.get(&bend).and_then(|bb| bb.terminator.as_ref()),
|
|
|
|
|
|
Some(MirInstruction::Jump { target, .. }) if *target == cond_bb
|
|
|
|
|
|
);
|
|
|
|
|
|
if backedge_to_cond {
|
|
|
|
|
|
if no_phi {
|
|
|
|
|
|
// Latch edge-copy(bend -> cond)
|
|
|
|
|
|
for (name, &phi_dst) in &phi_map {
|
|
|
|
|
|
if let Some(&latch_val) = body_vars.get(name) {
|
|
|
|
|
|
if let Some(bb) = f.get_block_mut(bend) {
|
|
|
|
|
|
bb.add_instruction(MirInstruction::Copy { dst: phi_dst, src: latch_val });
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
} else if let Some(bb) = f.get_block_mut(cond_bb) {
|
|
|
|
|
|
for (name, &phi_dst) in &phi_map {
|
|
|
|
|
|
if let Some(&latch_val) = body_vars.get(name) {
|
|
|
|
|
|
for inst in &mut bb.instructions {
|
|
|
|
|
|
if let MirInstruction::Phi { dst, inputs } = inst {
|
|
|
|
|
|
if *dst == phi_dst {
|
|
|
|
|
|
inputs.push((bend, latch_val));
|
|
|
|
|
|
break;
|
|
|
|
|
|
}
|
2025-09-14 23:22:05 +09:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2025-09-16 23:49:36 +09:00
|
|
|
|
} else if std::env::var("NYASH_CLI_VERBOSE").ok().as_deref() == Some("1") {
|
|
|
|
|
|
eprintln!(
|
|
|
|
|
|
"[bridge/loop] skipped latch bb{} -> cond bb{}",
|
|
|
|
|
|
bend.0, cond_bb.0
|
|
|
|
|
|
);
|
2025-09-14 23:22:05 +09:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// After the loop, keep vars mapped to the PHI values (current loop state)
|
2025-09-16 23:49:36 +09:00
|
|
|
|
for (name, &phi) in &phi_map {
|
|
|
|
|
|
vars.insert(name.clone(), phi);
|
|
|
|
|
|
}
|
2025-09-14 23:22:05 +09:00
|
|
|
|
|
|
|
|
|
|
Ok(exit_bb)
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
fn lower_stmt_list_with_vars(
|
|
|
|
|
|
f: &mut MirFunction,
|
|
|
|
|
|
start_bb: BasicBlockId,
|
|
|
|
|
|
stmts: &[StmtV0],
|
|
|
|
|
|
vars: &mut std::collections::HashMap<String, crate::mir::ValueId>,
|
2025-09-16 23:49:36 +09:00
|
|
|
|
loop_stack: &mut Vec<LoopContext>,
|
2025-09-14 23:22:05 +09:00
|
|
|
|
) -> Result<BasicBlockId, String> {
|
|
|
|
|
|
let mut cur = start_bb;
|
|
|
|
|
|
for s in stmts {
|
2025-09-16 23:49:36 +09:00
|
|
|
|
cur = lower_stmt_with_vars(f, cur, s, vars, loop_stack)?;
|
|
|
|
|
|
if let Some(bb) = f.blocks.get(&cur) {
|
|
|
|
|
|
if bb.is_terminated() {
|
|
|
|
|
|
break;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2025-09-14 23:22:05 +09:00
|
|
|
|
}
|
|
|
|
|
|
Ok(cur)
|
|
|
|
|
|
}
|
|
|
|
|
|
fn lower_args_with_vars(
|
|
|
|
|
|
f: &mut MirFunction,
|
|
|
|
|
|
cur_bb: BasicBlockId,
|
|
|
|
|
|
args: &[ExprV0],
|
|
|
|
|
|
vars: &mut std::collections::HashMap<String, crate::mir::ValueId>,
|
|
|
|
|
|
) -> Result<(Vec<crate::mir::ValueId>, BasicBlockId), String> {
|
|
|
|
|
|
let mut out = Vec::with_capacity(args.len());
|
|
|
|
|
|
let mut cur = cur_bb;
|
|
|
|
|
|
for a in args {
|
2025-09-16 23:49:36 +09:00
|
|
|
|
let (v, c) = lower_expr_with_vars(f, cur, a, vars)?;
|
|
|
|
|
|
out.push(v);
|
|
|
|
|
|
cur = c;
|
2025-09-07 07:36:15 +09:00
|
|
|
|
}
|
2025-09-14 23:22:05 +09:00
|
|
|
|
Ok((out, cur))
|
2025-09-07 07:36:15 +09:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-09-16 23:49:36 +09:00
|
|
|
|
fn lower_args(
|
|
|
|
|
|
f: &mut MirFunction,
|
|
|
|
|
|
cur_bb: BasicBlockId,
|
|
|
|
|
|
args: &[ExprV0],
|
|
|
|
|
|
) -> Result<(Vec<crate::mir::ValueId>, BasicBlockId), String> {
|
2025-09-07 07:36:15 +09:00
|
|
|
|
let mut out = Vec::with_capacity(args.len());
|
|
|
|
|
|
let mut cur = cur_bb;
|
|
|
|
|
|
for a in args {
|
2025-09-16 23:49:36 +09:00
|
|
|
|
let (v, c) = lower_expr(f, cur, a)?;
|
|
|
|
|
|
out.push(v);
|
|
|
|
|
|
cur = c;
|
2025-09-05 13:29:17 +09:00
|
|
|
|
}
|
2025-09-07 07:36:15 +09:00
|
|
|
|
Ok((out, cur))
|
2025-09-05 13:29:17 +09:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
pub fn maybe_dump_mir(module: &MirModule) {
|
|
|
|
|
|
if std::env::var("NYASH_CLI_VERBOSE").ok().as_deref() == Some("1") {
|
2025-09-16 04:23:20 +09:00
|
|
|
|
let p = MirPrinter::new();
|
2025-09-05 13:29:17 +09:00
|
|
|
|
println!("{}", p.print_module(module));
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-09-16 23:49:36 +09:00
|
|
|
|
#[cfg(test)]
|
|
|
|
|
|
mod tests {
|
|
|
|
|
|
use super::parse_json_v0_to_module;
|
|
|
|
|
|
use crate::mir::{BasicBlockId, MirInstruction, MirModule};
|
|
|
|
|
|
|
|
|
|
|
|
fn block_terminator(module: &MirModule, block: BasicBlockId) -> MirInstruction {
|
|
|
|
|
|
module
|
|
|
|
|
|
.get_function("main")
|
|
|
|
|
|
.unwrap()
|
|
|
|
|
|
.get_block(block)
|
|
|
|
|
|
.and_then(|bb| bb.terminator.clone())
|
|
|
|
|
|
.expect("terminator")
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
|
fn stage3_break_jumps_to_exit() {
|
|
|
|
|
|
let json = "{\"version\":0,\"kind\":\"Program\",\"body\":[{\"type\":\"Loop\",\"cond\":{\"type\":\"Bool\",\"value\":true},\"body\":[{\"type\":\"Break\"}]},{\"type\":\"Return\",\"expr\":{\"type\":\"Int\",\"value\":0}}]}";
|
|
|
|
|
|
let module = parse_json_v0_to_module(json).unwrap();
|
|
|
|
|
|
match block_terminator(&module, BasicBlockId::new(2)) {
|
|
|
|
|
|
MirInstruction::Jump { target, .. } => assert_eq!(target, BasicBlockId::new(3)),
|
|
|
|
|
|
other => panic!("expected jump, got {:?}", other),
|
|
|
|
|
|
}
|
|
|
|
|
|
module.verify().unwrap();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
|
fn stage3_continue_jumps_to_head() {
|
|
|
|
|
|
let json = "{\"version\":0,\"kind\":\"Program\",\"body\":[{\"type\":\"Loop\",\"cond\":{\"type\":\"Bool\",\"value\":true},\"body\":[{\"type\":\"Continue\"}]},{\"type\":\"Return\",\"expr\":{\"type\":\"Int\",\"value\":0}}]}";
|
|
|
|
|
|
let module = parse_json_v0_to_module(json).unwrap();
|
|
|
|
|
|
match block_terminator(&module, BasicBlockId::new(2)) {
|
|
|
|
|
|
MirInstruction::Jump { target, .. } => assert_eq!(target, BasicBlockId::new(1)),
|
|
|
|
|
|
other => panic!("expected jump, got {:?}", other),
|
|
|
|
|
|
}
|
|
|
|
|
|
module.verify().unwrap();
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-09-05 13:29:17 +09:00
|
|
|
|
// ========== Direct bridge (source → JSON v0 → MIR) ==========
|
|
|
|
|
|
|
|
|
|
|
|
#[derive(Clone, Debug)]
|
|
|
|
|
|
enum Tok {
|
|
|
|
|
|
Return,
|
|
|
|
|
|
Int(i64),
|
|
|
|
|
|
Plus,
|
|
|
|
|
|
Minus,
|
|
|
|
|
|
Star,
|
|
|
|
|
|
Slash,
|
|
|
|
|
|
LParen,
|
|
|
|
|
|
RParen,
|
|
|
|
|
|
Eof,
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
fn lex(input: &str) -> Result<Vec<Tok>, String> {
|
|
|
|
|
|
let bytes = input.as_bytes();
|
|
|
|
|
|
let mut i = 0usize;
|
|
|
|
|
|
let n = bytes.len();
|
|
|
|
|
|
let mut toks = Vec::new();
|
|
|
|
|
|
while i < n {
|
|
|
|
|
|
let c = bytes[i] as char;
|
2025-09-15 18:44:49 +09:00
|
|
|
|
// Treat semicolon as whitespace (Stage-1 minimal ASI: optional ';')
|
2025-09-16 23:49:36 +09:00
|
|
|
|
if c.is_whitespace() || c == ';' {
|
|
|
|
|
|
i += 1;
|
|
|
|
|
|
continue;
|
|
|
|
|
|
}
|
2025-09-05 13:29:17 +09:00
|
|
|
|
match c {
|
2025-09-16 23:49:36 +09:00
|
|
|
|
'+' => {
|
|
|
|
|
|
toks.push(Tok::Plus);
|
|
|
|
|
|
i += 1;
|
|
|
|
|
|
}
|
|
|
|
|
|
'-' => {
|
|
|
|
|
|
toks.push(Tok::Minus);
|
|
|
|
|
|
i += 1;
|
|
|
|
|
|
}
|
|
|
|
|
|
'*' => {
|
|
|
|
|
|
toks.push(Tok::Star);
|
|
|
|
|
|
i += 1;
|
|
|
|
|
|
}
|
|
|
|
|
|
'/' => {
|
|
|
|
|
|
toks.push(Tok::Slash);
|
|
|
|
|
|
i += 1;
|
|
|
|
|
|
}
|
|
|
|
|
|
'(' => {
|
|
|
|
|
|
toks.push(Tok::LParen);
|
|
|
|
|
|
i += 1;
|
|
|
|
|
|
}
|
|
|
|
|
|
')' => {
|
|
|
|
|
|
toks.push(Tok::RParen);
|
|
|
|
|
|
i += 1;
|
|
|
|
|
|
}
|
2025-09-05 13:29:17 +09:00
|
|
|
|
'0'..='9' => {
|
2025-09-16 23:49:36 +09:00
|
|
|
|
let start = i;
|
|
|
|
|
|
while i < n {
|
|
|
|
|
|
let cc = bytes[i] as char;
|
|
|
|
|
|
if cc.is_ascii_digit() {
|
|
|
|
|
|
i += 1;
|
|
|
|
|
|
} else {
|
|
|
|
|
|
break;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2025-09-05 13:29:17 +09:00
|
|
|
|
let s = std::str::from_utf8(&bytes[start..i]).unwrap();
|
|
|
|
|
|
let v: i64 = s.parse().map_err(|_| "invalid int")?;
|
|
|
|
|
|
toks.push(Tok::Int(v));
|
|
|
|
|
|
}
|
|
|
|
|
|
'r' => {
|
|
|
|
|
|
// return
|
2025-09-16 23:49:36 +09:00
|
|
|
|
if i + 6 <= n && &input[i..i + 6] == "return" {
|
|
|
|
|
|
toks.push(Tok::Return);
|
|
|
|
|
|
i += 6;
|
|
|
|
|
|
} else {
|
|
|
|
|
|
return Err("unexpected 'r'".into());
|
|
|
|
|
|
}
|
2025-09-05 13:29:17 +09:00
|
|
|
|
}
|
|
|
|
|
|
_ => return Err(format!("unexpected char '{}'", c)),
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
toks.push(Tok::Eof);
|
|
|
|
|
|
Ok(toks)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-09-16 23:49:36 +09:00
|
|
|
|
struct P {
|
|
|
|
|
|
toks: Vec<Tok>,
|
|
|
|
|
|
pos: usize,
|
|
|
|
|
|
}
|
2025-09-05 13:29:17 +09:00
|
|
|
|
impl P {
|
2025-09-16 23:49:36 +09:00
|
|
|
|
fn new(toks: Vec<Tok>) -> Self {
|
|
|
|
|
|
Self { toks, pos: 0 }
|
|
|
|
|
|
}
|
|
|
|
|
|
fn peek(&self) -> &Tok {
|
|
|
|
|
|
self.toks.get(self.pos).unwrap()
|
|
|
|
|
|
}
|
|
|
|
|
|
fn next(&mut self) -> Tok {
|
|
|
|
|
|
let t = self.toks.get(self.pos).unwrap().clone();
|
|
|
|
|
|
self.pos += 1;
|
|
|
|
|
|
t
|
|
|
|
|
|
}
|
|
|
|
|
|
fn expect_return(&mut self) -> Result<(), String> {
|
|
|
|
|
|
match self.next() {
|
|
|
|
|
|
Tok::Return => Ok(()),
|
|
|
|
|
|
_ => Err("expected 'return'".into()),
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
fn parse_program(&mut self) -> Result<ExprV0, String> {
|
|
|
|
|
|
self.expect_return()?;
|
|
|
|
|
|
self.parse_expr()
|
|
|
|
|
|
}
|
|
|
|
|
|
fn parse_expr(&mut self) -> Result<ExprV0, String> {
|
2025-09-05 13:29:17 +09:00
|
|
|
|
let mut left = self.parse_term()?;
|
2025-09-16 23:49:36 +09:00
|
|
|
|
loop {
|
|
|
|
|
|
match self.peek() {
|
|
|
|
|
|
Tok::Plus => {
|
|
|
|
|
|
self.next();
|
|
|
|
|
|
let r = self.parse_term()?;
|
|
|
|
|
|
left = ExprV0::Binary {
|
|
|
|
|
|
op: "+".into(),
|
|
|
|
|
|
lhs: Box::new(left),
|
|
|
|
|
|
rhs: Box::new(r),
|
|
|
|
|
|
};
|
|
|
|
|
|
}
|
|
|
|
|
|
Tok::Minus => {
|
|
|
|
|
|
self.next();
|
|
|
|
|
|
let r = self.parse_term()?;
|
|
|
|
|
|
left = ExprV0::Binary {
|
|
|
|
|
|
op: "-".into(),
|
|
|
|
|
|
lhs: Box::new(left),
|
|
|
|
|
|
rhs: Box::new(r),
|
|
|
|
|
|
};
|
|
|
|
|
|
}
|
|
|
|
|
|
_ => break,
|
|
|
|
|
|
}
|
2025-09-05 13:29:17 +09:00
|
|
|
|
}
|
|
|
|
|
|
Ok(left)
|
|
|
|
|
|
}
|
2025-09-16 23:49:36 +09:00
|
|
|
|
fn parse_term(&mut self) -> Result<ExprV0, String> {
|
2025-09-05 13:29:17 +09:00
|
|
|
|
let mut left = self.parse_factor()?;
|
2025-09-16 23:49:36 +09:00
|
|
|
|
loop {
|
|
|
|
|
|
match self.peek() {
|
|
|
|
|
|
Tok::Star => {
|
|
|
|
|
|
self.next();
|
|
|
|
|
|
let r = self.parse_factor()?;
|
|
|
|
|
|
left = ExprV0::Binary {
|
|
|
|
|
|
op: "*".into(),
|
|
|
|
|
|
lhs: Box::new(left),
|
|
|
|
|
|
rhs: Box::new(r),
|
|
|
|
|
|
};
|
|
|
|
|
|
}
|
|
|
|
|
|
Tok::Slash => {
|
|
|
|
|
|
self.next();
|
|
|
|
|
|
let r = self.parse_factor()?;
|
|
|
|
|
|
left = ExprV0::Binary {
|
|
|
|
|
|
op: "/".into(),
|
|
|
|
|
|
lhs: Box::new(left),
|
|
|
|
|
|
rhs: Box::new(r),
|
|
|
|
|
|
};
|
|
|
|
|
|
}
|
|
|
|
|
|
_ => break,
|
|
|
|
|
|
}
|
2025-09-05 13:29:17 +09:00
|
|
|
|
}
|
|
|
|
|
|
Ok(left)
|
|
|
|
|
|
}
|
2025-09-16 23:49:36 +09:00
|
|
|
|
fn parse_factor(&mut self) -> Result<ExprV0, String> {
|
2025-09-05 13:29:17 +09:00
|
|
|
|
match self.next() {
|
2025-09-16 23:49:36 +09:00
|
|
|
|
Tok::Int(v) => Ok(ExprV0::Int {
|
|
|
|
|
|
value: serde_json::Value::from(v),
|
|
|
|
|
|
}),
|
|
|
|
|
|
Tok::LParen => {
|
|
|
|
|
|
let e = self.parse_expr()?;
|
|
|
|
|
|
match self.next() {
|
|
|
|
|
|
Tok::RParen => Ok(e),
|
|
|
|
|
|
_ => Err(") expected".into()),
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2025-09-05 13:29:17 +09:00
|
|
|
|
_ => Err("factor expected".into()),
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
pub fn parse_source_v0_to_json(input: &str) -> Result<String, String> {
|
2025-09-16 23:49:36 +09:00
|
|
|
|
let toks = lex(input)?;
|
|
|
|
|
|
let mut p = P::new(toks);
|
2025-09-05 13:29:17 +09:00
|
|
|
|
let expr = p.parse_program()?;
|
2025-09-16 23:49:36 +09:00
|
|
|
|
let prog = ProgramV0 {
|
|
|
|
|
|
version: 0,
|
|
|
|
|
|
kind: "Program".into(),
|
|
|
|
|
|
body: vec![StmtV0::Return { expr }],
|
|
|
|
|
|
};
|
2025-09-05 13:29:17 +09:00
|
|
|
|
serde_json::to_string(&prog).map_err(|e| e.to_string())
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
pub fn parse_source_v0_to_module(input: &str) -> Result<MirModule, String> {
|
|
|
|
|
|
let json = parse_source_v0_to_json(input)?;
|
2025-09-16 23:49:36 +09:00
|
|
|
|
if std::env::var("NYASH_DUMP_JSON_IR").ok().as_deref() == Some("1") {
|
|
|
|
|
|
println!("{}", json);
|
|
|
|
|
|
}
|
2025-09-05 13:29:17 +09:00
|
|
|
|
parse_json_v0_to_module(&json)
|
|
|
|
|
|
}
|