feat(joinir): Phase 30.x jsonir v0 - JoinIR JSON serialization
Implement JSON serialization for JoinIR module. Implementation: - src/mir/join_ir/json.rs: JSON serializer (~250 lines, no external deps) - src/tests/joinir_json_min.rs: Integration tests (8 unit + 2 integration) - 10 tests total, all passing Features: - JoinModule → JSON serialization - All instruction types: Call, Jump, Ret, Compute - All MirLikeInst types: Const, BinOp, Compare, BoxCall - Full ConstValue support: Integer, Bool, String, Null - Full operator coverage: Add/Sub/Mul/Div/Or/And, Lt/Le/Gt/Ge/Eq/Ne - JSON string escaping for special characters Usage: use crate::mir::join_ir::json::join_module_to_json_string; let json = join_module_to_json_string(&module); Non-goals (this phase): - CLI flag (--emit-joinir-json) - JSON → JoinIR reverse conversion 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Submodule docs/private updated: 6523cdc838...1840731c68
398
src/mir/join_ir/json.rs
Normal file
398
src/mir/join_ir/json.rs
Normal file
@ -0,0 +1,398 @@
|
|||||||
|
//! 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, VarId,
|
||||||
|
};
|
||||||
|
|
||||||
|
/// 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("\\\""));
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -30,6 +30,9 @@ pub mod lowering;
|
|||||||
// Phase 29 L-5.2: Progress verification
|
// Phase 29 L-5.2: Progress verification
|
||||||
pub mod verify;
|
pub mod verify;
|
||||||
|
|
||||||
|
// Phase 30.x: JSON serialization (jsonir v0)
|
||||||
|
pub mod json;
|
||||||
|
|
||||||
// Re-export lowering functions for backward compatibility
|
// Re-export lowering functions for backward compatibility
|
||||||
pub use lowering::{
|
pub use lowering::{
|
||||||
lower_funcscanner_trim_to_joinir, lower_min_loop_to_joinir, lower_skip_ws_to_joinir,
|
lower_funcscanner_trim_to_joinir, lower_min_loop_to_joinir, lower_skip_ws_to_joinir,
|
||||||
|
|||||||
232
src/tests/joinir_json_min.rs
Normal file
232
src/tests/joinir_json_min.rs
Normal file
@ -0,0 +1,232 @@
|
|||||||
|
//! JoinIR JSON シリアライズテスト (Phase 30.x)
|
||||||
|
//!
|
||||||
|
//! 手動構築した JoinIR を JSON に変換し、構造の妥当性を検証する。
|
||||||
|
|
||||||
|
use crate::mir::join_ir::json::join_module_to_json_string;
|
||||||
|
use crate::mir::join_ir::{
|
||||||
|
BinOpKind, CompareOp, ConstValue, JoinContId, JoinFuncId, JoinFunction, JoinInst, JoinModule,
|
||||||
|
MirLikeInst,
|
||||||
|
};
|
||||||
|
use crate::mir::ValueId;
|
||||||
|
|
||||||
|
/// 手動構築した JoinModule の JSON 出力テスト(NYASH_JOINIR_EXPERIMENT 不要)
|
||||||
|
#[test]
|
||||||
|
fn test_manual_joinir_json() {
|
||||||
|
// skip_ws 相当の JoinIR を手動で構築
|
||||||
|
let mut module = JoinModule::new();
|
||||||
|
|
||||||
|
// skip 関数: skip(s) -> i
|
||||||
|
let mut skip_func = JoinFunction::new(
|
||||||
|
JoinFuncId::new(0),
|
||||||
|
"skip".to_string(),
|
||||||
|
vec![ValueId(3000)], // s
|
||||||
|
);
|
||||||
|
|
||||||
|
// i_init = 0
|
||||||
|
skip_func.body.push(JoinInst::Compute(MirLikeInst::Const {
|
||||||
|
dst: ValueId(3001),
|
||||||
|
value: ConstValue::Integer(0),
|
||||||
|
}));
|
||||||
|
|
||||||
|
// n = s.length()
|
||||||
|
skip_func.body.push(JoinInst::Compute(MirLikeInst::BoxCall {
|
||||||
|
dst: Some(ValueId(3002)),
|
||||||
|
box_name: "StringBox".to_string(),
|
||||||
|
method: "length".to_string(),
|
||||||
|
args: vec![ValueId(3000)],
|
||||||
|
}));
|
||||||
|
|
||||||
|
// loop_step(s, i_init, n) - 末尾呼び出し
|
||||||
|
skip_func.body.push(JoinInst::Call {
|
||||||
|
func: JoinFuncId::new(1),
|
||||||
|
args: vec![ValueId(3000), ValueId(3001), ValueId(3002)],
|
||||||
|
k_next: None,
|
||||||
|
dst: None,
|
||||||
|
});
|
||||||
|
|
||||||
|
module.add_function(skip_func);
|
||||||
|
|
||||||
|
// loop_step 関数: loop_step(s, i, n) -> i
|
||||||
|
let mut loop_step = JoinFunction::new(
|
||||||
|
JoinFuncId::new(1),
|
||||||
|
"loop_step".to_string(),
|
||||||
|
vec![ValueId(3100), ValueId(3101), ValueId(3102)], // s, i, n
|
||||||
|
);
|
||||||
|
|
||||||
|
// cond = i < n
|
||||||
|
loop_step.body.push(JoinInst::Compute(MirLikeInst::Compare {
|
||||||
|
dst: ValueId(3103),
|
||||||
|
op: CompareOp::Lt,
|
||||||
|
lhs: ValueId(3101),
|
||||||
|
rhs: ValueId(3102),
|
||||||
|
}));
|
||||||
|
|
||||||
|
// 条件分岐(簡略化: 条件付き ret)
|
||||||
|
loop_step.body.push(JoinInst::Ret {
|
||||||
|
value: Some(ValueId(3101)),
|
||||||
|
});
|
||||||
|
|
||||||
|
module.add_function(loop_step);
|
||||||
|
module.entry = Some(JoinFuncId::new(0));
|
||||||
|
|
||||||
|
// JSON に変換
|
||||||
|
let json = join_module_to_json_string(&module);
|
||||||
|
|
||||||
|
// 構造チェック
|
||||||
|
assert!(json.contains("\"version\":0"));
|
||||||
|
assert!(json.contains("\"entry\":0"));
|
||||||
|
assert!(json.contains("\"name\":\"skip\""));
|
||||||
|
assert!(json.contains("\"name\":\"loop_step\""));
|
||||||
|
|
||||||
|
// 命令チェック
|
||||||
|
assert!(json.contains("\"kind\":\"const\""));
|
||||||
|
assert!(json.contains("\"kind\":\"boxcall\""));
|
||||||
|
assert!(json.contains("\"kind\":\"compare\""));
|
||||||
|
assert!(json.contains("\"type\":\"call\""));
|
||||||
|
assert!(json.contains("\"type\":\"ret\""));
|
||||||
|
|
||||||
|
// 値チェック
|
||||||
|
assert!(json.contains("\"value_type\":\"integer\""));
|
||||||
|
assert!(json.contains("\"value\":0"));
|
||||||
|
assert!(json.contains("\"box\":\"StringBox\""));
|
||||||
|
assert!(json.contains("\"method\":\"length\""));
|
||||||
|
assert!(json.contains("\"op\":\"lt\""));
|
||||||
|
|
||||||
|
eprintln!("[joinir/json] manual JoinIR JSON output:");
|
||||||
|
eprintln!("{}", json);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 全命令タイプの JSON 出力をカバーするテスト
|
||||||
|
#[test]
|
||||||
|
fn test_all_instruction_types_json() {
|
||||||
|
let mut module = JoinModule::new();
|
||||||
|
let mut func = JoinFunction::new(JoinFuncId::new(0), "all_types".to_string(), vec![]);
|
||||||
|
|
||||||
|
// Const - Integer
|
||||||
|
func.body.push(JoinInst::Compute(MirLikeInst::Const {
|
||||||
|
dst: ValueId(1),
|
||||||
|
value: ConstValue::Integer(42),
|
||||||
|
}));
|
||||||
|
|
||||||
|
// Const - Bool
|
||||||
|
func.body.push(JoinInst::Compute(MirLikeInst::Const {
|
||||||
|
dst: ValueId(2),
|
||||||
|
value: ConstValue::Bool(true),
|
||||||
|
}));
|
||||||
|
|
||||||
|
// Const - String
|
||||||
|
func.body.push(JoinInst::Compute(MirLikeInst::Const {
|
||||||
|
dst: ValueId(3),
|
||||||
|
value: ConstValue::String("hello".to_string()),
|
||||||
|
}));
|
||||||
|
|
||||||
|
// Const - Null
|
||||||
|
func.body.push(JoinInst::Compute(MirLikeInst::Const {
|
||||||
|
dst: ValueId(4),
|
||||||
|
value: ConstValue::Null,
|
||||||
|
}));
|
||||||
|
|
||||||
|
// BinOp - all types
|
||||||
|
for (i, op) in [
|
||||||
|
BinOpKind::Add,
|
||||||
|
BinOpKind::Sub,
|
||||||
|
BinOpKind::Mul,
|
||||||
|
BinOpKind::Div,
|
||||||
|
BinOpKind::Or,
|
||||||
|
BinOpKind::And,
|
||||||
|
]
|
||||||
|
.iter()
|
||||||
|
.enumerate()
|
||||||
|
{
|
||||||
|
func.body.push(JoinInst::Compute(MirLikeInst::BinOp {
|
||||||
|
dst: ValueId(10 + i as u32),
|
||||||
|
op: *op,
|
||||||
|
lhs: ValueId(1),
|
||||||
|
rhs: ValueId(1),
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compare - all types
|
||||||
|
for (i, op) in [
|
||||||
|
CompareOp::Lt,
|
||||||
|
CompareOp::Le,
|
||||||
|
CompareOp::Gt,
|
||||||
|
CompareOp::Ge,
|
||||||
|
CompareOp::Eq,
|
||||||
|
CompareOp::Ne,
|
||||||
|
]
|
||||||
|
.iter()
|
||||||
|
.enumerate()
|
||||||
|
{
|
||||||
|
func.body.push(JoinInst::Compute(MirLikeInst::Compare {
|
||||||
|
dst: ValueId(20 + i as u32),
|
||||||
|
op: *op,
|
||||||
|
lhs: ValueId(1),
|
||||||
|
rhs: ValueId(1),
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
// BoxCall
|
||||||
|
func.body.push(JoinInst::Compute(MirLikeInst::BoxCall {
|
||||||
|
dst: Some(ValueId(30)),
|
||||||
|
box_name: "TestBox".to_string(),
|
||||||
|
method: "test_method".to_string(),
|
||||||
|
args: vec![ValueId(1), ValueId(2)],
|
||||||
|
}));
|
||||||
|
|
||||||
|
// Call
|
||||||
|
func.body.push(JoinInst::Call {
|
||||||
|
func: JoinFuncId::new(1),
|
||||||
|
args: vec![ValueId(1)],
|
||||||
|
k_next: Some(JoinContId::new(2)),
|
||||||
|
dst: Some(ValueId(40)),
|
||||||
|
});
|
||||||
|
|
||||||
|
// Jump (conditional)
|
||||||
|
func.body.push(JoinInst::Jump {
|
||||||
|
cont: JoinContId::new(3),
|
||||||
|
args: vec![ValueId(1)],
|
||||||
|
cond: Some(ValueId(2)),
|
||||||
|
});
|
||||||
|
|
||||||
|
// Jump (unconditional)
|
||||||
|
func.body.push(JoinInst::Jump {
|
||||||
|
cont: JoinContId::new(4),
|
||||||
|
args: vec![],
|
||||||
|
cond: None,
|
||||||
|
});
|
||||||
|
|
||||||
|
// Ret with value
|
||||||
|
func.body.push(JoinInst::Ret {
|
||||||
|
value: Some(ValueId(1)),
|
||||||
|
});
|
||||||
|
|
||||||
|
module.add_function(func);
|
||||||
|
|
||||||
|
// JSON に変換
|
||||||
|
let json = join_module_to_json_string(&module);
|
||||||
|
|
||||||
|
// 全 BinOp タイプをチェック
|
||||||
|
assert!(json.contains("\"op\":\"add\""));
|
||||||
|
assert!(json.contains("\"op\":\"sub\""));
|
||||||
|
assert!(json.contains("\"op\":\"mul\""));
|
||||||
|
assert!(json.contains("\"op\":\"div\""));
|
||||||
|
assert!(json.contains("\"op\":\"or\""));
|
||||||
|
assert!(json.contains("\"op\":\"and\""));
|
||||||
|
|
||||||
|
// 全 Compare タイプをチェック
|
||||||
|
assert!(json.contains("\"op\":\"lt\""));
|
||||||
|
assert!(json.contains("\"op\":\"le\""));
|
||||||
|
assert!(json.contains("\"op\":\"gt\""));
|
||||||
|
assert!(json.contains("\"op\":\"ge\""));
|
||||||
|
assert!(json.contains("\"op\":\"eq\""));
|
||||||
|
assert!(json.contains("\"op\":\"ne\""));
|
||||||
|
|
||||||
|
// 全 ConstValue タイプをチェック
|
||||||
|
assert!(json.contains("\"value_type\":\"integer\""));
|
||||||
|
assert!(json.contains("\"value_type\":\"bool\""));
|
||||||
|
assert!(json.contains("\"value_type\":\"string\""));
|
||||||
|
assert!(json.contains("\"value_type\":\"null\""));
|
||||||
|
|
||||||
|
eprintln!("[joinir/json] all_instruction_types test passed");
|
||||||
|
}
|
||||||
@ -6,6 +6,7 @@ pub mod identical_exec;
|
|||||||
pub mod identical_exec_collections;
|
pub mod identical_exec_collections;
|
||||||
pub mod identical_exec_instance;
|
pub mod identical_exec_instance;
|
||||||
pub mod identical_exec_string;
|
pub mod identical_exec_string;
|
||||||
|
pub mod joinir_json_min; // Phase 30.x: JoinIR JSON シリアライズテスト
|
||||||
pub mod joinir_runner_min; // Phase 27.2: JoinIR 実行器 A/B 比較テスト
|
pub mod joinir_runner_min; // Phase 27.2: JoinIR 実行器 A/B 比較テスト
|
||||||
pub mod joinir_runner_standalone; // Phase 27-shortterm S-3.2: JoinIR Runner 単体テスト
|
pub mod joinir_runner_standalone; // Phase 27-shortterm S-3.2: JoinIR Runner 単体テスト
|
||||||
pub mod joinir_vm_bridge_skip_ws; // Phase 27-shortterm S-4.4: JoinIR → Rust VM Bridge A/B Test
|
pub mod joinir_vm_bridge_skip_ws; // Phase 27-shortterm S-4.4: JoinIR → Rust VM Bridge A/B Test
|
||||||
|
|||||||
Reference in New Issue
Block a user