feat(joinir): Phase 170-A Step 1 - CaseALoweringShape enum and detection
Introduce structure-based Case-A loop routing to replace hardcoded function name matching. Phase 170-A is about enabling generic routing for ANY loop matching Case-A structural properties. New Module: case_a_lowering_shape.rs - CaseALoweringShape enum: StringExamination, ArrayAccumulation, IterationWithAccumulation, Generic, NotCaseA - CaseALoweringShape::detect(scope) - heuristic-based shape detection - Supports helper methods: is_recognized(), name() Motivation: - Current loop_to_join.rs:378-402 hardcodes 4 function names - New functions with same structure can't be reused - Structure-based approach enables generic lowering Phase 170-A Roadmap: - Step 1: CaseALoweringShape enum ✅ (this commit) - Step 2: loop_to_join.rs shape-based routing (next) - Step 3: Deprecate is_case_a_minimal_target() (future) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@ -0,0 +1,165 @@
|
||||
//! Case-A Loop Lowering Shape Detection
|
||||
//!
|
||||
//! Phase 170-A: Structure-based routing for Case-A loops
|
||||
//!
|
||||
//! Replaces hardcoded function name matching with AST-based pattern detection.
|
||||
//! Enables reuse of Case-A lowering functions for ANY loop with matching structure,
|
||||
//! not just hardcoded function names like "Main.skip/1".
|
||||
|
||||
/// Recognized Case-A loop body patterns
|
||||
///
|
||||
/// Used to dispatch to appropriate lowerer when function name is unavailable.
|
||||
///
|
||||
/// Case-A loops are characterized by:
|
||||
/// - Single exit group (one loop exit path)
|
||||
/// - Progress carrier (monotonically increasing/decreasing variable that drives termination)
|
||||
/// - Pinned parameters (loop-invariant values that don't change during iteration)
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum CaseALoweringShape {
|
||||
/// String examination loop: conditional carrier update
|
||||
///
|
||||
/// Examples: Main.skip/1, FuncScannerBox.trim/1
|
||||
///
|
||||
/// Pattern:
|
||||
/// - Loop body: single if statement examining character
|
||||
/// - Condition: examines string character (e.g., ch == " ")
|
||||
/// - Then branch: updates carrier (e.g., i = i + 1)
|
||||
/// - Else branch: exits loop (break)
|
||||
/// - Carriers: 1 (progress variable i or e)
|
||||
/// - Return value: final progress carrier
|
||||
///
|
||||
/// Signature: (StringBox, ...) -> Integer
|
||||
StringExamination,
|
||||
|
||||
/// Array accumulation loop: linear iteration with collection mutation
|
||||
///
|
||||
/// Examples: FuncScannerBox.append_defs/2
|
||||
///
|
||||
/// Pattern:
|
||||
/// - Loop body: access arr[i], mutate collection (push/add)
|
||||
/// - Condition: i < array.length()
|
||||
/// - Increment: i = i + 1
|
||||
/// - Exit: void or collection mutation
|
||||
/// - Carriers: 1 (progress variable i)
|
||||
/// - Pinned: 1-2 (array/collection, optional length)
|
||||
///
|
||||
/// Signature: (CollectionBox, ...) -> Void or CollectionBox
|
||||
ArrayAccumulation,
|
||||
|
||||
/// Iteration with accumulation: linear iteration + value accumulation
|
||||
///
|
||||
/// Examples: Stage1UsingResolverBox.resolve_for_source/5
|
||||
///
|
||||
/// Pattern:
|
||||
/// - Loop body: access arr[i], update accumulator
|
||||
/// - Multiple carriers: progress (i) + result (prefix, sum, etc.)
|
||||
/// - Condition: i < array.length()
|
||||
/// - Exit: accumulated value
|
||||
/// - Carriers: 2+ (progress i + accumulator)
|
||||
/// - Pinned: 2+ (array, initial value, etc.)
|
||||
///
|
||||
/// Signature: (CollectionBox, InitialValueBox, ...) -> ResultBox
|
||||
IterationWithAccumulation,
|
||||
|
||||
/// Generic Case-A: falls outside recognized patterns
|
||||
///
|
||||
/// Structure is Case-A (single exit + progress carrier) but body
|
||||
/// doesn't match any known pattern.
|
||||
///
|
||||
/// Lowering: Should either:
|
||||
/// 1. Implement generic extraction from LoopForm (Phase 170-B+), or
|
||||
/// 2. Return None to trigger fallback to legacy LoopBuilder
|
||||
Generic,
|
||||
|
||||
/// Unknown: not a Case-A loop at all
|
||||
///
|
||||
/// Structural requirements not met:
|
||||
/// - Multiple exit groups, OR
|
||||
/// - No progress carrier
|
||||
///
|
||||
/// Should use Pattern 1-4 routing instead.
|
||||
NotCaseA,
|
||||
}
|
||||
|
||||
impl CaseALoweringShape {
|
||||
/// Detect lowering shape from LoopScopeShape
|
||||
///
|
||||
/// Phase 170-A: Heuristic-based shape detection
|
||||
///
|
||||
/// Returns NotCaseA if structural requirements not met.
|
||||
/// Returns one of the above if structural analysis suggests a pattern.
|
||||
///
|
||||
/// # Heuristics
|
||||
/// - Single carrier (1) → likely StringExamination or ArrayAccumulation
|
||||
/// - Multiple carriers (2+) → likely IterationWithAccumulation
|
||||
/// - Pinned count → disambiguation hint
|
||||
///
|
||||
/// Note: More sophisticated pattern matching (analyzing loop body structure)
|
||||
/// is deferred to Phase 170-B.
|
||||
pub fn detect(scope: &super::shape::LoopScopeShape) -> Self {
|
||||
use super::shape::LoopScopeShape;
|
||||
|
||||
// Check if structurally Case-A
|
||||
if scope.progress_carrier.is_none() {
|
||||
return CaseALoweringShape::NotCaseA;
|
||||
}
|
||||
|
||||
// Phase 170-A: Simple heuristic based on carrier count
|
||||
match scope.carriers.len() {
|
||||
0 => {
|
||||
// This shouldn't happen if progress_carrier is Some, but be safe
|
||||
CaseALoweringShape::NotCaseA
|
||||
}
|
||||
1 => {
|
||||
// Single carrier: could be StringExamination or ArrayAccumulation
|
||||
// Further distinction requires analyzing loop body (Phase 170-B)
|
||||
// For now, return Generic to allow both paths to be tried
|
||||
CaseALoweringShape::Generic
|
||||
}
|
||||
2.. => {
|
||||
// Multiple carriers: likely Iteration with accumulation
|
||||
// (progress carrier + accumulator)
|
||||
CaseALoweringShape::IterationWithAccumulation
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Is this a recognized lowering shape?
|
||||
pub fn is_recognized(&self) -> bool {
|
||||
!matches!(self, CaseALoweringShape::NotCaseA | CaseALoweringShape::Generic)
|
||||
}
|
||||
|
||||
/// Get human-readable name for tracing/debugging
|
||||
pub fn name(&self) -> &'static str {
|
||||
match self {
|
||||
CaseALoweringShape::StringExamination => "StringExamination",
|
||||
CaseALoweringShape::ArrayAccumulation => "ArrayAccumulation",
|
||||
CaseALoweringShape::IterationWithAccumulation => "IterationWithAccumulation",
|
||||
CaseALoweringShape::Generic => "Generic",
|
||||
CaseALoweringShape::NotCaseA => "NotCaseA",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_shape_display() {
|
||||
assert_eq!(CaseALoweringShape::StringExamination.name(), "StringExamination");
|
||||
assert_eq!(CaseALoweringShape::ArrayAccumulation.name(), "ArrayAccumulation");
|
||||
assert_eq!(CaseALoweringShape::IterationWithAccumulation.name(), "IterationWithAccumulation");
|
||||
assert_eq!(CaseALoweringShape::Generic.name(), "Generic");
|
||||
assert_eq!(CaseALoweringShape::NotCaseA.name(), "NotCaseA");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_is_recognized() {
|
||||
assert!(CaseALoweringShape::StringExamination.is_recognized());
|
||||
assert!(CaseALoweringShape::ArrayAccumulation.is_recognized());
|
||||
assert!(CaseALoweringShape::IterationWithAccumulation.is_recognized());
|
||||
assert!(!CaseALoweringShape::Generic.is_recognized());
|
||||
assert!(!CaseALoweringShape::NotCaseA.is_recognized());
|
||||
}
|
||||
}
|
||||
@ -4,16 +4,19 @@
|
||||
//! - `shape`: 変数分類のSSOTと質問系API
|
||||
//! - `builder`: LoopForm / 既存箱からの組み立て(Case-A ルーティング含む)
|
||||
//! - `case_a`: Case-A minimal ターゲット判定(Phase 30 F-3.1)
|
||||
//! - `case_a_lowering_shape`: Phase 170-A Case-A lowering shape detection (構造ベース)
|
||||
//! - `context`: generic_case_a 用の共通コンテキスト
|
||||
//! - `structural`: ループの構造的性質解析(Phase 48-5.5)
|
||||
|
||||
mod builder;
|
||||
mod case_a;
|
||||
mod case_a_lowering_shape;
|
||||
mod context;
|
||||
mod shape;
|
||||
mod structural;
|
||||
|
||||
pub(crate) use case_a::is_case_a_minimal_target;
|
||||
pub(crate) use case_a_lowering_shape::CaseALoweringShape;
|
||||
pub(crate) use context::CaseAContext;
|
||||
pub(crate) use shape::LoopScopeShape;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user