diff --git a/crates/plotnik-compiler/src/bytecode/ir.rs b/crates/plotnik-compiler/src/bytecode/ir.rs index 8eed365..15224c6 100644 --- a/crates/plotnik-compiler/src/bytecode/ir.rs +++ b/crates/plotnik-compiler/src/bytecode/ir.rs @@ -606,11 +606,20 @@ impl MatchIR { // counts layout: pre(3) | neg(3) | post(3) | succ(5) | has_pred(1) | reserved(1) assert!( pre_count <= 7, - "pre_effects overflow: {pre_count} > 7 (use emit_match_with_cascade)" + "pre_effects overflow: {pre_count} > 7 (lowering should have cascaded)" + ); + assert!( + neg_count <= 7, + "neg_fields overflow: {neg_count} > 7 (lowering should have cascaded)" + ); + assert!( + post_count <= 7, + "post_effects overflow: {post_count} > 7 (lowering should have cascaded)" + ); + assert!( + succ_count <= 31, + "successors overflow: {succ_count} > 31 (lowering should have cascaded)" ); - assert!(neg_count <= 7, "neg_fields overflow: {neg_count} > 7"); - assert!(post_count <= 7, "post_effects overflow: {post_count} > 7"); - assert!(succ_count <= 31, "successors overflow: {succ_count} > 31"); let counts = ((pre_count as u16) << 13) | ((neg_count as u16) << 10) diff --git a/crates/plotnik-compiler/src/compile/compiler.rs b/crates/plotnik-compiler/src/compile/compiler.rs index 6f0e95e..5de4be3 100644 --- a/crates/plotnik-compiler/src/compile/compiler.rs +++ b/crates/plotnik-compiler/src/compile/compiler.rs @@ -15,6 +15,7 @@ use plotnik_bytecode::Nav; use super::capture::CaptureEffects; use super::dce::remove_unreachable; use super::epsilon_elim::eliminate_epsilons; +use super::lower::lower; use super::error::{CompileError, CompileResult}; use super::scope::StructScope; use super::verify::debug_verify_ir_fingerprint; @@ -86,6 +87,9 @@ impl<'a> Compiler<'a> { // Remove unreachable instructions (bypassed epsilons, etc.) remove_unreachable(&mut result); + // Lower to bytecode-compatible form (cascade overflows) + lower(&mut result); + Ok(result) } diff --git a/crates/plotnik-compiler/src/compile/expressions.rs b/crates/plotnik-compiler/src/compile/expressions.rs index 1dc74c3..e05bf97 100644 --- a/crates/plotnik-compiler/src/compile/expressions.rs +++ b/crates/plotnik-compiler/src/compile/expressions.rs @@ -49,7 +49,7 @@ impl Compiler<'_> { if let Some(p) = predicate { m = m.predicate(p); } - return self.emit_match_with_cascade(m); + return self.emit_match(m); } // Determine Up navigation based on trailing anchor @@ -123,7 +123,7 @@ impl Compiler<'_> { if let Some(p) = predicate { entry_match = entry_match.predicate(p); } - self.emit_match_with_cascade(entry_match); + self.emit_match(entry_match); entry } @@ -201,7 +201,7 @@ impl Compiler<'_> { if let Some(p) = predicate { entry_match = entry_match.predicate(p); } - self.emit_match_with_cascade(entry_match); + self.emit_match(entry_match); entry } @@ -236,7 +236,7 @@ impl Compiler<'_> { None => NodeTypeIR::Any, // `_` wildcard matches any node }; - self.emit_match_with_cascade( + self.emit_match( MatchIR::epsilon(entry, exit) .nav(nav) .node_type(node_type) diff --git a/crates/plotnik-compiler/src/compile/lower.rs b/crates/plotnik-compiler/src/compile/lower.rs new file mode 100644 index 0000000..65dec7b --- /dev/null +++ b/crates/plotnik-compiler/src/compile/lower.rs @@ -0,0 +1,206 @@ +//! Lowering pass: transforms unconstrained IR into bytecode-compatible form. +//! +//! This pass handles bytecode encoding constraints by splitting oversized +//! instructions into cascading epsilon chains: +//! - pre_effects > 7 → epsilon chain before match +//! - post_effects > 7 → epsilon chain after match +//! - neg_fields > 7 → epsilon chain for overflow checks +//! - successors > 28 → cascading epsilon branches + +use plotnik_bytecode::{MAX_MATCH_PAYLOAD_SLOTS, MAX_PRE_EFFECTS}; + +use crate::bytecode::{EffectIR, InstructionIR, Label, MatchIR}; +use crate::compile::CompileResult; + +const MAX_POST_EFFECTS: usize = 7; +const MAX_NEG_FIELDS: usize = 7; + +enum PostChain { + NegFields(Vec), + PostEffects(Vec), + Successors(Vec