2025-11-21 06:25:17 +09:00
|
|
|
use super::mir_json::common as mirjson_common;
|
2025-11-03 23:21:48 +09:00
|
|
|
use crate::mir::{
|
|
|
|
|
function::{FunctionSignature, MirFunction, MirModule},
|
2025-11-05 18:57:03 +09:00
|
|
|
BasicBlock, BasicBlockId, ConstValue, EffectMask, MirInstruction, MirType, ValueId,
|
2025-11-03 23:21:48 +09:00
|
|
|
};
|
|
|
|
|
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<MirModule, String> {
|
|
|
|
|
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())
|
2025-11-21 06:25:17 +09:00
|
|
|
.ok_or_else(|| format!("function '{}' entry block missing id", func_name))?
|
|
|
|
|
as u32;
|
2025-11-03 23:21:48 +09:00
|
|
|
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())
|
2025-11-21 06:25:17 +09:00
|
|
|
.ok_or_else(|| format!("function '{}' block missing id", func_name))?
|
|
|
|
|
as u32;
|
2025-11-03 23:21:48 +09:00
|
|
|
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())
|
2025-11-21 06:25:17 +09:00
|
|
|
.ok_or_else(|| {
|
|
|
|
|
format!(
|
|
|
|
|
"function '{}' block {} missing instructions array",
|
|
|
|
|
func_name, block_id
|
|
|
|
|
)
|
|
|
|
|
})?;
|
2025-11-03 23:21:48 +09:00
|
|
|
|
|
|
|
|
for inst in instructions {
|
2025-11-21 06:25:17 +09:00
|
|
|
let op = inst.get("op").and_then(|o| o.as_str()).ok_or_else(|| {
|
|
|
|
|
format!(
|
|
|
|
|
"function '{}' block {} missing op field",
|
|
|
|
|
func_name, block_id
|
|
|
|
|
)
|
|
|
|
|
})?;
|
2025-11-03 23:21:48 +09:00
|
|
|
match op {
|
|
|
|
|
"const" => {
|
|
|
|
|
let dst = require_u64(inst, "dst", "const dst")? as u32;
|
2025-11-21 06:25:17 +09:00
|
|
|
let vobj = inst
|
|
|
|
|
.get("value")
|
|
|
|
|
.ok_or_else(|| "const missing value".to_string())?;
|
2025-11-03 23:21:48 +09:00
|
|
|
let val = parse_const_value(vobj)?;
|
2025-11-21 06:25:17 +09:00
|
|
|
block_ref.add_instruction(MirInstruction::Const {
|
|
|
|
|
dst: ValueId::new(dst),
|
|
|
|
|
value: val,
|
|
|
|
|
});
|
2025-11-03 23:21:48 +09:00
|
|
|
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;
|
2025-11-21 06:25:17 +09:00
|
|
|
block_ref.add_instruction(MirInstruction::Copy {
|
|
|
|
|
dst: ValueId::new(dst),
|
|
|
|
|
src: ValueId::new(src),
|
|
|
|
|
});
|
2025-11-03 23:21:48 +09:00
|
|
|
max_value_id = max_value_id.max(dst + 1);
|
|
|
|
|
}
|
2025-11-04 20:46:43 +09:00
|
|
|
"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;
|
2025-11-21 06:25:17 +09:00
|
|
|
let operation = inst
|
|
|
|
|
.get("operation")
|
|
|
|
|
.and_then(Value::as_str)
|
|
|
|
|
.ok_or_else(|| "binop missing operation".to_string())?;
|
2025-11-04 20:46:43 +09:00
|
|
|
let bop = parse_binop(operation)?;
|
2025-11-21 06:25:17 +09:00
|
|
|
block_ref.add_instruction(MirInstruction::BinOp {
|
|
|
|
|
dst: ValueId::new(dst),
|
|
|
|
|
op: bop,
|
|
|
|
|
lhs: ValueId::new(lhs),
|
|
|
|
|
rhs: ValueId::new(rhs),
|
|
|
|
|
});
|
2025-11-04 20:46:43 +09:00
|
|
|
max_value_id = max_value_id.max(dst + 1);
|
|
|
|
|
}
|
2025-11-03 23:21:48 +09:00
|
|
|
"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;
|
2025-11-21 06:25:17 +09:00
|
|
|
let operation = inst
|
|
|
|
|
.get("operation")
|
|
|
|
|
.and_then(Value::as_str)
|
|
|
|
|
.unwrap_or("==");
|
2025-11-03 23:21:48 +09:00
|
|
|
let cop = parse_compare(operation)?;
|
2025-11-21 06:25:17 +09:00
|
|
|
block_ref.add_instruction(MirInstruction::Compare {
|
|
|
|
|
dst: ValueId::new(dst),
|
|
|
|
|
op: cop,
|
|
|
|
|
lhs: ValueId::new(lhs),
|
|
|
|
|
rhs: ValueId::new(rhs),
|
|
|
|
|
});
|
2025-11-03 23:21:48 +09:00
|
|
|
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;
|
2025-11-21 06:25:17 +09:00
|
|
|
block_ref.add_instruction(MirInstruction::Branch {
|
|
|
|
|
condition: ValueId::new(cond),
|
|
|
|
|
then_bb: BasicBlockId::new(then_bb),
|
|
|
|
|
else_bb: BasicBlockId::new(else_bb),
|
|
|
|
|
});
|
2025-11-03 23:21:48 +09:00
|
|
|
}
|
|
|
|
|
"jump" => {
|
|
|
|
|
let target = require_u64(inst, "target", "jump target")? as u32;
|
2025-11-21 06:25:17 +09:00
|
|
|
block_ref.add_instruction(MirInstruction::Jump {
|
|
|
|
|
target: BasicBlockId::new(target),
|
|
|
|
|
});
|
2025-11-03 23:21:48 +09:00
|
|
|
}
|
|
|
|
|
"phi" => {
|
|
|
|
|
let dst = require_u64(inst, "dst", "phi dst")? as u32;
|
2025-11-21 06:25:17 +09:00
|
|
|
let incoming = inst
|
|
|
|
|
.get("incoming")
|
|
|
|
|
.and_then(Value::as_array)
|
|
|
|
|
.ok_or_else(|| "phi incoming missing".to_string())?;
|
2025-11-03 23:21:48 +09:00
|
|
|
let mut pairs = Vec::with_capacity(incoming.len());
|
|
|
|
|
for entry in incoming {
|
2025-11-21 06:25:17 +09:00
|
|
|
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;
|
2025-11-03 23:21:48 +09:00
|
|
|
pairs.push((BasicBlockId::new(bb), ValueId::new(val)));
|
|
|
|
|
}
|
2025-11-21 06:25:17 +09:00
|
|
|
block_ref.add_instruction(MirInstruction::Phi {
|
|
|
|
|
dst: ValueId::new(dst),
|
|
|
|
|
inputs: pairs,
|
|
|
|
|
});
|
2025-11-03 23:21:48 +09:00
|
|
|
max_value_id = max_value_id.max(dst + 1);
|
|
|
|
|
}
|
|
|
|
|
"ret" => {
|
2025-11-21 06:25:17 +09:00
|
|
|
let value = inst
|
|
|
|
|
.get("value")
|
|
|
|
|
.and_then(|v| v.as_u64())
|
|
|
|
|
.map(|v| ValueId::new(v as u32));
|
2025-11-03 23:21:48 +09:00
|
|
|
block_ref.add_instruction(MirInstruction::Return { value });
|
2025-11-21 06:25:17 +09:00
|
|
|
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;
|
|
|
|
|
}
|
2025-11-03 23:21:48 +09:00
|
|
|
}
|
2025-11-05 18:57:03 +09:00
|
|
|
"newbox" => {
|
|
|
|
|
let dst = require_u64(inst, "dst", "newbox dst")? as u32;
|
2025-11-21 06:25:17 +09:00
|
|
|
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();
|
2025-11-05 18:57:03 +09:00
|
|
|
let mut args: Vec<ValueId> = Vec::with_capacity(args_v.len());
|
|
|
|
|
for a in args_v {
|
2025-11-21 06:25:17 +09:00
|
|
|
let id = a
|
|
|
|
|
.as_u64()
|
|
|
|
|
.ok_or_else(|| "newbox arg must be integer".to_string())?
|
|
|
|
|
as u32;
|
2025-11-05 18:57:03 +09:00
|
|
|
args.push(ValueId::new(id));
|
|
|
|
|
}
|
2025-11-21 06:25:17 +09:00
|
|
|
block_ref.add_instruction(MirInstruction::NewBox {
|
|
|
|
|
dst: ValueId::new(dst),
|
|
|
|
|
box_type: ty,
|
|
|
|
|
args,
|
|
|
|
|
});
|
2025-11-05 18:57:03 +09:00
|
|
|
max_value_id = max_value_id.max(dst + 1);
|
|
|
|
|
}
|
|
|
|
|
"boxcall" => {
|
|
|
|
|
// { op:"boxcall", box:<vid>, method:"name", args:[vid...], dst?:<vid> }
|
|
|
|
|
let box_id = require_u64(inst, "box", "boxcall box")? as u32;
|
2025-11-21 06:25:17 +09:00
|
|
|
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();
|
2025-11-05 18:57:03 +09:00
|
|
|
let mut args: Vec<ValueId> = Vec::with_capacity(args_v.len());
|
|
|
|
|
for a in args_v {
|
2025-11-21 06:25:17 +09:00
|
|
|
let id = a
|
|
|
|
|
.as_u64()
|
|
|
|
|
.ok_or_else(|| "boxcall arg must be integer".to_string())?
|
|
|
|
|
as u32;
|
2025-11-05 18:57:03 +09:00
|
|
|
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,
|
|
|
|
|
});
|
2025-11-21 06:25:17 +09:00
|
|
|
if let Some(dv) = dst_opt {
|
|
|
|
|
max_value_id = max_value_id.max(dv.as_u32() + 1);
|
|
|
|
|
}
|
2025-11-05 18:57:03 +09:00
|
|
|
}
|
2025-11-03 23:21:48 +09:00
|
|
|
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<u64, String> {
|
2025-11-21 06:25:17 +09:00
|
|
|
node.get(key)
|
|
|
|
|
.and_then(Value::as_u64)
|
|
|
|
|
.ok_or_else(|| format!("{} missing field '{}'", context, key))
|
2025-11-03 23:21:48 +09:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn parse_const_value(value_obj: &Value) -> Result<ConstValue, String> {
|
|
|
|
|
// 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<crate::mir::types::CompareOp, String> {
|
|
|
|
|
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)),
|
|
|
|
|
})
|
|
|
|
|
}
|
2025-11-04 20:46:43 +09:00
|
|
|
|
|
|
|
|
fn parse_binop(op: &str) -> Result<crate::mir::types::BinaryOp, String> {
|
|
|
|
|
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)),
|
|
|
|
|
})
|
|
|
|
|
}
|