synkit_core/traits/
peek.rs

1use super::stream::{SpannedLike, TokenStream};
2
3/// Trait for lookahead without consuming tokens.
4///
5/// `Peek` enables the parser to inspect the next token to decide which
6/// production to use, without consuming it. This is essential for
7/// implementing LL(1) and similar parsing strategies.
8///
9/// # Associated Types
10///
11/// - `Token`: The token type to peek at (e.g., `MyTok`)
12///
13/// # Required Methods
14///
15/// - `is(token)`: Returns `true` if the token matches this type
16///
17/// # Provided Methods
18///
19/// - `peek(stream)`: Checks if the next token in the stream matches
20///
21/// # Example
22///
23/// ```ignore
24/// use synkit::{Peek, TokenStream};
25///
26/// struct IfKeyword;
27///
28/// impl Peek for IfKeyword {
29///     type Token = MyTok;
30///
31///     fn is(token: &Self::Token) -> bool {
32///         matches!(token, MyTok::If)
33///     }
34/// }
35///
36/// // In parser:
37/// fn parse_statement(stream: &mut impl TokenStream<Token = MyTok>) {
38///     if stream.peek::<IfKeyword>() {
39///         parse_if_statement(stream)
40///     } else {
41///         parse_expression_statement(stream)
42///     }
43/// }
44/// ```
45///
46/// # Usage Patterns
47///
48/// ## Simple Token Matching
49///
50/// ```ignore
51/// if stream.peek::<Comma>() {
52///     stream.parse::<Comma>()?;
53/// }
54/// ```
55///
56/// ## Alternative Productions
57///
58/// ```ignore
59/// if stream.peek::<NumberLiteral>() {
60///     Expr::Number(stream.parse()?)
61/// } else if stream.peek::<StringLiteral>() {
62///     Expr::String(stream.parse()?)
63/// } else {
64///     return Err(Error::unexpected());
65/// }
66/// ```
67pub trait Peek: Sized {
68    /// The token type to peek at.
69    type Token: Clone;
70
71    /// Check if a token matches this type.
72    ///
73    /// # Arguments
74    ///
75    /// * `token` - The token to check
76    ///
77    /// # Returns
78    ///
79    /// `true` if the token matches this type
80    fn is(token: &Self::Token) -> bool;
81
82    /// Peek at stream without consuming tokens.
83    ///
84    /// Default implementation calls `is()` on the next token.
85    ///
86    /// # Arguments
87    ///
88    /// * `stream` - The token stream to peek into
89    ///
90    /// # Returns
91    ///
92    /// `true` if the next token matches, `false` if no match or stream empty
93    #[inline]
94    fn peek<S: TokenStream<Token = Self::Token>>(stream: &S) -> bool {
95        stream
96            .peek_token()
97            .map(|t| Self::is(t.value_ref()))
98            .unwrap_or(false)
99    }
100}
101
102impl<T: Peek> Peek for Box<T> {
103    type Token = T::Token;
104
105    #[inline]
106    fn is(token: &Self::Token) -> bool {
107        T::is(token)
108    }
109}