feat(joinir): Phase 92 P0-3 - ConditionalStep → JoinIR Select generation
Complete implementation of P5b escape sequence handling pattern lowering: Core changes: - skeleton_types.rs: Add `cond` field to ConditionalStep for escape condition - escape_pattern_recognizer.rs: Extract and return escape_cond AST - pattern_recognizer.rs: Pass escape_cond to ConditionalStep - canonicalizer.rs: Store escape_cond in ConditionalStep variant JoinIR lowering: - carrier_update_emitter.rs: Add emit_conditional_step_update() function - Lower condition AST via lower_condition_to_joinir - Compute both then/else branches (carrier + delta) - Emit JoinInst::Select for conditional carrier update - loop_with_break_minimal.rs: Add skeleton parameter, detect ConditionalStep - pattern2_with_break.rs: Pass skeleton to lowering function Backward compatibility: - skeleton=None preserves existing Pattern1-4 behavior - fixtures.rs, tests.rs updated for new signature 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@ -20,6 +20,9 @@ pub struct EscapeSkipPatternInfo {
|
|||||||
pub quote_char: char,
|
pub quote_char: char,
|
||||||
pub escape_char: char,
|
pub escape_char: char,
|
||||||
pub body_stmts: Vec<ASTNode>,
|
pub body_stmts: Vec<ASTNode>,
|
||||||
|
/// Phase 92 P0-3: The condition expression for conditional increment
|
||||||
|
/// e.g., `ch == '\\'` for escape sequence handling
|
||||||
|
pub escape_cond: Box<ASTNode>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Detect escape sequence handling pattern in loop body
|
/// Detect escape sequence handling pattern in loop body
|
||||||
@ -57,8 +60,9 @@ pub fn detect_escape_skip_pattern(body: &[ASTNode]) -> Option<EscapeSkipPatternI
|
|||||||
// Find escape check after break - scan for second "if" with increment
|
// Find escape check after break - scan for second "if" with increment
|
||||||
let escape_idx = find_escape_in_if(body, break_idx)?;
|
let escape_idx = find_escape_in_if(body, break_idx)?;
|
||||||
|
|
||||||
// Extract counter_name, escape_delta, and normal_delta from the escape if statement
|
// Extract counter_name, escape_delta, normal_delta, and condition from the escape if statement
|
||||||
let (counter_name, escape_delta, normal_delta) = extract_delta_pair_from_if(body, escape_idx)?;
|
// Phase 92 P0-3: Now also extracts condition expression
|
||||||
|
let (counter_name, escape_delta, normal_delta, escape_cond) = extract_delta_pair_from_if(body, escape_idx)?;
|
||||||
|
|
||||||
// Extract body statements before break check
|
// Extract body statements before break check
|
||||||
let body_stmts = body[..break_idx].to_vec();
|
let body_stmts = body[..break_idx].to_vec();
|
||||||
@ -70,6 +74,7 @@ pub fn detect_escape_skip_pattern(body: &[ASTNode]) -> Option<EscapeSkipPatternI
|
|||||||
quote_char: '"', // Default for JSON/CSV (Phase 91 MVP)
|
quote_char: '"', // Default for JSON/CSV (Phase 91 MVP)
|
||||||
escape_char: '\\', // Default for JSON/CSV (Phase 91 MVP)
|
escape_char: '\\', // Default for JSON/CSV (Phase 91 MVP)
|
||||||
body_stmts,
|
body_stmts,
|
||||||
|
escape_cond, // Phase 92 P0-3: Condition for JoinIR Select
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -139,17 +144,20 @@ fn find_escape_in_if(body: &[ASTNode], after_idx: usize) -> Option<usize> {
|
|||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Extract both escape_delta and normal_delta from if statement
|
/// Extract escape_delta, normal_delta, and condition from if statement
|
||||||
///
|
///
|
||||||
/// Handles both:
|
/// Handles both:
|
||||||
/// - if ch == escape_char { i = i + 2 } else { i = i + 1 }
|
/// - if ch == escape_char { i = i + 2 } else { i = i + 1 }
|
||||||
/// - if ch == escape_char { i = i + 2 } (followed by separate increment)
|
/// - if ch == escape_char { i = i + 2 } (followed by separate increment)
|
||||||
fn extract_delta_pair_from_if(body: &[ASTNode], idx: usize) -> Option<(String, i64, i64)> {
|
///
|
||||||
|
/// Phase 92 P0-3: Now returns the condition expression for JoinIR Select generation
|
||||||
|
fn extract_delta_pair_from_if(body: &[ASTNode], idx: usize) -> Option<(String, i64, i64, Box<ASTNode>)> {
|
||||||
if idx >= body.len() {
|
if idx >= body.len() {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
if let ASTNode::If {
|
if let ASTNode::If {
|
||||||
|
condition,
|
||||||
then_body,
|
then_body,
|
||||||
else_body,
|
else_body,
|
||||||
..
|
..
|
||||||
@ -204,7 +212,8 @@ fn extract_delta_pair_from_if(body: &[ASTNode], idx: usize) -> Option<(String, i
|
|||||||
found_delta?
|
found_delta?
|
||||||
};
|
};
|
||||||
|
|
||||||
Some((counter_name, escape_delta, normal_delta))
|
// Phase 92 P0-3: Return condition along with deltas
|
||||||
|
Some((counter_name, escape_delta, normal_delta, condition.clone()))
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|||||||
@ -717,6 +717,7 @@ pub(crate) fn can_lower(builder: &MirBuilder, ctx: &super::router::LoopPatternCo
|
|||||||
///
|
///
|
||||||
/// Wrapper around cf_loop_pattern2_with_break to match router signature
|
/// Wrapper around cf_loop_pattern2_with_break to match router signature
|
||||||
/// Phase 200-C: Pass fn_body to cf_loop_pattern2_with_break
|
/// Phase 200-C: Pass fn_body to cf_loop_pattern2_with_break
|
||||||
|
/// Phase 92 P0-3: Pass skeleton for ConditionalStep support
|
||||||
pub(crate) fn lower(
|
pub(crate) fn lower(
|
||||||
builder: &mut MirBuilder,
|
builder: &mut MirBuilder,
|
||||||
ctx: &super::router::LoopPatternContext,
|
ctx: &super::router::LoopPatternContext,
|
||||||
@ -727,6 +728,7 @@ pub(crate) fn lower(
|
|||||||
ctx.func_name,
|
ctx.func_name,
|
||||||
ctx.debug,
|
ctx.debug,
|
||||||
ctx.fn_body,
|
ctx.fn_body,
|
||||||
|
ctx.skeleton, // Phase 92 P0-3: Pass skeleton for ConditionalStep
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -735,6 +737,7 @@ impl MirBuilder {
|
|||||||
///
|
///
|
||||||
/// **Refactored**: Now uses PatternPipelineContext for unified preprocessing
|
/// **Refactored**: Now uses PatternPipelineContext for unified preprocessing
|
||||||
/// **Phase 200-C**: Added fn_body parameter for capture analysis
|
/// **Phase 200-C**: Added fn_body parameter for capture analysis
|
||||||
|
/// **Phase 92 P0-3**: Added skeleton parameter for ConditionalStep support
|
||||||
///
|
///
|
||||||
/// # Pipeline (Phase 179-B)
|
/// # Pipeline (Phase 179-B)
|
||||||
/// 1. Build preprocessing context → PatternPipelineContext
|
/// 1. Build preprocessing context → PatternPipelineContext
|
||||||
@ -754,10 +757,12 @@ impl MirBuilder {
|
|||||||
debug: bool,
|
debug: bool,
|
||||||
) -> Result<Option<ValueId>, String> {
|
) -> Result<Option<ValueId>, String> {
|
||||||
// Phase 200-C: Delegate to impl function with fn_body=None for backward compatibility
|
// Phase 200-C: Delegate to impl function with fn_body=None for backward compatibility
|
||||||
self.cf_loop_pattern2_with_break_impl(condition, _body, _func_name, debug, None)
|
// Phase 92 P0-3: skeleton=None for backward compatibility
|
||||||
|
self.cf_loop_pattern2_with_break_impl(condition, _body, _func_name, debug, None, None)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Phase 200-C: Pattern 2 implementation with optional fn_body for capture analysis
|
/// Phase 200-C: Pattern 2 implementation with optional fn_body for capture analysis
|
||||||
|
/// Phase 92 P0-3: Added skeleton parameter for ConditionalStep support
|
||||||
fn cf_loop_pattern2_with_break_impl(
|
fn cf_loop_pattern2_with_break_impl(
|
||||||
&mut self,
|
&mut self,
|
||||||
condition: &ASTNode,
|
condition: &ASTNode,
|
||||||
@ -765,6 +770,7 @@ impl MirBuilder {
|
|||||||
_func_name: &str,
|
_func_name: &str,
|
||||||
debug: bool,
|
debug: bool,
|
||||||
fn_body: Option<&[ASTNode]>,
|
fn_body: Option<&[ASTNode]>,
|
||||||
|
skeleton: Option<&crate::mir::loop_canonicalizer::LoopSkeleton>,
|
||||||
) -> Result<Option<ValueId>, String> {
|
) -> Result<Option<ValueId>, String> {
|
||||||
use crate::mir::join_ir::lowering::loop_with_break_minimal::lower_loop_with_break_minimal;
|
use crate::mir::join_ir::lowering::loop_with_break_minimal::lower_loop_with_break_minimal;
|
||||||
|
|
||||||
@ -869,6 +875,7 @@ impl MirBuilder {
|
|||||||
analysis_body, // Phase 191/192: Pass normalized body AST for init lowering
|
analysis_body, // Phase 191/192: Pass normalized body AST for init lowering
|
||||||
Some(&mut inputs.body_local_env), // Phase 191: Pass mutable body-local environment
|
Some(&mut inputs.body_local_env), // Phase 191: Pass mutable body-local environment
|
||||||
&mut inputs.join_value_space, // Phase 201: Unified ValueId allocation (Local region)
|
&mut inputs.join_value_space, // Phase 201: Unified ValueId allocation (Local region)
|
||||||
|
skeleton, // Phase 92 P0-3: Pass skeleton for ConditionalStep support
|
||||||
) {
|
) {
|
||||||
Ok((module, meta)) => (module, meta),
|
Ok((module, meta)) => (module, meta),
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
|
|||||||
@ -362,6 +362,101 @@ pub fn emit_carrier_update(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ============================================================================
|
||||||
|
// Phase 92 P0-3: ConditionalStep Support
|
||||||
|
// ============================================================================
|
||||||
|
|
||||||
|
use crate::ast::ASTNode;
|
||||||
|
use crate::mir::join_ir::lowering::condition_lowerer::lower_condition_to_joinir;
|
||||||
|
use crate::mir::join_ir::VarId;
|
||||||
|
use crate::mir::MirType;
|
||||||
|
|
||||||
|
/// Emit JoinIR instructions for conditional step update (Phase 92 P0-3)
|
||||||
|
///
|
||||||
|
/// Handles the P5b escape sequence pattern where carrier update depends on a condition:
|
||||||
|
/// ```text
|
||||||
|
/// if escape_cond { carrier = carrier + then_delta }
|
||||||
|
/// else { carrier = carrier + else_delta }
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// This generates:
|
||||||
|
/// 1. Lower condition expression to get cond_id
|
||||||
|
/// 2. Compute then_result = carrier + then_delta
|
||||||
|
/// 3. Compute else_result = carrier + else_delta
|
||||||
|
/// 4. JoinInst::Select { dst: carrier_new, cond: cond_id, then_val: then_result, else_val: else_result }
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `carrier` - Carrier variable information (name, ValueId)
|
||||||
|
/// * `cond_ast` - AST node for the condition expression (e.g., `ch == '\\'`)
|
||||||
|
/// * `then_delta` - Delta to add when condition is true
|
||||||
|
/// * `else_delta` - Delta to add when condition is false
|
||||||
|
/// * `alloc_value` - ValueId allocator closure
|
||||||
|
/// * `env` - ConditionEnv for variable resolution
|
||||||
|
/// * `instructions` - Output vector to append instructions to
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
///
|
||||||
|
/// ValueId of the computed update result (the dst of Select)
|
||||||
|
pub fn emit_conditional_step_update(
|
||||||
|
carrier: &CarrierVar,
|
||||||
|
cond_ast: &ASTNode,
|
||||||
|
then_delta: i64,
|
||||||
|
else_delta: i64,
|
||||||
|
alloc_value: &mut dyn FnMut() -> ValueId,
|
||||||
|
env: &ConditionEnv,
|
||||||
|
instructions: &mut Vec<JoinInst>,
|
||||||
|
) -> Result<ValueId, String> {
|
||||||
|
// Step 1: Lower the condition expression
|
||||||
|
let (cond_id, cond_insts) = lower_condition_to_joinir(cond_ast, alloc_value, env)?;
|
||||||
|
instructions.extend(cond_insts);
|
||||||
|
|
||||||
|
// Step 2: Get carrier parameter ValueId from env
|
||||||
|
let carrier_param = env
|
||||||
|
.get(&carrier.name)
|
||||||
|
.ok_or_else(|| format!("Carrier '{}' not found in ConditionEnv", carrier.name))?;
|
||||||
|
|
||||||
|
// Step 3: Compute then_result = carrier + then_delta
|
||||||
|
let then_const_id = alloc_value();
|
||||||
|
instructions.push(JoinInst::Compute(MirLikeInst::Const {
|
||||||
|
dst: then_const_id,
|
||||||
|
value: ConstValue::Integer(then_delta),
|
||||||
|
}));
|
||||||
|
let then_result = alloc_value();
|
||||||
|
instructions.push(JoinInst::Compute(MirLikeInst::BinOp {
|
||||||
|
dst: then_result,
|
||||||
|
op: BinOpKind::Add,
|
||||||
|
lhs: carrier_param,
|
||||||
|
rhs: then_const_id,
|
||||||
|
}));
|
||||||
|
|
||||||
|
// Step 4: Compute else_result = carrier + else_delta
|
||||||
|
let else_const_id = alloc_value();
|
||||||
|
instructions.push(JoinInst::Compute(MirLikeInst::Const {
|
||||||
|
dst: else_const_id,
|
||||||
|
value: ConstValue::Integer(else_delta),
|
||||||
|
}));
|
||||||
|
let else_result = alloc_value();
|
||||||
|
instructions.push(JoinInst::Compute(MirLikeInst::BinOp {
|
||||||
|
dst: else_result,
|
||||||
|
op: BinOpKind::Add,
|
||||||
|
lhs: carrier_param,
|
||||||
|
rhs: else_const_id,
|
||||||
|
}));
|
||||||
|
|
||||||
|
// Step 5: Emit Select instruction
|
||||||
|
let carrier_new: VarId = alloc_value();
|
||||||
|
instructions.push(JoinInst::Select {
|
||||||
|
dst: carrier_new,
|
||||||
|
cond: cond_id,
|
||||||
|
then_val: then_result,
|
||||||
|
else_val: else_result,
|
||||||
|
type_hint: Some(MirType::Integer), // Carrier is always Integer
|
||||||
|
});
|
||||||
|
|
||||||
|
Ok(carrier_new)
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|||||||
@ -63,9 +63,10 @@ mod tests;
|
|||||||
|
|
||||||
use crate::mir::join_ir::lowering::carrier_info::{CarrierInfo, JoinFragmentMeta};
|
use crate::mir::join_ir::lowering::carrier_info::{CarrierInfo, JoinFragmentMeta};
|
||||||
use crate::mir::join_ir::lowering::carrier_update_emitter::{
|
use crate::mir::join_ir::lowering::carrier_update_emitter::{
|
||||||
emit_carrier_update, emit_carrier_update_with_env,
|
emit_carrier_update, emit_carrier_update_with_env, emit_conditional_step_update,
|
||||||
};
|
};
|
||||||
use crate::mir::join_ir::lowering::condition_to_joinir::ConditionEnv;
|
use crate::mir::join_ir::lowering::condition_to_joinir::ConditionEnv;
|
||||||
|
use crate::mir::loop_canonicalizer::UpdateKind;
|
||||||
use crate::mir::join_ir::lowering::join_value_space::JoinValueSpace;
|
use crate::mir::join_ir::lowering::join_value_space::JoinValueSpace;
|
||||||
use crate::mir::join_ir::lowering::loop_body_local_env::LoopBodyLocalEnv;
|
use crate::mir::join_ir::lowering::loop_body_local_env::LoopBodyLocalEnv;
|
||||||
use crate::mir::join_ir::lowering::loop_scope_shape::LoopScopeShape;
|
use crate::mir::join_ir::lowering::loop_scope_shape::LoopScopeShape;
|
||||||
@ -145,6 +146,7 @@ use std::collections::BTreeMap; // Phase 222.5-D: HashMap → BTreeMap for deter
|
|||||||
/// * `body_ast` - Phase 191: Loop body AST for body-local init lowering
|
/// * `body_ast` - Phase 191: Loop body AST for body-local init lowering
|
||||||
/// * `body_local_env` - Phase 185-2: Optional mutable body-local variable environment for init expressions
|
/// * `body_local_env` - Phase 185-2: Optional mutable body-local variable environment for init expressions
|
||||||
/// * `join_value_space` - Phase 201: Unified JoinIR ValueId allocator (Local region: 1000+)
|
/// * `join_value_space` - Phase 201: Unified JoinIR ValueId allocator (Local region: 1000+)
|
||||||
|
/// * `skeleton` - Phase 92 P0-3: Optional LoopSkeleton for ConditionalStep support
|
||||||
pub(crate) fn lower_loop_with_break_minimal(
|
pub(crate) fn lower_loop_with_break_minimal(
|
||||||
_scope: LoopScopeShape,
|
_scope: LoopScopeShape,
|
||||||
condition: &ASTNode,
|
condition: &ASTNode,
|
||||||
@ -155,6 +157,7 @@ pub(crate) fn lower_loop_with_break_minimal(
|
|||||||
body_ast: &[ASTNode],
|
body_ast: &[ASTNode],
|
||||||
mut body_local_env: Option<&mut LoopBodyLocalEnv>,
|
mut body_local_env: Option<&mut LoopBodyLocalEnv>,
|
||||||
join_value_space: &mut JoinValueSpace,
|
join_value_space: &mut JoinValueSpace,
|
||||||
|
skeleton: Option<&crate::mir::loop_canonicalizer::LoopSkeleton>,
|
||||||
) -> Result<(JoinModule, JoinFragmentMeta), String> {
|
) -> Result<(JoinModule, JoinFragmentMeta), String> {
|
||||||
// Phase 170-D-impl-3: Validate that conditions only use supported variable scopes
|
// Phase 170-D-impl-3: Validate that conditions only use supported variable scopes
|
||||||
// LoopConditionScopeBox checks that loop conditions don't reference loop-body-local variables
|
// LoopConditionScopeBox checks that loop conditions don't reference loop-body-local variables
|
||||||
@ -585,6 +588,34 @@ pub(crate) fn lower_loop_with_break_minimal(
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Phase 92 P0-3: Check if skeleton has ConditionalStep for this carrier
|
||||||
|
if let Some(skel) = skeleton {
|
||||||
|
if let Some(carrier_slot) = skel.carriers.iter().find(|c| c.name == *carrier_name) {
|
||||||
|
if let UpdateKind::ConditionalStep { cond, then_delta, else_delta } = &carrier_slot.update_kind {
|
||||||
|
// Use emit_conditional_step_update instead of emit_carrier_update
|
||||||
|
eprintln!(
|
||||||
|
"[joinir/pattern2] Phase 92 P0-3: ConditionalStep detected for carrier '{}': then={}, else={}",
|
||||||
|
carrier_name, then_delta, else_delta
|
||||||
|
);
|
||||||
|
let updated_value = emit_conditional_step_update(
|
||||||
|
carrier,
|
||||||
|
&*cond,
|
||||||
|
*then_delta,
|
||||||
|
*else_delta,
|
||||||
|
&mut alloc_value,
|
||||||
|
env,
|
||||||
|
&mut carrier_update_block,
|
||||||
|
)?;
|
||||||
|
updated_carrier_values.push(updated_value);
|
||||||
|
eprintln!(
|
||||||
|
"[joinir/pattern2] Phase 92 P0-3: ConditionalStep carrier '{}' updated → {:?}",
|
||||||
|
carrier_name, updated_value
|
||||||
|
);
|
||||||
|
continue; // Skip normal carrier update
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Get the update expression for this carrier
|
// Get the update expression for this carrier
|
||||||
let update_expr = carrier_updates.get(carrier_name).ok_or_else(|| {
|
let update_expr = carrier_updates.get(carrier_name).ok_or_else(|| {
|
||||||
format!(
|
format!(
|
||||||
|
|||||||
@ -144,6 +144,7 @@ fn test_pattern2_header_condition_via_exprlowerer() {
|
|||||||
&[],
|
&[],
|
||||||
None,
|
None,
|
||||||
&mut join_value_space,
|
&mut join_value_space,
|
||||||
|
None, // Phase 92 P0-3: skeleton=None for backward compatibility
|
||||||
);
|
);
|
||||||
|
|
||||||
assert!(result.is_ok(), "ExprLowerer header path should succeed");
|
assert!(result.is_ok(), "ExprLowerer header path should succeed");
|
||||||
|
|||||||
@ -132,6 +132,7 @@ pub fn build_pattern2_minimal_structured() -> JoinModule {
|
|||||||
&[],
|
&[],
|
||||||
None,
|
None,
|
||||||
&mut join_value_space,
|
&mut join_value_space,
|
||||||
|
None, // Phase 92 P0-3: skeleton=None for backward compatibility
|
||||||
)
|
)
|
||||||
.expect("pattern2 minimal lowering should succeed");
|
.expect("pattern2 minimal lowering should succeed");
|
||||||
|
|
||||||
|
|||||||
@ -314,7 +314,8 @@ pub fn canonicalize_loop_expr(
|
|||||||
// Chosen: Pattern2Break (same as skip_whitespace, but with richer Skeleton)
|
// Chosen: Pattern2Break (same as skip_whitespace, but with richer Skeleton)
|
||||||
// Notes: Added for parity/observability, lowering deferred to Phase 92
|
// Notes: Added for parity/observability, lowering deferred to Phase 92
|
||||||
|
|
||||||
if let Some((counter_name, normal_delta, escape_delta, _quote_char, _escape_char, body_stmts)) =
|
// Phase 92 P0-3: Now also extracts escape_cond for JoinIR Select generation
|
||||||
|
if let Some((counter_name, normal_delta, escape_delta, _quote_char, _escape_char, body_stmts, escape_cond)) =
|
||||||
try_extract_escape_skip_pattern(body)
|
try_extract_escape_skip_pattern(body)
|
||||||
{
|
{
|
||||||
// Build skeleton for escape skip pattern (P5b)
|
// Build skeleton for escape skip pattern (P5b)
|
||||||
@ -334,12 +335,14 @@ pub fn canonicalize_loop_expr(
|
|||||||
|
|
||||||
// Step 3: Update step with ConditionalStep (escape_delta vs normal_delta)
|
// Step 3: Update step with ConditionalStep (escape_delta vs normal_delta)
|
||||||
// Pattern: normal i = i + 1, escape i = i + escape_delta (e.g., +2)
|
// Pattern: normal i = i + 1, escape i = i + escape_delta (e.g., +2)
|
||||||
// Represented as UpdateKind::ConditionalStep with both deltas
|
// Represented as UpdateKind::ConditionalStep with both deltas and condition
|
||||||
|
// Phase 92 P0-3: Now includes escape_cond for JoinIR Select generation
|
||||||
skeleton.steps.push(SkeletonStep::Update {
|
skeleton.steps.push(SkeletonStep::Update {
|
||||||
carrier_name: counter_name.clone(),
|
carrier_name: counter_name.clone(),
|
||||||
update_kind: UpdateKind::ConditionalStep {
|
update_kind: UpdateKind::ConditionalStep {
|
||||||
then_delta: escape_delta, // Escape branch: +2 or other
|
cond: escape_cond.clone(), // Phase 92 P0-3: Condition for Select
|
||||||
else_delta: normal_delta, // Normal branch: +1
|
then_delta: escape_delta, // Escape branch: +2 or other
|
||||||
|
else_delta: normal_delta, // Normal branch: +1
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -348,6 +351,7 @@ pub fn canonicalize_loop_expr(
|
|||||||
name: counter_name,
|
name: counter_name,
|
||||||
role: CarrierRole::Counter,
|
role: CarrierRole::Counter,
|
||||||
update_kind: UpdateKind::ConditionalStep {
|
update_kind: UpdateKind::ConditionalStep {
|
||||||
|
cond: escape_cond, // Phase 92 P0-3: Condition for Select
|
||||||
then_delta: escape_delta,
|
then_delta: escape_delta,
|
||||||
else_delta: normal_delta,
|
else_delta: normal_delta,
|
||||||
},
|
},
|
||||||
@ -1787,8 +1791,10 @@ mod tests {
|
|||||||
assert_eq!(carrier.role, CarrierRole::Counter, "Carrier should be a Counter");
|
assert_eq!(carrier.role, CarrierRole::Counter, "Carrier should be a Counter");
|
||||||
|
|
||||||
// Verify ConditionalStep with escape_delta=2, normal_delta=1
|
// Verify ConditionalStep with escape_delta=2, normal_delta=1
|
||||||
|
// Phase 92 P0-3: ConditionalStep now includes cond
|
||||||
match &carrier.update_kind {
|
match &carrier.update_kind {
|
||||||
UpdateKind::ConditionalStep {
|
UpdateKind::ConditionalStep {
|
||||||
|
cond: _, // Phase 92 P0-3: Condition for Select (don't check exact AST)
|
||||||
then_delta,
|
then_delta,
|
||||||
else_delta,
|
else_delta,
|
||||||
} => {
|
} => {
|
||||||
|
|||||||
@ -310,16 +310,21 @@ mod tests {
|
|||||||
/// }
|
/// }
|
||||||
/// ```
|
/// ```
|
||||||
///
|
///
|
||||||
/// Returns (counter_name, normal_delta, escape_delta, quote_char, escape_char, body_stmts)
|
/// Returns (counter_name, normal_delta, escape_delta, quote_char, escape_char, body_stmts, escape_cond)
|
||||||
/// if pattern matches.
|
/// if pattern matches.
|
||||||
///
|
///
|
||||||
/// # Phase 91 P5b: Escape Sequence Pattern Detection
|
/// # Phase 91 P5b: Escape Sequence Pattern Detection
|
||||||
///
|
///
|
||||||
/// This function delegates to `ast_feature_extractor::detect_escape_skip_pattern`
|
/// This function delegates to `ast_feature_extractor::detect_escape_skip_pattern`
|
||||||
/// for SSOT implementation.
|
/// for SSOT implementation.
|
||||||
|
///
|
||||||
|
/// # Phase 92 P0-3: Added escape_cond
|
||||||
|
///
|
||||||
|
/// The escape_cond is the condition expression for the conditional increment
|
||||||
|
/// (e.g., `ch == '\\'`). This is needed for JoinIR Select generation.
|
||||||
pub fn try_extract_escape_skip_pattern(
|
pub fn try_extract_escape_skip_pattern(
|
||||||
body: &[ASTNode],
|
body: &[ASTNode],
|
||||||
) -> Option<(String, i64, i64, char, char, Vec<ASTNode>)> {
|
) -> Option<(String, i64, i64, char, char, Vec<ASTNode>, Box<ASTNode>)> {
|
||||||
ast_detect_escape(body).map(|info| {
|
ast_detect_escape(body).map(|info| {
|
||||||
(
|
(
|
||||||
info.counter_name,
|
info.counter_name,
|
||||||
@ -328,6 +333,7 @@ pub fn try_extract_escape_skip_pattern(
|
|||||||
info.quote_char,
|
info.quote_char,
|
||||||
info.escape_char,
|
info.escape_char,
|
||||||
info.body_stmts,
|
info.body_stmts,
|
||||||
|
info.escape_cond, // Phase 92 P0-3: Condition for JoinIR Select
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@ -106,7 +106,15 @@ pub enum UpdateKind {
|
|||||||
/// ```
|
/// ```
|
||||||
///
|
///
|
||||||
/// Phase 91 P5b: Used for escape sequence handling and similar conditional increments
|
/// Phase 91 P5b: Used for escape sequence handling and similar conditional increments
|
||||||
ConditionalStep { then_delta: i64, else_delta: i64 },
|
/// Phase 92 P0-3: Added condition expression for JoinIR Select generation
|
||||||
|
ConditionalStep {
|
||||||
|
/// The condition expression (e.g., `ch == '\\'`)
|
||||||
|
cond: Box<ASTNode>,
|
||||||
|
/// Delta for then branch (when condition is true)
|
||||||
|
then_delta: i64,
|
||||||
|
/// Delta for else branch (when condition is false)
|
||||||
|
else_delta: i64,
|
||||||
|
},
|
||||||
|
|
||||||
/// Conditional update with AST expressions (`if cond { x = a } else { x = b }`)
|
/// Conditional update with AST expressions (`if cond { x = a } else { x = b }`)
|
||||||
Conditional {
|
Conditional {
|
||||||
|
|||||||
Reference in New Issue
Block a user