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

@ -71,6 +71,16 @@ pub enum LoopPattern {
}
impl LoopFrontendBinding {
/// Phase 52: Check if `me` receiver is needed for this loop
///
/// Returns true if any external_ref starts with "me" (e.g., "me.tokens")
/// This indicates an instance method that needs access to `me`.
pub fn needs_me_receiver(&self) -> bool {
self.external_refs
.iter()
.any(|r| r == "me" || r.starts_with("me."))
}
/// print_tokens 専用のバインディングを生成
///
/// print_tokens の構造:
@ -135,6 +145,44 @@ impl LoopFrontendBinding {
None
}
/// Phase 52: ドット区切りの変数名を Field ノード構造に変換
///
/// 例: "me.tokens" → {"type": "Field", "object": {"type": "Var", "name": "me"}, "field": "tokens"}
/// 例: "arr" → {"type": "Var", "name": "arr"}
fn receiver_to_json(receiver: &str) -> serde_json::Value {
use serde_json::json;
if let Some(dot_pos) = receiver.find('.') {
// ドット区切りがある場合 → Field ノードに分解
let object_name = &receiver[..dot_pos];
let field_name = &receiver[dot_pos + 1..];
// ネストしたフィールドアクセス (e.g., "a.b.c") は再帰的に処理
if field_name.contains('.') {
// "me.tokens.inner" → Field(Field(Var("me"), "tokens"), "inner")
let inner_receiver = Self::receiver_to_json(field_name);
json!({
"type": "Field",
"object": { "type": "Var", "name": object_name },
"field": inner_receiver
})
} else {
// 単一レベルのフィールドアクセス (e.g., "me.tokens")
json!({
"type": "Field",
"object": { "type": "Var", "name": object_name },
"field": field_name
})
}
} else {
// ドットなし → 単純な Var ノード
json!({
"type": "Var",
"name": receiver
})
}
}
/// JoinIR Frontend 用の JSON v0 Local 宣言を生成
///
/// Returns: (i_local, acc_local, n_local) の JSON Value タプル
@ -142,6 +190,7 @@ impl LoopFrontendBinding {
/// Note: JoinIR Frontend expects specific type names:
/// - "Int" for integer literals (with "value" field)
/// - "Var" for variable references (with "name" field)
/// - "Field" for field access (with "object", "field" fields) - Phase 52
/// - "Method" for method calls (with "receiver", "method", "args" fields)
/// - "NewBox" for box instantiation
pub fn generate_local_declarations(
@ -201,14 +250,15 @@ impl LoopFrontendBinding {
})
}
BoundExpr::MethodCall { receiver, method } => {
// メソッド呼び出しを評価print_tokens の me.tokens.length() 等)
// JoinIR Frontend expects "Method" type
// Phase 52: メソッド呼び出しを評価print_tokens の me.tokens.length() 等)
// receiver が "me.tokens" のようにドット区切りの場合は Field ノードに分解
let receiver_json = Self::receiver_to_json(receiver);
json!({
"type": "Local",
"name": "n",
"expr": {
"type": "Method",
"receiver": { "type": "Var", "name": receiver },
"receiver": receiver_json,
"method": method,
"args": []
}
@ -349,4 +399,40 @@ mod tests {
assert_eq!(n_local["expr"]["type"], "Var");
assert_eq!(n_local["expr"]["name"], "n");
}
// Phase 52: receiver_to_json のテスト
#[test]
fn test_receiver_to_json_simple_var() {
let json = LoopFrontendBinding::receiver_to_json("arr");
assert_eq!(json["type"], "Var");
assert_eq!(json["name"], "arr");
}
#[test]
fn test_receiver_to_json_field_access() {
let json = LoopFrontendBinding::receiver_to_json("me.tokens");
assert_eq!(json["type"], "Field");
assert_eq!(json["object"]["type"], "Var");
assert_eq!(json["object"]["name"], "me");
assert_eq!(json["field"], "tokens");
}
#[test]
fn test_print_tokens_n_local_has_field() {
// Phase 52: print_tokens の n (me.tokens.length()) が Field ノードを使っているか確認
let binding = LoopFrontendBinding::for_print_tokens();
let (_i_local, _acc_local, n_local) = binding.generate_local_declarations();
// n = me.tokens.length() の構造を確認
assert_eq!(n_local["name"], "n");
assert_eq!(n_local["expr"]["type"], "Method");
assert_eq!(n_local["expr"]["method"], "length");
// receiver が Field ノードであることを確認
let receiver = &n_local["expr"]["receiver"];
assert_eq!(receiver["type"], "Field");
assert_eq!(receiver["object"]["type"], "Var");
assert_eq!(receiver["object"]["name"], "me");
assert_eq!(receiver["field"], "tokens");
}
}