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
12 changes: 12 additions & 0 deletions crates/plotnik-lib/src/engine/cursor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,18 @@ impl<'t> CursorWrapper<'t> {
self.cursor.field_id()
}

/// Get current tree depth (root is 0).
#[inline]
pub fn depth(&self) -> u32 {
self.cursor.depth()
}

/// Move cursor to parent node.
#[inline]
pub fn goto_parent(&mut self) -> bool {
self.cursor.goto_parent()
}

/// Check if a node type is trivia.
#[inline]
pub fn is_trivia(&self, node: &Node<'_>) -> bool {
Expand Down
12 changes: 6 additions & 6 deletions crates/plotnik-lib/src/engine/frame.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@ pub struct Frame {
pub return_addr: u16,
/// Parent frame index (for cactus stack).
pub parent: Option<u32>,
/// Cursor position before Call navigation (for restoration on Return).
pub cursor_position: u32,
/// Tree depth at Call time (for level restoration on Return).
pub saved_depth: u32,
}

/// Append-only arena for frames (cactus stack implementation).
Expand All @@ -35,25 +35,25 @@ impl FrameArena {
}

/// Push a new frame, returns its index.
pub fn push(&mut self, return_addr: u16, cursor_position: u32) -> u32 {
pub fn push(&mut self, return_addr: u16, saved_depth: u32) -> u32 {
let idx = self.frames.len() as u32;
self.frames.push(Frame {
return_addr,
parent: self.current,
cursor_position,
saved_depth,
});
self.current = Some(idx);
idx
}

/// Pop the current frame, returning its return address and cursor position.
/// Pop the current frame, returning its return address and saved depth.
///
/// Panics if the stack is empty.
pub fn pop(&mut self) -> (u16, u32) {
let current_idx = self.current.expect("pop on empty frame stack");
let frame = self.frames[current_idx as usize];
self.current = frame.parent;
(frame.return_addr, frame.cursor_position)
(frame.return_addr, frame.saved_depth)
}

/// Restore frame state for backtracking.
Expand Down
23 changes: 15 additions & 8 deletions crates/plotnik-lib/src/engine/vm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -231,12 +231,13 @@ impl<'t> VM<'t> {

self.navigate_to_field(c.nav, c.node_field, tracer)?;

// Save cursor position AFTER navigation - this is where the callee starts matching.
// On Return, we restore to this position so Nav::Next in quantifier loops works correctly.
let saved_cursor = self.cursor.descendant_index();
// Save tree depth AFTER navigation. On Return, we go up to this depth.
// This allows continue_search advances at the same level to be preserved,
// while still restoring level when the callee descended into children.
let saved_depth = self.cursor.depth();

tracer.trace_call(c.target.get());
self.frames.push(c.next.get(), saved_cursor);
self.frames.push(c.next.get(), saved_depth);
self.recursion_depth += 1;
self.ip = c.target.get();
Ok(())
Expand Down Expand Up @@ -296,18 +297,24 @@ impl<'t> VM<'t> {
return Err(RuntimeError::Accept);
}

let (return_addr, saved_cursor) = self.frames.pop();
let (return_addr, saved_depth) = self.frames.pop();
self.recursion_depth -= 1;

// Prune frames (O(1) amortized)
self.frames.prune(self.checkpoints.max_frame_ref());

// Set matched_node BEFORE restoring cursor so effects after
// Set matched_node BEFORE going up so effects after
// a Call can capture the node that the callee matched.
self.matched_node = Some(self.cursor.node());

// Restore cursor position for subsequent navigation (e.g., Nav::Next in quantifier loop)
self.cursor.goto_descendant(saved_cursor);
// Go up to saved depth level. This preserves sibling advances
// (continue_search at same level) while restoring level when
// the callee descended into children.
while self.cursor.depth() > saved_depth {
if !self.cursor.goto_parent() {
break;
}
}

self.ip = return_addr;
Ok(())
Expand Down