phase29ai(p5): route JoinIR plan extraction via single_planner
This commit is contained in:
@ -407,7 +407,7 @@ mod tests {
|
||||
},
|
||||
];
|
||||
|
||||
let ctx = LoopPatternContext::new(&condition, &body, "parse_number_like", true);
|
||||
let ctx = LoopPatternContext::new(&condition, &body, "parse_number_like", true, false);
|
||||
let builder = MirBuilder::new();
|
||||
|
||||
assert_eq!(ctx.pattern_kind, LoopPatternKind::Pattern2Break);
|
||||
@ -452,7 +452,7 @@ mod tests {
|
||||
},
|
||||
];
|
||||
|
||||
let ctx = LoopPatternContext::new(&condition, &body, "_atoi", true);
|
||||
let ctx = LoopPatternContext::new(&condition, &body, "_atoi", true, false);
|
||||
let builder = MirBuilder::new();
|
||||
|
||||
// Verify pattern classification
|
||||
|
||||
@ -28,6 +28,7 @@ use crate::mir::loop_pattern_detection::{LoopFeatures, LoopPatternKind};
|
||||
use crate::mir::builder::control_flow::plan::lowerer::PlanLowerer;
|
||||
use crate::mir::builder::control_flow::plan::normalizer::PlanNormalizer;
|
||||
use crate::mir::builder::control_flow::plan::verifier::PlanVerifier;
|
||||
use crate::mir::builder::control_flow::plan::single_planner;
|
||||
|
||||
/// AST Feature Extractor (declared in mod.rs as pub module, import from parent)
|
||||
use super::ast_feature_extractor as ast_features;
|
||||
@ -49,6 +50,9 @@ pub(crate) struct LoopPatternContext<'a> {
|
||||
/// Debug logging enabled
|
||||
pub debug: bool,
|
||||
|
||||
/// In static box context? (affects Pattern8 routing)
|
||||
pub in_static_box: bool,
|
||||
|
||||
/// Has continue statement(s) in body? (Phase 194+)
|
||||
#[allow(dead_code)]
|
||||
pub has_continue: bool,
|
||||
@ -93,6 +97,7 @@ impl<'a> LoopPatternContext<'a> {
|
||||
body: &'a [ASTNode],
|
||||
func_name: &'a str,
|
||||
debug: bool,
|
||||
in_static_box: bool,
|
||||
) -> Self {
|
||||
// Use AST Feature Extractor for break/continue detection
|
||||
let has_continue = ast_features::detect_continue_in_body(body);
|
||||
@ -110,6 +115,7 @@ impl<'a> LoopPatternContext<'a> {
|
||||
body,
|
||||
func_name,
|
||||
debug,
|
||||
in_static_box,
|
||||
has_continue,
|
||||
has_break,
|
||||
features,
|
||||
@ -126,9 +132,10 @@ impl<'a> LoopPatternContext<'a> {
|
||||
body: &'a [ASTNode],
|
||||
func_name: &'a str,
|
||||
debug: bool,
|
||||
in_static_box: bool,
|
||||
fn_body: &'a [ASTNode],
|
||||
) -> Self {
|
||||
let mut ctx = Self::new(condition, body, func_name, debug);
|
||||
let mut ctx = Self::new(condition, body, func_name, debug, in_static_box);
|
||||
ctx.fn_body = Some(fn_body);
|
||||
ctx
|
||||
}
|
||||
@ -159,157 +166,7 @@ fn lower_via_plan(
|
||||
PlanLowerer::lower(builder, core_plan, ctx)
|
||||
}
|
||||
|
||||
/// Phase 287: Plan extractor function type for routing table
|
||||
///
|
||||
/// Extractors have two signatures:
|
||||
/// - Simple: (condition, body) - Pattern1/4/9
|
||||
/// - WithFnBody: (condition, body, fn_body) - Pattern6/7
|
||||
type SimplePlanExtractor = fn(&ASTNode, &[ASTNode]) -> Result<Option<crate::mir::builder::control_flow::plan::DomainPlan>, String>;
|
||||
|
||||
/// Phase 287: Plan extractor table entry
|
||||
struct PlanExtractorEntry {
|
||||
/// Pattern name for debug logging
|
||||
name: &'static str,
|
||||
|
||||
/// Extractor function
|
||||
extractor: PlanExtractorVariant,
|
||||
}
|
||||
|
||||
/// Phase 287: Extractor function variant (handles different signatures)
|
||||
enum PlanExtractorVariant {
|
||||
/// Simple extractor: (condition, body)
|
||||
Simple(SimplePlanExtractor),
|
||||
|
||||
/// Extractor with fn_body: (condition, body, fn_body) - Pattern6
|
||||
WithFnBody(fn(&ASTNode, &[ASTNode], Option<&[ASTNode]>) -> Result<Option<crate::mir::builder::control_flow::plan::DomainPlan>, String>),
|
||||
|
||||
/// Extractor with post_loop_code: (condition, body, post_loop_code) - Pattern7
|
||||
///
|
||||
/// NOTE (Phase 286): Currently always called with &[] for post_loop_code.
|
||||
/// This variant is kept for future extension (post-loop segment analysis).
|
||||
/// The _post_loop_code parameter in Pattern7 extractor is intentionally unused.
|
||||
WithPostLoop(fn(&ASTNode, &[ASTNode], &[ASTNode]) -> Result<Option<crate::mir::builder::control_flow::plan::DomainPlan>, String>),
|
||||
}
|
||||
|
||||
/// Phase 287: Plan extractor routing table (SSOT)
|
||||
///
|
||||
/// Order is important: more specific patterns first
|
||||
/// - Pattern6/7: Need fn_body for capture analysis
|
||||
/// - Pattern8: Bool predicate scan (early-exit return, more specific)
|
||||
/// - Pattern3: If-phi merge (carrier with conditional update, more specific than Pattern4)
|
||||
/// - Pattern4: Continue loop (more specific than Pattern1)
|
||||
/// - Pattern9: Accum const loop (2 carriers, more specific than Pattern1)
|
||||
/// - Pattern1: Simple while (fallback)
|
||||
static PLAN_EXTRACTORS: &[PlanExtractorEntry] = &[
|
||||
PlanExtractorEntry {
|
||||
name: "Pattern6_ScanWithInit (Phase 273)",
|
||||
extractor: PlanExtractorVariant::WithFnBody(super::pattern6_scan_with_init::extract_scan_with_init_plan),
|
||||
},
|
||||
PlanExtractorEntry {
|
||||
name: "Pattern7_SplitScan (Phase 273)",
|
||||
extractor: PlanExtractorVariant::WithPostLoop(super::pattern7_split_scan::extract_split_scan_plan),
|
||||
},
|
||||
PlanExtractorEntry {
|
||||
name: "Pattern5_InfiniteEarlyExit (Phase 286 P3.2)",
|
||||
extractor: PlanExtractorVariant::Simple(super::extractors::pattern5::extract_pattern5_plan),
|
||||
},
|
||||
PlanExtractorEntry {
|
||||
name: "Pattern8_BoolPredicateScan (Phase 286 P2.4)",
|
||||
extractor: PlanExtractorVariant::Simple(super::extractors::pattern8::extract_pattern8_plan),
|
||||
},
|
||||
PlanExtractorEntry {
|
||||
name: "Pattern3_IfPhi (Phase 286 P2.6)",
|
||||
extractor: PlanExtractorVariant::Simple(super::extractors::pattern3::extract_pattern3_plan),
|
||||
},
|
||||
PlanExtractorEntry {
|
||||
name: "Pattern4_Continue (Phase 286 P2)",
|
||||
extractor: PlanExtractorVariant::Simple(super::extractors::pattern4::extract_pattern4_plan),
|
||||
},
|
||||
PlanExtractorEntry {
|
||||
name: "Pattern9_AccumConstLoop (Phase 286 P2.3)",
|
||||
extractor: PlanExtractorVariant::Simple(super::extractors::pattern9::extract_pattern9_plan),
|
||||
},
|
||||
PlanExtractorEntry {
|
||||
name: "Pattern2_Break (Phase 286 P3.1)",
|
||||
extractor: PlanExtractorVariant::Simple(super::extractors::pattern2::extract_pattern2_plan),
|
||||
},
|
||||
PlanExtractorEntry {
|
||||
name: "Pattern1_SimpleWhile (Phase 286 P2.1)",
|
||||
extractor: PlanExtractorVariant::Simple(super::extractors::pattern1::extract_pattern1_plan),
|
||||
},
|
||||
];
|
||||
|
||||
/// Phase 287: Try all plan extractors in priority order
|
||||
///
|
||||
/// Returns Ok(Some(value_id)) if any extractor succeeds
|
||||
/// Returns Ok(None) if no extractor matches
|
||||
/// Returns Err if extraction or lowering fails
|
||||
fn try_plan_extractors(
|
||||
builder: &mut MirBuilder,
|
||||
ctx: &LoopPatternContext,
|
||||
) -> Result<Option<ValueId>, String> {
|
||||
use super::super::trace;
|
||||
|
||||
for entry in PLAN_EXTRACTORS {
|
||||
// Phase 286 P2.6: Pattern1 Plan guard (structural Fail-Fast)
|
||||
// Pattern1 should only match Pattern1SimpleWhile pattern_kind
|
||||
// This prevents Pattern1 from incorrectly matching Pattern3 fixtures
|
||||
if entry.name.contains("Pattern1") {
|
||||
use crate::mir::loop_pattern_detection::LoopPatternKind;
|
||||
if ctx.pattern_kind != LoopPatternKind::Pattern1SimpleWhile {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// Try extraction based on variant
|
||||
let plan_opt = match &entry.extractor {
|
||||
PlanExtractorVariant::Simple(extractor) => {
|
||||
extractor(ctx.condition, ctx.body)?
|
||||
}
|
||||
PlanExtractorVariant::WithFnBody(extractor) => {
|
||||
// Pattern6: needs fn_body for capture analysis
|
||||
extractor(ctx.condition, ctx.body, ctx.fn_body)?
|
||||
}
|
||||
PlanExtractorVariant::WithPostLoop(extractor) => {
|
||||
// Pattern7: uses empty slice for post_loop_code (3rd param)
|
||||
extractor(ctx.condition, ctx.body, &[])?
|
||||
}
|
||||
};
|
||||
|
||||
// If extraction succeeded, check if we can lower it
|
||||
if let Some(domain_plan) = plan_opt {
|
||||
// Phase 286 P3: Pattern8 static box filtering
|
||||
// Plan extractors are pure (no builder access), so we filter here
|
||||
// Static box loops with `me.method()` should be handled by ReceiverNormalizeBox, not Pattern8
|
||||
if entry.name.contains("Pattern8") && builder.comp_ctx.current_static_box.is_some() {
|
||||
if ctx.debug {
|
||||
trace::trace().debug(
|
||||
"route/plan",
|
||||
&format!("{} extracted but rejected: static box context (fallback to legacy)", entry.name),
|
||||
);
|
||||
}
|
||||
// Skip this pattern, try next extractor
|
||||
continue;
|
||||
}
|
||||
|
||||
let log_msg = format!("route=plan strategy=extract pattern={}", entry.name);
|
||||
trace::trace().pattern("route", &log_msg, true);
|
||||
|
||||
// Phase 286 P2.6.1: Fail-Fast 統一 - extract 成功 → normalize/lower 失敗は即 Err
|
||||
// Pattern3 stub fallback 撤去(normalizer 実装済み)
|
||||
return lower_via_plan(builder, domain_plan, ctx);
|
||||
} else {
|
||||
// Extraction returned None - try next extractor
|
||||
if ctx.debug {
|
||||
let debug_msg = format!("{} extraction returned None, trying next pattern", entry.name);
|
||||
trace::trace().debug("route", &debug_msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// No extractor matched
|
||||
Ok(None)
|
||||
}
|
||||
// Phase 29ai P5: Plan extractor routing moved to `plan::single_planner`.
|
||||
|
||||
/// Phase 272 P0.2 Refactoring: can_lower() strategy classification
|
||||
///
|
||||
@ -473,10 +330,9 @@ pub(crate) fn route_loop_pattern(
|
||||
) -> Result<Option<ValueId>, String> {
|
||||
use super::super::trace;
|
||||
|
||||
// Phase 287: Try all Plan extractors in priority order (Pattern6/7/4/9/1)
|
||||
// This replaces ~100 lines of repetitive extraction blocks
|
||||
if let Some(value_id) = try_plan_extractors(builder, ctx)? {
|
||||
return Ok(Some(value_id));
|
||||
// Phase 29ai P5: Single entrypoint for plan extraction (router has no rule table).
|
||||
if let Some(domain_plan) = single_planner::try_build_domain_plan(ctx)? {
|
||||
return lower_via_plan(builder, domain_plan, ctx);
|
||||
}
|
||||
|
||||
// Phase 183: Route based on pre-classified pattern kind
|
||||
|
||||
@ -452,15 +452,16 @@ impl MirBuilder {
|
||||
}
|
||||
),
|
||||
);
|
||||
let in_static_box = self.comp_ctx.current_static_box.is_some();
|
||||
let ctx = if let Some(ref fn_body) = fn_body_clone {
|
||||
trace::trace().routing(
|
||||
"router",
|
||||
func_name,
|
||||
&format!("Creating ctx with fn_body ({} nodes)", fn_body.len()),
|
||||
);
|
||||
LoopPatternContext::with_fn_body(condition, body, &func_name, debug, fn_body)
|
||||
LoopPatternContext::with_fn_body(condition, body, &func_name, debug, in_static_box, fn_body)
|
||||
} else {
|
||||
LoopPatternContext::new(condition, body, &func_name, debug)
|
||||
LoopPatternContext::new(condition, body, &func_name, debug, in_static_box)
|
||||
};
|
||||
|
||||
// Phase 137-4: Router parity verification (after ctx is created)
|
||||
|
||||
@ -35,6 +35,8 @@ pub(in crate::mir::builder) mod facts;
|
||||
pub(in crate::mir::builder) mod normalize;
|
||||
pub(in crate::mir::builder) mod planner;
|
||||
pub(in crate::mir::builder) mod emit;
|
||||
// Phase 29ai P5: JoinIR router → single plan extraction entrypoint
|
||||
pub(in crate::mir::builder) mod single_planner;
|
||||
|
||||
// ============================================================================
|
||||
// DomainPlan (Pattern固有)
|
||||
|
||||
@ -0,0 +1,14 @@
|
||||
//! Phase 29ai P5: Legacy rule wrappers (thin bridge)
|
||||
//!
|
||||
//! These wrappers must preserve the legacy extractor behavior and error strings.
|
||||
|
||||
pub(in crate::mir::builder) mod pattern1;
|
||||
pub(in crate::mir::builder) mod pattern2;
|
||||
pub(in crate::mir::builder) mod pattern3;
|
||||
pub(in crate::mir::builder) mod pattern4;
|
||||
pub(in crate::mir::builder) mod pattern5;
|
||||
pub(in crate::mir::builder) mod pattern6;
|
||||
pub(in crate::mir::builder) mod pattern7;
|
||||
pub(in crate::mir::builder) mod pattern8;
|
||||
pub(in crate::mir::builder) mod pattern9;
|
||||
|
||||
@ -0,0 +1,9 @@
|
||||
use crate::mir::builder::control_flow::joinir::patterns::router::LoopPatternContext;
|
||||
use crate::mir::builder::control_flow::plan::DomainPlan;
|
||||
use crate::mir::builder::control_flow::joinir::patterns::extractors::pattern1::extract_pattern1_plan;
|
||||
|
||||
pub(in crate::mir::builder) fn extract(
|
||||
ctx: &LoopPatternContext,
|
||||
) -> Result<Option<DomainPlan>, String> {
|
||||
extract_pattern1_plan(ctx.condition, ctx.body)
|
||||
}
|
||||
@ -0,0 +1,9 @@
|
||||
use crate::mir::builder::control_flow::joinir::patterns::router::LoopPatternContext;
|
||||
use crate::mir::builder::control_flow::plan::DomainPlan;
|
||||
use crate::mir::builder::control_flow::joinir::patterns::extractors::pattern2::extract_pattern2_plan;
|
||||
|
||||
pub(in crate::mir::builder) fn extract(
|
||||
ctx: &LoopPatternContext,
|
||||
) -> Result<Option<DomainPlan>, String> {
|
||||
extract_pattern2_plan(ctx.condition, ctx.body)
|
||||
}
|
||||
@ -0,0 +1,9 @@
|
||||
use crate::mir::builder::control_flow::joinir::patterns::router::LoopPatternContext;
|
||||
use crate::mir::builder::control_flow::plan::DomainPlan;
|
||||
use crate::mir::builder::control_flow::joinir::patterns::extractors::pattern3::extract_pattern3_plan;
|
||||
|
||||
pub(in crate::mir::builder) fn extract(
|
||||
ctx: &LoopPatternContext,
|
||||
) -> Result<Option<DomainPlan>, String> {
|
||||
extract_pattern3_plan(ctx.condition, ctx.body)
|
||||
}
|
||||
@ -0,0 +1,9 @@
|
||||
use crate::mir::builder::control_flow::joinir::patterns::router::LoopPatternContext;
|
||||
use crate::mir::builder::control_flow::plan::DomainPlan;
|
||||
use crate::mir::builder::control_flow::joinir::patterns::extractors::pattern4::extract_pattern4_plan;
|
||||
|
||||
pub(in crate::mir::builder) fn extract(
|
||||
ctx: &LoopPatternContext,
|
||||
) -> Result<Option<DomainPlan>, String> {
|
||||
extract_pattern4_plan(ctx.condition, ctx.body)
|
||||
}
|
||||
@ -0,0 +1,9 @@
|
||||
use crate::mir::builder::control_flow::joinir::patterns::router::LoopPatternContext;
|
||||
use crate::mir::builder::control_flow::plan::DomainPlan;
|
||||
use crate::mir::builder::control_flow::joinir::patterns::extractors::pattern5::extract_pattern5_plan;
|
||||
|
||||
pub(in crate::mir::builder) fn extract(
|
||||
ctx: &LoopPatternContext,
|
||||
) -> Result<Option<DomainPlan>, String> {
|
||||
extract_pattern5_plan(ctx.condition, ctx.body)
|
||||
}
|
||||
@ -0,0 +1,9 @@
|
||||
use crate::mir::builder::control_flow::joinir::patterns::router::LoopPatternContext;
|
||||
use crate::mir::builder::control_flow::plan::DomainPlan;
|
||||
use crate::mir::builder::control_flow::joinir::patterns::pattern6_scan_with_init::extract_scan_with_init_plan;
|
||||
|
||||
pub(in crate::mir::builder) fn extract(
|
||||
ctx: &LoopPatternContext,
|
||||
) -> Result<Option<DomainPlan>, String> {
|
||||
extract_scan_with_init_plan(ctx.condition, ctx.body, ctx.fn_body)
|
||||
}
|
||||
@ -0,0 +1,9 @@
|
||||
use crate::mir::builder::control_flow::joinir::patterns::router::LoopPatternContext;
|
||||
use crate::mir::builder::control_flow::plan::DomainPlan;
|
||||
use crate::mir::builder::control_flow::joinir::patterns::pattern7_split_scan::extract_split_scan_plan;
|
||||
|
||||
pub(in crate::mir::builder) fn extract(
|
||||
ctx: &LoopPatternContext,
|
||||
) -> Result<Option<DomainPlan>, String> {
|
||||
extract_split_scan_plan(ctx.condition, ctx.body, &[])
|
||||
}
|
||||
@ -0,0 +1,9 @@
|
||||
use crate::mir::builder::control_flow::joinir::patterns::router::LoopPatternContext;
|
||||
use crate::mir::builder::control_flow::plan::DomainPlan;
|
||||
use crate::mir::builder::control_flow::joinir::patterns::extractors::pattern8::extract_pattern8_plan;
|
||||
|
||||
pub(in crate::mir::builder) fn extract(
|
||||
ctx: &LoopPatternContext,
|
||||
) -> Result<Option<DomainPlan>, String> {
|
||||
extract_pattern8_plan(ctx.condition, ctx.body)
|
||||
}
|
||||
@ -0,0 +1,9 @@
|
||||
use crate::mir::builder::control_flow::joinir::patterns::router::LoopPatternContext;
|
||||
use crate::mir::builder::control_flow::plan::DomainPlan;
|
||||
use crate::mir::builder::control_flow::joinir::patterns::extractors::pattern9::extract_pattern9_plan;
|
||||
|
||||
pub(in crate::mir::builder) fn extract(
|
||||
ctx: &LoopPatternContext,
|
||||
) -> Result<Option<DomainPlan>, String> {
|
||||
extract_pattern9_plan(ctx.condition, ctx.body)
|
||||
}
|
||||
18
src/mir/builder/control_flow/plan/single_planner/mod.rs
Normal file
18
src/mir/builder/control_flow/plan/single_planner/mod.rs
Normal file
@ -0,0 +1,18 @@
|
||||
//! Phase 29ai P5: Single-planner bridge (router → 1 entrypoint)
|
||||
//!
|
||||
//! SSOT entrypoint for DomainPlan extraction. Router should call only this.
|
||||
//! Contract: keep `Result<_, String>` to preserve existing behavior/messages.
|
||||
|
||||
use crate::mir::builder::control_flow::joinir::patterns::router::LoopPatternContext;
|
||||
|
||||
use super::DomainPlan;
|
||||
|
||||
pub(in crate::mir::builder) mod legacy_rules;
|
||||
mod rules;
|
||||
|
||||
pub(in crate::mir::builder) fn try_build_domain_plan(
|
||||
ctx: &LoopPatternContext,
|
||||
) -> Result<Option<DomainPlan>, String> {
|
||||
rules::try_build_domain_plan(ctx)
|
||||
}
|
||||
|
||||
111
src/mir/builder/control_flow/plan/single_planner/rules.rs
Normal file
111
src/mir/builder/control_flow/plan/single_planner/rules.rs
Normal file
@ -0,0 +1,111 @@
|
||||
//! Phase 29ai P5: Rule list (order SSOT) + guards
|
||||
//!
|
||||
//! IMPORTANT: Keep rule order identical to the legacy `PLAN_EXTRACTORS` table
|
||||
//! (observability/behavior must not change).
|
||||
|
||||
use crate::mir::builder::control_flow::joinir::patterns::router::LoopPatternContext;
|
||||
use crate::mir::loop_pattern_detection::LoopPatternKind;
|
||||
|
||||
use super::legacy_rules;
|
||||
use crate::mir::builder::control_flow::plan::DomainPlan;
|
||||
|
||||
pub(super) fn try_build_domain_plan(ctx: &LoopPatternContext) -> Result<Option<DomainPlan>, String> {
|
||||
use crate::mir::builder::control_flow::joinir::trace;
|
||||
|
||||
// NOTE: Names must match the previous PLAN_EXTRACTORS entries exactly.
|
||||
// Order must match exactly.
|
||||
let rules: &[RuleEntry] = &[
|
||||
RuleEntry {
|
||||
name: "Pattern6_ScanWithInit (Phase 273)",
|
||||
kind: RuleKind::Pattern6,
|
||||
},
|
||||
RuleEntry {
|
||||
name: "Pattern7_SplitScan (Phase 273)",
|
||||
kind: RuleKind::Pattern7,
|
||||
},
|
||||
RuleEntry {
|
||||
name: "Pattern5_InfiniteEarlyExit (Phase 286 P3.2)",
|
||||
kind: RuleKind::Simple(legacy_rules::pattern5::extract),
|
||||
},
|
||||
RuleEntry {
|
||||
name: "Pattern8_BoolPredicateScan (Phase 286 P2.4)",
|
||||
kind: RuleKind::Simple(legacy_rules::pattern8::extract),
|
||||
},
|
||||
RuleEntry {
|
||||
name: "Pattern3_IfPhi (Phase 286 P2.6)",
|
||||
kind: RuleKind::Simple(legacy_rules::pattern3::extract),
|
||||
},
|
||||
RuleEntry {
|
||||
name: "Pattern4_Continue (Phase 286 P2)",
|
||||
kind: RuleKind::Simple(legacy_rules::pattern4::extract),
|
||||
},
|
||||
RuleEntry {
|
||||
name: "Pattern9_AccumConstLoop (Phase 286 P2.3)",
|
||||
kind: RuleKind::Simple(legacy_rules::pattern9::extract),
|
||||
},
|
||||
RuleEntry {
|
||||
name: "Pattern2_Break (Phase 286 P3.1)",
|
||||
kind: RuleKind::Simple(legacy_rules::pattern2::extract),
|
||||
},
|
||||
RuleEntry {
|
||||
name: "Pattern1_SimpleWhile (Phase 286 P2.1)",
|
||||
kind: RuleKind::Simple(legacy_rules::pattern1::extract),
|
||||
},
|
||||
];
|
||||
|
||||
for entry in rules {
|
||||
// Phase 286 P2.6: Pattern1 Plan guard (structural Fail-Fast)
|
||||
// Pattern1 should only match Pattern1SimpleWhile pattern_kind
|
||||
// This prevents Pattern1 from incorrectly matching Pattern3 fixtures
|
||||
if entry.name.contains("Pattern1") && ctx.pattern_kind != LoopPatternKind::Pattern1SimpleWhile
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
let plan_opt = match entry.kind {
|
||||
RuleKind::Simple(extract) => extract(ctx),
|
||||
RuleKind::Pattern6 => legacy_rules::pattern6::extract(ctx),
|
||||
RuleKind::Pattern7 => legacy_rules::pattern7::extract(ctx),
|
||||
}?;
|
||||
|
||||
if let Some(domain_plan) = plan_opt {
|
||||
// Phase 286 P3: Pattern8 static box filtering
|
||||
// Plan extractors are pure (no builder access), so we filter here.
|
||||
if entry.name.contains("Pattern8") && ctx.in_static_box {
|
||||
if ctx.debug {
|
||||
trace::trace().debug(
|
||||
"route/plan",
|
||||
&format!(
|
||||
"{} extracted but rejected: static box context (fallback to legacy)",
|
||||
entry.name
|
||||
),
|
||||
);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
let log_msg = format!("route=plan strategy=extract pattern={}", entry.name);
|
||||
trace::trace().pattern("route", &log_msg, true);
|
||||
return Ok(Some(domain_plan));
|
||||
} else if ctx.debug {
|
||||
let debug_msg =
|
||||
format!("{} extraction returned None, trying next pattern", entry.name);
|
||||
trace::trace().debug("route", &debug_msg);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
struct RuleEntry {
|
||||
name: &'static str,
|
||||
kind: RuleKind,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
enum RuleKind {
|
||||
Simple(fn(&LoopPatternContext) -> Result<Option<DomainPlan>, String>),
|
||||
Pattern6,
|
||||
Pattern7,
|
||||
}
|
||||
Reference in New Issue
Block a user