refactor: extract helpers (builder_calls: math/env/me; json_v0_bridge: new_block/merge_values) — non-functional cleanup; cargo check + smokes green

This commit is contained in:
Selfhosting Dev
2025-09-17 08:20:44 +09:00
parent a3282abdd9
commit 31f90012e0
2 changed files with 141 additions and 250 deletions

View File

@ -4,6 +4,111 @@ use crate::ast::{ASTNode, LiteralValue, MethodCallExpr};
use crate::mir::{slot_registry, TypeOpKind};
impl super::MirBuilder {
/// Try handle math.* function in function-style (sin/cos/abs/min/max).
/// Returns Some(result) if handled, otherwise None.
fn try_handle_math_function(
&mut self,
name: &str,
raw_args: Vec<ASTNode>,
) -> Option<Result<ValueId, String>> {
let is_math_func = matches!(name, "sin" | "cos" | "abs" | "min" | "max");
if !is_math_func {
return None;
}
// Build numeric args directly for math.* to preserve f64 typing
let mut math_args: Vec<ValueId> = Vec::new();
for a in raw_args.into_iter() {
match a {
ASTNode::New { class, arguments, .. } if class == "FloatBox" && arguments.len() == 1 => {
match self.build_expression(arguments[0].clone()) { v @ Ok(_) => math_args.push(v.unwrap()), err @ Err(_) => return Some(err), }
}
ASTNode::New { class, arguments, .. } if class == "IntegerBox" && arguments.len() == 1 => {
let iv = match self.build_expression(arguments[0].clone()) { Ok(v) => v, Err(e) => return Some(Err(e)) };
let fv = self.value_gen.next();
if let Err(e) = self.emit_instruction(MirInstruction::TypeOp { dst: fv, op: TypeOpKind::Cast, value: iv, ty: MirType::Float }) { return Some(Err(e)); }
math_args.push(fv);
}
ASTNode::Literal { value: LiteralValue::Float(_), .. } => {
match self.build_expression(a) { v @ Ok(_) => math_args.push(v.unwrap()), err @ Err(_) => return Some(err), }
}
other => {
match self.build_expression(other) { v @ Ok(_) => math_args.push(v.unwrap()), err @ Err(_) => return Some(err), }
}
}
}
// new MathBox()
let math_recv = self.value_gen.next();
if let Err(e) = self.emit_instruction(MirInstruction::NewBox { dst: math_recv, box_type: "MathBox".to_string(), args: vec![] }) { return Some(Err(e)); }
self.value_origin_newbox.insert(math_recv, "MathBox".to_string());
// birth()
let birt_mid = slot_registry::resolve_slot_by_type_name("MathBox", "birth");
if let Err(e) = self.emit_box_or_plugin_call(None, math_recv, "birth".to_string(), birt_mid, vec![], EffectMask::READ) { return Some(Err(e)); }
// call method
let dst = self.value_gen.next();
if let Err(e) = self.emit_box_or_plugin_call(Some(dst), math_recv, name.to_string(), None, math_args, EffectMask::READ) { return Some(Err(e)); }
Some(Ok(dst))
}
/// Try handle env.* extern methods like env.console.log via FieldAccess(object, field).
fn try_handle_env_method(
&mut self,
object: &ASTNode,
method: &str,
arguments: &Vec<ASTNode>,
) -> Option<Result<ValueId, String>> {
let ASTNode::FieldAccess { object: env_obj, field: env_field, .. } = object else { return None; };
if let ASTNode::Variable { name: env_name, .. } = env_obj.as_ref() {
if env_name != "env" { return None; }
// Build arguments once
let mut arg_values = Vec::new();
for arg in arguments {
match self.build_expression(arg.clone()) { Ok(v) => arg_values.push(v), Err(e) => return Some(Err(e)) }
}
let iface = env_field.as_str();
let m = method;
let mut extern_call = |iface_name: &str, method_name: &str, effects: EffectMask, returns: bool| -> Result<ValueId, String> {
let result_id = self.value_gen.next();
self.emit_instruction(MirInstruction::ExternCall { dst: if returns { Some(result_id) } else { None }, iface_name: iface_name.to_string(), method_name: method_name.to_string(), args: arg_values.clone(), effects })?;
if returns {
Ok(result_id)
} else {
let void_id = self.value_gen.next();
self.emit_instruction(MirInstruction::Const { dst: void_id, value: super::ConstValue::Void })?;
Ok(void_id)
}
};
return Some(match (iface, m) {
("future", "delay") => extern_call("env.future", "delay", EffectMask::READ.add(Effect::Io), true),
("task", "currentToken") => extern_call("env.task", "currentToken", EffectMask::READ, true),
("task", "cancelCurrent") => extern_call("env.task", "cancelCurrent", EffectMask::IO, false),
("console", "log") => extern_call("env.console", "log", EffectMask::IO, false),
("console", "readLine") => extern_call("env.console", "readLine", EffectMask::IO, true),
("canvas", m) if matches!(m, "fillRect" | "fillText") => extern_call("env.canvas", m, EffectMask::IO, false),
_ => return None,
});
}
None
}
/// Try direct static call for `me` in static box
fn try_handle_me_direct_call(
&mut self,
method: &str,
arguments: &Vec<ASTNode>,
) -> Option<Result<ValueId, String>> {
let Some(cls_name) = self.current_static_box.clone() else { return None; };
// Build args
let mut arg_values = Vec::new();
for a in arguments {
match self.build_expression(a.clone()) { Ok(v) => arg_values.push(v), Err(e) => return Some(Err(e)) }
}
let result_id = self.value_gen.next();
let fun_name = format!("{}.{}{}", cls_name, method, format!("/{}", arg_values.len()));
let fun_val = self.value_gen.next();
if let Err(e) = self.emit_instruction(MirInstruction::Const { dst: fun_val, value: super::ConstValue::String(fun_name) }) { return Some(Err(e)); }
if let Err(e) = self.emit_instruction(MirInstruction::Call { dst: Some(result_id), func: fun_val, args: arg_values, effects: EffectMask::READ.add(Effect::ReadHeap) }) { return Some(Err(e)); }
Some(Ok(result_id))
}
// Build function call: name(args)
pub(super) fn build_function_call(
&mut self,
@ -33,77 +138,9 @@ impl super::MirBuilder {
// Keep original args for special handling (math.*)
let raw_args = args.clone();
let dst = self.value_gen.next();
if let Some(res) = self.try_handle_math_function(&name, raw_args) { return res; }
// Special-case: math.* as function-style (sin/cos/abs/min/max)
let is_math_func = matches!(name.as_str(), "sin" | "cos" | "abs" | "min" | "max");
if is_math_func {
// Build numeric args directly for math.* to preserve f64 typing
let mut math_args: Vec<ValueId> = Vec::new();
for a in raw_args.into_iter() {
match a {
ASTNode::New {
class, arguments, ..
} if class == "FloatBox" && arguments.len() == 1 => {
let v = self.build_expression(arguments[0].clone())?;
math_args.push(v);
}
ASTNode::New {
class, arguments, ..
} if class == "IntegerBox" && arguments.len() == 1 => {
let iv = self.build_expression(arguments[0].clone())?;
let fv = self.value_gen.next();
self.emit_instruction(MirInstruction::TypeOp {
dst: fv,
op: TypeOpKind::Cast,
value: iv,
ty: MirType::Float,
})?;
math_args.push(fv);
}
ASTNode::Literal {
value: LiteralValue::Float(_),
..
} => {
let v = self.build_expression(a)?;
math_args.push(v);
}
other => {
let v = self.build_expression(other)?;
math_args.push(v);
}
}
}
// new MathBox()
let math_recv = self.value_gen.next();
self.emit_instruction(MirInstruction::NewBox {
dst: math_recv,
box_type: "MathBox".to_string(),
args: vec![],
})?;
self.value_origin_newbox
.insert(math_recv, "MathBox".to_string());
// birth()
let birt_mid = slot_registry::resolve_slot_by_type_name("MathBox", "birth");
self.emit_box_or_plugin_call(
None,
math_recv,
"birth".to_string(),
birt_mid,
vec![],
EffectMask::READ,
)?;
// call method
self.emit_box_or_plugin_call(
Some(dst),
math_recv,
name,
None,
math_args,
EffectMask::READ,
)?;
return Ok(dst);
}
let dst = self.value_gen.next();
// Default: call via fully-qualified function name string
let mut arg_values = Vec::new();
@ -151,130 +188,10 @@ impl super::MirBuilder {
return Ok(dst);
}
}
// ExternCall: env.X.* pattern via field access (e.g., env.future.delay)
if let ASTNode::FieldAccess {
object: env_obj,
field: env_field,
..
} = object.clone()
{
if let ASTNode::Variable { name: env_name, .. } = *env_obj {
if env_name == "env" {
let mut arg_values = Vec::new();
for arg in &arguments {
arg_values.push(self.build_expression(arg.clone())?);
}
match (env_field.as_str(), method.as_str()) {
("future", "delay") => {
let result_id = self.value_gen.next();
self.emit_instruction(MirInstruction::ExternCall {
dst: Some(result_id),
iface_name: "env.future".to_string(),
method_name: "delay".to_string(),
args: arg_values,
effects: EffectMask::READ.add(Effect::Io),
})?;
return Ok(result_id);
}
("task", "currentToken") => {
let result_id = self.value_gen.next();
self.emit_instruction(MirInstruction::ExternCall {
dst: Some(result_id),
iface_name: "env.task".to_string(),
method_name: "currentToken".to_string(),
args: arg_values,
effects: EffectMask::READ,
})?;
return Ok(result_id);
}
("task", "cancelCurrent") => {
self.emit_instruction(MirInstruction::ExternCall {
dst: None,
iface_name: "env.task".to_string(),
method_name: "cancelCurrent".to_string(),
args: arg_values,
effects: EffectMask::IO,
})?;
let void_id = self.value_gen.next();
self.emit_instruction(MirInstruction::Const {
dst: void_id,
value: super::ConstValue::Void,
})?;
return Ok(void_id);
}
("console", "log") => {
self.emit_instruction(MirInstruction::ExternCall {
dst: None,
iface_name: "env.console".to_string(),
method_name: "log".to_string(),
args: arg_values,
effects: EffectMask::IO,
})?;
let void_id = self.value_gen.next();
self.emit_instruction(MirInstruction::Const {
dst: void_id,
value: super::ConstValue::Void,
})?;
return Ok(void_id);
}
("console", "readLine") => {
let result_id = self.value_gen.next();
self.emit_instruction(MirInstruction::ExternCall {
dst: Some(result_id),
iface_name: "env.console".to_string(),
method_name: "readLine".to_string(),
args: arg_values,
effects: EffectMask::IO,
})?;
return Ok(result_id);
}
("canvas", m @ ("fillRect" | "fillText")) => {
self.emit_instruction(MirInstruction::ExternCall {
dst: None,
iface_name: "env.canvas".to_string(),
method_name: m.to_string(),
args: arg_values,
effects: EffectMask::IO,
})?;
let void_id = self.value_gen.next();
self.emit_instruction(MirInstruction::Const {
dst: void_id,
value: super::ConstValue::Void,
})?;
return Ok(void_id);
}
_ => {}
}
}
}
}
if let Some(res) = self.try_handle_env_method(&object, &method, &arguments) { return res; }
// If object is `me` within a static box, lower to direct Call: BoxName.method/N
if let ASTNode::Me { .. } = object {
if let Some(cls_name) = self.current_static_box.clone() {
let mut arg_values: Vec<ValueId> = Vec::new();
for a in &arguments {
arg_values.push(self.build_expression(a.clone())?);
}
let result_id = self.value_gen.next();
let fun_name = format!(
"{}.{}{}",
cls_name,
method,
format!("/{}", arg_values.len())
);
let fun_val = self.value_gen.next();
self.emit_instruction(MirInstruction::Const {
dst: fun_val,
value: super::ConstValue::String(fun_name),
})?;
self.emit_instruction(MirInstruction::Call {
dst: Some(result_id),
func: fun_val,
args: arg_values,
effects: EffectMask::READ.add(Effect::ReadHeap),
})?;
return Ok(result_id);
}
if let Some(res) = self.try_handle_me_direct_call(&method, &arguments) { return res; }
}
// Build the object expression (wrapper allows simple access if needed in future)
let _mc = MethodCallExpr { object: Box::new(object.clone()), method: method.clone(), arguments: arguments.clone(), span: crate::ast::Span::unknown() };

View File

@ -4,6 +4,7 @@ use crate::mir::{
MirInstruction, MirModule, MirPrinter, MirType, ValueId,
};
use std::collections::HashMap;
use std::collections::HashSet;
#[derive(Clone, Copy)]
pub(super) struct LoopContext {
@ -110,6 +111,34 @@ fn new_block(f: &mut MirFunction) -> BasicBlockId {
id
}
/// Merge two incoming values either by inserting Copy on predecessor edges
/// (no_phi mode) or by adding a Phi at the merge block head.
fn merge_values(
f: &mut MirFunction,
no_phi: bool,
merge_bb: BasicBlockId,
pred_a: BasicBlockId,
val_a: ValueId,
pred_b: BasicBlockId,
val_b: ValueId,
) -> ValueId {
if val_a == val_b {
return val_a;
}
let dst = f.next_value_id();
if no_phi {
if let Some(bb) = f.get_block_mut(pred_a) {
bb.add_instruction(MirInstruction::Copy { dst, src: val_a });
}
if let Some(bb) = f.get_block_mut(pred_b) {
bb.add_instruction(MirInstruction::Copy { dst, src: val_b });
}
} else if let Some(bb) = f.get_block_mut(merge_bb) {
bb.insert_instruction_after_phis(MirInstruction::Phi { dst, inputs: vec![(pred_a, val_a), (pred_b, val_b)] });
}
dst
}
fn lower_throw(
env: &BridgeEnv,
f: &mut MirFunction,
@ -802,7 +831,6 @@ fn lower_stmt_with_vars(
}
(else_bb, base_vars.clone())
};
use std::collections::HashSet;
let no_phi = env.mir_no_phi;
let mut names: HashSet<String> = base_vars.keys().cloned().collect();
for k in then_vars.keys() {
@ -817,72 +845,18 @@ fn lower_stmt_with_vars(
let exists_base = base_vars.contains_key(&name);
match (tv, ev, exists_base) {
(Some(tval), Some(eval), _) => {
let merged = if tval == eval {
tval
} else {
let dst = f.next_value_id();
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) {
bb.insert_instruction_after_phis(MirInstruction::Phi {
dst,
inputs: vec![(tend, tval), (else_end_pred, eval)],
});
}
dst
};
let merged = merge_values(f, no_phi, merge_bb, tend, tval, else_end_pred, eval);
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();
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) {
bb.insert_instruction_after_phis(MirInstruction::Phi {
dst,
inputs: vec![(tend, tval), (else_end_pred, bval)],
});
}
dst
};
let merged = merge_values(f, no_phi, merge_bb, tend, tval, else_end_pred, bval);
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();
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) {
bb.insert_instruction_after_phis(MirInstruction::Phi {
dst,
inputs: vec![(tend, bval), (else_end_pred, eval)],
});
}
dst
};
let merged = merge_values(f, no_phi, merge_bb, tend, bval, else_end_pred, eval);
vars.insert(name, merged);
}
}