1mod expr;
10mod items;
11mod skeleton;
12mod ty;
13
14use wolf_ast::{File, NodeId};
15use wolf_diag::{Diagnostic, codes};
16use wolf_lexer::{Token, TokenKind};
17use wolf_span::{FileId, Span};
18
19pub const MAX_DEPTH: u32 = 256;
21
22pub const STACK_SIZE: usize = 64 * 1024 * 1024;
26
27#[derive(Debug)]
29pub struct Parsed {
30 pub file: File,
32 pub diagnostics: Vec<Diagnostic>,
34}
35
36pub fn parse(file: FileId, text: &str, tokens: &[Token]) -> Parsed {
38 let mut parser = Parser::new(file, text, tokens);
39 let tree = parser.file();
40 let too_deep = parser.depth_reported;
41 let mut diagnostics = parser.diagnostics;
42 if !too_deep {
43 diagnostics.extend(skeleton::check(file, &tree));
44 }
45 Parsed {
46 file: tree,
47 diagnostics,
48 }
49}
50
51#[derive(Clone, Copy, PartialEq, Eq)]
53enum Lines {
54 Significant,
56 Ignored,
58}
59
60pub(crate) struct Parser<'a> {
61 file: FileId,
62 text: &'a str,
63 tokens: Vec<Token>,
65 pos: usize,
66 lines: Vec<Lines>,
67 pub(crate) diagnostics: Vec<Diagnostic>,
68 next_id: u32,
69 depth: u32,
70 depth_reported: bool,
71 pub(crate) resync_to_top: bool,
73 poisoned: bool,
76 pub(crate) line_errors: usize,
78}
79
80impl<'a> Parser<'a> {
81 fn new(file: FileId, text: &'a str, tokens: &[Token]) -> Parser<'a> {
82 let mut tokens = tokens.to_vec();
83 let ends_with_eof = tokens.last().is_some_and(|t| t.kind == TokenKind::Eof);
84 if !ends_with_eof {
85 let end = u32::try_from(text.len()).unwrap_or(u32::MAX);
86 tokens.push(Token {
87 kind: TokenKind::Eof,
88 span: Span::new(file, end, end),
89 });
90 }
91 Parser {
92 file,
93 text,
94 tokens,
95 pos: 0,
96 lines: vec![Lines::Significant],
97 diagnostics: Vec::new(),
98 next_id: 0,
99 depth: 0,
100 depth_reported: false,
101 resync_to_top: false,
102 poisoned: false,
103 line_errors: 0,
104 }
105 }
106
107 fn raw(&self, n: usize) -> &Token {
111 let index = (self.pos + n).min(self.tokens.len().saturating_sub(1));
112 match self.tokens.get(index) {
114 Some(token) => token,
115 None => &EOF_TOKEN,
116 }
117 }
118
119 fn ignoring_lines(&self) -> bool {
120 self.lines.last() == Some(&Lines::Ignored)
121 }
122
123 fn visible_index(&self, n: usize) -> usize {
125 let mut index = self.pos;
126 let mut seen = 0;
127 loop {
128 let token = self.tokens.get(index);
129 let skip = self.ignoring_lines() && token.is_some_and(|t| t.kind == TokenKind::Newline);
130 if !skip {
131 if seen == n || token.is_none_or(|t| t.kind == TokenKind::Eof) {
132 return index.min(self.tokens.len().saturating_sub(1));
133 }
134 seen += 1;
135 }
136 index += 1;
137 }
138 }
139
140 pub(crate) fn nth(&self, n: usize) -> &Token {
142 match self.tokens.get(self.visible_index(n)) {
143 Some(token) => token,
144 None => &EOF_TOKEN,
145 }
146 }
147
148 pub(crate) fn peek(&self) -> &TokenKind {
149 &self.nth(0).kind
150 }
151
152 pub(crate) fn peek_span(&self) -> Span {
153 self.nth(0).span
154 }
155
156 pub(crate) fn at(&self, kind: &TokenKind) -> bool {
158 same_kind(self.peek(), kind)
159 }
160
161 pub(crate) fn nth_is(&self, n: usize, kind: &TokenKind) -> bool {
162 same_kind(&self.nth(n).kind, kind)
163 }
164
165 pub(crate) fn at_word(&self, word: &str) -> bool {
167 self.nth_word(0, word)
168 }
169
170 pub(crate) fn nth_word(&self, n: usize, word: &str) -> bool {
171 let token = self.nth(n);
172 token.kind == TokenKind::Ident && self.source(token.span) == word
173 }
174
175 pub(crate) fn bump(&mut self) -> Token {
177 let index = self.visible_index(0);
178 let token = self.tokens.get(index).cloned().unwrap_or(EOF_TOKEN.clone());
179 if token.kind == TokenKind::Error {
180 self.poisoned = true;
181 }
182 if token.kind != TokenKind::Eof {
183 self.pos = index + 1;
184 } else {
185 self.pos = index;
186 }
187 token
188 }
189
190 pub(crate) fn eat(&mut self, kind: &TokenKind) -> bool {
191 if self.at(kind) {
192 self.bump();
193 true
194 } else {
195 false
196 }
197 }
198
199 pub(crate) fn eat_then_lines(&mut self, kind: &TokenKind) -> bool {
202 if self.eat(kind) {
203 self.skip_newlines();
204 true
205 } else {
206 false
207 }
208 }
209
210 pub(crate) fn expect_then_lines(&mut self, kind: &TokenKind, what: &str) -> Option<Span> {
212 let span = self.expect(kind, what)?;
213 self.skip_newlines();
214 Some(span)
215 }
216
217 pub(crate) fn eat_word(&mut self, word: &str) -> bool {
218 if self.at_word(word) {
219 self.bump();
220 true
221 } else {
222 false
223 }
224 }
225
226 pub(crate) fn expect(&mut self, kind: &TokenKind, what: &str) -> Option<Span> {
228 if self.at(kind) {
229 return Some(self.bump().span);
230 }
231 self.unexpected(what);
232 None
233 }
234
235 pub(crate) fn skip_newlines(&mut self) {
237 while self.raw(0).kind == TokenKind::Newline {
238 self.pos += 1;
239 }
240 }
241
242 pub(crate) fn at_line_end(&self) -> bool {
244 matches!(self.raw(0).kind, TokenKind::Newline | TokenKind::Eof)
245 }
246
247 pub(crate) fn source(&self, span: Span) -> &'a str {
248 self.text.get(span.range()).unwrap_or("")
249 }
250
251 pub(crate) fn prev_span(&self) -> Span {
252 let index = self.pos.saturating_sub(1);
253 self.tokens
254 .get(index)
255 .map(|t| t.span)
256 .unwrap_or(Span::new(self.file, 0, 0))
257 }
258
259 pub(crate) fn span_from(&self, start: Span) -> Span {
260 let end = self.prev_span();
261 if end.end < start.start {
262 start
263 } else {
264 start.to(end)
265 }
266 }
267
268 pub(crate) fn with_lines<T>(&mut self, mode: Lines, f: impl FnOnce(&mut Self) -> T) -> T {
269 self.lines.push(mode);
270 let result = f(self);
271 self.lines.pop();
272 result
273 }
274
275 pub(crate) fn ignore_lines<T>(&mut self, f: impl FnOnce(&mut Self) -> T) -> T {
276 self.with_lines(Lines::Ignored, f)
277 }
278
279 pub(crate) fn significant_lines<T>(&mut self, f: impl FnOnce(&mut Self) -> T) -> T {
280 self.with_lines(Lines::Significant, f)
281 }
282
283 pub(crate) fn id(&mut self) -> NodeId {
284 let id = NodeId(self.next_id);
285 self.next_id += 1;
286 id
287 }
288
289 pub(crate) fn report(&mut self, diagnostic: Diagnostic) {
292 if self.depth_reported && diagnostic.code != codes::E0314 {
294 return;
295 }
296 self.diagnostics.push(diagnostic);
297 }
298
299 pub(crate) fn unexpected(&mut self, expected: &str) {
302 let token = self.nth(0).clone();
303 if token.kind == TokenKind::Error || self.depth_reported || self.poisoned {
304 return;
305 }
306 let found = describe(&token, self.source(token.span));
307 let span = if token.kind == TokenKind::Newline {
309 Span::new(token.span.file, token.span.start, token.span.start)
310 } else {
311 token.span
312 };
313 self.report(
314 Diagnostic::new(codes::E0301, format!("expected {expected}, found {found}"))
315 .primary(span, format!("expected {expected}")),
316 );
317 }
318
319 pub(crate) fn end_line_if_poisoned(&mut self) -> bool {
322 if !self.poisoned {
323 return false;
324 }
325 self.recover();
326 self.poisoned = false;
327 true
328 }
329
330 pub(crate) fn start_line(&mut self) -> usize {
333 self.poisoned = false;
334 self.diagnostics.len()
335 }
336
337 pub(crate) fn end_line_after_error(&mut self, errors_before: usize) -> bool {
340 if self.resync_to_top {
342 return true;
343 }
344 if self.end_line_if_poisoned() {
345 return true;
346 }
347 if matches!(self.raw(0).kind, TokenKind::DotDot | TokenKind::DotDotEq) {
348 let span = self.raw(0).span;
349 self.report(
350 Diagnostic::new(codes::E0307, "range outside `for` or index brackets").primary(
351 span,
352 "ranges are allowed only in `for i in a..b` and `items[a..b]`",
353 ),
354 );
355 self.recover();
356 return true;
357 }
358 if self.diagnostics.len() > errors_before {
359 self.recover();
360 return true;
361 }
362 false
363 }
364
365 pub(crate) fn recover(&mut self) {
368 let mut depth = 0u32;
369 loop {
370 let kind = self.raw(0).kind.clone();
371 match kind {
372 TokenKind::Eof => return,
373 TokenKind::Newline if depth == 0 && !self.ignoring_lines() => return,
374 TokenKind::LParen | TokenKind::LBracket | TokenKind::LBrace => depth += 1,
375 TokenKind::RParen | TokenKind::RBracket | TokenKind::RBrace => {
376 if depth == 0 {
377 return;
378 }
379 depth -= 1;
380 }
381 _ => {}
382 }
383 self.pos += 1;
384 }
385 }
386
387 pub(crate) fn enter(&mut self) -> bool {
389 self.depth += 1;
390 if self.depth <= MAX_DEPTH {
391 return true;
392 }
393 if !self.depth_reported {
394 self.depth_reported = true;
395 let span = self.peek_span();
396 self.report(
397 Diagnostic::new(codes::E0314, "nesting too deep")
398 .primary(span, format!("more than {MAX_DEPTH} nested levels"))
399 .help("split the expression or the code into smaller parts"),
400 );
401 }
402 false
403 }
404
405 pub(crate) fn leave(&mut self) {
406 self.depth = self.depth.saturating_sub(1);
407 }
408
409 pub(crate) fn give_up(&mut self) {
411 self.pos = self.tokens.len().saturating_sub(1);
412 }
413
414 pub(crate) fn too_deep(&self) -> bool {
415 self.depth_reported
416 }
417}
418
419static EOF_TOKEN: Token = Token {
420 kind: TokenKind::Eof,
421 span: Span {
422 file: FILE_ZERO,
423 start: 0,
424 end: 0,
425 },
426};
427
428const FILE_ZERO: FileId = wolf_span::FileId::ZERO;
429
430pub(crate) fn same_kind(a: &TokenKind, b: &TokenKind) -> bool {
432 std::mem::discriminant(a) == std::mem::discriminant(b)
433}
434
435pub(crate) fn describe(token: &Token, source: &str) -> String {
437 match token.kind {
438 TokenKind::Ident => format!("identifier `{source}`"),
439 TokenKind::Int(_) | TokenKind::Float(_) | TokenKind::Str(_) => format!("`{source}`"),
440 _ => token.kind.to_string(),
441 }
442}