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 pub(crate) labels: Vec<String>,
81}
82
83impl<'a> Parser<'a> {
84 fn new(file: FileId, text: &'a str, tokens: &[Token]) -> Parser<'a> {
85 let mut tokens = tokens.to_vec();
86 let ends_with_eof = tokens.last().is_some_and(|t| t.kind == TokenKind::Eof);
87 if !ends_with_eof {
88 let end = u32::try_from(text.len()).unwrap_or(u32::MAX);
89 tokens.push(Token {
90 kind: TokenKind::Eof,
91 span: Span::new(file, end, end),
92 });
93 }
94 Parser {
95 file,
96 text,
97 tokens,
98 pos: 0,
99 lines: vec![Lines::Significant],
100 diagnostics: Vec::new(),
101 next_id: 0,
102 depth: 0,
103 depth_reported: false,
104 resync_to_top: false,
105 poisoned: false,
106 line_errors: 0,
107 labels: Vec::new(),
108 }
109 }
110
111 fn raw(&self, n: usize) -> &Token {
115 let index = (self.pos + n).min(self.tokens.len().saturating_sub(1));
116 match self.tokens.get(index) {
118 Some(token) => token,
119 None => &EOF_TOKEN,
120 }
121 }
122
123 fn ignoring_lines(&self) -> bool {
124 self.lines.last() == Some(&Lines::Ignored)
125 }
126
127 fn visible_index(&self, n: usize) -> usize {
129 let mut index = self.pos;
130 let mut seen = 0;
131 loop {
132 let token = self.tokens.get(index);
133 let skip = self.ignoring_lines() && token.is_some_and(|t| t.kind == TokenKind::Newline);
134 if !skip {
135 if seen == n || token.is_none_or(|t| t.kind == TokenKind::Eof) {
136 return index.min(self.tokens.len().saturating_sub(1));
137 }
138 seen += 1;
139 }
140 index += 1;
141 }
142 }
143
144 pub(crate) fn nth(&self, n: usize) -> &Token {
146 match self.tokens.get(self.visible_index(n)) {
147 Some(token) => token,
148 None => &EOF_TOKEN,
149 }
150 }
151
152 pub(crate) fn peek(&self) -> &TokenKind {
153 &self.nth(0).kind
154 }
155
156 pub(crate) fn peek_span(&self) -> Span {
157 self.nth(0).span
158 }
159
160 pub(crate) fn at(&self, kind: &TokenKind) -> bool {
162 same_kind(self.peek(), kind)
163 }
164
165 pub(crate) fn nth_is(&self, n: usize, kind: &TokenKind) -> bool {
166 same_kind(&self.nth(n).kind, kind)
167 }
168
169 pub(crate) fn at_word(&self, word: &str) -> bool {
171 self.nth_word(0, word)
172 }
173
174 pub(crate) fn nth_word(&self, n: usize, word: &str) -> bool {
175 let token = self.nth(n);
176 token.kind == TokenKind::Ident && self.source(token.span) == word
177 }
178
179 pub(crate) fn bump(&mut self) -> Token {
181 let index = self.visible_index(0);
182 let token = self.tokens.get(index).cloned().unwrap_or(EOF_TOKEN.clone());
183 if token.kind == TokenKind::Error {
184 self.poisoned = true;
185 }
186 if token.kind != TokenKind::Eof {
187 self.pos = index + 1;
188 } else {
189 self.pos = index;
190 }
191 token
192 }
193
194 pub(crate) fn eat(&mut self, kind: &TokenKind) -> bool {
195 if self.at(kind) {
196 self.bump();
197 true
198 } else {
199 false
200 }
201 }
202
203 pub(crate) fn eat_then_lines(&mut self, kind: &TokenKind) -> bool {
206 if self.eat(kind) {
207 self.skip_newlines();
208 true
209 } else {
210 false
211 }
212 }
213
214 pub(crate) fn expect_then_lines(&mut self, kind: &TokenKind, what: &str) -> Option<Span> {
216 let span = self.expect(kind, what)?;
217 self.skip_newlines();
218 Some(span)
219 }
220
221 pub(crate) fn eat_word(&mut self, word: &str) -> bool {
222 if self.at_word(word) {
223 self.bump();
224 true
225 } else {
226 false
227 }
228 }
229
230 pub(crate) fn expect(&mut self, kind: &TokenKind, what: &str) -> Option<Span> {
232 if self.at(kind) {
233 return Some(self.bump().span);
234 }
235 self.unexpected(what);
236 None
237 }
238
239 pub(crate) fn skip_newlines(&mut self) {
241 while self.raw(0).kind == TokenKind::Newline {
242 self.pos += 1;
243 }
244 }
245
246 pub(crate) fn at_line_end(&self) -> bool {
248 matches!(self.raw(0).kind, TokenKind::Newline | TokenKind::Eof)
249 }
250
251 pub(crate) fn source(&self, span: Span) -> &'a str {
252 self.text.get(span.range()).unwrap_or("")
253 }
254
255 pub(crate) fn prev_span(&self) -> Span {
256 let index = self.pos.saturating_sub(1);
257 self.tokens
258 .get(index)
259 .map(|t| t.span)
260 .unwrap_or(Span::new(self.file, 0, 0))
261 }
262
263 pub(crate) fn span_from(&self, start: Span) -> Span {
264 let end = self.prev_span();
265 if end.end < start.start {
266 start
267 } else {
268 start.to(end)
269 }
270 }
271
272 pub(crate) fn with_lines<T>(&mut self, mode: Lines, f: impl FnOnce(&mut Self) -> T) -> T {
273 self.lines.push(mode);
274 let result = f(self);
275 self.lines.pop();
276 result
277 }
278
279 pub(crate) fn ignore_lines<T>(&mut self, f: impl FnOnce(&mut Self) -> T) -> T {
280 self.with_lines(Lines::Ignored, f)
281 }
282
283 pub(crate) fn significant_lines<T>(&mut self, f: impl FnOnce(&mut Self) -> T) -> T {
284 self.with_lines(Lines::Significant, f)
285 }
286
287 pub(crate) fn id(&mut self) -> NodeId {
288 let id = NodeId(self.next_id);
289 self.next_id += 1;
290 id
291 }
292
293 pub(crate) fn report(&mut self, diagnostic: Diagnostic) {
296 if self.depth_reported && diagnostic.code != codes::E0314 {
298 return;
299 }
300 self.diagnostics.push(diagnostic);
301 }
302
303 pub(crate) fn unexpected(&mut self, expected: &str) {
306 let token = self.nth(0).clone();
307 if token.kind == TokenKind::Error || self.depth_reported || self.poisoned {
308 return;
309 }
310 let found = describe(&token, self.source(token.span));
311 let span = if token.kind == TokenKind::Newline {
313 Span::new(token.span.file, token.span.start, token.span.start)
314 } else {
315 token.span
316 };
317 self.report(
318 Diagnostic::new(codes::E0301, format!("expected {expected}, found {found}"))
319 .primary(span, format!("expected {expected}")),
320 );
321 }
322
323 pub(crate) fn end_line_if_poisoned(&mut self) -> bool {
326 if !self.poisoned {
327 return false;
328 }
329 self.recover();
330 self.poisoned = false;
331 true
332 }
333
334 pub(crate) fn start_line(&mut self) -> usize {
337 self.poisoned = false;
338 self.diagnostics.len()
339 }
340
341 pub(crate) fn end_line_after_error(&mut self, errors_before: usize) -> bool {
344 if self.resync_to_top {
346 return true;
347 }
348 if self.end_line_if_poisoned() {
349 return true;
350 }
351 if matches!(self.raw(0).kind, TokenKind::DotDot | TokenKind::DotDotEq) {
352 let span = self.raw(0).span;
353 self.report(
354 Diagnostic::new(codes::E0307, "range outside `for` or index brackets").primary(
355 span,
356 "ranges are allowed only in `for i in a..b` and `items[a..b]`",
357 ),
358 );
359 self.recover();
360 return true;
361 }
362 if self.diagnostics.len() > errors_before {
363 self.recover();
364 return true;
365 }
366 false
367 }
368
369 pub(crate) fn recover(&mut self) {
372 let mut depth = 0u32;
373 loop {
374 let kind = self.raw(0).kind.clone();
375 match kind {
376 TokenKind::Eof => return,
377 TokenKind::Newline if depth == 0 && !self.ignoring_lines() => return,
378 TokenKind::LParen | TokenKind::LBracket | TokenKind::LBrace => depth += 1,
379 TokenKind::RParen | TokenKind::RBracket | TokenKind::RBrace => {
380 if depth == 0 {
381 return;
382 }
383 depth -= 1;
384 }
385 _ => {}
386 }
387 self.pos += 1;
388 }
389 }
390
391 pub(crate) fn enter(&mut self) -> bool {
393 self.depth += 1;
394 if self.depth <= MAX_DEPTH {
395 return true;
396 }
397 if !self.depth_reported {
398 self.depth_reported = true;
399 let span = self.peek_span();
400 self.report(
401 Diagnostic::new(codes::E0314, "nesting too deep")
402 .primary(span, format!("more than {MAX_DEPTH} nested levels"))
403 .help("split the expression or the code into smaller parts"),
404 );
405 }
406 false
407 }
408
409 pub(crate) fn leave(&mut self) {
410 self.depth = self.depth.saturating_sub(1);
411 }
412
413 pub(crate) fn give_up(&mut self) {
415 self.pos = self.tokens.len().saturating_sub(1);
416 }
417
418 pub(crate) fn too_deep(&self) -> bool {
419 self.depth_reported
420 }
421}
422
423static EOF_TOKEN: Token = Token {
424 kind: TokenKind::Eof,
425 span: Span {
426 file: FILE_ZERO,
427 start: 0,
428 end: 0,
429 },
430};
431
432const FILE_ZERO: FileId = wolf_span::FileId::ZERO;
433
434pub(crate) fn same_kind(a: &TokenKind, b: &TokenKind) -> bool {
436 std::mem::discriminant(a) == std::mem::discriminant(b)
437}
438
439pub(crate) fn describe(token: &Token, source: &str) -> String {
441 match token.kind {
442 TokenKind::Ident => format!("identifier `{source}`"),
443 TokenKind::Int(_) | TokenKind::Float(_) | TokenKind::Str(_) => format!("`{source}`"),
444 _ => token.kind.to_string(),
445 }
446}