• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

vcfxb / wright-lang / 15061769677

16 May 2025 06:02AM UTC coverage: 75.523% (+0.7%) from 74.809%
15061769677

push

github

vcfxb
chore: cargo fmt

6 of 13 new or added lines in 4 files covered. (46.15%)

27 existing lines in 2 files now uncovered.

938 of 1242 relevant lines covered (75.52%)

29.83 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

79.27
/wright/src/parser.rs
1
//! This parser module is responsible for turning the stream of [Token]s from the [Lexer] into a tree of [AST] nodes.
2
//!
3
//! [AST]: crate::ast
4
//! [Token]: crate::lexer::token::Token
5

6
use error::{ParserError, ParserErrorKind};
7

8
use super::lexer::Lexer;
9
use crate::{
10
    lexer::token::{Token, TokenTy},
11
    source_tracking::fragment::Fragment,
12
};
13
use std::collections::VecDeque;
14

15
mod decl;
16
pub mod error;
17
mod identifier;
18
mod literal;
19
mod path;
20
mod ty;
21
pub mod whitespace;
22

23
/// The [Parser] struct wraps a [Lexer] and adds lookahead and functions that are useful for parsing.
24
#[derive(Debug)]
25
pub struct Parser {
26
    lexer: Lexer,
27
    lookahead: VecDeque<Token>,
28
}
29

