feat(joinir): Phase 248 - Normalized JoinIR infrastructure

Major refactoring of JoinIR normalization pipeline:

Key changes:
- Structured→Normalized→MIR(direct) pipeline established
- ShapeGuard enhanced with Pattern2 loop validation
- dev_env.rs: New development fixtures and env control
- fixtures.rs: jsonparser_parse_number_real fixture
- normalized_bridge/direct.rs: Direct MIR generation from Normalized
- pattern2_step_schedule.rs: Extracted step scheduling logic

Files changed:
- normalized.rs: Enhanced NormalizedJoinModule with DevEnv support
- shape_guard.rs: Pattern2-specific validation (+300 lines)
- normalized_bridge.rs: Unified bridge with direct path
- loop_with_break_minimal.rs: Integrated step scheduling
- Deleted: step_schedule.rs (moved to pattern2_step_schedule.rs)

New files:
- param_guess.rs: Loop parameter inference
- pattern2_step_schedule.rs: Step scheduling for Pattern2
- phase43-norm-canon-p2-mid.md: Design doc

Tests: 937/937 PASS (+6 from baseline 931)

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

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
nyash-codex
2025-12-12 03:15:45 +09:00
parent 59caf5864c
commit ed8e2d3142
32 changed files with 1559 additions and 421 deletions

View File

@ -1,38 +1,111 @@
#![cfg(feature = "normalized_dev")]
use once_cell::sync::Lazy;
use std::sync::Mutex;
use std::sync::{Mutex, MutexGuard};
/// RAII guard for normalized_dev env toggling (NYASH_JOINIR_NORMALIZED_DEV_RUN).
/// 汚染防止のため tests/runner の両方で再利用できるようにここに置く
/// ネストを許可し、最初の呼び出し時の状態だけを保存・復元する
pub struct NormalizedDevEnvGuard {
_lock: std::sync::MutexGuard<'static, ()>,
prev: Option<String>,
active: bool,
}
static NORMALIZED_ENV_LOCK: Lazy<Mutex<()>> = Lazy::new(|| Mutex::new(()));
#[derive(Default)]
struct EnvState {
stack: Vec<Option<String>>,
}
static NORMALIZED_ENV_STATE: Lazy<Mutex<EnvState>> = Lazy::new(|| Mutex::new(EnvState::default()));
static NORMALIZED_TEST_LOCK: Lazy<Mutex<()>> = Lazy::new(|| Mutex::new(()));
impl NormalizedDevEnvGuard {
pub fn new(enabled: bool) -> Self {
let lock = NORMALIZED_ENV_LOCK
let mut state = NORMALIZED_ENV_STATE
.lock()
.expect("normalized env mutex poisoned");
// Save current value before overriding.
let prev = std::env::var("NYASH_JOINIR_NORMALIZED_DEV_RUN").ok();
state.stack.push(prev);
if enabled {
std::env::set_var("NYASH_JOINIR_NORMALIZED_DEV_RUN", "1");
} else {
std::env::remove_var("NYASH_JOINIR_NORMALIZED_DEV_RUN");
}
Self { _lock: lock, prev }
Self { active: true }
}
}
impl Drop for NormalizedDevEnvGuard {
fn drop(&mut self) {
if let Some(prev) = &self.prev {
std::env::set_var("NYASH_JOINIR_NORMALIZED_DEV_RUN", prev);
} else {
std::env::remove_var("NYASH_JOINIR_NORMALIZED_DEV_RUN");
if !self.active {
return;
}
let mut state = NORMALIZED_ENV_STATE
.lock()
.expect("normalized env mutex poisoned");
if let Some(prev) = state.stack.pop() {
if let Some(prev) = prev {
std::env::set_var("NYASH_JOINIR_NORMALIZED_DEV_RUN", prev);
} else {
std::env::remove_var("NYASH_JOINIR_NORMALIZED_DEV_RUN");
}
}
}
}
/// normalized_dev feature + env の ON/OFF をまとめた判定。
pub fn normalized_dev_enabled() -> bool {
crate::config::env::normalized_dev_enabled()
}
/// normalized_dev かつ test/debug ログが有効なときだけ true。
pub fn normalized_dev_logs_enabled() -> bool {
crate::config::env::normalized_dev_enabled() && crate::config::env::joinir_test_debug_enabled()
}
/// テスト用コンテキストenv を ON にしつつロックで並列汚染を防ぐ。
pub struct NormalizedTestContext<'a> {
_lock: MutexGuard<'a, ()>,
_env_guard: NormalizedDevEnvGuard,
}
impl<'a> NormalizedTestContext<'a> {
fn new(lock: MutexGuard<'a, ()>) -> Self {
let env_guard = NormalizedDevEnvGuard::new(true);
NormalizedTestContext {
_lock: lock,
_env_guard: env_guard,
}
}
}
/// テストで使う共通ガード。
pub fn test_ctx() -> NormalizedTestContext<'static> {
let lock = NORMALIZED_TEST_LOCK
.lock()
.unwrap_or_else(|e| e.into_inner());
NormalizedTestContext::new(lock)
}
/// 簡易ラッパー:クロージャを normalized_dev ON で実行。
pub fn with_dev_env<F, R>(f: F) -> R
where
F: FnOnce() -> R,
{
let _ctx = test_ctx();
f()
}
/// env が既に ON のときはそのまま、OFF のときだけ with_dev_env を噛ませる。
pub fn with_dev_env_if_unset<F, R>(f: F) -> R
where
F: FnOnce() -> R,
{
if normalized_dev_enabled() {
f()
} else {
with_dev_env(f)
}
}