use super::mir_json::common as mirjson_common; use crate::mir::{ function::{FunctionSignature, MirFunction, MirModule}, BasicBlock, BasicBlockId, ConstValue, EffectMask, MirInstruction, MirType, ValueId, }; use serde_json::Value; /// Parse minimal MIR JSON v0 (no schema_version, root has `functions` and each function has `blocks`). /// Supported ops (minimal): const(i64), copy, compare(op/lhs/rhs), branch(cond/then/else), jump(target), phi(dst,incoming), ret(value?). pub fn parse_mir_v0_to_module(json: &str) -> Result { let value: Value = serde_json::from_str(json).map_err(|e| format!("invalid JSON: {}", e))?; let functions = value .get("functions") .and_then(|f| f.as_array()) .ok_or_else(|| "JSON missing functions array".to_string())?; let mut module = MirModule::new("mir_json_v0".to_string()); for func in functions { let func_name = func .get("name") .and_then(|n| n.as_str()) .unwrap_or("main") .to_string(); let blocks = func .get("blocks") .and_then(|b| b.as_array()) .ok_or_else(|| format!("function '{}' missing blocks array", func_name))?; if blocks.is_empty() { return Err(format!("function '{}' has no blocks", func_name)); } let entry_id = blocks .get(0) .and_then(|b| b.get("id")) .and_then(|id| id.as_u64()) .ok_or_else(|| format!("function '{}' entry block missing id", func_name))? as u32; let entry_bb = BasicBlockId::new(entry_id); let mut signature = FunctionSignature { name: func_name.clone(), params: Vec::new(), return_type: MirType::Unknown, effects: crate::mir::EffectMask::PURE, }; let mut mir_fn = MirFunction::new(signature.clone(), entry_bb); let mut max_value_id: u32 = 0; for block in blocks { let block_id = block .get("id") .and_then(|id| id.as_u64()) .ok_or_else(|| format!("function '{}' block missing id", func_name))? as u32; let bb_id = BasicBlockId::new(block_id); if mir_fn.get_block(bb_id).is_none() { mir_fn.add_block(BasicBlock::new(bb_id)); } let block_ref = mir_fn .get_block_mut(bb_id) .expect("block must exist after insertion"); let instructions = block .get("instructions") .and_then(|insts| insts.as_array()) .ok_or_else(|| { format!( "function '{}' block {} missing instructions array", func_name, block_id ) })?; for inst in instructions { let op = inst.get("op").and_then(|o| o.as_str()).ok_or_else(|| { format!( "function '{}' block {} missing op field", func_name, block_id ) })?; match op { "const" => { let dst = require_u64(inst, "dst", "const dst")? as u32; let vobj = inst .get("value") .ok_or_else(|| "const missing value".to_string())?; let val = parse_const_value(vobj)?; block_ref.add_instruction(MirInstruction::Const { dst: ValueId::new(dst), value: val, }); max_value_id = max_value_id.max(dst + 1); } "copy" => { let dst = require_u64(inst, "dst", "copy dst")? as u32; let src = require_u64(inst, "src", "copy src")? as u32; block_ref.add_instruction(MirInstruction::Copy { dst: ValueId::new(dst), src: ValueId::new(src), }); max_value_id = max_value_id.max(dst + 1); } "binop" => { let dst = require_u64(inst, "dst", "binop dst")? as u32; let lhs = require_u64(inst, "lhs", "binop lhs")? as u32; let rhs = require_u64(inst, "rhs", "binop rhs")? as u32; let operation = inst .get("operation") .and_then(Value::as_str) .ok_or_else(|| "binop missing operation".to_string())?; let bop = parse_binop(operation)?; block_ref.add_instruction(MirInstruction::BinOp { dst: ValueId::new(dst), op: bop, lhs: ValueId::new(lhs), rhs: ValueId::new(rhs), }); max_value_id = max_value_id.max(dst + 1); } "compare" => { let dst = require_u64(inst, "dst", "compare dst")? as u32; let lhs = require_u64(inst, "lhs", "compare lhs")? as u32; let rhs = require_u64(inst, "rhs", "compare rhs")? as u32; let operation = inst .get("operation") .and_then(Value::as_str) .unwrap_or("=="); let cop = parse_compare(operation)?; block_ref.add_instruction(MirInstruction::Compare { dst: ValueId::new(dst), op: cop, lhs: ValueId::new(lhs), rhs: ValueId::new(rhs), }); max_value_id = max_value_id.max(dst + 1); } "branch" => { let cond = require_u64(inst, "cond", "branch cond")? as u32; let then_bb = require_u64(inst, "then", "branch then")? as u32; let else_bb = require_u64(inst, "else", "branch else")? as u32; block_ref.add_instruction(MirInstruction::Branch { condition: ValueId::new(cond), then_bb: BasicBlockId::new(then_bb), else_bb: BasicBlockId::new(else_bb), }); } "jump" => { let target = require_u64(inst, "target", "jump target")? as u32; block_ref.add_instruction(MirInstruction::Jump { target: BasicBlockId::new(target), }); } "phi" => { let dst = require_u64(inst, "dst", "phi dst")? as u32; let incoming = inst .get("incoming") .and_then(Value::as_array) .ok_or_else(|| "phi incoming missing".to_string())?; let mut pairs = Vec::with_capacity(incoming.len()); for entry in incoming { let pair = entry .as_array() .ok_or_else(|| "phi incoming entry must be array".to_string())?; if pair.len() != 2 { return Err("phi incoming entry must have 2 elements".into()); } let val = pair[0] .as_u64() .ok_or_else(|| "phi incoming value must be integer".to_string())? as u32; let bb = pair[1] .as_u64() .ok_or_else(|| "phi incoming block must be integer".to_string())? as u32; pairs.push((BasicBlockId::new(bb), ValueId::new(val))); } block_ref.add_instruction(MirInstruction::Phi { dst: ValueId::new(dst), inputs: pairs, }); max_value_id = max_value_id.max(dst + 1); } "ret" => { let value = inst .get("value") .and_then(|v| v.as_u64()) .map(|v| ValueId::new(v as u32)); block_ref.add_instruction(MirInstruction::Return { value }); if let Some(val) = value { signature.return_type = MirType::Integer; max_value_id = max_value_id.max(val.as_u32() + 1); } else { signature.return_type = MirType::Void; } } "newbox" => { let dst = require_u64(inst, "dst", "newbox dst")? as u32; let ty = inst .get("type") .and_then(Value::as_str) .ok_or_else(|| "newbox missing type".to_string())? .to_string(); let args_v = inst .get("args") .and_then(Value::as_array) .cloned() .unwrap_or_default(); let mut args: Vec = Vec::with_capacity(args_v.len()); for a in args_v { let id = a .as_u64() .ok_or_else(|| "newbox arg must be integer".to_string())? as u32; args.push(ValueId::new(id)); } block_ref.add_instruction(MirInstruction::NewBox { dst: ValueId::new(dst), box_type: ty, args, }); max_value_id = max_value_id.max(dst + 1); } "boxcall" => { // { op:"boxcall", box:, method:"name", args:[vid...], dst?: } let box_id = require_u64(inst, "box", "boxcall box")? as u32; let method = inst .get("method") .and_then(Value::as_str) .ok_or_else(|| "boxcall missing method".to_string())? .to_string(); let dst_opt = inst .get("dst") .and_then(Value::as_u64) .map(|v| ValueId::new(v as u32)); let args_v = inst .get("args") .and_then(Value::as_array) .cloned() .unwrap_or_default(); let mut args: Vec = Vec::with_capacity(args_v.len()); for a in args_v { let id = a .as_u64() .ok_or_else(|| "boxcall arg must be integer".to_string())? as u32; args.push(ValueId::new(id)); } block_ref.add_instruction(MirInstruction::BoxCall { dst: dst_opt, box_val: ValueId::new(box_id), method, method_id: None, args, effects: EffectMask::READ, }); if let Some(dv) = dst_opt { max_value_id = max_value_id.max(dv.as_u32() + 1); } } other => { return Err(format!("unsupported op '{}' in mir_json_v0 loader", other)); } } } } // Set max value id (best effort) mir_fn.next_value_id = max_value_id; module.functions.insert(func_name.clone(), mir_fn); } Ok(module) } fn require_u64(node: &Value, key: &str, context: &str) -> Result { node.get(key) .and_then(Value::as_u64) .ok_or_else(|| format!("{} missing field '{}'", context, key)) } fn parse_const_value(value_obj: &Value) -> Result { // Delegate to common generic parser (int/float/bool/string/handle(StringBox)) // Keeps behavior superset of previous (int-only) without changing existing callers. mirjson_common::parse_const_value_generic(value_obj).map_err(|e| format!("{}", e)) } fn parse_compare(op: &str) -> Result { use crate::mir::types::CompareOp; Ok(match op { "==" => CompareOp::Eq, "!=" => CompareOp::Ne, "<" => CompareOp::Lt, "<=" => CompareOp::Le, ">" => CompareOp::Gt, ">=" => CompareOp::Ge, s => return Err(format!("unsupported compare op '{}'", s)), }) } fn parse_binop(op: &str) -> Result { use crate::mir::types::BinaryOp; Ok(match op { "+" => BinaryOp::Add, "-" => BinaryOp::Sub, "*" => BinaryOp::Mul, "/" => BinaryOp::Div, "%" => BinaryOp::Mod, s => return Err(format!("unsupported binary op '{}'", s)), }) }