@@ -420,6 +420,69 @@ impl Compiler<'_> {
420420 . push ( MatchIR :: at ( label) . next_many ( successors) . into ( ) ) ;
421421 }
422422
423+ /// Emit a Match instruction, cascading pre-effects if they exceed the bytecode limit.
424+ ///
425+ /// When `pre_effects.len() > MAX_PRE_EFFECTS` (7), splits overflow into leading
426+ /// epsilon transitions to avoid bytecode encoding overflow. The original label
427+ /// is preserved as the entry point of the cascade.
428+ ///
429+ /// Returns the entry label (same as `instr.label`).
430+ pub ( super ) fn emit_match_with_cascade ( & mut self , mut instr : MatchIR ) -> Label {
431+ use crate :: bytecode:: MAX_PRE_EFFECTS ;
432+
433+ let entry = instr. label ;
434+
435+ if instr. pre_effects . len ( ) <= MAX_PRE_EFFECTS {
436+ self . instructions . push ( instr. into ( ) ) ;
437+ return entry;
438+ }
439+
440+ // Move all pre-effects to epsilon chain
441+ let all_effects = std:: mem:: take ( & mut instr. pre_effects ) ;
442+
443+ // Create new label for the actual match instruction
444+ let match_label = self . fresh_label ( ) ;
445+ instr. label = match_label;
446+ self . instructions . push ( instr. into ( ) ) ;
447+
448+ // Emit cascade from entry → ... → match_label
449+ self . emit_effects_chain ( entry, match_label, all_effects) ;
450+
451+ entry
452+ }
453+
454+ /// Emit a chain of epsilon transitions to execute effects in order.
455+ ///
456+ /// Splits effects into batches of `MAX_PRE_EFFECTS` (7), emitting epsilon
457+ /// transitions: `entry → intermediate1 → ... → exit`.
458+ fn emit_effects_chain ( & mut self , entry : Label , exit : Label , mut effects : Vec < EffectIR > ) {
459+ use crate :: bytecode:: MAX_PRE_EFFECTS ;
460+
461+ if effects. is_empty ( ) {
462+ // Just link entry to exit
463+ self . instructions . push ( MatchIR :: epsilon ( entry, exit) . into ( ) ) ;
464+ return ;
465+ }
466+
467+ if effects. len ( ) <= MAX_PRE_EFFECTS {
468+ self . instructions
469+ . push ( MatchIR :: epsilon ( entry, exit) . pre_effects ( effects) . into ( ) ) ;
470+ return ;
471+ }
472+
473+ // Take first batch, recurse for rest
474+ let first_batch: Vec < _ > = effects. drain ( ..MAX_PRE_EFFECTS ) . collect ( ) ;
475+ let intermediate = self . fresh_label ( ) ;
476+
477+ self . instructions . push (
478+ MatchIR :: epsilon ( entry, intermediate)
479+ . pre_effects ( first_batch)
480+ . into ( ) ,
481+ ) ;
482+
483+ self . emit_effects_chain ( intermediate, exit, effects) ;
484+ }
485+
423486 /// Emit a wildcard navigation step that accepts any node.
424487 ///
425488 /// Used for skip-retry logic in quantifiers: navigates to the next position
0 commit comments