feat(joinir): Phase 51 Field access / NewBox 式タイプ拡張

JoinIR Frontend の式タイプを拡張し、Field access と NewBox のパースと
MIR 変換をサポート。

## 新機能

### Field access (me.tokens 等)
- expr.rs に "Field" タイプハンドラ追加
- JoinInst::FieldAccess バリアント追加
- MIR 変換: FieldAccess → BoxCall (getter pattern)

### NewBox (new ArrayBox() 等)
- expr.rs に "NewBox" タイプハンドラ追加
- JoinInst::NewBox バリアント追加
- MIR 変換: NewBox → MirInstruction::NewBox

## 修正ファイル

- src/mir/join_ir/mod.rs: JoinInst 拡張
- src/mir/join_ir/frontend/ast_lowerer/expr.rs: パース対応
- src/mir/join_ir_vm_bridge/convert.rs: MIR 変換
- src/mir/join_ir_runner.rs: ハンドラ追加
- src/mir/join_ir/json.rs: JSON シリアライズ

## 注意

print_tokens/array_filter の JoinIR 完走には Phase 52 で
JSON 生成側 (LoopFrontendBinding) の修正が必要。

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

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
nyash-codex
2025-11-28 21:10:30 +09:00
parent 3dc691d39f
commit 6bb6f38a1c
6 changed files with 148 additions and 1 deletions

View File

@ -187,6 +187,65 @@ impl AstToJoinIrLowerer {
(dst, insts) (dst, insts)
} }
// Phase 51: フィールドアクセス対応me.tokens 等)
"Field" => {
let object_expr = &expr["object"];
let field_name = expr["field"]
.as_str()
.expect("Field must have 'field' string");
// object を再帰的に extract_value
let (object_var, object_insts) = self.extract_value(object_expr, ctx);
// 結果変数を割り当て
let dst = ctx.alloc_var();
// FieldAccess 命令を生成
let field_inst = JoinInst::FieldAccess {
dst,
object: object_var,
field: field_name.to_string(),
};
let mut insts = object_insts;
insts.push(field_inst);
(dst, insts)
}
// Phase 51: NewBox 対応new ArrayBox() 等)
"NewBox" => {
let box_name = expr["box_name"]
.as_str()
.expect("NewBox must have 'box_name' string");
let empty_args = vec![];
let args_array = expr["args"].as_array().unwrap_or(&empty_args);
// args を再帰的に extract_value
let mut arg_vars = Vec::new();
let mut arg_insts = Vec::new();
for arg_expr in args_array {
let (arg_var, arg_inst) = self.extract_value(arg_expr, ctx);
arg_vars.push(arg_var);
arg_insts.extend(arg_inst);
}
// 結果変数を割り当て
let dst = ctx.alloc_var();
// NewBox 命令を生成
let newbox_inst = JoinInst::NewBox {
dst,
box_name: box_name.to_string(),
args: arg_vars,
};
let mut insts = arg_insts;
insts.push(newbox_inst);
(dst, insts)
}
_ => panic!("Unsupported expr type: {}", expr_type), _ => panic!("Unsupported expr type: {}", expr_type),
} }
} }

View File

@ -231,6 +231,33 @@ fn write_inst<W: Write>(inst: &JoinInst, out: &mut W) -> std::io::Result<()> {
} }
write!(out, "}}")?; write!(out, "}}")?;
} }
// Phase 51: FieldAccess instruction JSON serialization
JoinInst::FieldAccess { dst, object, field } => {
write!(out, "{{\"type\":\"field_access\"")?;
write!(out, ",\"dst\":{}", dst.0)?;
write!(out, ",\"object\":{}", object.0)?;
write!(out, ",\"field\":\"{}\"", escape_json_string(field))?;
write!(out, "}}")?;
}
// Phase 51: NewBox instruction JSON serialization
JoinInst::NewBox {
dst,
box_name,
args,
} => {
write!(out, "{{\"type\":\"new_box\"")?;
write!(out, ",\"dst\":{}", dst.0)?;
write!(out, ",\"box_name\":\"{}\"", escape_json_string(box_name))?;
write!(out, ",\"args\":[")?;
for (i, arg) in args.iter().enumerate() {
if i > 0 {
write!(out, ",")?;
}
write!(out, "{}", arg.0)?;
}
write!(out, "]")?;
write!(out, "}}")?;
}
JoinInst::Compute(mir_like) => { JoinInst::Compute(mir_like) => {
write!(out, "{{\"type\":\"compute\",\"op\":")?; write!(out, "{{\"type\":\"compute\",\"op\":")?;
write_mir_like_inst(mir_like, out)?; write_mir_like_inst(mir_like, out)?;

View File

@ -322,6 +322,24 @@ pub enum JoinInst {
args: Vec<VarId>, args: Vec<VarId>,
}, },
/// Phase 51: フィールドアクセス
/// object.field の構造を JoinIR で表現
/// MIR 変換時に Load 命令に変換
FieldAccess {
dst: VarId,
object: VarId,
field: String,
},
/// Phase 51: Box インスタンス生成
/// new BoxName(args...) の構造を JoinIR で表現
/// MIR 変換時に NewBox 命令に変換
NewBox {
dst: VarId,
box_name: String,
args: Vec<VarId>,
},
/// Phase 41-4: 深いネスト if の複数変数 mergeelse なし) /// Phase 41-4: 深いネスト if の複数変数 mergeelse なし)
/// ///
/// # Pattern /// # Pattern

View File

@ -225,6 +225,22 @@ fn execute_function(
"NestedIfMerge is not supported in JoinIR Runner (use JoinIR→MIR→VM bridge instead)" "NestedIfMerge is not supported in JoinIR Runner (use JoinIR→MIR→VM bridge instead)"
)); ));
} }
// Phase 51: FieldAccess instruction execution
JoinInst::FieldAccess { .. } => {
// Phase 51: FieldAccess は JoinIR Runner では未対応
// JoinIR → MIR 変換経由で VM が実行する
return Err(JoinRuntimeError::new(
"FieldAccess is not supported in JoinIR Runner (use JoinIR→MIR→VM bridge instead)"
));
}
// Phase 51: NewBox instruction execution
JoinInst::NewBox { .. } => {
// Phase 51: NewBox は JoinIR Runner では未対応
// JoinIR → MIR 変換経由で VM が実行する
return Err(JoinRuntimeError::new(
"NewBox is not supported in JoinIR Runner (use JoinIR→MIR→VM bridge instead)"
));
}
} }
} }

View File

@ -217,6 +217,33 @@ pub(crate) fn convert_join_function_to_mir(
}; };
current_instructions.push(mir_inst); current_instructions.push(mir_inst);
} }
// Phase 51: FieldAccess → MIR BoxCall (getter pattern)
JoinInst::FieldAccess { dst, object, field } => {
// object.field を BoxCall(object, field, []) に変換
// フィールドアクセスはメソッド呼び出しとして扱うgetter pattern
let mir_inst = MirInstruction::BoxCall {
dst: Some(*dst),
box_val: *object,
method: field.clone(),
method_id: None,
args: vec![],
effects: EffectMask::PURE,
};
current_instructions.push(mir_inst);
}
// Phase 51: NewBox → MIR NewBox
JoinInst::NewBox {
dst,
box_name,
args,
} => {
let mir_inst = MirInstruction::NewBox {
dst: *dst,
box_type: box_name.clone(),
args: args.clone(),
};
current_instructions.push(mir_inst);
}
JoinInst::Call { JoinInst::Call {
func, func,
args, args,