From 6bcc70e07eebac25cb28e3ff6fa8516d54d6b63f Mon Sep 17 00:00:00 2001 From: nyash-codex Date: Sun, 14 Dec 2025 03:07:53 +0900 Subject: [PATCH] =?UTF-8?q?refactor(joinir):=20Phase=2089=20=E3=83=AA?= =?UTF-8?q?=E3=83=95=E3=82=A1=E3=82=AF=E3=82=BF=E3=83=AA=E3=83=B3=E3=82=B0?= =?UTF-8?q?=20-=205)=20fixture=E5=90=8DSSOT=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 変更内容: - 新規ファイル: src/mir/join_ir/normalized/dev_fixtures.rs (SSOT) - NormalizedDevFixture enum で fixture 名・パス・ルーティング統一管理 - ALL_DEV_FIXTURES 配列で一覧化 - fixture_content() / load_and_lower() ヘルパー実装 - FunctionRoute を route.rs に分離 - ast_lowerer/route.rs 新規作成 - resolve_function_route() を route.rs に移動 - dev fixtures を SSOT から自動登録 - fixtures.rs を簡潔化 - 4つの builder 関数を SSOT 呼び出しに変更 - 散在していた include_str! パスを削除 メリット: - typo・不一致によるルーティングミスを防止 - 新しい fixture 追加時は1箇所のみ変更 - 責務の明確化(route.rs / dev_fixtures.rs) テスト結果: - lib tests: 993 passed (回帰なし) - normalized_dev tests: 61 passed / 1 failed (ベースライン維持) Generated with Claude Code Co-Authored-By: Claude Sonnet 4.5 --- src/mir/join_ir/frontend/ast_lowerer/mod.rs | 94 +---------- src/mir/join_ir/frontend/ast_lowerer/route.rs | 94 +++++++++++ src/mir/join_ir/normalized.rs | 2 + src/mir/join_ir/normalized/dev_fixtures.rs | 158 ++++++++++++++++++ src/mir/join_ir/normalized/fixtures.rs | 80 +-------- 5 files changed, 264 insertions(+), 164 deletions(-) create mode 100644 src/mir/join_ir/frontend/ast_lowerer/route.rs create mode 100644 src/mir/join_ir/normalized/dev_fixtures.rs diff --git a/src/mir/join_ir/frontend/ast_lowerer/mod.rs b/src/mir/join_ir/frontend/ast_lowerer/mod.rs index 48bb4fea..452b7c77 100644 --- a/src/mir/join_ir/frontend/ast_lowerer/mod.rs +++ b/src/mir/join_ir/frontend/ast_lowerer/mod.rs @@ -35,106 +35,16 @@ mod loop_patterns; // Removed: loop_patterns_old (obsolete legacy dispatcher, all patterns now in loop_patterns/) mod nested_if; mod read_quoted; +pub(crate) mod route; mod stmt_handlers; #[cfg(test)] mod tests; pub(crate) use context::ExtractCtx; +pub(crate) use route::{resolve_function_route, FunctionRoute}; pub(crate) use stmt_handlers::StatementEffect; -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -enum FunctionRoute { - IfReturn, - LoopFrontend, - NestedIf, - ReadQuoted, -} - -fn resolve_function_route(func_name: &str) -> Result { - const TABLE: &[(&str, FunctionRoute)] = &[ - ("test", FunctionRoute::IfReturn), - ("local", FunctionRoute::IfReturn), - ("_read_value_from_pair", FunctionRoute::IfReturn), - ("simple", FunctionRoute::LoopFrontend), - ("filter", FunctionRoute::LoopFrontend), - ("print_tokens", FunctionRoute::LoopFrontend), - ("map", FunctionRoute::LoopFrontend), - ("reduce", FunctionRoute::LoopFrontend), - ("fold", FunctionRoute::LoopFrontend), - ("jsonparser_skip_ws_mini", FunctionRoute::LoopFrontend), - ("jsonparser_skip_ws_real", FunctionRoute::LoopFrontend), - ("jsonparser_atoi_mini", FunctionRoute::LoopFrontend), - ("jsonparser_atoi_real", FunctionRoute::LoopFrontend), - ("jsonparser_parse_number_real", FunctionRoute::LoopFrontend), - ("pattern3_if_sum_multi_min", FunctionRoute::LoopFrontend), - ("jsonparser_if_sum_min", FunctionRoute::LoopFrontend), - ("selfhost_token_scan_p2", FunctionRoute::LoopFrontend), - ("selfhost_token_scan_p2_accum", FunctionRoute::LoopFrontend), - ("selfhost_args_parse_p2", FunctionRoute::LoopFrontend), - ("selfhost_if_sum_p3", FunctionRoute::LoopFrontend), - ("selfhost_if_sum_p3_ext", FunctionRoute::LoopFrontend), - ("selfhost_stmt_count_p3", FunctionRoute::LoopFrontend), - // Phase 54: selfhost P2/P3 shape growth - ("selfhost_verify_schema_p2", FunctionRoute::LoopFrontend), - ("selfhost_detect_format_p3", FunctionRoute::LoopFrontend), - // Phase 48-A: Pattern4 continue minimal - ("pattern4_continue_minimal", FunctionRoute::LoopFrontend), - // Phase 48-B: JsonParser continue skip_ws fixtures - ( - "jsonparser_parse_array_continue_skip_ws", - FunctionRoute::LoopFrontend, - ), - ( - "jsonparser_parse_object_continue_skip_ws", - FunctionRoute::LoopFrontend, - ), - // Phase 88: JsonParser _unescape_string core (step2 + continue) minimal fixture - ( - "jsonparser_unescape_string_step2_min", - FunctionRoute::LoopFrontend, - ), - // Phase 89 P1: ContinueReturn pattern minimal fixture - ( - "pattern_continue_return_minimal", - FunctionRoute::LoopFrontend, - ), - ]; - - if let Some((_, route)) = TABLE.iter().find(|(name, _)| *name == func_name) { - return Ok(*route); - } - - if func_name == "parse_loop" { - if crate::config::env::joinir_dev_enabled() - && std::env::var("HAKO_JOINIR_NESTED_IF").ok().as_deref() == Some("1") - { - return Ok(FunctionRoute::NestedIf); - } - return Err( - "[joinir/frontend] 'parse_loop' requires HAKO_JOINIR_NESTED_IF=1 (dev only)" - .to_string(), - ); - } - - if func_name == "read_quoted_from" { - if crate::config::env::joinir_dev_enabled() - && std::env::var("HAKO_JOINIR_READ_QUOTED").ok().as_deref() == Some("1") - { - return Ok(FunctionRoute::ReadQuoted); - } - return Err( - "[joinir/frontend] 'read_quoted_from' requires HAKO_JOINIR_READ_QUOTED=1 (dev only)" - .to_string(), - ); - } - - Err(format!( - "[joinir/frontend] unsupported function '{}' (dev fixture not registered)", - func_name - )) -} - /// AST/CFG → JoinIR 変換器 /// /// Phase 34-2: Program(JSON v0) から tiny IfSelect ケースを JoinIR に変換 diff --git a/src/mir/join_ir/frontend/ast_lowerer/route.rs b/src/mir/join_ir/frontend/ast_lowerer/route.rs new file mode 100644 index 00000000..afb8d822 --- /dev/null +++ b/src/mir/join_ir/frontend/ast_lowerer/route.rs @@ -0,0 +1,94 @@ +//! Function routing for AST lowering +//! +//! Phase 89 リファクタリング: +//! - FunctionRoute の定義とルーティングロジックを集約 +//! - normalized_dev fixture は SSOT (dev_fixtures.rs) から自動登録 + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub(crate) enum FunctionRoute { + IfReturn, + LoopFrontend, + NestedIf, + ReadQuoted, +} + +pub(crate) fn resolve_function_route(func_name: &str) -> Result { + // Dev fixtures を SSOT から自動登録 + #[cfg(feature = "normalized_dev")] + { + use crate::mir::join_ir::normalized::dev_fixtures::ALL_DEV_FIXTURES; + for fixture in ALL_DEV_FIXTURES { + if func_name == fixture.function_name() { + return Ok(fixture.route()); + } + } + } + + // 通常のルーティングテーブル + const TABLE: &[(&str, FunctionRoute)] = &[ + ("test", FunctionRoute::IfReturn), + ("local", FunctionRoute::IfReturn), + ("_read_value_from_pair", FunctionRoute::IfReturn), + ("simple", FunctionRoute::LoopFrontend), + ("filter", FunctionRoute::LoopFrontend), + ("print_tokens", FunctionRoute::LoopFrontend), + ("map", FunctionRoute::LoopFrontend), + ("reduce", FunctionRoute::LoopFrontend), + ("fold", FunctionRoute::LoopFrontend), + ("jsonparser_skip_ws_mini", FunctionRoute::LoopFrontend), + ("jsonparser_skip_ws_real", FunctionRoute::LoopFrontend), + ("jsonparser_atoi_mini", FunctionRoute::LoopFrontend), + ("jsonparser_atoi_real", FunctionRoute::LoopFrontend), + ("jsonparser_parse_number_real", FunctionRoute::LoopFrontend), + ("pattern3_if_sum_multi_min", FunctionRoute::LoopFrontend), + ("jsonparser_if_sum_min", FunctionRoute::LoopFrontend), + ("selfhost_token_scan_p2", FunctionRoute::LoopFrontend), + ("selfhost_token_scan_p2_accum", FunctionRoute::LoopFrontend), + ("selfhost_args_parse_p2", FunctionRoute::LoopFrontend), + ("selfhost_if_sum_p3", FunctionRoute::LoopFrontend), + ("selfhost_if_sum_p3_ext", FunctionRoute::LoopFrontend), + ("selfhost_stmt_count_p3", FunctionRoute::LoopFrontend), + // Phase 54: selfhost P2/P3 shape growth + ("selfhost_verify_schema_p2", FunctionRoute::LoopFrontend), + ("selfhost_detect_format_p3", FunctionRoute::LoopFrontend), + // Phase 88: JsonParser _unescape_string core (step2 + continue) minimal fixture + ( + "jsonparser_unescape_string_step2_min", + FunctionRoute::LoopFrontend, + ), + // Note: Phase 48-A/48-B/89-P1 fixtures are auto-registered from dev_fixtures.rs + ]; + + if let Some((_, route)) = TABLE.iter().find(|(name, _)| *name == func_name) { + return Ok(*route); + } + + if func_name == "parse_loop" { + if crate::config::env::joinir_dev_enabled() + && std::env::var("HAKO_JOINIR_NESTED_IF").ok().as_deref() == Some("1") + { + return Ok(FunctionRoute::NestedIf); + } + return Err( + "[joinir/frontend] 'parse_loop' requires HAKO_JOINIR_NESTED_IF=1 (dev only)" + .to_string(), + ); + } + + if func_name == "read_quoted_from" { + if crate::config::env::joinir_dev_enabled() + && std::env::var("HAKO_JOINIR_READ_QUOTED").ok().as_deref() == Some("1") + { + return Ok(FunctionRoute::ReadQuoted); + } + return Err( + "[joinir/frontend] 'read_quoted_from' requires HAKO_JOINIR_READ_QUOTED=1 (dev only)" + .to_string(), + ); + } + + Err(format!( + "[joinir/frontend] unsupported function '{}' (dev fixture not registered)", + func_name + )) +} diff --git a/src/mir/join_ir/normalized.rs b/src/mir/join_ir/normalized.rs index dfeeaced..2479f7b5 100644 --- a/src/mir/join_ir/normalized.rs +++ b/src/mir/join_ir/normalized.rs @@ -20,6 +20,8 @@ pub mod fixtures; #[cfg(feature = "normalized_dev")] pub mod dev_env; #[cfg(feature = "normalized_dev")] +pub mod dev_fixtures; +#[cfg(feature = "normalized_dev")] pub mod shape_guard; #[cfg(feature = "normalized_dev")] use crate::mir::join_ir::normalized::shape_guard::NormalizedDevShape; diff --git a/src/mir/join_ir/normalized/dev_fixtures.rs b/src/mir/join_ir/normalized/dev_fixtures.rs new file mode 100644 index 00000000..4b7bdedc --- /dev/null +++ b/src/mir/join_ir/normalized/dev_fixtures.rs @@ -0,0 +1,158 @@ +//! Normalized dev fixture の SSOT (Single Source of Truth) +//! +//! Phase 89 リファクタリング: +//! - fixture 名・パス・ルーティング先を一箇所で管理 +//! - 散在する文字列リテラルを減らし、typo・不一致を防止 + +#![cfg(feature = "normalized_dev")] + +use super::super::frontend::ast_lowerer::route::FunctionRoute; + +/// Normalized dev fixture の列挙型(SSOT) +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum NormalizedDevFixture { + /// Pattern 4: Continue minimal (Phase 48-A) + Pattern4ContinueMinimal, + /// Pattern 4: JsonParser parse_array continue skip_ws (Phase 48-B) + Pattern4JsonParserParseArrayContinueSkipWs, + /// Pattern 4: JsonParser parse_object continue skip_ws (Phase 48-B) + Pattern4JsonParserParseObjectContinueSkipWs, + /// Pattern Continue + Early Return minimal (Phase 89 P1) + PatternContinueReturnMin, +} + +impl NormalizedDevFixture { + /// 関数名(allowlist・ルーティング用) + pub fn function_name(&self) -> &'static str { + match self { + Self::Pattern4ContinueMinimal => "pattern4_continue_minimal", + Self::Pattern4JsonParserParseArrayContinueSkipWs => { + "jsonparser_parse_array_continue_skip_ws" + } + Self::Pattern4JsonParserParseObjectContinueSkipWs => { + "jsonparser_parse_object_continue_skip_ws" + } + Self::PatternContinueReturnMin => "pattern_continue_return_minimal", + } + } + + /// include_str! パス(ドキュメント内 fixture) + pub fn fixture_path(&self) -> &'static str { + match self { + Self::Pattern4ContinueMinimal => { + "../../../../docs/private/roadmap2/phases/normalized_dev/fixtures/pattern4_continue_min.program.json" + } + Self::Pattern4JsonParserParseArrayContinueSkipWs => { + "../../../../docs/private/roadmap2/phases/normalized_dev/fixtures/jsonparser_parse_array_continue_skip_ws.program.json" + } + Self::Pattern4JsonParserParseObjectContinueSkipWs => { + "../../../../docs/private/roadmap2/phases/normalized_dev/fixtures/jsonparser_parse_object_continue_skip_ws.program.json" + } + Self::PatternContinueReturnMin => { + "../../../../docs/private/roadmap2/phases/normalized_dev/fixtures/pattern_continue_return_min.program.json" + } + } + } + + /// ルーティング先 + pub fn route(&self) -> FunctionRoute { + match self { + // すべて LoopFrontend ルーティング + Self::Pattern4ContinueMinimal + | Self::Pattern4JsonParserParseArrayContinueSkipWs + | Self::Pattern4JsonParserParseObjectContinueSkipWs + | Self::PatternContinueReturnMin => FunctionRoute::LoopFrontend, + } + } + + /// fixture の内容文字列を取得(include_str! ラッパー) + /// + /// Note: include_str! はコンパイル時展開なので、このメソッドは直接呼ばず + /// fixtures.rs の各 builder 関数内で使用される想定 + pub fn fixture_content(&self) -> &'static str { + match self { + Self::Pattern4ContinueMinimal => include_str!( + "../../../../docs/private/roadmap2/phases/normalized_dev/fixtures/pattern4_continue_min.program.json" + ), + Self::Pattern4JsonParserParseArrayContinueSkipWs => include_str!( + "../../../../docs/private/roadmap2/phases/normalized_dev/fixtures/jsonparser_parse_array_continue_skip_ws.program.json" + ), + Self::Pattern4JsonParserParseObjectContinueSkipWs => include_str!( + "../../../../docs/private/roadmap2/phases/normalized_dev/fixtures/jsonparser_parse_object_continue_skip_ws.program.json" + ), + Self::PatternContinueReturnMin => include_str!( + "../../../../docs/private/roadmap2/phases/normalized_dev/fixtures/pattern_continue_return_min.program.json" + ), + } + } + + /// fixture を読み込んで JoinModule に変換 + pub fn load_and_lower(&self) -> super::super::JoinModule { + use super::super::frontend::ast_lowerer::AstToJoinIrLowerer; + + let fixture_json = self.fixture_content(); + let program_json: serde_json::Value = serde_json::from_str(fixture_json) + .unwrap_or_else(|e| { + panic!( + "{} fixture should be valid JSON: {}", + self.function_name(), + e + ) + }); + + let mut lowerer = AstToJoinIrLowerer::new(); + lowerer.lower_program_json(&program_json) + } +} + +/// すべての normalized dev fixtures を列挙 +pub const ALL_DEV_FIXTURES: &[NormalizedDevFixture] = &[ + NormalizedDevFixture::Pattern4ContinueMinimal, + NormalizedDevFixture::Pattern4JsonParserParseArrayContinueSkipWs, + NormalizedDevFixture::Pattern4JsonParserParseObjectContinueSkipWs, + NormalizedDevFixture::PatternContinueReturnMin, +]; + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_all_fixtures_have_unique_names() { + use std::collections::HashSet; + let names: HashSet<_> = ALL_DEV_FIXTURES + .iter() + .map(|f| f.function_name()) + .collect(); + assert_eq!(names.len(), ALL_DEV_FIXTURES.len(), "Fixture names must be unique"); + } + + #[test] + fn test_all_fixtures_have_valid_paths() { + for fixture in ALL_DEV_FIXTURES { + let path = fixture.fixture_path(); + assert!( + path.ends_with(".program.json"), + "Fixture path must end with .program.json: {}", + path + ); + assert!( + path.contains("normalized_dev/fixtures/"), + "Fixture path must be in normalized_dev/fixtures/: {}", + path + ); + } + } + + #[test] + fn test_pattern4_fixtures_route_to_loop_frontend() { + for fixture in ALL_DEV_FIXTURES { + assert_eq!( + fixture.route(), + FunctionRoute::LoopFrontend, + "{} should route to LoopFrontend", + fixture.function_name() + ); + } + } +} diff --git a/src/mir/join_ir/normalized/fixtures.rs b/src/mir/join_ir/normalized/fixtures.rs index 6b653dc5..004c933d 100644 --- a/src/mir/join_ir/normalized/fixtures.rs +++ b/src/mir/join_ir/normalized/fixtures.rs @@ -728,72 +728,24 @@ pub fn build_pattern3_if_sum_min_structured_for_normalized_dev() -> JoinModule { /// /// Fixture: docs/private/roadmap2/phases/normalized_dev/fixtures/pattern4_continue_min.program.json pub fn build_pattern4_continue_min_structured_for_normalized_dev() -> JoinModule { - const FIXTURE: &str = include_str!( - "../../../../docs/private/roadmap2/phases/normalized_dev/fixtures/pattern4_continue_min.program.json" - ); - - let program_json: serde_json::Value = - serde_json::from_str(FIXTURE).expect("pattern4_continue_min fixture should be valid JSON"); - - let mut lowerer = AstToJoinIrLowerer::new(); - let module = lowerer.lower_program_json(&program_json); - - if joinir_dev_enabled() && joinir_test_debug_enabled() { - eprintln!( - "[joinir/normalized-dev] pattern4_continue_min structured module: {:#?}", - module - ); - } - - module + use super::dev_fixtures::NormalizedDevFixture; + NormalizedDevFixture::Pattern4ContinueMinimal.load_and_lower() } /// JsonParser _parse_array の whitespace continue ループを Structured で組み立てるヘルパー(dev-only)。 /// /// Fixture: docs/private/roadmap2/phases/normalized_dev/fixtures/jsonparser_parse_array_continue_skip_ws.program.json pub fn build_jsonparser_parse_array_continue_skip_ws_structured_for_normalized_dev() -> JoinModule { - const FIXTURE: &str = include_str!( - "../../../../docs/private/roadmap2/phases/normalized_dev/fixtures/jsonparser_parse_array_continue_skip_ws.program.json" - ); - - let program_json: serde_json::Value = serde_json::from_str(FIXTURE) - .expect("jsonparser_parse_array_continue_skip_ws fixture should be valid JSON"); - - let mut lowerer = AstToJoinIrLowerer::new(); - let module = lowerer.lower_program_json(&program_json); - - if joinir_dev_enabled() && joinir_test_debug_enabled() { - eprintln!( - "[joinir/normalized-dev] jsonparser_parse_array_continue_skip_ws structured module: {:#?}", - module - ); - } - - module + use super::dev_fixtures::NormalizedDevFixture; + NormalizedDevFixture::Pattern4JsonParserParseArrayContinueSkipWs.load_and_lower() } /// JsonParser _parse_object の whitespace continue ループを Structured で組み立てるヘルパー(dev-only)。 /// /// Fixture: docs/private/roadmap2/phases/normalized_dev/fixtures/jsonparser_parse_object_continue_skip_ws.program.json pub fn build_jsonparser_parse_object_continue_skip_ws_structured_for_normalized_dev() -> JoinModule { - const FIXTURE: &str = include_str!( - "../../../../docs/private/roadmap2/phases/normalized_dev/fixtures/jsonparser_parse_object_continue_skip_ws.program.json" - ); - - let program_json: serde_json::Value = serde_json::from_str(FIXTURE) - .expect("jsonparser_parse_object_continue_skip_ws fixture should be valid JSON"); - - let mut lowerer = AstToJoinIrLowerer::new(); - let module = lowerer.lower_program_json(&program_json); - - if joinir_dev_enabled() && joinir_test_debug_enabled() { - eprintln!( - "[joinir/normalized-dev] jsonparser_parse_object_continue_skip_ws structured module: {:#?}", - module - ); - } - - module + use super::dev_fixtures::NormalizedDevFixture; + NormalizedDevFixture::Pattern4JsonParserParseObjectContinueSkipWs.load_and_lower() } /// JsonParser _unescape_string の「i+=2 + continue」コアを Structured で組み立てるヘルパー(dev-only)。 @@ -827,24 +779,8 @@ pub fn build_jsonparser_unescape_string_step2_min_structured_for_normalized_dev( /// /// Fixture: docs/private/roadmap2/phases/normalized_dev/fixtures/pattern_continue_return_min.program.json pub fn build_pattern_continue_return_min_structured_for_normalized_dev() -> JoinModule { - const FIXTURE: &str = include_str!( - "../../../../docs/private/roadmap2/phases/normalized_dev/fixtures/pattern_continue_return_min.program.json" - ); - - let program_json: serde_json::Value = - serde_json::from_str(FIXTURE).expect("pattern_continue_return_min fixture should be valid JSON"); - - let mut lowerer = AstToJoinIrLowerer::new(); - let module = lowerer.lower_program_json(&program_json); - - if joinir_dev_enabled() && joinir_test_debug_enabled() { - eprintln!( - "[joinir/normalized-dev] pattern_continue_return_min structured module: {:#?}", - module - ); - } - - module + use super::dev_fixtures::NormalizedDevFixture; + NormalizedDevFixture::PatternContinueReturnMin.load_and_lower() } /// まとめて import したいとき用のプレリュード。