From e1671fed15aba3c6a78d9340def47715841887b7 Mon Sep 17 00:00:00 2001 From: Sergei Zharinov Date: Sat, 3 Jan 2026 14:09:07 -0300 Subject: [PATCH] refactor(compile): implement skip-retry iteration for quantifiers --- crates/plotnik-lib/src/compile/quantifier.rs | 216 ++++++++++++------ crates/plotnik-lib/src/compile/scope.rs | 18 ++ crates/plotnik-lib/src/emit/codegen_tests.rs | 8 + ...gen_tests__alternations_in_quantifier.snap | 47 ++-- ...sts__captures_optional_wrapper_struct.snap | 6 +- ...odegen_tests__captures_wrapper_struct.snap | 34 +-- ...gen_tests__definitions_nested_capture.snap | 77 +++++++ ...__codegen_tests__optional_first_child.snap | 14 +- ...odegen_tests__optional_null_injection.snap | 8 +- ..._tests__quantifiers_first_child_array.snap | 29 ++- ...__codegen_tests__quantifiers_optional.snap | 8 +- ...tests__quantifiers_optional_nongreedy.snap | 8 +- ...emit__codegen_tests__quantifiers_plus.snap | 18 +- ...gen_tests__quantifiers_plus_nongreedy.snap | 18 +- ..._tests__quantifiers_repeat_navigation.snap | 23 +- ...s__quantifiers_sequence_in_called_def.snap | 30 +-- ...emit__codegen_tests__quantifiers_star.snap | 21 +- ...gen_tests__quantifiers_star_nongreedy.snap | 21 +- ...degen_tests__quantifiers_struct_array.snap | 32 +-- ...odegen_tests__sequences_in_quantifier.snap | 26 ++- 20 files changed, 464 insertions(+), 198 deletions(-) create mode 100644 crates/plotnik-lib/src/emit/snapshots/plotnik_lib__emit__codegen_tests__definitions_nested_capture.snap diff --git a/crates/plotnik-lib/src/compile/quantifier.rs b/crates/plotnik-lib/src/compile/quantifier.rs index 5c133645..f1eefee0 100644 --- a/crates/plotnik-lib/src/compile/quantifier.rs +++ b/crates/plotnik-lib/src/compile/quantifier.rs @@ -13,7 +13,7 @@ use crate::parser::cst::SyntaxKind; use crate::analyze::type_check::TypeId; use super::capture::{check_needs_struct_wrapper, get_row_type_id, CaptureEffects}; -use super::navigation::{is_star_or_plus_quantifier, repeat_nav_for}; +use super::navigation::is_star_or_plus_quantifier; use super::Compiler; /// Quantifier operator classification. @@ -333,86 +333,167 @@ impl Compiler<'_> { }; let is_greedy = kind.is_greedy(); - let repeat_nav = repeat_nav_for(first_nav); match kind { QuantifierKind::Plus | QuantifierKind::PlusNonGreedy => { - // +: first_body → loop → [repeat_body, exit] + // Plus with skip-retry: must match at least once + // First iteration has no exit fallback (backtrack propagates to caller) let loop_entry = self.fresh_label(); - let first_body_entry = compile_body(self, first_nav, loop_entry); - let repeat_body_entry = compile_body(self, repeat_nav, loop_entry); + // Compile body ONCE with Nav::Stay + let body_entry = compile_body(self, Some(Nav::Stay), loop_entry); - let successors = if is_greedy { - vec![repeat_body_entry, match_exit] - } else { - vec![match_exit, repeat_body_entry] - }; - self.emit_epsilon(loop_entry, successors); + // First iteration: skip-retry but NO exit (must match at least one) + let first_nav_mode = first_nav.unwrap_or(Nav::Down); + let first_iterate = self.compile_skip_retry_iteration_no_exit( + first_nav_mode, body_entry, is_greedy + ); + + // Repeat iteration: skip-retry with exit option + let repeat_iterate = self.compile_skip_retry_iteration( + Nav::Next, body_entry, match_exit, is_greedy + ); + + // loop_entry → [repeat_iterate, exit] + self.emit_branch_epsilon_at(loop_entry, repeat_iterate, match_exit, is_greedy); - first_body_entry + first_iterate } QuantifierKind::Star | QuantifierKind::StarNonGreedy => { if needs_split_exits { - // Star with split exits: entry → [first_body → loop → [repeat_body, match_exit], skip_exit] + // Star with split exits: uses skip-retry with separate exit paths let skip = skip_exit.expect("split exits requires skip_exit"); - self.compile_star_with_split_exits( + self.compile_star_with_skip_retry_split_exits( inner, match_exit, skip, first_nav, element_capture, is_greedy, needs_struct_wrapper, row_type_id, ) } else { - // Regular star: entry → [first_body → loop → [repeat_body, exit], exit] + // Regular star with skip-retry: + // When pattern fails (even on descendant), retry with next sibling let loop_entry = self.fresh_label(); - let first_body_entry = compile_body(self, first_nav, loop_entry); - let repeat_body_entry = compile_body(self, repeat_nav, loop_entry); - - // Entry point branches: first iteration or exit - let entry = self.fresh_label(); - let successors = if is_greedy { - vec![first_body_entry, match_exit] - } else { - vec![match_exit, first_body_entry] - }; - self.emit_epsilon(entry, successors); - - // Loop point branches: repeat iteration or exit - let loop_successors = if is_greedy { - vec![repeat_body_entry, match_exit] - } else { - vec![match_exit, repeat_body_entry] - }; - self.emit_epsilon(loop_entry, loop_successors); - - entry + // Compile body ONCE with Nav::Stay (navigation handled separately) + let body_entry = compile_body(self, Some(Nav::Stay), loop_entry); + + // First iteration: Down navigation with skip-retry + let first_nav_mode = first_nav.unwrap_or(Nav::Down); + let first_iterate = self.compile_skip_retry_iteration( + first_nav_mode, body_entry, match_exit, is_greedy + ); + + // Repeat iteration: Next navigation with skip-retry + let repeat_iterate = self.compile_skip_retry_iteration( + Nav::Next, body_entry, match_exit, is_greedy + ); + + // loop_entry → [repeat_iterate, exit] + self.emit_branch_epsilon_at(loop_entry, repeat_iterate, match_exit, is_greedy); + + // entry → [first_iterate, exit] + self.emit_branch_epsilon(first_iterate, match_exit, is_greedy) } } QuantifierKind::Optional | QuantifierKind::OptionalNonGreedy => { - // ?: branch to body or exit - let body_entry = compile_body(self, first_nav, match_exit); + // Optional with skip-retry: matches 0 or 1 time + // Compile body with Nav::Stay + let body_entry = compile_body(self, Some(Nav::Stay), match_exit); - if needs_split_exits { - // Split exits for optional - let skip = skip_exit.expect("split exits requires skip_exit"); - self.emit_branch_epsilon(body_entry, skip, is_greedy) + // Build exit-with-null path for when no match found + let skip_with_null = if needs_split_exits { + skip_exit.expect("split exits requires skip_exit") } else { - // Regular optional with null emission for skipped captures - // First handle captures passed as effects (e.g., `(x)? @cap`) - let skip_with_null = self.emit_null_for_skip_path(match_exit, &element_capture); - // Then handle internal captures (e.g., `{(x) @cap}?`) - let skip_with_internal_null = self.emit_null_for_internal_captures(skip_with_null, inner); - self.emit_branch_epsilon(body_entry, skip_with_internal_null, is_greedy) - } + let null_exit = self.emit_null_for_skip_path(match_exit, &element_capture); + self.emit_null_for_internal_captures(null_exit, inner) + }; + + // Skip-retry iteration leading to null exit + let first_nav_mode = first_nav.unwrap_or(Nav::Down); + let iterate = self.compile_skip_retry_iteration( + first_nav_mode, body_entry, skip_with_null, is_greedy + ); + + // entry → [iterate, skip_with_null] + self.emit_branch_epsilon(iterate, skip_with_null, is_greedy) } } } - /// Helper for star with split exits - handles the complex case where skip and match - /// paths need different continuations. + /// Compile a "try-skip-retry" iteration for quantifiers. + /// + /// Structure: + /// ```text + /// navigate: Match(nav, wildcard) → try + /// try: epsilon → [body, retry_or_exit] + /// retry_or_exit: epsilon → [retry_nav, exit] + /// retry_nav: Match(Nav::Next, wildcard) → try + /// ``` + /// + /// When the body fails (even deep inside on a descendant), we backtrack to `try`, + /// which falls through to `retry_or_exit`, advancing to the next sibling and retrying. + /// Only when siblings are exhausted do we take the exit path. + /// + /// Returns the navigate label (entry point for this iteration). + fn compile_skip_retry_iteration( + &mut self, + nav: Nav, + body_entry: Label, + exit: Label, + is_greedy: bool, + ) -> Label { + let try_label = self.fresh_label(); + let retry_or_exit = self.fresh_label(); + + // retry_nav: advance and loop back to try + let retry_nav = self.fresh_label(); + self.emit_wildcard_nav(retry_nav, Nav::Next, try_label); + + // retry_or_exit: epsilon → [retry_nav, exit] + self.emit_branch_epsilon_at(retry_or_exit, retry_nav, exit, is_greedy); + + // try: epsilon → [body, retry_or_exit] + self.emit_branch_epsilon_at(try_label, body_entry, retry_or_exit, is_greedy); + + // navigate: wildcard nav → try + let navigate = self.fresh_label(); + self.emit_wildcard_nav(navigate, nav, try_label); + + navigate + } + + /// Like `compile_skip_retry_iteration` but with no exit fallback. + /// + /// Used for Plus quantifier's first iteration where at least one match is required. + /// If all siblings fail, backtrack propagates to caller (quantifier fails). + fn compile_skip_retry_iteration_no_exit( + &mut self, + nav: Nav, + body_entry: Label, + is_greedy: bool, + ) -> Label { + let try_label = self.fresh_label(); + + // retry_nav: advance and loop back to try (no exit option) + let retry_nav = self.fresh_label(); + self.emit_wildcard_nav(retry_nav, Nav::Next, try_label); + + // try: epsilon → [body, retry_nav] + // If pattern fails, we advance and retry. If no more siblings, + // retry_nav's navigation fails and we backtrack to outer checkpoint. + self.emit_branch_epsilon_at(try_label, body_entry, retry_nav, is_greedy); + + // navigate: wildcard nav → try + let navigate = self.fresh_label(); + self.emit_wildcard_nav(navigate, nav, try_label); + + navigate + } + + /// Helper for star with split exits and skip-retry. + /// Skip path goes to skip_exit, match path goes to match_exit. #[allow(clippy::too_many_arguments)] - fn compile_star_with_split_exits( + fn compile_star_with_skip_retry_split_exits( &mut self, inner: &Expr, match_exit: Label, @@ -424,22 +505,29 @@ impl Compiler<'_> { row_type_id: Option, ) -> Label { let loop_entry = self.fresh_label(); - let repeat_nav = repeat_nav_for(nav_override); - let first_body = if needs_struct_wrapper { - self.compile_struct_for_array(inner, loop_entry, nav_override, row_type_id) + // Compile body ONCE with Nav::Stay + let body_entry = if needs_struct_wrapper { + self.compile_struct_for_array(inner, loop_entry, Some(Nav::Stay), row_type_id) } else { - self.compile_expr_inner(inner, loop_entry, nav_override, capture.clone()) + self.compile_expr_inner(inner, loop_entry, Some(Nav::Stay), capture) }; - let repeat_body = if needs_struct_wrapper { - self.compile_struct_for_array(inner, loop_entry, repeat_nav, row_type_id) - } else { - self.compile_expr_inner(inner, loop_entry, repeat_nav, capture) - }; + // First iteration: skip-retry with skip_exit as fallback + let first_nav_mode = nav_override.unwrap_or(Nav::Down); + let first_iterate = self.compile_skip_retry_iteration( + first_nav_mode, body_entry, skip_exit, is_greedy + ); + + // Repeat iteration: skip-retry with match_exit as fallback + let repeat_iterate = self.compile_skip_retry_iteration( + Nav::Next, body_entry, match_exit, is_greedy + ); + + // loop_entry → [repeat_iterate, match_exit] + self.emit_branch_epsilon_at(loop_entry, repeat_iterate, match_exit, is_greedy); - let entry = self.emit_branch_epsilon(first_body, skip_exit, is_greedy); - self.emit_branch_epsilon_at(loop_entry, repeat_body, match_exit, is_greedy); - entry + // entry → [first_iterate, skip_exit] + self.emit_branch_epsilon(first_iterate, skip_exit, is_greedy) } } diff --git a/crates/plotnik-lib/src/compile/scope.rs b/crates/plotnik-lib/src/compile/scope.rs index 111e2624..04fc1b44 100644 --- a/crates/plotnik-lib/src/compile/scope.rs +++ b/crates/plotnik-lib/src/compile/scope.rs @@ -428,6 +428,24 @@ impl Compiler<'_> { })); } + /// Emit a wildcard navigation step that accepts any node. + /// + /// Used for skip-retry logic in quantifiers: navigates to the next position + /// and matches any node there. If navigation fails (no more siblings/children), + /// the VM backtracks automatically. + pub(super) fn emit_wildcard_nav(&mut self, label: Label, nav: Nav, successor: Label) { + self.instructions.push(Instruction::Match(MatchIR { + label, + nav, + node_type: None, + node_field: None, + pre_effects: vec![], + neg_fields: vec![], + post_effects: vec![], + successors: vec![successor], + })); + } + /// Emit an epsilon branch preferring `prefer` when greedy, `other` when non-greedy. pub(super) fn emit_branch_epsilon(&mut self, prefer: Label, other: Label, is_greedy: bool) -> Label { let entry = self.fresh_label(); diff --git a/crates/plotnik-lib/src/emit/codegen_tests.rs b/crates/plotnik-lib/src/emit/codegen_tests.rs index e25675f8..6abd7b2d 100644 --- a/crates/plotnik-lib/src/emit/codegen_tests.rs +++ b/crates/plotnik-lib/src/emit/codegen_tests.rs @@ -400,6 +400,14 @@ fn definitions_reference() { "#}); } +#[test] +fn definitions_nested_capture() { + snap!(indoc! {r#" + Inner = (call (identifier) @name) + Outer = (parent {(Inner) @item}* @items) + "#}); +} + // ============================================================================ // 9. RECURSION // ============================================================================ diff --git a/crates/plotnik-lib/src/emit/snapshots/plotnik_lib__emit__codegen_tests__alternations_in_quantifier.snap b/crates/plotnik-lib/src/emit/snapshots/plotnik_lib__emit__codegen_tests__alternations_in_quantifier.snap index 793fe976..ffbb1243 100644 --- a/crates/plotnik-lib/src/emit/snapshots/plotnik_lib__emit__codegen_tests__alternations_in_quantifier.snap +++ b/crates/plotnik-lib/src/emit/snapshots/plotnik_lib__emit__codegen_tests__alternations_in_quantifier.snap @@ -50,28 +50,27 @@ Test: 02 ε [Obj] 04 04 (object) 05 05 ε [Arr] 07 - 07 ε 33, 15 + 07 ε 42, 19 09 ε [EndArr Set(M5)] 11 - 11 △ 13 - 12 ▶ - 13 ε [EndObj] 12 - 15 ε [EndArr Set(M5)] 13 - 17 ε [EndObj Push] 53 - 19 ε [EndEnum Set(M4)] 17 - 21 ▽ (pair) [Node Set(M0)] 19 - 23 ε [Enum(M2)] 21 - 25 ε [EndEnum Set(M4)] 17 - 27 ▽ (shorthand_property_identifier) [Node Set(M1)] 25 - 29 ε [Enum(M3)] 27 - 31 ε 23, 29 - 33 ε [Obj] 31 - 35 ε [EndObj Push] 53 - 37 ε [EndEnum Set(M4)] 35 - 39 ▷ (pair) [Node Set(M0)] 37 - 41 ε [Enum(M2)] 39 - 43 ε [EndEnum Set(M4)] 35 - 45 ▷ (shorthand_property_identifier) [Node Set(M1)] 43 - 47 ε [Enum(M3)] 45 - 49 ε 41, 47 - 51 ε [Obj] 49 - 53 ε 51, 09 + 11 △ 17 + 12 ε [EndObj Push] 14 + 14 ε 48, 09 + 16 ▶ + 17 ε [EndObj] 16 + 19 ε [EndArr Set(M5)] 17 + 21 ε [EndEnum Set(M4)] 12 + 23 (pair) [Node Set(M0)] 21 + 25 ε [Enum(M2)] 23 + 27 ε [EndEnum Set(M4)] 12 + 29 (shorthand_property_identifier) [Node Set(M1)] 27 + 31 ε [Enum(M3)] 29 + 33 ε 25, 31 + 35 ε [Obj] 33 + 37 ▷ 40 + 38 ε 37, 19 + 40 ε 35, 38 + 42 ▽ 40 + 43 ▷ 46 + 44 ε 43, 09 + 46 ε 35, 44 + 48 ▷ 46 diff --git a/crates/plotnik-lib/src/emit/snapshots/plotnik_lib__emit__codegen_tests__captures_optional_wrapper_struct.snap b/crates/plotnik-lib/src/emit/snapshots/plotnik_lib__emit__codegen_tests__captures_optional_wrapper_struct.snap index 196062d5..9b0dc494 100644 --- a/crates/plotnik-lib/src/emit/snapshots/plotnik_lib__emit__codegen_tests__captures_optional_wrapper_struct.snap +++ b/crates/plotnik-lib/src/emit/snapshots/plotnik_lib__emit__codegen_tests__captures_optional_wrapper_struct.snap @@ -42,7 +42,7 @@ Test: 01 ε 02 02 ε [Obj] 04 04 ε [Obj] 06 - 06 ε 17, 19 + 06 ε 26, 19 08 ▶ 09 ε [EndObj] 08 11 ε [EndObj Set(M2)] 09 @@ -50,3 +50,7 @@ Test: 15 (identifier) [Node Set(M0)] 13 17 ε [Obj] 15 19 ε [Null Set(M1)] 11 + 21 ▷ 24 + 22 ε 21, 19 + 24 ε 17, 22 + 26 ▽ 24 diff --git a/crates/plotnik-lib/src/emit/snapshots/plotnik_lib__emit__codegen_tests__captures_wrapper_struct.snap b/crates/plotnik-lib/src/emit/snapshots/plotnik_lib__emit__codegen_tests__captures_wrapper_struct.snap index 400f0a50..8b454146 100644 --- a/crates/plotnik-lib/src/emit/snapshots/plotnik_lib__emit__codegen_tests__captures_wrapper_struct.snap +++ b/crates/plotnik-lib/src/emit/snapshots/plotnik_lib__emit__codegen_tests__captures_wrapper_struct.snap @@ -46,20 +46,22 @@ Test: 01 ε 02 02 ε [Obj] 04 04 ε [Arr] 06 - 06 ε 23, 11 - 08 ▶ - 09 ε [EndObj] 08 - 11 ε [EndArr Set(M3)] 09 - 13 ε [EndObj Push] 37 - 15 ε [EndObj Set(M2)] 13 - 17 ▷ (number) [Node Set(M1)] 15 - 19 (identifier) [Node Set(M0)] 17 - 21 ε [Obj] 19 + 06 ε 32, 15 + 08 ε [EndObj Push] 10 + 10 ε 38, 15 + 12 ▶ + 13 ε [EndObj] 12 + 15 ε [EndArr Set(M3)] 13 + 17 ε [EndObj Set(M2)] 08 + 19 ▷ (number) [Node Set(M1)] 17 + 21 (identifier) [Node Set(M0)] 19 23 ε [Obj] 21 - 25 ε [EndObj Push] 37 - 27 ε [EndObj Set(M2)] 25 - 29 ▷ (number) [Node Set(M1)] 27 - 31 ▷ (identifier) [Node Set(M0)] 29 - 33 ε [Obj] 31 - 35 ε [Obj] 33 - 37 ε 35, 11 + 25 ε [Obj] 23 + 27 ▷ 30 + 28 ε 27, 15 + 30 ε 25, 28 + 32 ▽ 30 + 33 ▷ 36 + 34 ε 33, 15 + 36 ε 25, 34 + 38 ▷ 36 diff --git a/crates/plotnik-lib/src/emit/snapshots/plotnik_lib__emit__codegen_tests__definitions_nested_capture.snap b/crates/plotnik-lib/src/emit/snapshots/plotnik_lib__emit__codegen_tests__definitions_nested_capture.snap new file mode 100644 index 00000000..ea18a96f --- /dev/null +++ b/crates/plotnik-lib/src/emit/snapshots/plotnik_lib__emit__codegen_tests__definitions_nested_capture.snap @@ -0,0 +1,77 @@ +--- +source: crates/plotnik-lib/src/emit/codegen_tests.rs +--- +Inner = (call (identifier) @name) +Outer = (parent {(Inner) @item}* @items) +--- +[flags] +linked = false + +[strings] +S0 "Beauty will save the world" +S1 "name" +S2 "item" +S3 "items" +S4 "Inner" +S5 "Outer" +S6 "call" +S7 "identifier" +S8 "parent" + +[type_defs] +T0 = +T1 = Struct M0:1 ; { name } +T2 = Struct M1:2 ; { name, item } +T3 = ArrayStar(T2) ; T2* +T4 = Struct M3:1 ; { items } + +[type_members] +M0: S1 → T0 ; name: +M1: S1 → T0 ; name: +M2: S2 → T0 ; item: +M3: S3 → T3 ; items: T3 + +[type_names] +N0: S4 → T1 ; Inner +N1: S5 → T4 ; Outer + +[entrypoints] +Inner = 01 :: T1 +Outer = 11 :: T4 + +[transitions] + 00 ε ◼ + +Inner: + 01 ε 02 + 02 ε [Obj] 04 + 04 (call) 05 + 05 ▽ (identifier) [Node Set(M0)] 07 + 07 △ 08 + 08 ε [EndObj] 10 + 10 ▶ + +Outer: + 11 ε 12 + 12 ε [Obj] 14 + 14 (parent) 15 + 15 ε [Arr] 17 + 17 ε 41, 29 + 19 ε [EndArr Set(M3)] 21 + 21 △ 27 + 22 ε [EndObj Push] 24 + 24 ε 47, 19 + 26 ▶ + 27 ε [EndObj] 26 + 29 ε [EndArr Set(M3)] 27 + 31 ε [Set(M2)] 22 + 33 (Inner) 31 ⯇ + 34 ε [Obj] 33 + 36 ▷ 39 + 37 ε 36, 29 + 39 ε 34, 37 + 41 ▽ 39 + 42 ▷ 45 + 43 ε 42, 19 + 45 ε 34, 43 + 47 ▷ 45 diff --git a/crates/plotnik-lib/src/emit/snapshots/plotnik_lib__emit__codegen_tests__optional_first_child.snap b/crates/plotnik-lib/src/emit/snapshots/plotnik_lib__emit__codegen_tests__optional_first_child.snap index ad63e996..01f65f11 100644 --- a/crates/plotnik-lib/src/emit/snapshots/plotnik_lib__emit__codegen_tests__optional_first_child.snap +++ b/crates/plotnik-lib/src/emit/snapshots/plotnik_lib__emit__codegen_tests__optional_first_child.snap @@ -41,11 +41,15 @@ Test: 01 ε 02 02 ε [Obj] 04 04 (program) 05 - 05 ε 16, 14 + 05 ε 23, 14 07 ▶ 08 ε [EndObj] 07 - 10 ▽ (number) [Node Set(M1)] 18 - 12 ▷ (number) [Node Set(M1)] 18 + 10 ▽ (number) [Node Set(M1)] 24 + 12 ▷ (number) [Node Set(M1)] 24 14 ε [Null Set(M0)] 10 - 16 ▽ (identifier) [Node Set(M0)] 12 - 18 △ 08 + 16 (identifier) [Node Set(M0)] 12 + 18 ▷ 21 + 19 ε 18, 14 + 21 ε 16, 19 + 23 ▽ 21 + 24 △ 08 diff --git a/crates/plotnik-lib/src/emit/snapshots/plotnik_lib__emit__codegen_tests__optional_null_injection.snap b/crates/plotnik-lib/src/emit/snapshots/plotnik_lib__emit__codegen_tests__optional_null_injection.snap index 04170092..4c09c85d 100644 --- a/crates/plotnik-lib/src/emit/snapshots/plotnik_lib__emit__codegen_tests__optional_null_injection.snap +++ b/crates/plotnik-lib/src/emit/snapshots/plotnik_lib__emit__codegen_tests__optional_null_injection.snap @@ -34,9 +34,13 @@ Test: 01 ε 02 02 ε [Obj] 04 04 (function_declaration) 05 - 05 ε 07, 13 - 07 ▽ (decorator) [Node Set(M0)] 09 + 05 ε 20, 13 + 07 (decorator) [Node Set(M0)] 09 09 △ 11 10 ▶ 11 ε [EndObj] 10 13 ε [Null Set(M0)] 11 + 15 ▷ 18 + 16 ε 15, 13 + 18 ε 07, 16 + 20 ▽ 18 diff --git a/crates/plotnik-lib/src/emit/snapshots/plotnik_lib__emit__codegen_tests__quantifiers_first_child_array.snap b/crates/plotnik-lib/src/emit/snapshots/plotnik_lib__emit__codegen_tests__quantifiers_first_child_array.snap index 67f355ea..b55da9d3 100644 --- a/crates/plotnik-lib/src/emit/snapshots/plotnik_lib__emit__codegen_tests__quantifiers_first_child_array.snap +++ b/crates/plotnik-lib/src/emit/snapshots/plotnik_lib__emit__codegen_tests__quantifiers_first_child_array.snap @@ -42,14 +42,21 @@ Test: 02 ε [Obj] 04 04 (array) 05 05 ε [Arr] 07 - 07 ε 20, 18 - 09 ▶ - 10 ε [EndObj] 09 - 12 ▽ (number) [Node Set(M1)] 26 - 14 ▷ (number) [Node Set(M1)] 26 - 16 ε [EndArr Set(M0)] 14 - 18 ε [EndArr Set(M0)] 12 - 20 ▽ (identifier) [Node Push] 24 - 22 ▷ (identifier) [Node Push] 24 - 24 ε 22, 16 - 26 △ 10 + 07 ε 29, 22 + 09 (identifier) [Node Push] 11 + 11 ε 35, 20 + 13 ▶ + 14 ε [EndObj] 13 + 16 ▽ (number) [Node Set(M1)] 36 + 18 ▷ (number) [Node Set(M1)] 36 + 20 ε [EndArr Set(M0)] 18 + 22 ε [EndArr Set(M0)] 16 + 24 ▷ 27 + 25 ε 24, 22 + 27 ε 09, 25 + 29 ▽ 27 + 30 ▷ 33 + 31 ε 30, 20 + 33 ε 09, 31 + 35 ▷ 33 + 36 △ 14 diff --git a/crates/plotnik-lib/src/emit/snapshots/plotnik_lib__emit__codegen_tests__quantifiers_optional.snap b/crates/plotnik-lib/src/emit/snapshots/plotnik_lib__emit__codegen_tests__quantifiers_optional.snap index 04170092..4c09c85d 100644 --- a/crates/plotnik-lib/src/emit/snapshots/plotnik_lib__emit__codegen_tests__quantifiers_optional.snap +++ b/crates/plotnik-lib/src/emit/snapshots/plotnik_lib__emit__codegen_tests__quantifiers_optional.snap @@ -34,9 +34,13 @@ Test: 01 ε 02 02 ε [Obj] 04 04 (function_declaration) 05 - 05 ε 07, 13 - 07 ▽ (decorator) [Node Set(M0)] 09 + 05 ε 20, 13 + 07 (decorator) [Node Set(M0)] 09 09 △ 11 10 ▶ 11 ε [EndObj] 10 13 ε [Null Set(M0)] 11 + 15 ▷ 18 + 16 ε 15, 13 + 18 ε 07, 16 + 20 ▽ 18 diff --git a/crates/plotnik-lib/src/emit/snapshots/plotnik_lib__emit__codegen_tests__quantifiers_optional_nongreedy.snap b/crates/plotnik-lib/src/emit/snapshots/plotnik_lib__emit__codegen_tests__quantifiers_optional_nongreedy.snap index 4da8c5fd..e0d8289c 100644 --- a/crates/plotnik-lib/src/emit/snapshots/plotnik_lib__emit__codegen_tests__quantifiers_optional_nongreedy.snap +++ b/crates/plotnik-lib/src/emit/snapshots/plotnik_lib__emit__codegen_tests__quantifiers_optional_nongreedy.snap @@ -34,9 +34,13 @@ Test: 01 ε 02 02 ε [Obj] 04 04 (function_declaration) 05 - 05 ε 13, 07 - 07 ▽ (decorator) [Node Set(M0)] 09 + 05 ε 13, 20 + 07 (decorator) [Node Set(M0)] 09 09 △ 11 10 ▶ 11 ε [EndObj] 10 13 ε [Null Set(M0)] 11 + 15 ▷ 18 + 16 ε 13, 15 + 18 ε 16, 07 + 20 ▽ 18 diff --git a/crates/plotnik-lib/src/emit/snapshots/plotnik_lib__emit__codegen_tests__quantifiers_plus.snap b/crates/plotnik-lib/src/emit/snapshots/plotnik_lib__emit__codegen_tests__quantifiers_plus.snap index 4739079f..5894d6a6 100644 --- a/crates/plotnik-lib/src/emit/snapshots/plotnik_lib__emit__codegen_tests__quantifiers_plus.snap +++ b/crates/plotnik-lib/src/emit/snapshots/plotnik_lib__emit__codegen_tests__quantifiers_plus.snap @@ -33,9 +33,15 @@ Test: 01 ε 02 02 ε [Obj] 04 04 ε [Arr] 06 - 06 (identifier) [Node Push] 15 - 08 ▶ - 09 ε [EndObj] 08 - 11 ε [EndArr Set(M0)] 09 - 13 ▷ (identifier) [Node Push] 15 - 15 ε 13, 11 + 06 ▽ 17 + 07 (identifier) [Node Push] 09 + 09 ε 24, 14 + 11 ▶ + 12 ε [EndObj] 11 + 14 ε [EndArr Set(M0)] 12 + 16 ▷ 17 + 17 ε 07, 16 + 19 ▷ 22 + 20 ε 19, 14 + 22 ε 07, 20 + 24 ▷ 22 diff --git a/crates/plotnik-lib/src/emit/snapshots/plotnik_lib__emit__codegen_tests__quantifiers_plus_nongreedy.snap b/crates/plotnik-lib/src/emit/snapshots/plotnik_lib__emit__codegen_tests__quantifiers_plus_nongreedy.snap index b4aa3301..b5178d51 100644 --- a/crates/plotnik-lib/src/emit/snapshots/plotnik_lib__emit__codegen_tests__quantifiers_plus_nongreedy.snap +++ b/crates/plotnik-lib/src/emit/snapshots/plotnik_lib__emit__codegen_tests__quantifiers_plus_nongreedy.snap @@ -33,9 +33,15 @@ Test: 01 ε 02 02 ε [Obj] 04 04 ε [Arr] 06 - 06 (identifier) [Node Push] 15 - 08 ▶ - 09 ε [EndObj] 08 - 11 ε [EndArr Set(M0)] 09 - 13 ▷ (identifier) [Node Push] 15 - 15 ε 11, 13 + 06 ▽ 17 + 07 (identifier) [Node Push] 09 + 09 ε 14, 24 + 11 ▶ + 12 ε [EndObj] 11 + 14 ε [EndArr Set(M0)] 12 + 16 ▷ 17 + 17 ε 16, 07 + 19 ▷ 22 + 20 ε 14, 19 + 22 ε 20, 07 + 24 ▷ 22 diff --git a/crates/plotnik-lib/src/emit/snapshots/plotnik_lib__emit__codegen_tests__quantifiers_repeat_navigation.snap b/crates/plotnik-lib/src/emit/snapshots/plotnik_lib__emit__codegen_tests__quantifiers_repeat_navigation.snap index 0cffcc8f..46f013c7 100644 --- a/crates/plotnik-lib/src/emit/snapshots/plotnik_lib__emit__codegen_tests__quantifiers_repeat_navigation.snap +++ b/crates/plotnik-lib/src/emit/snapshots/plotnik_lib__emit__codegen_tests__quantifiers_repeat_navigation.snap @@ -35,12 +35,19 @@ Test: 02 ε [Obj] 04 04 (function_declaration) 05 05 ε [Arr] 07 - 07 ε 17, 15 + 07 ε 26, 19 09 ε [EndArr Set(M0)] 11 - 11 △ 13 - 12 ▶ - 13 ε [EndObj] 12 - 15 ε [EndArr Set(M0)] 13 - 17 ▽ (decorator) [Node Push] 21 - 19 ▷ (decorator) [Node Push] 21 - 21 ε 19, 09 + 11 △ 17 + 12 (decorator) [Node Push] 14 + 14 ε 32, 09 + 16 ▶ + 17 ε [EndObj] 16 + 19 ε [EndArr Set(M0)] 17 + 21 ▷ 24 + 22 ε 21, 19 + 24 ε 12, 22 + 26 ▽ 24 + 27 ▷ 30 + 28 ε 27, 09 + 30 ε 12, 28 + 32 ▷ 30 diff --git a/crates/plotnik-lib/src/emit/snapshots/plotnik_lib__emit__codegen_tests__quantifiers_sequence_in_called_def.snap b/crates/plotnik-lib/src/emit/snapshots/plotnik_lib__emit__codegen_tests__quantifiers_sequence_in_called_def.snap index 8e27ed10..f8d5d524 100644 --- a/crates/plotnik-lib/src/emit/snapshots/plotnik_lib__emit__codegen_tests__quantifiers_sequence_in_called_def.snap +++ b/crates/plotnik-lib/src/emit/snapshots/plotnik_lib__emit__codegen_tests__quantifiers_sequence_in_called_def.snap @@ -56,7 +56,7 @@ Collect: 09 ε 10 10 ε [Obj] 12 12 ε [Arr] 14 - 14 ε 35, 28 + 14 ε 44, 32 Test: 16 ε 17 @@ -66,15 +66,19 @@ Test: 21 △ 22 22 ε [EndObj] 24 24 ▶ - 25 ▶ - 26 ε [EndObj] 25 - 28 ε [EndArr Set(M3)] 26 - 30 ε [EndObj Push] 44 - 32 ε [Set(M2)] 30 - 34 (Item) 32 ⯇ - 35 ε [Obj] 34 - 37 ε [EndObj Push] 44 - 39 ε [Set(M2)] 37 - 41 ▷ (Item) 39 ⯇ - 42 ε [Obj] 41 - 44 ε 42, 28 + 25 ε [EndObj Push] 27 + 27 ε 50, 32 + 29 ▶ + 30 ε [EndObj] 29 + 32 ε [EndArr Set(M3)] 30 + 34 ε [Set(M2)] 25 + 36 (Item) 34 ⯇ + 37 ε [Obj] 36 + 39 ▷ 42 + 40 ε 39, 32 + 42 ε 37, 40 + 44 ▽ 42 + 45 ▷ 48 + 46 ε 45, 32 + 48 ε 37, 46 + 50 ▷ 48 diff --git a/crates/plotnik-lib/src/emit/snapshots/plotnik_lib__emit__codegen_tests__quantifiers_star.snap b/crates/plotnik-lib/src/emit/snapshots/plotnik_lib__emit__codegen_tests__quantifiers_star.snap index 654e3288..aad152fe 100644 --- a/crates/plotnik-lib/src/emit/snapshots/plotnik_lib__emit__codegen_tests__quantifiers_star.snap +++ b/crates/plotnik-lib/src/emit/snapshots/plotnik_lib__emit__codegen_tests__quantifiers_star.snap @@ -33,10 +33,17 @@ Test: 01 ε 02 02 ε [Obj] 04 04 ε [Arr] 06 - 06 ε 13, 11 - 08 ▶ - 09 ε [EndObj] 08 - 11 ε [EndArr Set(M0)] 09 - 13 (identifier) [Node Push] 17 - 15 ▷ (identifier) [Node Push] 17 - 17 ε 15, 11 + 06 ε 22, 15 + 08 (identifier) [Node Push] 10 + 10 ε 28, 15 + 12 ▶ + 13 ε [EndObj] 12 + 15 ε [EndArr Set(M0)] 13 + 17 ▷ 20 + 18 ε 17, 15 + 20 ε 08, 18 + 22 ▽ 20 + 23 ▷ 26 + 24 ε 23, 15 + 26 ε 08, 24 + 28 ▷ 26 diff --git a/crates/plotnik-lib/src/emit/snapshots/plotnik_lib__emit__codegen_tests__quantifiers_star_nongreedy.snap b/crates/plotnik-lib/src/emit/snapshots/plotnik_lib__emit__codegen_tests__quantifiers_star_nongreedy.snap index 6921986c..668ca802 100644 --- a/crates/plotnik-lib/src/emit/snapshots/plotnik_lib__emit__codegen_tests__quantifiers_star_nongreedy.snap +++ b/crates/plotnik-lib/src/emit/snapshots/plotnik_lib__emit__codegen_tests__quantifiers_star_nongreedy.snap @@ -33,10 +33,17 @@ Test: 01 ε 02 02 ε [Obj] 04 04 ε [Arr] 06 - 06 ε 11, 13 - 08 ▶ - 09 ε [EndObj] 08 - 11 ε [EndArr Set(M0)] 09 - 13 (identifier) [Node Push] 17 - 15 ▷ (identifier) [Node Push] 17 - 17 ε 11, 15 + 06 ε 15, 22 + 08 (identifier) [Node Push] 10 + 10 ε 15, 28 + 12 ▶ + 13 ε [EndObj] 12 + 15 ε [EndArr Set(M0)] 13 + 17 ▷ 20 + 18 ε 15, 17 + 20 ε 18, 08 + 22 ▽ 20 + 23 ▷ 26 + 24 ε 15, 23 + 26 ε 24, 08 + 28 ▷ 26 diff --git a/crates/plotnik-lib/src/emit/snapshots/plotnik_lib__emit__codegen_tests__quantifiers_struct_array.snap b/crates/plotnik-lib/src/emit/snapshots/plotnik_lib__emit__codegen_tests__quantifiers_struct_array.snap index 2450fa3a..b2d0712e 100644 --- a/crates/plotnik-lib/src/emit/snapshots/plotnik_lib__emit__codegen_tests__quantifiers_struct_array.snap +++ b/crates/plotnik-lib/src/emit/snapshots/plotnik_lib__emit__codegen_tests__quantifiers_struct_array.snap @@ -45,18 +45,22 @@ Test: 02 ε [Obj] 04 04 (array) 05 05 ε [Arr] 07 - 07 ε 23, 15 + 07 ε 32, 19 09 ε [EndArr Set(M2)] 11 - 11 △ 13 - 12 ▶ - 13 ε [EndObj] 12 - 15 ε [EndArr Set(M2)] 13 - 17 ε [EndObj Push] 33 - 19 ▷ (number) [Node Set(M1)] 17 - 21 ▽ (identifier) [Node Set(M0)] 19 - 23 ε [Obj] 21 - 25 ε [EndObj Push] 33 - 27 ▷ (number) [Node Set(M1)] 25 - 29 ▷ (identifier) [Node Set(M0)] 27 - 31 ε [Obj] 29 - 33 ε 31, 09 + 11 △ 17 + 12 ε [EndObj Push] 14 + 14 ε 38, 09 + 16 ▶ + 17 ε [EndObj] 16 + 19 ε [EndArr Set(M2)] 17 + 21 ▷ (number) [Node Set(M1)] 12 + 23 (identifier) [Node Set(M0)] 21 + 25 ε [Obj] 23 + 27 ▷ 30 + 28 ε 27, 19 + 30 ε 25, 28 + 32 ▽ 30 + 33 ▷ 36 + 34 ε 33, 09 + 36 ε 25, 34 + 38 ▷ 36 diff --git a/crates/plotnik-lib/src/emit/snapshots/plotnik_lib__emit__codegen_tests__sequences_in_quantifier.snap b/crates/plotnik-lib/src/emit/snapshots/plotnik_lib__emit__codegen_tests__sequences_in_quantifier.snap index 60f800aa..1e3881ea 100644 --- a/crates/plotnik-lib/src/emit/snapshots/plotnik_lib__emit__codegen_tests__sequences_in_quantifier.snap +++ b/crates/plotnik-lib/src/emit/snapshots/plotnik_lib__emit__codegen_tests__sequences_in_quantifier.snap @@ -36,14 +36,20 @@ Test: 02 ε [Obj] 04 04 (parent) 05 05 ε [Arr] 07 - 07 ε 19, 15 + 07 ε 27, 19 09 ε [EndArr Set(M0)] 11 - 11 △ 13 - 12 ▶ - 13 ε [EndObj] 12 - 15 ε [EndArr Set(M0)] 13 - 17 ▷ (b) [Push] 23 - 19 ▽ (a) 17 - 20 ▷ (b) [Push] 23 - 22 ▷ (a) 20 - 23 ε 22, 09 + 11 △ 17 + 12 ▷ (b) [Push] 14 + 14 ε 33, 09 + 16 ▶ + 17 ε [EndObj] 16 + 19 ε [EndArr Set(M0)] 17 + 21 (a) 12 + 22 ▷ 25 + 23 ε 22, 19 + 25 ε 21, 23 + 27 ▽ 25 + 28 ▷ 31 + 29 ε 28, 09 + 31 ε 21, 29 + 33 ▷ 31