nifty_syntax/parser/
mod.rs1mod expr;
5mod item;
6mod stmt;
7#[cfg(test)]
8mod tests;
9
10use crate::ast::{File, Ident};
11use crate::diagnostic::Diagnostic;
12use crate::lexer;
13use crate::span::{FileId, Span};
14use crate::token::{Token, TokenKind};
15
16#[derive(Debug)]
18struct Reported;
19
20type ParseResult<T> = Result<T, Reported>;
21
22pub(crate) fn parse(source: &str, file: FileId) -> (File, Vec<Diagnostic>) {
24 let (tokens, mut diagnostics) = lexer::lex(source, file);
25 let mut parser = Parser {
26 file,
27 source,
28 tokens,
29 pos: 0,
30 diagnostics: Vec::new(),
31 };
32 let file = parser.parse_file();
33 diagnostics.extend(parser.diagnostics);
34 diagnostics.sort_by_key(|diagnostic| diagnostic.span.start);
35 (file, diagnostics)
36}
37
38struct Parser<'src> {
39 file: FileId,
40 source: &'src str,
41 tokens: Vec<Token>,
43 pos: usize,
44 diagnostics: Vec<Diagnostic>,
45}
46
47impl Parser<'_> {
48 fn parse_file(&mut self) -> File {
49 let mut items = Vec::new();
50 loop {
51 self.skip_newlines();
52 match self.peek() {
53 TokenKind::Eof => return File { items },
54 TokenKind::RBrace => {
55 let token = self.bump();
56 self.report("this `}` has no matching `{`", token.span);
57 }
58 _ => match self.parse_item() {
59 Ok(item) => {
60 items.push(item);
61 self.finish_line();
62 }
63 Err(Reported) => self.recover_to_line_end(),
64 },
65 }
66 }
67 }
68
69 fn current(&self) -> Token {
70 self.tokens[self.pos]
71 }
72
73 fn peek(&self) -> TokenKind {
74 self.current().kind
75 }
76
77 fn peek_nth(&self, n: usize) -> TokenKind {
78 self.tokens
79 .get(self.pos + n)
80 .map_or(TokenKind::Eof, |token| token.kind)
81 }
82
83 fn at(&self, kind: TokenKind) -> bool {
84 self.peek() == kind
85 }
86
87 fn bump(&mut self) -> Token {
88 let token = self.current();
89 if token.kind != TokenKind::Eof {
90 self.pos += 1;
91 }
92 token
93 }
94
95 fn eat(&mut self, kind: TokenKind) -> Option<Token> {
96 self.at(kind).then(|| self.bump())
97 }
98
99 fn expect(&mut self, kind: TokenKind) -> ParseResult<Token> {
100 if let Some(token) = self.eat(kind) {
101 return Ok(token);
102 }
103 self.unexpected(&kind.describe())
104 }
105
106 fn expect_ident(&mut self) -> ParseResult<Ident> {
107 let token = self.expect(TokenKind::Ident)?;
108 Ok(Ident {
109 name: self.text(token.span).to_owned(),
110 span: token.span,
111 })
112 }
113
114 fn unexpected<T>(&mut self, expected: &str) -> ParseResult<T> {
116 let found = self.current();
117 Err(self.report(
118 format!("expected {expected}, found {}", found.kind.describe()),
119 found.span,
120 ))
121 }
122
123 fn report(&mut self, message: impl Into<String>, span: Span) -> Reported {
124 self.diagnostics.push(Diagnostic::new(message, span));
125 Reported
126 }
127
128 fn text(&self, span: Span) -> &str {
129 &self.source[span.range()]
130 }
131
132 fn skip_newlines(&mut self) {
133 while self.eat(TokenKind::Newline).is_some() {}
134 }
135
136 fn span_from(&self, start: usize) -> Span {
138 let end = self
139 .pos
140 .checked_sub(1)
141 .map_or(start, |previous| self.tokens[previous].span.end);
142 Span::new(self.file, start, end)
143 }
144
145 fn finish_line(&mut self) {
147 match self.peek() {
148 TokenKind::Newline => {
149 self.bump();
150 }
151 TokenKind::RBrace | TokenKind::Eof => {}
152 _ => {
153 let token = self.current();
154 self.report(
155 format!(
156 "expected end of line, found {}; put each statement on its own line",
157 token.kind.describe()
158 ),
159 token.span,
160 );
161 self.recover_to_line_end();
162 }
163 }
164 }
165
166 fn recover_to_line_end(&mut self) {
169 let mut depth = 0usize;
170 loop {
171 let kind = self.peek();
172 match kind {
173 TokenKind::Eof => return,
174 TokenKind::Newline | TokenKind::RBrace if depth == 0 => return,
175 TokenKind::LParen | TokenKind::LBracket | TokenKind::LBrace => depth += 1,
176 TokenKind::RParen | TokenKind::RBracket | TokenKind::RBrace => {
177 depth = depth.saturating_sub(1);
178 }
179 _ => {}
180 }
181 self.bump();
182 }
183 }
184
185 fn comma_list<T>(
187 &mut self,
188 close: TokenKind,
189 mut item: impl FnMut(&mut Self) -> ParseResult<T>,
190 ) -> ParseResult<Vec<T>> {
191 let mut items = Vec::new();
192 loop {
193 self.skip_newlines();
194 if self.eat(close).is_some() {
195 return Ok(items);
196 }
197 items.push(item(self)?);
198 self.skip_newlines();
199 if self.eat(TokenKind::Comma).is_none() {
200 self.expect(close)?;
201 return Ok(items);
202 }
203 }
204 }
205}