regen/sdk/token/
token_stream.rs

1use crate::sdk::{TokenImpl, TokenType};
2
3/// A generic implementation of a stream of tokens with type `T`
4///
5/// The TokenStream implementation does not own the Tokens.
6/// It returns references to the tokens on demand. The caller can clone them if needed.
7///
8/// The token streams are used internally to generate the AST.
9/// They are not meant to be used independently.
10#[derive(Debug)]
11pub struct TokenStream<'t, T>
12where
13    T: TokenType,
14{
15    /// Token data
16    tokens: &'t [TokenImpl<T>],
17    /// Current token index
18    index: usize,
19    /// Position stack
20    stack: Vec<usize>,
21    /// Max stack size
22    max_stack_size: usize,
23    /// Best guess at which token is causing a syntax error
24    best_error_guess: usize,
25}
26
27impl<'t, T> TokenStream<'t, T>
28where
29    T: TokenType,
30{
31    /// Create a new TokenStream with the given tokens
32    pub fn new(tokens: &'t [TokenImpl<T>], max_stack_size: usize) -> Self {
33        Self {
34            tokens,
35            index: 0,
36            stack: Vec::new(),
37            max_stack_size,
38            best_error_guess: 0,
39        }
40    }
41
42    /// Returns if there is no token left after the current position
43    #[inline]
44    pub fn is_exhausted(&self) -> bool {
45        self.index >= self.tokens.len()
46    }
47
48    /// Get the best guess at which token is causing a syntax error
49    pub fn get_guess_err_token(&self) -> Option<&'t TokenImpl<T>> {
50        self.tokens
51            .get(self.best_error_guess)
52            .or(self.tokens.get(self.index))
53    }
54
55    /// Set the error guess at the current index
56    pub fn set_error(&mut self, force: bool) {
57        if force || self.index > self.best_error_guess {
58            self.best_error_guess = self.index;
59        }
60    }
61
62    /// Returns the next token if available, and advance the position
63    /// A reference is returned to avoid copying the token
64    pub fn consume(&mut self) -> Option<&'t TokenImpl<T>> {
65        match self.tokens.get(self.index) {
66            Some(token) => {
67                self.index += 1;
68                Some(token)
69            }
70            None => None,
71        }
72    }
73
74    /// Push the current position to stack so it can be restored
75    pub fn push(&mut self) -> bool {
76        if self.stack.len() >= self.max_stack_size {
77            return false;
78        }
79        self.stack.push(self.index);
80        true
81    }
82
83    /// Pop position stack without restoring the position
84    #[inline]
85    pub fn pop(&mut self) {
86        self.stack.pop();
87    }
88
89    /// Restore the position to be the index on the stack top.
90    /// This does not pop the stack.
91    #[inline]
92    pub fn restore(&mut self) {
93        self.index = *self.stack.last().unwrap();
94    }
95}
96
97/// Helper for parsing an optional parameter using a TokenStream
98///
99/// This will save the current position (push), try to execute the parse (which returns an [`Option`]),
100/// and restore the position if the parse failed.
101///
102/// See source code for [`crate::grammar`] for examples.
103#[macro_export]
104macro_rules! optional {
105    ( $ts:ident, $inner_optional:expr ) => {
106        // save the pos to restore in case of failure
107        if !$ts.push() {
108            None
109        } else {
110            let inner = $inner_optional;
111            if inner.is_none() {
112                // restore if failure
113                $ts.restore();
114            }
115            // remove the saved pos
116            $ts.pop();
117            inner
118        }
119    };
120}
121
122/// Helper for parsing a required parameter using a TokenStream
123///
124/// This will try to execute the parse (which returns an [`Option`]).
125/// If the parse failed, it will mark an error on the TokenStream.
126///
127/// Note that this does not change the control flow. The caller should
128/// still use the `?` operator for early return if needed. This is intentional
129/// to make the early return more obvious.
130#[macro_export]
131macro_rules! required {
132    ( $ts:ident, $inner_required:expr ) => {{
133        let inner = $inner_required;
134        if inner.is_none() {
135            $ts.set_error(false);
136        }
137        inner
138    }};
139}
140
141/// helper for parsing a list of parameters using a TokenStream
142///
143/// Optional list would be
144/// ```nocompile
145/// let mut v = vec![];
146/// list!(ts, v, $inner)
147/// ```
148///
149/// Required (at least one) would be
150/// ```nocompile
151/// let mut v = vec![required!(ts, $inner)?];
152/// list!(ts, v, $inner)
153/// ```
154#[macro_export]
155macro_rules! list {
156    ( $ts:ident, $list:ident, $inner:expr ) => {{
157        loop {
158            if !$ts.push() {
159                break;
160            }
161            match $inner {
162                Some(item) => {
163                    $list.push(item);
164                    $ts.pop();
165                }
166                None => {
167                    // restore if failure
168                    $ts.restore();
169                    $ts.pop();
170                    break;
171                }
172            }
173        }
174        $list
175    }};
176}