plotnik_bytecode/bytecode/
nav.rs

1//! Navigation command encoding for bytecode instructions.
2//!
3//! Navigation determines how the VM moves through the tree-sitter AST.
4
5/// Navigation command for VM execution.
6#[derive(Clone, Copy, PartialEq, Eq, Debug, Default)]
7pub enum Nav {
8    /// Epsilon transition: pure control flow, no cursor movement or node check.
9    /// Used for branching, quantifier loops, and effect-only transitions.
10    #[default]
11    Epsilon,
12    /// Stay at current position.
13    Stay,
14    /// Stay at current position, exact match only (no continue_search).
15    StayExact,
16    Next,
17    NextSkip,
18    NextExact,
19    Down,
20    DownSkip,
21    DownExact,
22    Up(u8),
23    UpSkipTrivia(u8),
24    UpExact(u8),
25}
26
27impl Nav {
28    /// Decode from bytecode byte.
29    ///
30    /// Byte layout:
31    /// - Bits 7-6: Mode (00=Standard, 01=Up, 10=UpSkipTrivia, 11=UpExact)
32    /// - Bits 5-0: Payload (enum value for Standard, level count for Up variants)
33    pub fn from_byte(b: u8) -> Self {
34        let mode = b >> 6;
35        let payload = b & 0x3F;
36
37        match mode {
38            0b00 => match payload {
39                0 => Self::Epsilon,
40                1 => Self::Stay,
41                2 => Self::StayExact,
42                3 => Self::Next,
43                4 => Self::NextSkip,
44                5 => Self::NextExact,
45                6 => Self::Down,
46                7 => Self::DownSkip,
47                8 => Self::DownExact,
48                _ => panic!("invalid nav standard: {payload}"),
49            },
50            0b01 => {
51                assert!(payload >= 1, "invalid nav up level: {payload}");
52                Self::Up(payload)
53            }
54            0b10 => {
55                assert!(payload >= 1, "invalid nav up_skip_trivia level: {payload}");
56                Self::UpSkipTrivia(payload)
57            }
58            0b11 => {
59                assert!(payload >= 1, "invalid nav up_exact level: {payload}");
60                Self::UpExact(payload)
61            }
62            _ => unreachable!(),
63        }
64    }
65
66    /// Encode to bytecode byte.
67    pub fn to_byte(self) -> u8 {
68        match self {
69            Self::Epsilon => 0,
70            Self::Stay => 1,
71            Self::StayExact => 2,
72            Self::Next => 3,
73            Self::NextSkip => 4,
74            Self::NextExact => 5,
75            Self::Down => 6,
76            Self::DownSkip => 7,
77            Self::DownExact => 8,
78            Self::Up(n) => {
79                assert!((1..=63).contains(&n), "Up level overflow: {n} > 63");
80                0b01_000000 | n
81            }
82            Self::UpSkipTrivia(n) => {
83                assert!(
84                    (1..=63).contains(&n),
85                    "UpSkipTrivia level overflow: {n} > 63"
86                );
87                0b10_000000 | n
88            }
89            Self::UpExact(n) => {
90                assert!((1..=63).contains(&n), "UpExact level overflow: {n} > 63");
91                0b11_000000 | n
92            }
93        }
94    }
95
96    /// Convert navigation to its exact variant (no search loop).
97    ///
98    /// Used by alternation branches which should match at their exact
99    /// cursor position only - the search among positions is owned by
100    /// the parent context (quantifier's skip-retry, sequence advancement).
101    pub fn to_exact(self) -> Self {
102        match self {
103            Self::Epsilon => Self::Epsilon, // Epsilon stays epsilon
104            Self::Down | Self::DownSkip => Self::DownExact,
105            Self::Next | Self::NextSkip => Self::NextExact,
106            Self::Stay => Self::StayExact,
107            Self::Up(n) | Self::UpSkipTrivia(n) => Self::UpExact(n),
108            // Already exact variants
109            Self::DownExact | Self::NextExact | Self::StayExact | Self::UpExact(_) => self,
110        }
111    }
112}