refactor(joinir): Phase 89 リファクタリング - 5) fixture名SSOT化

変更内容:
- 新規ファイル: 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 <noreply@anthropic.com>
This commit is contained in:
nyash-codex
2025-12-14 03:07:53 +09:00
parent 5b5f3b55d4
commit 6bcc70e07e
5 changed files with 264 additions and 164 deletions

View File

@ -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()
);
}
}
}

View File

@ -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 したいとき用のプレリュード。