Skip to main content

varyk_syntax/parser/
mod.rs

1//! The parser: a hand-written recursive-descent / Pratt parser over a
2//! token slice. This module holds the token-cursor primitives shared by
3//! every parsing function; `expr.rs` holds expression parsing, `item.rs`
4//! item parsing (`fn`, `struct`, `mod`) and the top-level [`Parser::parse_program`],
5//! `stmt.rs` statement parsing, and `ty.rs` type parsing.
6
7mod expr;
8mod item;
9mod stmt;
10mod ty;
11
12use crate::ast::Program;
13use crate::error::{FixIt, SyntaxError, V0001, V0002};
14use crate::span::{FileId, Span};
15use crate::token::{Token, TokenKind};
16
17/// Parses a whole token stream for a single file into a [`Program`] plus
18/// every syntax error found. On the first `V0002` (or an unrecoverable
19/// `V0001`, such as a reserved keyword where an item is expected), parsing
20/// stops and the items parsed so far are returned alongside the errors.
21pub fn parse(tokens: &[Token], file: FileId) -> (Program, Vec<SyntaxError>) {
22    let mut parser = Parser::new(tokens, file);
23    let program = parser.parse_program();
24    (program, parser.errors().to_vec())
25}
26
27/// Parses a token slice for a single file. No error recovery beyond what
28/// each parsing function documents: `V0002` always stops parsing outright,
29/// since without a reliable recovery point there is nothing safe to guess.
30/// Most other diagnostic codes are recorded and parsing continues (for
31/// example the `&` / `&mut` sigil's `V0010`), but a few `V0001`s have no
32/// operand left to fall back to — a reserved keyword or an unsupported
33/// macro name where an expression is expected — and stop parsing too, per
34/// the call sites that produce them.
35pub(crate) struct Parser<'a> {
36    tokens: &'a [Token],
37    pos: usize,
38    file: FileId,
39    errors: Vec<SyntaxError>,
40    /// Set while parsing an `if` or `while` condition, so
41    /// that a `{` right after a path is treated as the start of the
42    /// block, not a struct literal `Name { ... }` — the same ambiguity
43    /// Rust resolves the same way.
44    in_condition: bool,
45    /// Set right before parsing a `for` loop's head starts, and consumed
46    /// by the very next `(` reached as a primary expression (`parse_primary`),
47    /// if any: it never survives past that first primary. Lets a range
48    /// found directly inside that group (`for i in (0..3)`) get a message
49    /// naming the parentheses themselves, rather than the generic "only in
50    /// the head of a `for` loop" one, since this group IS the head.
51    for_head_leading_paren: bool,
52}
53
54impl<'a> Parser<'a> {
55    pub(crate) fn new(tokens: &'a [Token], file: FileId) -> Self {
56        Self {
57            tokens,
58            pos: 0,
59            file,
60            errors: Vec::new(),
61            in_condition: false,
62            for_head_leading_paren: false,
63        }
64    }
65
66    /// Every syntax error recorded so far, in the order they were found.
67    pub(crate) fn errors(&self) -> &[SyntaxError] {
68        &self.errors
69    }
70
71    // --- Cursor -----------------------------------------------------------
72
73    fn peek(&self) -> Option<&TokenKind> {
74        self.tokens.get(self.pos).map(|t| &t.kind)
75    }
76
77    /// The kind of the token `offset` positions past the current one,
78    /// without consuming anything. `peek_at(0)` is [`Parser::peek`].
79    fn peek_at(&self, offset: usize) -> Option<&TokenKind> {
80        self.tokens.get(self.pos + offset).map(|t| &t.kind)
81    }
82
83    fn peek_token(&self) -> Option<&Token> {
84        self.tokens.get(self.pos)
85    }
86
87    /// Consumes and returns the current token, or `None` at the end of the
88    /// stream.
89    fn bump(&mut self) -> Option<Token> {
90        let token = self.tokens.get(self.pos).cloned();
91        if token.is_some() {
92            self.pos += 1;
93        }
94        token
95    }
96
97    /// The span an error should carry when there is no current token to
98    /// point at: a zero-width span right after the last token (or at byte
99    /// 0 of an empty file).
100    fn eof_span(&self) -> Span {
101        let end = self.tokens.last().map(|t| t.span.end).unwrap_or(0);
102        Span::new(self.file, end, end)
103    }
104
105    /// The span of the current token, or [`Parser::eof_span`] if the
106    /// stream is exhausted.
107    fn current_span(&self) -> Span {
108        self.peek_token()
109            .map(|t| t.span)
110            .unwrap_or_else(|| self.eof_span())
111    }
112
113    /// A span running from the start of `from` to the end of `to`, in this
114    /// parser's file.
115    fn span_from(&self, from: Span, to: Span) -> Span {
116        Span::new(self.file, from.start, to.end)
117    }
118
119    /// Consumes the current token if its kind equals `kind`, without
120    /// recording an error either way if it does not match.
121    fn bump_if(&mut self, kind: &TokenKind) -> bool {
122        if self.peek() == Some(kind) {
123            self.bump();
124            true
125        } else {
126            false
127        }
128    }
129
130    /// Runs `f` with the `in_condition` flag temporarily cleared,
131    /// restoring it afterwards regardless of whether `f` succeeds. Used
132    /// wherever a nested context — parentheses, call arguments, a block's
133    /// body — already disambiguates struct-literal syntax on its own, so
134    /// the enclosing condition's suppression must not leak into it (a
135    /// leak would make `if f(Point { x: 1 }) { }` or
136    /// `if { Point { x: 1 } } { }` fail to parse, even though the
137    /// parentheses/braces already remove the ambiguity `if`'s own
138    /// condition has).
139    fn without_condition<T>(&mut self, f: impl FnOnce(&mut Self) -> T) -> T {
140        let was_in_condition = self.in_condition;
141        self.in_condition = false;
142        let result = f(self);
143        self.in_condition = was_in_condition;
144        result
145    }
146
147    fn push_error(&mut self, code: &'static str, span: Span, message: impl Into<String>) {
148        self.errors.push(SyntaxError::new(span, code, message));
149    }
150
151    fn push_error_with_fix_it(
152        &mut self,
153        code: &'static str,
154        span: Span,
155        message: impl Into<String>,
156        fix_it: FixIt,
157    ) {
158        self.errors
159            .push(SyntaxError::new(span, code, message).with_fix_it(fix_it));
160    }
161
162    /// Consumes the current token if its kind equals `expected`, recording
163    /// `V0002` and returning `Err` otherwise.
164    fn expect(&mut self, expected: TokenKind, what: &str) -> Result<Token, ()> {
165        if self.peek() == Some(&expected) {
166            Ok(self.bump().expect("peek just confirmed a token is present"))
167        } else {
168            let span = self.current_span();
169            self.push_error(V0002, span, format!("expected {what}"));
170            Err(())
171        }
172    }
173
174    /// Consumes an [`crate::token::TokenKind::Identifier`], recording
175    /// `V0002` and returning `Err` for anything else, or `V0001` for a
176    /// reserved Rust keyword (spec 4.1) or for `self` (spec 2.5): `self` is
177    /// a keyword everywhere except a method's own receiver position, which
178    /// `item.rs`'s `parse_self_receiver` reads before this function ever
179    /// sees it.
180    fn expect_identifier(&mut self, what: &str) -> Result<crate::ast::Ident, ()> {
181        match self.peek() {
182            Some(TokenKind::ReservedKeyword(word)) => {
183                let message =
184                    format!("`{word}` is a Rust keyword and cannot be used as a name in Varyk");
185                let span = self.current_span();
186                self.bump();
187                self.push_error(V0001, span, message);
188                Err(())
189            }
190            Some(TokenKind::SelfKw) => {
191                let span = self.current_span();
192                self.bump();
193                self.push_error(
194                    V0001,
195                    span,
196                    "`self` is a keyword and can only be used as a method's first parameter",
197                );
198                Err(())
199            }
200            Some(TokenKind::Identifier(_)) => {
201                let token = self.bump().expect("peek just confirmed a token is present");
202                let name = match token.kind {
203                    TokenKind::Identifier(name) => name,
204                    _ => unreachable!("matched above"),
205                };
206                Ok(crate::ast::Ident {
207                    name,
208                    span: token.span,
209                })
210            }
211            _ => {
212                let span = self.current_span();
213                self.push_error(V0002, span, format!("expected {what}"));
214                Err(())
215            }
216        }
217    }
218
219    /// Like [`Parser::expect_identifier`], but also rejects `_`: valid as a
220    /// `let` binding's discard, but not as a function, struct, module,
221    /// field, or parameter name (rustc itself rejects `fn _`).
222    fn expect_name_identifier(&mut self, what: &str) -> Result<crate::ast::Ident, ()> {
223        let ident = self.expect_identifier(what)?;
224        if ident.name == "_" {
225            self.push_error(V0002, ident.span, "`_` cannot be used as a name here");
226            return Err(());
227        }
228        Ok(ident)
229    }
230}