Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions crates/plotnik-lib/src/compile/navigation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@ pub fn repeat_nav_for(first_nav: Option<Nav>) -> Option<Nav> {
Some(Nav::DownSkip) => Some(Nav::NextSkip),
Some(Nav::DownExact) => Some(Nav::NextExact),
Some(nav @ (Nav::Next | Nav::NextSkip | Nav::NextExact)) => Some(nav),
None | Some(Nav::Stay) => Some(Nav::Next),
_ => None,
}
}
Expand Down
11 changes: 11 additions & 0 deletions crates/plotnik-lib/src/emit/codegen_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -235,6 +235,17 @@ fn quantifiers_repeat_navigation() {
"#});
}

/// Regression test: sequence quantifiers in called definitions need sibling navigation.
/// Previously, `{...}*` compiled without navigation, causing infinite loops.
#[test]
fn quantifiers_sequence_in_called_def() {
snap!(indoc! {r#"
Item = (identifier) @name
Collect = {(Item) @item}* @items
Test = (parent (Collect))
"#});
}

// ============================================================================
// 5. SEQUENCES
// ============================================================================
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ Test:
25 ε [EndObj Push] 37
27 ε [EndObj Set(M2)] 25
29 ▷ (number) [Node Set(M1)] 27
31 (identifier) [Node Set(M0)] 29
31 (identifier) [Node Set(M0)] 29
33 ε [Obj] 31
35 ε [Obj] 33
37 ε 35, 11
Original file line number Diff line number Diff line change
Expand Up @@ -37,5 +37,5 @@ Test:
08 ▶
09 ε [EndObj] 08
11 ε [EndArr Set(M0)] 09
13 (identifier) [Node Push] 15
13 (identifier) [Node Push] 15
15 ε 13, 11
Original file line number Diff line number Diff line change
Expand Up @@ -37,5 +37,5 @@ Test:
08 ▶
09 ε [EndObj] 08
11 ε [EndArr Set(M0)] 09
13 (identifier) [Node Push] 15
13 (identifier) [Node Push] 15
15 ε 11, 13
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
---
source: crates/plotnik-lib/src/emit/codegen_tests.rs
---
Item = (identifier) @name
Collect = {(Item) @item}* @items
Test = (parent (Collect))
---
[flags]
linked = false

[strings]
S0 "Beauty will save the world"
S1 "name"
S2 "item"
S3 "items"
S4 "Item"
S5 "Collect"
S6 "Test"
S7 "identifier"
S8 "parent"

[type_defs]
T0 = <Node>
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: <Node>
M1: S1 → T0 ; name: <Node>
M2: S2 → T0 ; item: <Node>
M3: S3 → T3 ; items: T3

[type_names]
N0: S4 → T1 ; Item
N1: S5 → T4 ; Collect
N2: S6 → T4 ; Test

[entrypoints]
Collect = 09 :: T4
Item = 01 :: T1
Test = 16 :: T4

[transitions]
00 ε ◼

Item:
01 ε 02
02 ε [Obj] 04
04 (identifier) [Node Set(M0)] 06
06 ε [EndObj] 08
08 ▶

Collect:
09 ε 10
10 ε [Obj] 12
12 ε [Arr] 14
14 ε 35, 28

Test:
16 ε 17
17 ε [Obj] 19
19 (parent) 20
20 ▽ (Collect) 21 ⯇
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
Original file line number Diff line number Diff line change
Expand Up @@ -38,5 +38,5 @@ Test:
09 ε [EndObj] 08
11 ε [EndArr Set(M0)] 09
13 (identifier) [Node Push] 17
15 (identifier) [Node Push] 17
15 (identifier) [Node Push] 17
17 ε 15, 11
Original file line number Diff line number Diff line change
Expand Up @@ -38,5 +38,5 @@ Test:
09 ε [EndObj] 08
11 ε [EndArr Set(M0)] 09
13 (identifier) [Node Push] 17
15 (identifier) [Node Push] 17
15 (identifier) [Node Push] 17
17 ε 11, 15
2 changes: 2 additions & 0 deletions crates/plotnik-lib/src/engine/checkpoint.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ pub struct Checkpoint {
pub effect_watermark: usize,
/// Frame arena state at checkpoint.
pub frame_index: Option<u32>,
/// Recursion depth at checkpoint.
pub recursion_depth: u32,
/// Resume point (raw step index).
pub ip: u16,
}
Expand Down
11 changes: 9 additions & 2 deletions crates/plotnik-lib/src/engine/vm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -133,9 +133,10 @@ impl<'t> VM<'t> {
self.emit_effect(effect_op, tracer);
}

self.matched_node = None;

// Only clear matched_node for non-epsilon transitions.
// For epsilon, preserve matched_node from previous match or return.
if !m.is_epsilon() {
self.matched_node = None;
self.navigate_and_match(m, tracer)?;
}

Expand Down Expand Up @@ -213,6 +214,7 @@ impl<'t> VM<'t> {
descendant_index: self.cursor.descendant_index(),
effect_watermark: self.effects.len(),
frame_index: self.frames.current(),
recursion_depth: self.recursion_depth,
ip: m.successor(i).get(),
});
tracer.trace_checkpoint_created(self.ip);
Expand Down Expand Up @@ -296,6 +298,10 @@ impl<'t> VM<'t> {
// Prune frames (O(1) amortized)
self.frames.prune(self.checkpoints.max_frame_ref());

// Set matched_node to current cursor position so effects after
// a Call can capture the node that the callee matched.
self.matched_node = Some(self.cursor.node());

self.ip = return_addr;
Ok(())
}
Expand All @@ -306,6 +312,7 @@ impl<'t> VM<'t> {
self.cursor.goto_descendant(cp.descendant_index);
self.effects.truncate(cp.effect_watermark);
self.frames.restore(cp.frame_index);
self.recursion_depth = cp.recursion_depth;
self.ip = cp.ip;
Err(RuntimeError::Backtracked)
}
Expand Down