1use crate::ast::{self, Span};
2use crate::lex;
3use crate::lex::TokenKind::*;
4use crate::lex::{Token, TokenKind};
5use crate::types::Type;
6
7pub const MAX_TUPLE_ARITY: usize = 5;
8pub const TUPLE_FIELDS: &[&str] = &["First", "Second", "Third", "Fourth", "Fifth"];
9const MAX_DEPTH: u32 = 64;
10const MAX_ERRORS: usize = 50;
11const MAX_LOOKAHEAD: usize = 256;
12
13mod annotations;
14mod control_flow;
15mod definitions;
16mod directives;
17mod error;
18mod expressions;
19mod identifiers;
20mod patterns;
21mod pratt;
22mod strings;
23
24pub use error::ParseError;
25
26pub struct ParseResult {
27 pub ast: Vec<ast::Expression>,
28 pub errors: Vec<ParseError>,
29}
30
31impl ParseResult {
32 pub fn failed(&self) -> bool {
33 !self.errors.is_empty()
34 }
35}
36
37pub struct Parser<'source> {
38 stream: TokenStream<'source>,
39 previous_token: Token<'source>,
40 pub errors: Vec<ParseError>,
41 file_id: u32,
42 in_control_flow_header: bool,
43 source: &'source str,
44 depth: u32,
45}
46
47impl<'source> Parser<'source> {
48 pub fn new(tokens: Vec<Token<'source>>, source: &'source str) -> Parser<'source> {
49 Self::with_file_id(tokens, source, 0)
50 }
51
52 pub fn lex_and_parse_file(source: &str, file_id: u32) -> ParseResult {
53 let lex_result = lex::Lexer::new(source, file_id).lex();
54
55 if lex_result.failed() {
56 return ParseResult {
57 ast: vec![],
58 errors: lex_result.errors,
59 };
60 }
61
62 Parser::with_file_id(lex_result.tokens, source, file_id).parse()
63 }
64
65 fn with_file_id(
66 tokens: Vec<Token<'source>>,
67 source: &'source str,
68 file_id: u32,
69 ) -> Parser<'source> {
70 let stream = TokenStream::new(tokens);
71 let first_token = stream.peek();
72
73 let mut parser = Parser {
74 stream,
75 previous_token: first_token,
76 errors: Default::default(),
77 file_id,
78 in_control_flow_header: false,
79 source,
80 depth: 0,
81 };
82
83 parser.skip_comments();
84
85 parser
86 }
87
88 pub fn parse(mut self) -> ParseResult {
89 let mut top_items = vec![];
90
91 self.skip_comments();
92
93 while !self.at_eof() && !self.too_many_errors() {
94 let position = self.position();
95 let item = self.parse_top_item();
96 if !matches!(item, ast::Expression::Unit { .. }) {
97 top_items.push(item);
98 }
99 self.advance_if(Semicolon);
100 if self.position() == position {
101 self.next();
102 }
103 }
104
105 ParseResult {
106 ast: top_items,
107 errors: self.errors,
108 }
109 }
110
111 pub fn parse_top_item(&mut self) -> ast::Expression {
112 let doc_with_span = self.collect_doc_comments();
113
114 let attributes = self.parse_attributes();
115
116 let pub_token = if self.is(Pub) {
117 Some(self.current_token())
118 } else {
119 None
120 };
121 let is_public = pub_token.is_some();
122 if is_public {
123 self.next();
124 }
125
126 if is_public && self.is(Impl) {
127 let token = pub_token.unwrap();
128 let span = ast::Span::new(self.file_id, token.byte_offset, token.byte_length);
129 let error = ParseError::new("Misplaced `pub`", span, "not allowed here")
130 .with_parse_code("syntax_error")
131 .with_help("Place `pub` on individual methods inside the `impl` block instead");
132 self.errors.push(error);
133 }
134
135 let is_documentable = matches!(
136 self.current_token().kind,
137 Enum | Struct | Interface | Function | Const | Var | Type
138 );
139
140 if let Some((_, ref span)) = doc_with_span
141 && !is_documentable
142 {
143 self.error_detached_doc_comment(*span);
144 }
145
146 let doc = doc_with_span.map(|(text, _)| text);
147
148 let expression = match self.current_token().kind {
149 Enum => self.parse_enum_definition(doc, attributes),
150 Struct => self.parse_struct_definition(doc, attributes),
151 Interface => self.parse_interface_definition(doc),
152 Function => self.parse_function(doc, attributes),
153 Impl => self.parse_impl_block(),
154 Const => self.parse_const_definition(doc),
155 Var => self.parse_var_declaration(doc),
156 Import => self.parse_import(),
157 Type => self.parse_type_alias_with_doc(doc),
158 Comment => {
159 let start = self.current_token();
160 self.skip_comments();
161 ast::Expression::Unit {
162 ty: Type::uninferred(),
163 span: self.span_from_tokens(start),
164 }
165 }
166 _ => self.unexpected_token("top_item"),
167 };
168
169 if is_public {
170 return expression.set_public();
171 }
172
173 expression
174 }
175
176 pub fn parse_block_item(&mut self) -> ast::Expression {
177 match self.current_token().kind {
178 Enum => {
179 self.track_error(
180 "misplaced",
181 "Move this enum definition to the top level of the file.",
182 );
183 self.parse_enum_definition(None, vec![])
184 }
185 Struct => {
186 self.track_error(
187 "misplaced",
188 "Move this struct definition to the top level of the file.",
189 );
190 self.parse_struct_definition(None, vec![])
191 }
192 Type => {
193 self.track_error(
194 "misplaced",
195 "Move this type alias to the top level of the file.",
196 );
197 self.parse_type_alias_with_doc(None)
198 }
199 Import => {
200 self.track_error(
201 "misplaced",
202 "Move this import to the top level of the file.",
203 );
204 self.parse_import()
205 }
206 Impl => {
207 self.track_error(
208 "misplaced",
209 "Move this `impl` block to the top level of the file.",
210 );
211 self.parse_impl_block()
212 }
213 Interface => {
214 self.track_error(
215 "misplaced",
216 "Move this interface definition to the top level of the file.",
217 );
218 self.parse_interface_definition(None)
219 }
220 Function => self.parse_function(None, vec![]),
221 Const => self.parse_const_definition(None),
222
223 Let => self.parse_let(),
224 Return => self.parse_return(),
225 For => self.parse_for(),
226 While => self.parse_while(),
227 Loop => self.parse_loop(),
228 Break => self.parse_break(),
229 Continue => self.parse_continue(),
230 Defer => self.parse_defer(),
231 Directive => self.parse_directive(),
232 _ => self.parse_assignment(),
233 }
234 }
235
236 fn current_token(&self) -> Token<'source> {
237 self.stream.peek()
238 }
239
240 fn newline_before_current(&self) -> bool {
241 let prev_end = (self.previous_token.byte_offset + self.previous_token.byte_length) as usize;
242 let curr_start = self.current_token().byte_offset as usize;
243 if prev_end <= curr_start && curr_start <= self.source.len() {
244 return self.source[prev_end..curr_start].contains('\n');
245 }
246 false
247 }
248
249 fn next(&mut self) {
250 self.previous_token = self.current_token();
251 self.stream.consume();
252 self.skip_comments();
253 }
254
255 fn skip_comments(&mut self) {
256 while self.is(Comment) {
257 self.previous_token = self.current_token();
258 self.stream.consume();
259 }
260 }
261
262 fn collect_doc_comments(&mut self) -> Option<(std::string::String, ast::Span)> {
263 let mut docs = Vec::new();
264 let mut first_span: Option<ast::Span> = None;
265
266 while self.is(DocComment) {
267 let token = self.current_token();
268 if first_span.is_none() {
269 first_span = Some(self.span_from_token(token));
270 }
271 docs.push(token.text.to_string());
272 self.previous_token = token;
273 self.stream.consume();
274 self.skip_comments();
275 }
276
277 if docs.is_empty() {
278 None
279 } else {
280 Some((docs.join("\n"), first_span.unwrap()))
281 }
282 }
283
284 fn expect_comma_or(&mut self, closing: TokenKind) {
285 if self.is(Comma) || self.is(closing) || self.at_item_boundary() {
286 self.advance_if(Comma);
287 return;
288 }
289
290 self.track_error(
291 format!("expected `,` or {}", closing),
292 "Add a comma between elements.",
293 );
294
295 loop {
296 if self.at_eof() || self.is(Comma) || self.is(closing) || self.at_item_boundary() {
297 break;
298 }
299 self.next();
300 }
301
302 self.advance_if(Comma);
303 }
304
305 pub fn at_eof(&self) -> bool {
306 self.is(EOF)
307 }
308
309 fn at_range(&self) -> bool {
310 matches!(self.current_token().kind, DotDot | DotDotEqual)
311 }
312
313 fn advance_if(&mut self, token_kind: TokenKind) -> bool {
314 if self.is(token_kind) {
315 self.next();
316 return true;
317 }
318
319 false
320 }
321
322 fn is(&self, token_kind: TokenKind) -> bool {
323 self.current_token().kind == token_kind
324 }
325
326 fn is_not(&self, token_kind: TokenKind) -> bool {
327 if self.at_eof() {
328 return false;
329 }
330
331 self.current_token().kind != token_kind
332 }
333
334 fn ensure(&mut self, token_kind: TokenKind) {
335 if self.current_token().kind != token_kind {
336 self.track_ensure_error(token_kind);
337 }
338
339 if self.at_eof() {
340 return;
341 }
342
343 self.next();
344 }
345
346 fn ensure_progress(&mut self, start_position: usize, closing: TokenKind) {
347 if self.stream.position == start_position && self.is_not(closing) && !self.at_eof() {
348 self.next();
349 }
350 }
351
352 fn span_from_token(&self, token: Token<'source>) -> ast::Span {
353 ast::Span::new(self.file_id, token.byte_offset, token.byte_length)
354 }
355
356 fn span_from_tokens(&self, start_token: Token<'source>) -> ast::Span {
357 let end_byte_offset = self.previous_token.byte_offset + self.previous_token.byte_length;
358 let byte_length = end_byte_offset.saturating_sub(start_token.byte_offset);
359
360 ast::Span::new(self.file_id, start_token.byte_offset, byte_length)
361 }
362
363 fn span_from_offset(&self, start_byte_offset: u32) -> ast::Span {
364 let end_byte_offset = self.previous_token.byte_offset + self.previous_token.byte_length;
365 let byte_length = end_byte_offset.saturating_sub(start_byte_offset);
366
367 ast::Span::new(self.file_id, start_byte_offset, byte_length)
368 }
369
370 fn is_type_args_call(&self) -> bool {
371 let mut position = 1; let mut depth = 1;
373
374 loop {
375 if position > MAX_LOOKAHEAD {
376 return false;
377 }
378 match self.stream.peek_ahead(position).kind {
379 LeftAngleBracket => depth += 1,
380 RightAngleBracket if depth == 1 => {
381 let next = self.stream.peek_ahead(position + 1).kind;
382 return next == LeftParen
383 || (next == Dot
384 && self.stream.peek_ahead(position + 2).kind == Identifier
385 && self.stream.peek_ahead(position + 3).kind == LeftParen);
386 }
387 RightAngleBracket => depth -= 1,
388 LeftParen => {
389 let mut paren_depth = 1;
390 position += 1;
391 while paren_depth > 0 {
392 if position > MAX_LOOKAHEAD {
393 return false;
394 }
395 match self.stream.peek_ahead(position).kind {
396 LeftParen => paren_depth += 1,
397 RightParen => paren_depth -= 1,
398 EOF => return false,
399 _ => {}
400 }
401 position += 1;
402 }
403 continue;
404 }
405 EOF | Plus | Minus | Star | Slash | Percent | EqualDouble | NotEqual
406 | AmpersandDouble | PipeDouble | Semicolon | LeftCurlyBrace | RightCurlyBrace
407 | LeftSquareBracket | RightSquareBracket => return false,
408 _ => {}
409 }
410 position += 1;
411 }
412 }
413
414 fn has_block_after_struct(&self) -> bool {
415 let mut depth = 1;
416 let mut i = 0;
417 while depth > 0 {
418 i += 1;
419 if i > MAX_LOOKAHEAD {
420 return false;
421 }
422 let token = self.stream.peek_ahead(i);
423 match token.kind {
424 LeftCurlyBrace => depth += 1,
425 RightCurlyBrace => depth -= 1,
426 EOF => return false,
427 _ => {}
428 }
429 }
430 let after = self.stream.peek_ahead(i + 1);
431 matches!(
432 after.kind,
433 LeftCurlyBrace
434 | RightParen
435 | EqualDouble
436 | NotEqual
437 | LeftAngleBracket
438 | RightAngleBracket
439 | LessThanOrEqual
440 | GreaterThanOrEqual
441 | AmpersandDouble
442 | PipeDouble
443 | Plus
444 | Minus
445 | Star
446 | Slash
447 | Percent
448 )
449 }
450
451 fn is_struct_instantiation(&self) -> bool {
452 if self.previous_token.kind != Identifier {
453 return false;
454 }
455
456 let is_uppercase = self
457 .previous_token
458 .text
459 .starts_with(|c: char| c.is_uppercase());
460 let first_ahead = self.stream.peek_ahead(1);
461
462 if first_ahead.kind == DotDot {
463 return true;
464 }
465
466 if first_ahead.kind == RightCurlyBrace {
467 if self.in_control_flow_header {
468 return is_uppercase && self.has_block_after_struct();
469 }
470 return is_uppercase;
471 }
472
473 if first_ahead.kind == Identifier {
474 let second_ahead = self.stream.peek_ahead(2);
475 return match second_ahead.kind {
476 Colon => self.stream.peek_ahead(3).kind != Colon,
477 Comma | RightCurlyBrace => {
478 if self.in_control_flow_header {
479 is_uppercase && self.has_block_after_struct()
480 } else {
481 is_uppercase
482 }
483 }
484 _ => false,
485 };
486 }
487
488 false
489 }
490
491 fn enter_recursion(&mut self) -> bool {
492 if self.depth >= MAX_DEPTH {
493 let span = self.span_from_token(self.current_token());
494 self.track_error_at(span, "too deeply nested", "Reduce nesting depth");
495 return false;
496 }
497 self.depth += 1;
498 true
499 }
500
501 fn leave_recursion(&mut self) {
502 self.depth -= 1;
503 }
504
505 fn too_many_errors(&self) -> bool {
506 self.errors.len() >= MAX_ERRORS
507 }
508
509 fn position(&self) -> u32 {
510 self.current_token().byte_offset
511 }
512
513 fn at_sync_point(&self) -> bool {
514 matches!(
515 self.current_token().kind,
516 Semicolon
517 | RightCurlyBrace
518 | RightParen
519 | RightSquareBracket
520 | Comma
521 | Function
522 | Struct
523 | Enum
524 | Const
525 | Impl
526 | Interface
527 | Type
528 | Import
529 )
530 }
531
532 fn can_start_annotation(&self) -> bool {
533 matches!(self.current_token().kind, Identifier | Function | LeftParen)
534 }
535
536 fn at_item_boundary(&self) -> bool {
537 matches!(
538 self.current_token().kind,
539 Let | Function | Struct | Enum | Impl | Interface | Type | Const | Import
540 )
541 }
542
543 fn resync_on_error(&mut self) {
544 if !self.at_eof() {
545 self.next();
546 }
547
548 while !self.at_sync_point() && !self.at_eof() {
549 self.next();
550 }
551 }
552
553 fn track_error(
554 &mut self,
555 label: impl Into<std::string::String>,
556 help: impl Into<std::string::String>,
557 ) {
558 let current = self.current_token();
559 let span = ast::Span::new(self.file_id, current.byte_offset, current.byte_length);
560 self.track_error_at(span, label, help);
561 }
562
563 fn track_error_at(
564 &mut self,
565 span: ast::Span,
566 label: impl Into<std::string::String>,
567 help: impl Into<std::string::String>,
568 ) {
569 if self.too_many_errors() {
570 return;
571 }
572 let error = ParseError::new("Syntax error", span, label.into())
573 .with_parse_code("syntax_error")
574 .with_help(help.into());
575
576 self.errors.push(error);
577 }
578
579 fn track_ensure_error(&mut self, expected_token: TokenKind) {
580 if self.too_many_errors() {
581 return;
582 }
583 let current = self.current_token();
584
585 let error_code = match expected_token {
586 Semicolon => "missing_semicolon",
587 RightCurlyBrace => "unclosed_block",
588 _ => "unexpected_token",
589 };
590
591 let span = ast::Span::new(self.file_id, current.byte_offset, current.byte_length);
592 let error = ParseError::new("Syntax error", span, format!("expected {}", expected_token))
593 .with_parse_code(error_code);
594
595 self.errors.push(error);
596 }
597
598 fn close_brace_span(&mut self, start: Token<'source>, error_anchor: Token<'source>) -> Span {
599 if self.is(RightCurlyBrace) {
600 let close = self.current_token();
601 self.next();
602 let end = close.byte_offset + close.byte_length;
603 Span::new(
604 self.file_id,
605 start.byte_offset,
606 end.saturating_sub(start.byte_offset),
607 )
608 } else {
609 self.error_unclosed_block(&error_anchor);
610 self.span_from_tokens(start)
611 }
612 }
613
614 fn error_unclosed_block(&mut self, open_brace: &Token) {
615 let span = ast::Span::new(self.file_id, open_brace.byte_offset, open_brace.byte_length);
616 let error = ParseError::new("Unclosed block", span, "opening brace here")
617 .with_parse_code("unclosed_block")
618 .with_help("Add a closing `}`");
619
620 self.errors.push(error);
621 }
622
623 fn error_tuple_arity(&mut self, arity: usize, span: Span) {
624 let help = if arity == 0 {
625 "Use `()` for unit type".to_string()
626 } else if arity == 1 {
627 "Use the type directly without wrapping in a tuple".to_string()
628 } else {
629 "For >5 elements, use a struct with named fields".to_string()
630 };
631
632 let error = ParseError::new(
633 "Invalid tuple",
634 span,
635 format!("{}-element tuple not allowed", arity),
636 )
637 .with_parse_code("tuple_element_count")
638 .with_help(help);
639
640 self.errors.push(error);
641 }
642
643 fn error_duplicate_field_in_pattern(
644 &mut self,
645 field_name: &str,
646 first_span: Span,
647 second_span: Span,
648 ) {
649 let error = ParseError::new(
650 "Duplicate field",
651 first_span,
652 format!("first use of `{}`", field_name),
653 )
654 .with_span_label(second_span, "used again")
655 .with_parse_code("duplicate_field_in_pattern")
656 .with_help("Remove the duplicate binding");
657
658 self.errors.push(error);
659 }
660
661 fn error_duplicate_impl_parent(&mut self, first_span: Span, second_span: Span) {
662 let error = ParseError::new("Duplicate impl", first_span, "first use")
663 .with_span_label(second_span, "used again")
664 .with_parse_code("duplicate_impl_parent")
665 .with_help("Remove the duplicate parent");
666
667 self.errors.push(error);
668 }
669
670 fn error_duplicate_struct_field(&mut self, name: &str, first_span: Span, second_span: Span) {
671 let error = ParseError::new("Duplicate field", first_span, "first defined")
672 .with_span_label(second_span, "defined again")
673 .with_parse_code("duplicate_struct_field")
674 .with_help(format!("Remove the duplicate field `{}`", name));
675
676 self.errors.push(error);
677 }
678
679 fn error_duplicate_enum_variant(&mut self, name: &str, first_span: Span, second_span: Span) {
680 let error = ParseError::new("Duplicate variant", first_span, "first defined")
681 .with_span_label(second_span, "defined again")
682 .with_parse_code("duplicate_enum_variant")
683 .with_help(format!("Remove the duplicate variant `{}`", name));
684
685 self.errors.push(error);
686 }
687
688 fn error_duplicate_interface_method(
689 &mut self,
690 name: &str,
691 first_span: Span,
692 second_span: Span,
693 ) {
694 let error = ParseError::new("Duplicate method", first_span, "first defined")
695 .with_span_label(second_span, "defined again")
696 .with_parse_code("duplicate_interface_method")
697 .with_help(format!("Remove the duplicate method `{}`", name));
698
699 self.errors.push(error);
700 }
701
702 fn error_float_pattern_not_allowed(&mut self, span: Span, float_text: &str) {
703 let error = ParseError::new("Invalid pattern", span, "float literal not allowed here")
704 .with_parse_code("float_pattern")
705 .with_help(format!(
706 "Use a guard instead: `x if x == {} =>`",
707 float_text
708 ));
709
710 self.errors.push(error);
711 }
712
713 fn error_uppercase_binding(&mut self, span: Span) {
714 let error = ParseError::new("Invalid binding name", span, "uppercase not allowed here")
715 .with_parse_code("uppercase_binding")
716 .with_help("Lowercase the binding");
717
718 self.errors.push(error);
719 }
720
721 fn error_detached_doc_comment(&mut self, span: Span) {
722 let error = ParseError::new("Unattached doc comment", span, "is detached")
723 .with_parse_code("detached_doc_comment")
724 .with_help("Place the doc comment on the line immediately above a symbol definition");
725
726 self.errors.push(error);
727 }
728
729 fn error_interface_method_with_type_parameters(&mut self, span: Span, count: usize) {
730 let label = if count == 1 {
731 "type parameter not allowed"
732 } else {
733 "type parameters not allowed"
734 };
735 let error = ParseError::new("Invalid interface method", span, label)
736 .with_parse_code("interface_method_with_type_parameters")
737 .with_help(
738 "Interface methods cannot have type parameters, because Go interfaces do not support generic methods",
739 );
740
741 self.errors.push(error);
742 }
743
744 pub(crate) fn parse_integer_text(&mut self, text: &str) -> ast::Literal {
745 self.parse_integer_text_with(text, false)
746 }
747
748 pub(crate) fn parse_integer_text_with(
749 &mut self,
750 text: &str,
751 preserve_decimal_text: bool,
752 ) -> ast::Literal {
753 let clean = if text.contains('_') {
754 std::borrow::Cow::Owned(text.replace('_', ""))
755 } else {
756 std::borrow::Cow::Borrowed(text)
757 };
758
759 let (n, is_decimal) = if clean.starts_with("0x") || clean.starts_with("0X") {
760 let value = u64::from_str_radix(&clean[2..], 16).unwrap_or_else(|_| {
761 self.track_error(
762 format!("hex literal '{text}' is too large"),
763 "Maximum value is `0xFFFFFFFFFFFFFFFF`.",
764 );
765 0
766 });
767 (value, false)
768 } else if clean.starts_with("0o") || clean.starts_with("0O") {
769 let value = u64::from_str_radix(&clean[2..], 8).unwrap_or_else(|_| {
770 self.track_error(
771 format!("octal literal '{text}' is too large"),
772 "Maximum value is `0o1777777777777777777777`.",
773 );
774 0
775 });
776 (value, false)
777 } else if clean.starts_with("0b") || clean.starts_with("0B") {
778 let value = u64::from_str_radix(&clean[2..], 2).unwrap_or_else(|_| {
779 self.track_error(
780 format!("binary literal '{text}' is too large"),
781 "Value must fit in 64 bits.",
782 );
783 0
784 });
785 (value, false)
786 } else if clean.len() > 1
787 && clean.starts_with('0')
788 && clean.chars().skip(1).all(|c| c.is_ascii_digit())
789 {
790 let value = u64::from_str_radix(&clean[1..], 8).unwrap_or_else(|_| {
791 self.track_error(
792 format!("octal literal '{text}' is too large"),
793 "Maximum value is `01777777777777777777777`.",
794 );
795 0
796 });
797 (value, false)
798 } else {
799 let value = clean.parse().unwrap_or_else(|_| {
800 self.track_error(
801 format!("integer literal '{text}' is too large"),
802 "Maximum value is `18446744073709551615`.",
803 );
804 0
805 });
806 (value, true)
807 };
808
809 let original_text = if is_decimal && !preserve_decimal_text {
810 None
811 } else {
812 Some(text.to_string())
813 };
814
815 ast::Literal::Integer {
816 value: n,
817 text: original_text,
818 }
819 }
820
821 fn unexpected_token(&mut self, ctx: &str) -> ast::Expression {
822 let token = self.current_token();
823 let token_descriptor = if token.text.is_empty() {
824 format!("{:?}", token.kind)
825 } else {
826 format!("`{}`", token.text)
827 };
828
829 let span = ast::Span::new(self.file_id, token.byte_offset, token.byte_length);
830
831 let (label, error_code, help) = match ctx {
832 "expr" => (
833 format!("expected expression, found {}", token_descriptor),
834 "expected_expression",
835 "Check your syntax.",
836 ),
837 "pattern" => (
838 format!("unexpected {} in pattern", token_descriptor),
839 "invalid_pattern",
840 "Patterns include literals, variables, and destructuring.",
841 ),
842 "literal" => (
843 format!("expected literal, found {}", token_descriptor),
844 "expected_literal",
845 "Literals include numbers, strings, characters, and booleans.",
846 ),
847 "top_item" if token.text == "trait" => (
848 format!("unexpected {}", token_descriptor),
849 "trait_unsupported",
850 "Lisette uses `interface` with Go-style structural typing. Types automatically satisfy interfaces if they have the required methods.",
851 ),
852 "top_item" if token.text == "use" => (
853 "unexpected syntax for import".to_string(),
854 "use_unsupported",
855 "Use `import` instead of `use` for imports: `import \"module/path\"`",
856 ),
857 "top_item" => (
858 "expected declaration".to_string(),
859 "expected_declaration",
860 "At the top level of a file, Lisette expects `fn`, `struct`, `enum`, `interface`, `import`, or `type`.",
861 ),
862 _ => (
863 format!("unexpected {}", token_descriptor),
864 "unexpected_token",
865 "Check your syntax.",
866 ),
867 };
868
869 let error = ParseError::new("Syntax error", span, label)
870 .with_parse_code(error_code)
871 .with_help(help);
872
873 if !self.too_many_errors() {
874 self.errors.push(error);
875 }
876
877 self.resync_on_error();
878
879 ast::Expression::Unit {
880 ty: Type::uninferred(),
881 span,
882 }
883 }
884}
885
886struct TokenStream<'source> {
887 tokens: Vec<Token<'source>>,
888 position: usize,
889}
890
891impl<'source> TokenStream<'source> {
892 fn new(tokens: Vec<Token<'source>>) -> Self {
893 Self {
894 tokens,
895 position: 0,
896 }
897 }
898
899 fn peek(&self) -> Token<'source> {
900 self.tokens
901 .get(self.position)
902 .copied()
903 .unwrap_or_else(|| Token {
904 kind: TokenKind::EOF,
905 text: "",
906 byte_offset: self
907 .tokens
908 .last()
909 .map(|t| t.byte_offset + t.byte_length)
910 .unwrap_or(0),
911 byte_length: 0,
912 })
913 }
914
915 fn peek_ahead(&self, n: usize) -> Token<'source> {
916 self.tokens
917 .get(self.position + n)
918 .copied()
919 .unwrap_or_else(|| Token {
920 kind: TokenKind::EOF,
921 text: "",
922 byte_offset: self
923 .tokens
924 .last()
925 .map(|t| t.byte_offset + t.byte_length)
926 .unwrap_or(0),
927 byte_length: 0,
928 })
929 }
930
931 fn consume(&mut self) -> Token<'source> {
932 let token = self.peek();
933 if self.position < self.tokens.len() {
934 self.position += 1;
935 }
936 token
937 }
938}