Files
hakorune/src/mir/join_ir/json.rs
nyash-codex 51ff558904 feat(phase32): L-2.1 Stage-1 UsingResolver JoinIR integration + cleanup
Phase 32 L-2.1 complete implementation:

1. Stage-1 UsingResolver main line JoinIR connection
   - CFG-based LoopForm construction for resolve_for_source/5
   - LoopToJoinLowerer integration with handwritten fallback
   - JSON snapshot tests 6/6 PASS

2. JoinIR/VM Bridge improvements
   - Simplified join_ir_vm_bridge.rs dispatch logic
   - Enhanced json.rs serialization
   - PHI core boxes cleanup (local_scope_inspector, loop_exit_liveness, loop_var_classifier)

3. Stage-1 CLI enhancements
   - Extended args.rs, groups.rs, mod.rs for new options
   - Improved stage1_bridge module (args, env, mod)
   - Updated stage1_cli.hako

4. MIR builder cleanup
   - Simplified if_form.rs control flow
   - Removed dead code from loop_builder.rs
   - Enhanced phi_merge.rs

5. Runner module updates
   - json_v0_bridge/lowering.rs improvements
   - dispatch.rs, selfhost.rs, modes/vm.rs cleanup

6. Documentation updates
   - CURRENT_TASK.md, AGENTS.md
   - Various docs/ updates

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-26 10:17:37 +09:00

399 lines
13 KiB
Rust

//! JoinIR JSON シリアライザ (jsonir v0)
//!
//! JoinModule を JSON 形式でシリアライズする。
//! 用途: デバッグ、テスト、将来の selfhost JoinIR ロワーの参照フォーマット。
//!
//! 仕様: docs/private/roadmap2/phases/phase-30-final-joinir-world/joinir_json.md
use std::io::Write;
use super::{
BinOpKind, CompareOp, ConstValue, JoinFunction, JoinInst, JoinModule, MirLikeInst,
};
/// JoinModule を JSON としてシリアライズする
///
/// # Example
/// ```ignore
/// let mut output = Vec::new();
/// write_join_module_as_json(&module, &mut output)?;
/// let json_str = String::from_utf8(output)?;
/// ```
pub fn write_join_module_as_json<W: Write>(module: &JoinModule, out: &mut W) -> std::io::Result<()> {
write!(out, "{{")?;
write!(out, "\"version\":0")?;
// entry
match module.entry {
Some(entry_id) => write!(out, ",\"entry\":{}", entry_id.0)?,
None => write!(out, ",\"entry\":null")?,
}
// functions
write!(out, ",\"functions\":[")?;
let mut first_func = true;
for func in module.functions.values() {
if !first_func {
write!(out, ",")?;
}
first_func = false;
write_function(func, out)?;
}
write!(out, "]")?;
write!(out, "}}")?;
Ok(())
}
fn write_function<W: Write>(func: &JoinFunction, out: &mut W) -> std::io::Result<()> {
write!(out, "{{")?;
write!(out, "\"id\":{}", func.id.0)?;
write!(out, ",\"name\":\"{}\"" , escape_json_string(&func.name))?;
// params
write!(out, ",\"params\":[")?;
for (i, param) in func.params.iter().enumerate() {
if i > 0 {
write!(out, ",")?;
}
write!(out, "{}", param.0)?;
}
write!(out, "]")?;
// exit_cont
match func.exit_cont {
Some(cont_id) => write!(out, ",\"exit_cont\":{}", cont_id.0)?,
None => write!(out, ",\"exit_cont\":null")?,
}
// body
write!(out, ",\"body\":[")?;
for (i, inst) in func.body.iter().enumerate() {
if i > 0 {
write!(out, ",")?;
}
write_inst(inst, out)?;
}
write!(out, "]")?;
write!(out, "}}")?;
Ok(())
}
fn write_inst<W: Write>(inst: &JoinInst, out: &mut W) -> std::io::Result<()> {
match inst {
JoinInst::Call {
func,
args,
k_next,
dst,
} => {
write!(out, "{{\"type\":\"call\"")?;
write!(out, ",\"func\":{}", func.0)?;
write!(out, ",\"args\":[")?;
for (i, arg) in args.iter().enumerate() {
if i > 0 {
write!(out, ",")?;
}
write!(out, "{}", arg.0)?;
}
write!(out, "]")?;
match k_next {
Some(k) => write!(out, ",\"k_next\":{}", k.0)?,
None => write!(out, ",\"k_next\":null")?,
}
match dst {
Some(d) => write!(out, ",\"dst\":{}", d.0)?,
None => write!(out, ",\"dst\":null")?,
}
write!(out, "}}")?;
}
JoinInst::Jump { cont, args, cond } => {
write!(out, "{{\"type\":\"jump\"")?;
write!(out, ",\"cont\":{}", cont.0)?;
write!(out, ",\"args\":[")?;
for (i, arg) in args.iter().enumerate() {
if i > 0 {
write!(out, ",")?;
}
write!(out, "{}", arg.0)?;
}
write!(out, "]")?;
match cond {
Some(c) => write!(out, ",\"cond\":{}", c.0)?,
None => write!(out, ",\"cond\":null")?,
}
write!(out, "}}")?;
}
JoinInst::Ret { value } => {
write!(out, "{{\"type\":\"ret\"")?;
match value {
Some(v) => write!(out, ",\"value\":{}", v.0)?,
None => write!(out, ",\"value\":null")?,
}
write!(out, "}}")?;
}
JoinInst::Compute(mir_like) => {
write!(out, "{{\"type\":\"compute\",\"op\":")?;
write_mir_like_inst(mir_like, out)?;
write!(out, "}}")?;
}
}
Ok(())
}
fn write_mir_like_inst<W: Write>(inst: &MirLikeInst, out: &mut W) -> std::io::Result<()> {
match inst {
MirLikeInst::Const { dst, value } => {
write!(out, "{{\"kind\":\"const\"")?;
write!(out, ",\"dst\":{}", dst.0)?;
match value {
ConstValue::Integer(n) => {
write!(out, ",\"value_type\":\"integer\"")?;
write!(out, ",\"value\":{}", n)?;
}
ConstValue::Bool(b) => {
write!(out, ",\"value_type\":\"bool\"")?;
write!(out, ",\"value\":{}", b)?;
}
ConstValue::String(s) => {
write!(out, ",\"value_type\":\"string\"")?;
write!(out, ",\"value\":\"{}\"", escape_json_string(s))?;
}
ConstValue::Null => {
write!(out, ",\"value_type\":\"null\"")?;
write!(out, ",\"value\":null")?;
}
}
write!(out, "}}")?;
}
MirLikeInst::BinOp { dst, op, lhs, rhs } => {
write!(out, "{{\"kind\":\"binop\"")?;
write!(out, ",\"dst\":{}", dst.0)?;
write!(out, ",\"op\":\"{}\"", binop_to_str(*op))?;
write!(out, ",\"lhs\":{}", lhs.0)?;
write!(out, ",\"rhs\":{}", rhs.0)?;
write!(out, "}}")?;
}
MirLikeInst::Compare { dst, op, lhs, rhs } => {
write!(out, "{{\"kind\":\"compare\"")?;
write!(out, ",\"dst\":{}", dst.0)?;
write!(out, ",\"op\":\"{}\"", compare_to_str(*op))?;
write!(out, ",\"lhs\":{}", lhs.0)?;
write!(out, ",\"rhs\":{}", rhs.0)?;
write!(out, "}}")?;
}
MirLikeInst::BoxCall {
dst,
box_name,
method,
args,
} => {
write!(out, "{{\"kind\":\"boxcall\"")?;
match dst {
Some(d) => write!(out, ",\"dst\":{}", d.0)?,
None => write!(out, ",\"dst\":null")?,
}
write!(out, ",\"box\":\"{}\"", escape_json_string(box_name))?;
write!(out, ",\"method\":\"{}\"", escape_json_string(method))?;
write!(out, ",\"args\":[")?;
for (i, arg) in args.iter().enumerate() {
if i > 0 {
write!(out, ",")?;
}
write!(out, "{}", arg.0)?;
}
write!(out, "]")?;
write!(out, "}}")?;
}
}
Ok(())
}
fn binop_to_str(op: BinOpKind) -> &'static str {
match op {
BinOpKind::Add => "add",
BinOpKind::Sub => "sub",
BinOpKind::Mul => "mul",
BinOpKind::Div => "div",
BinOpKind::Or => "or",
BinOpKind::And => "and",
}
}
fn compare_to_str(op: CompareOp) -> &'static str {
match op {
CompareOp::Lt => "lt",
CompareOp::Le => "le",
CompareOp::Gt => "gt",
CompareOp::Ge => "ge",
CompareOp::Eq => "eq",
CompareOp::Ne => "ne",
}
}
/// JSON 文字列のエスケープ
fn escape_json_string(s: &str) -> String {
let mut escaped = String::with_capacity(s.len());
for c in s.chars() {
match c {
'"' => escaped.push_str("\\\""),
'\\' => escaped.push_str("\\\\"),
'\n' => escaped.push_str("\\n"),
'\r' => escaped.push_str("\\r"),
'\t' => escaped.push_str("\\t"),
c if c.is_control() => {
escaped.push_str(&format!("\\u{:04x}", c as u32));
}
c => escaped.push(c),
}
}
escaped
}
/// JoinModule を JSON 文字列として返す(テスト用ヘルパー)
pub fn join_module_to_json_string(module: &JoinModule) -> String {
let mut output = Vec::new();
write_join_module_as_json(module, &mut output).expect("JSON serialization failed");
String::from_utf8(output).expect("Invalid UTF-8 in JSON output")
}
#[cfg(test)]
mod tests {
use super::*;
use crate::mir::join_ir::{JoinContId, JoinFuncId};
use crate::mir::ValueId;
#[test]
fn test_empty_module() {
let module = JoinModule::new();
let json = join_module_to_json_string(&module);
assert!(json.contains("\"version\":0"));
assert!(json.contains("\"entry\":null"));
assert!(json.contains("\"functions\":[]"));
}
#[test]
fn test_simple_function() {
let mut module = JoinModule::new();
let mut func = JoinFunction::new(JoinFuncId::new(0), "test".to_string(), vec![ValueId(100)]);
func.body.push(JoinInst::Ret {
value: Some(ValueId(100)),
});
module.add_function(func);
module.entry = Some(JoinFuncId::new(0));
let json = join_module_to_json_string(&module);
assert!(json.contains("\"entry\":0"));
assert!(json.contains("\"name\":\"test\""));
assert!(json.contains("\"params\":[100]"));
assert!(json.contains("\"type\":\"ret\""));
assert!(json.contains("\"value\":100"));
}
#[test]
fn test_const_instruction() {
let mut module = JoinModule::new();
let mut func = JoinFunction::new(JoinFuncId::new(0), "main".to_string(), vec![]);
func.body.push(JoinInst::Compute(MirLikeInst::Const {
dst: ValueId(1),
value: ConstValue::Integer(42),
}));
module.add_function(func);
let json = join_module_to_json_string(&module);
assert!(json.contains("\"kind\":\"const\""));
assert!(json.contains("\"value_type\":\"integer\""));
assert!(json.contains("\"value\":42"));
}
#[test]
fn test_binop_instruction() {
let mut module = JoinModule::new();
let mut func = JoinFunction::new(JoinFuncId::new(0), "main".to_string(), vec![]);
func.body.push(JoinInst::Compute(MirLikeInst::BinOp {
dst: ValueId(3),
op: BinOpKind::Add,
lhs: ValueId(1),
rhs: ValueId(2),
}));
module.add_function(func);
let json = join_module_to_json_string(&module);
assert!(json.contains("\"kind\":\"binop\""));
assert!(json.contains("\"op\":\"add\""));
assert!(json.contains("\"lhs\":1"));
assert!(json.contains("\"rhs\":2"));
}
#[test]
fn test_call_instruction() {
let mut module = JoinModule::new();
let mut func = JoinFunction::new(JoinFuncId::new(0), "main".to_string(), vec![]);
func.body.push(JoinInst::Call {
func: JoinFuncId::new(1),
args: vec![ValueId(100), ValueId(101)],
k_next: Some(JoinContId::new(5)),
dst: Some(ValueId(200)),
});
module.add_function(func);
let json = join_module_to_json_string(&module);
assert!(json.contains("\"type\":\"call\""));
assert!(json.contains("\"func\":1"));
assert!(json.contains("\"args\":[100,101]"));
assert!(json.contains("\"k_next\":5"));
assert!(json.contains("\"dst\":200"));
}
#[test]
fn test_jump_instruction() {
let mut module = JoinModule::new();
let mut func = JoinFunction::new(JoinFuncId::new(0), "main".to_string(), vec![]);
func.body.push(JoinInst::Jump {
cont: JoinContId::new(3),
args: vec![ValueId(10)],
cond: Some(ValueId(5)),
});
module.add_function(func);
let json = join_module_to_json_string(&module);
assert!(json.contains("\"type\":\"jump\""));
assert!(json.contains("\"cont\":3"));
assert!(json.contains("\"cond\":5"));
}
#[test]
fn test_boxcall_instruction() {
let mut module = JoinModule::new();
let mut func = JoinFunction::new(JoinFuncId::new(0), "main".to_string(), vec![]);
func.body.push(JoinInst::Compute(MirLikeInst::BoxCall {
dst: Some(ValueId(10)),
box_name: "StringBox".to_string(),
method: "length".to_string(),
args: vec![ValueId(1)],
}));
module.add_function(func);
let json = join_module_to_json_string(&module);
assert!(json.contains("\"kind\":\"boxcall\""));
assert!(json.contains("\"box\":\"StringBox\""));
assert!(json.contains("\"method\":\"length\""));
}
#[test]
fn test_string_escaping() {
let mut module = JoinModule::new();
let mut func = JoinFunction::new(JoinFuncId::new(0), "main".to_string(), vec![]);
func.body.push(JoinInst::Compute(MirLikeInst::Const {
dst: ValueId(1),
value: ConstValue::String("hello\nworld\"test".to_string()),
}));
module.add_function(func);
let json = join_module_to_json_string(&module);
assert!(json.contains("\\n"));
assert!(json.contains("\\\""));
}
}