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}