plotnik_lib/ir/
nav.rs

1//! Tree navigation instructions for query execution.
2//!
3//! Navigation decisions are resolved at graph construction time, not runtime.
4//! Each transition carries its own `Nav` instruction.
5
6/// Navigation instruction determining cursor movement and skip policy.
7#[derive(Debug, Clone, Copy, PartialEq, Eq)]
8#[repr(C)]
9pub struct Nav {
10    pub kind: NavKind,
11    /// Ascent level count for `Up*` variants, ignored otherwise.
12    pub level: u8,
13}
14
15impl Nav {
16    pub const fn stay() -> Self {
17        Self {
18            kind: NavKind::Stay,
19            level: 0,
20        }
21    }
22
23    pub const fn next() -> Self {
24        Self {
25            kind: NavKind::Next,
26            level: 0,
27        }
28    }
29
30    pub const fn next_skip_trivia() -> Self {
31        Self {
32            kind: NavKind::NextSkipTrivia,
33            level: 0,
34        }
35    }
36
37    pub const fn next_exact() -> Self {
38        Self {
39            kind: NavKind::NextExact,
40            level: 0,
41        }
42    }
43
44    pub const fn down() -> Self {
45        Self {
46            kind: NavKind::Down,
47            level: 0,
48        }
49    }
50
51    pub const fn down_skip_trivia() -> Self {
52        Self {
53            kind: NavKind::DownSkipTrivia,
54            level: 0,
55        }
56    }
57
58    pub const fn down_exact() -> Self {
59        Self {
60            kind: NavKind::DownExact,
61            level: 0,
62        }
63    }
64
65    pub const fn up(level: u8) -> Self {
66        Self {
67            kind: NavKind::Up,
68            level,
69        }
70    }
71
72    /// Constrained ascent requires `level == 1`. Multi-level ascent with
73    /// intermediate constraints must decompose into separate transitions.
74    pub const fn up_skip_trivia(level: u8) -> Self {
75        assert!(
76            level == 1,
77            "UpSkipTrivia requires level == 1; decompose for intermediate constraints"
78        );
79        Self {
80            kind: NavKind::UpSkipTrivia,
81            level,
82        }
83    }
84
85    /// Constrained ascent requires `level == 1`. Multi-level ascent with
86    /// intermediate constraints must decompose into separate transitions.
87    pub const fn up_exact(level: u8) -> Self {
88        assert!(
89            level == 1,
90            "UpExact requires level == 1; decompose for intermediate constraints"
91        );
92        Self {
93            kind: NavKind::UpExact,
94            level,
95        }
96    }
97
98    /// Returns true if this is a Stay navigation (no movement).
99    #[inline]
100    pub const fn is_stay(&self) -> bool {
101        matches!(self.kind, NavKind::Stay)
102    }
103
104    /// Returns true if this is a horizontal sibling traversal (Next*).
105    #[inline]
106    pub const fn is_next(&self) -> bool {
107        matches!(
108            self.kind,
109            NavKind::Next | NavKind::NextSkipTrivia | NavKind::NextExact
110        )
111    }
112
113    /// Returns true if this descends into children (Down*).
114    #[inline]
115    pub const fn is_down(&self) -> bool {
116        matches!(
117            self.kind,
118            NavKind::Down | NavKind::DownSkipTrivia | NavKind::DownExact
119        )
120    }
121
122    /// Returns true if this ascends to parent(s) (Up*).
123    #[inline]
124    pub const fn is_up(&self) -> bool {
125        matches!(
126            self.kind,
127            NavKind::Up | NavKind::UpSkipTrivia | NavKind::UpExact
128        )
129    }
130
131    /// Returns true if this navigation skips only trivia nodes.
132    #[inline]
133    pub const fn is_skip_trivia(&self) -> bool {
134        matches!(
135            self.kind,
136            NavKind::NextSkipTrivia | NavKind::DownSkipTrivia | NavKind::UpSkipTrivia
137        )
138    }
139
140    /// Returns true if this navigation requires exact position (no skipping).
141    #[inline]
142    pub const fn is_exact(&self) -> bool {
143        matches!(
144            self.kind,
145            NavKind::NextExact | NavKind::DownExact | NavKind::UpExact
146        )
147    }
148}
149
150/// Navigation kind determining movement direction and skip policy.
151#[derive(Debug, Clone, Copy, PartialEq, Eq)]
152#[repr(u8)]
153pub enum NavKind {
154    /// No movement. Used only for first transition when cursor is at root.
155    Stay = 0,
156
157    // Sibling traversal (horizontal)
158    /// Skip any nodes to find match.
159    Next = 1,
160    /// Skip trivia only, fail if non-trivia skipped.
161    NextSkipTrivia = 2,
162    /// No skipping, current sibling must match.
163    NextExact = 3,
164
165    // Enter children (descend)
166    /// Skip any among children.
167    Down = 4,
168    /// Skip trivia only among children.
169    DownSkipTrivia = 5,
170    /// First child must match, no skip.
171    DownExact = 6,
172
173    // Exit children (ascend)
174    /// Ascend `level` levels, no constraint.
175    Up = 7,
176    /// Validate last non-trivia, ascend `level` levels.
177    UpSkipTrivia = 8,
178    /// Validate last child, ascend `level` levels.
179    UpExact = 9,
180}