feat(joinir): Phase 52-53 LoopFrontendBinding JSON + Statement Handlers

Phase 52: LoopFrontendBinding JSON generation fixes
- Add receiver_to_json() for Field node structure (me.tokens)
- Add needs_me_receiver() for instance method detection
- Fix "condition" → "cond" key for JoinIR Frontend
- Add me parameter propagation in loop_patterns.rs
- Add JoinIR-compatible type fields in ast_json.rs
  - Variable → "type": "Var"
  - Literal → "type": "Int"/"Bool" (literal_to_joinir_json)
  - BinaryOp → "type": "Binary"/"Compare" (is_compare_op)
  - MethodCall → "type": "Method"

Phase 53: Statement Handler module for loop body
- NEW: stmt_handlers.rs with StatementEffect type
- Support: Local, Assignment, Print, Method, If statements
- If lowering: single variable update → Select instruction
- Remove hardcoded assert in loop_patterns.rs
- Replace with generic lower_statement() calls

Test results: 56 JoinIR tests PASS, 7 loop_frontend_binding tests PASS

🤖 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-29 04:42:16 +09:00
parent 6bb6f38a1c
commit e27934d91a
6 changed files with 545 additions and 53 deletions

View File

@ -79,19 +79,36 @@ pub fn ast_to_json(ast: &ASTNode) -> Value {
"static": is_static,
"override": is_override,
}),
ASTNode::Variable { name, .. } => json!({"kind":"Variable","name":name}),
ASTNode::Literal { value, .. } => json!({"kind":"Literal","value": lit_to_json(&value)}),
// Phase 52: Variable → Var ードJoinIR Frontend 互換)
ASTNode::Variable { name, .. } => json!({
"kind": "Variable",
"type": "Var", // JoinIR Frontend expects "type": "Var"
"name": name
}),
// Phase 52: Literal → Int/Bool/String ードJoinIR Frontend 互換)
ASTNode::Literal { value, .. } => literal_to_joinir_json(&value),
// Phase 52: BinaryOp → Binary/Compare ードJoinIR Frontend 互換)
ASTNode::BinaryOp {
operator,
left,
right,
..
} => json!({
"kind":"BinaryOp",
"op": bin_to_str(&operator),
"left": ast_to_json(&left),
"right": ast_to_json(&right),
}),
} => {
let op_str = bin_to_str(&operator);
// JoinIR Frontend distinguishes between Binary (arithmetic) and Compare
let type_str = if is_compare_op(&operator) { "Compare" } else { "Binary" };
json!({
"kind": "BinaryOp",
"type": type_str,
"op": op_str,
// JoinIR Frontend expects "lhs"/"rhs" not "left"/"right"
"lhs": ast_to_json(&left),
"rhs": ast_to_json(&right),
// Also keep "left"/"right" for backward compatibility
"left": ast_to_json(&left),
"right": ast_to_json(&right),
})
}
ASTNode::UnaryOp {
operator, operand, ..
} => json!({
@ -99,16 +116,22 @@ pub fn ast_to_json(ast: &ASTNode) -> Value {
"op": un_to_str(&operator),
"operand": ast_to_json(&operand),
}),
// Phase 52: MethodCall → Method ードJoinIR Frontend 互換)
ASTNode::MethodCall {
object,
method,
arguments,
..
} => json!({
"kind":"MethodCall",
"object": ast_to_json(&object),
"kind": "MethodCall",
"type": "Method", // JoinIR Frontend expects "type": "Method"
// JoinIR Frontend expects "receiver" not "object"
"receiver": ast_to_json(&object),
"object": ast_to_json(&object), // Keep for backward compatibility
"method": method,
"arguments": arguments.into_iter().map(|a| ast_to_json(&a)).collect::<Vec<_>>()
// JoinIR Frontend expects "args" not "arguments"
"args": arguments.iter().map(|a| ast_to_json(a)).collect::<Vec<_>>(),
"arguments": arguments.into_iter().map(|a| ast_to_json(&a)).collect::<Vec<_>>() // Keep for backward compatibility
}),
ASTNode::FunctionCall {
name, arguments, ..
@ -142,6 +165,28 @@ pub fn ast_to_json(ast: &ASTNode) -> Value {
})).collect::<Vec<_>>(),
"else": ast_to_json(&else_expr),
}),
// Phase 52: FieldAccess → Field ードJoinIR Frontend 互換)
ASTNode::FieldAccess { object, field, .. } => json!({
"kind": "FieldAccess",
"type": "Field", // JoinIR Frontend expects "type": "Field"
"object": ast_to_json(&object),
"field": field
}),
// Phase 52: Me → Var("me") ードJoinIR Frontend 互換)
ASTNode::Me { .. } => json!({
"kind": "Me",
"type": "Var", // JoinIR Frontend expects "type": "Var"
"name": "me"
}),
// Phase 52: New → NewBox ードJoinIR Frontend 互換)
ASTNode::New {
class, arguments, ..
} => json!({
"kind": "New",
"type": "NewBox", // JoinIR Frontend expects "type": "NewBox"
"box_name": class,
"args": arguments.into_iter().map(|a| ast_to_json(&a)).collect::<Vec<_>>()
}),
other => json!({"kind":"Unsupported","debug": format!("{:?}", other)}),
}
}
@ -459,3 +504,55 @@ fn str_to_un(s: &str) -> Option<UnaryOperator> {
_ => return None,
})
}
/// Phase 52: Check if a binary operator is a comparison operator
fn is_compare_op(op: &BinaryOperator) -> bool {
matches!(
op,
BinaryOperator::Equal
| BinaryOperator::NotEqual
| BinaryOperator::Less
| BinaryOperator::Greater
| BinaryOperator::LessEqual
| BinaryOperator::GreaterEqual
)
}
/// Phase 52: Convert a literal value to JoinIR-compatible JSON format
///
/// JoinIR Frontend expects:
/// - Integer: {"type": "Int", "value": <number>}
/// - Boolean: {"type": "Bool", "value": <bool>}
/// - String: {"type": "String", "value": <string>}
fn literal_to_joinir_json(v: &LiteralValue) -> Value {
match v {
LiteralValue::Integer(i) => json!({
"kind": "Literal",
"type": "Int", // JoinIR Frontend expects "type": "Int"
"value": i
}),
LiteralValue::Bool(b) => json!({
"kind": "Literal",
"type": "Bool", // JoinIR Frontend expects "type": "Bool"
"value": b
}),
LiteralValue::String(s) => json!({
"kind": "Literal",
"type": "String",
"value": s
}),
LiteralValue::Float(f) => json!({
"kind": "Literal",
"type": "Float",
"value": f
}),
LiteralValue::Null => json!({
"kind": "Literal",
"type": "Null"
}),
LiteralValue::Void => json!({
"kind": "Literal",
"type": "Void"
}),
}
}