30
impl Parser {
31
    /// Construct a new parser around a given [Lexer].
32
    pub fn new(lexer: Lexer) -> Self {
25✔
33
        Parser {
25✔
34
            lexer,
25✔
35
            lookahead: VecDeque::new(),
25✔
36
        }
25✔
37
    }
25✔
38

39
    /// Get the number of remaining bytes on this parser. This is potentially useful for checking
40
    /// if a parser has advanced between two calls (or checking if a parser has reached end of input).
41
    pub fn bytes_remaining(&self) -> usize {
1✔
42
        let bytes_remaining_in_lookahead_buffer = self
1✔
43
            .lookahead
1✔
44
            .iter()
1✔
45
            .map(|t| t.fragment.len())
1✔
46
            .sum::<usize>();
1✔
47

48
        let bytes_remaining_in_lexer = self.lexer.bytes_remaining();
1✔
49

50
        bytes_remaining_in_lexer + bytes_remaining_in_lookahead_buffer
1✔
51
    }
1✔
52

53
    /// Get the next [Token] from this [Parser]. This may be a token that's already been peeked.
54
    ///
55
    /// Skips any non-document comments encountered via the lexer implementation.
56
    ///
57
    /// Return an error if a [Token] with [TokenTy::Unknown] is encountered.
58
    pub fn next_token(&mut self) -> Result<Option<Token>, ParserError> {
3✔
59
        let token = self
3✔
60
            .lookahead
3✔
61
            .pop_front()
3✔
62
            .or_else(|| self.lexer.next_token());
3✔
63

64
        // Check for unknown tokens, which should always convert to an error.
65
        match token {
3✔
66
            Some(Token {
67
                variant: TokenTy::Unknown,
68
                fragment,
×
69
            }) => Err(ParserErrorKind::EncounteredUnknownToken.at(fragment)),
×
70
            known_token_or_none => Ok(known_token_or_none),
3✔
71
        }
72
    }
3✔
73

74
    /// Advance this [Parser] by `n` [Token]s. If this [Parser] runs out of [Token]s, panic.
75
    ///
76
    /// Panics
77
    /// - If `n` is greater than the number of remaining tokens.
78
    pub fn advance(&mut self, n: usize) {
12✔
79
        // Add tokens to the lookahead buffer until we have enough to split off.
80
        while self.lookahead.len() < n {
12✔
UNCOV
81
            let token = self
×
UNCOV
82
                .lexer
×
UNCOV
83
                .next_token()
×
UNCOV
84
                .expect("advance: `n` <= number of remaining tokens");
×
UNCOV
85

×
UNCOV
86
            self.lookahead.push_back(token);
×
UNCOV
87
        }
×
88

89
        // Split them off.
90
        self.lookahead = self.lookahead.split_off(n);
12✔
91
    }
12✔
92

93
    /// Peek at the next token from the [Lexer] (cached in the lookahead queue if peeked before).
94
    pub fn peek(&mut self) -> Option<&Token> {
84✔
95
        if self.lookahead.is_empty() {
84✔
96
            self.lookahead.push_back(self.lexer.next_token()?);
40✔
97
        }
44✔
98

99
        self.lookahead.front()
82✔
100
    }
84✔
101

102
    /// Peek the [Fragment] of the next [Token].
UNCOV
103
    pub fn peek_fragment(&mut self) -> Option<&Fragment> {
×
UNCOV
104
        self.peek().map(|token| &token.fragment)
×
UNCOV
105
    }
×
106

107
    /// Peek the [TokenTy] of the next [Token].
108
    pub fn peek_variant(&mut self) -> Option<TokenTy> {
19✔
109
        self.peek().map(|token| token.variant)
19✔
110
    }
19✔
111

112
    /// Peek the [Fragment] of the next [Token] and clone it or return a clone of the
113
    /// remainder [Fragment] of the internal [Lexer]
114
    /// (which will be empty, since there wasn't a [Token] to peek).
115
    ///
116
    /// This is likely only useful for error reporting -- a clone of a potentially empty fragment is
117
    /// rarely ever useful otherwise.
118
    pub fn peek_fragment_or_rest_cloned(&mut self) -> Fragment {
15✔
119
        match self.peek() {
15✔
120
            Some(Token { fragment, .. }) => fragment.clone(),
14✔
121
            None => {
122
                let rest = self.lexer.remaining.clone();
1✔
123

124
                // Assert that we're making the right assumptions about the remaining fragment.
125
                // These are (unidiomatically) done using debug_assert -- perhaps that changes eventually
126
                // however it should be fine for now, since this can only produce logic bugs (never memory or
127
                // concurrency bugs).
128
                debug_assert!(rest.is_valid());
1✔
129
                debug_assert!(rest.is_empty());
1✔
130
                debug_assert!(rest.is_empty_at_end_of_source());
1✔
131

132
                rest
1✔
133
            }
134
        }
135
    }
15✔
136

137
    /// Get the [Lexer] that's wrapped.
138
    pub fn lexer(&self) -> &Lexer {
1✔
139
        &self.lexer
1✔
140
    }
1✔
141

142
    /// Lookahead `k` [Token]s.
143
    ///
144
    /// If `k == 0` then this is effectively peeking at the next [Token] from the wrapped [Lexer].
UNCOV
145
    pub fn lookahead(&mut self, k: usize) -> Option<&Token> {
×
UNCOV
146
        while self.lookahead.len() <= k {
×
UNCOV
147
            self.lookahead.push_back(self.lexer.next_token()?);
×
148
        }
149

UNCOV
150
        self.lookahead.get(k)
×
UNCOV
151
    }
×
152

153
    /// Similar to [Parser::lookahead] but instead returns a slice of `n` [Token]s, starting with the next [Token].
154
    ///
155
    /// Returns [None] if `n` is greater than the number of remaining [Token]s for this [Parser].
156
    pub fn lookahead_window(&mut self, n: usize) -> Option<&[Token]> {
59✔
157
        while self.lookahead.len() < n {
92✔
158
            self.lookahead.push_back(self.lexer.next_token()?);
63✔
159
        }
160

161
        // Use make contiguous here to get a unified/single slice.
162
        Some(&self.lookahead.make_contiguous()[..n])
29✔
163
    }
59✔
164

165
    /// Get the next [Token] from this parser if its [Token::variant] is the given `token_ty`.
166
    pub fn next_if_is(&mut self, token_ty: TokenTy) -> Option<Token> {
50✔
167
        // Peeking successfully first means that the lookahead vec will never be empty here.
168
        (self.peek()?.variant == token_ty)
50✔
169
            // SAFETY: We just peeked a token to check its variant so this unwrap is always ok.
170
            .then(|| unsafe { self.lookahead.pop_front().unwrap_unchecked() })
49✔
171
    }
50✔
172

173
    /// Peek at the next [Token]s of this [Parser] and determine if the [Token::variant]s match this
174
    /// sequence of [TokenTy]s.
175
    pub fn matches(&mut self, seq: &[TokenTy]) -> bool {
59✔
176
        // Use the rare let-else to ensure there are at minimum, the given number of tokens remaining.
177
        let Some(lookahead_window) = self.lookahead_window(seq.len()) else {
59✔
178
            return false;
30✔
179
        };
180

181
        // Use a zipped iterator to compare all the token variants.
182
        lookahead_window
29✔
183
            .iter()
29✔
184
            .zip(seq)
29✔
185
            .all(|(token, matches)| token.variant == *matches)
53✔
186
    }
59✔
187
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc