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 self.recover_to_comma_or(closing);
296 }
297
298 pub(super) fn recover_to_comma_or(&mut self, closing: TokenKind) {
299 while !self.at_eof() && !self.is(Comma) && !self.is(closing) && !self.at_item_boundary() {
300 self.next();
301 }
302
303 self.advance_if(Comma);
304 }
305
306 pub fn at_eof(&self) -> bool {
307 self.is(EOF)
308 }
309
310 fn at_range(&self) -> bool {
311 matches!(self.current_token().kind, DotDot | DotDotEqual)
312 }
313
314 fn advance_if(&mut self, token_kind: TokenKind) -> bool {
315 if self.is(token_kind) {
316 self.next();
317 return true;
318 }
319
320 false
321 }
322
323 fn is(&self, token_kind: TokenKind) -> bool {
324 self.current_token().kind == token_kind
325 }
326
327 fn is_not(&self, token_kind: TokenKind) -> bool {
328 if self.at_eof() {
329 return false;
330 }
331
332 self.current_token().kind != token_kind
333 }
334
335 fn ensure(&mut self, token_kind: TokenKind) {
336 if self.current_token().kind != token_kind {
337 self.track_ensure_error(token_kind);
338 }
339
340 if self.at_eof() {
341 return;
342 }
343
344 self.next();
345 }
346
347 fn ensure_progress(&mut self, start_position: usize, closing: TokenKind) {
348 if self.stream.position == start_position && self.is_not(closing) && !self.at_eof() {
349 self.next();
350 }
351 }
352
353 fn span_from_token(&self, token: Token<'source>) -> ast::Span {
354 ast::Span::new(self.file_id, token.byte_offset, token.byte_length)
355 }
356
357 fn span_from_tokens(&self, start_token: Token<'source>) -> ast::Span {
358 let end_byte_offset = self.previous_token.byte_offset + self.previous_token.byte_length;
359 let byte_length = end_byte_offset.saturating_sub(start_token.byte_offset);
360
361 ast::Span::new(self.file_id, start_token.byte_offset, byte_length)
362 }
363
364 fn span_from_offset(&self, start_byte_offset: u32) -> ast::Span {
365 let end_byte_offset = self.previous_token.byte_offset + self.previous_token.byte_length;
366 let byte_length = end_byte_offset.saturating_sub(start_byte_offset);
367
368 ast::Span::new(self.file_id, start_byte_offset, byte_length)
369 }
370
371 fn is_type_args_call(&self) -> bool {
372 let mut position = 1; let mut depth = 1;
374
375 loop {
376 if position > MAX_LOOKAHEAD {
377 return false;
378 }
379 match self.stream.peek_ahead(position).kind {
380 LeftAngleBracket => depth += 1,
381 RightAngleBracket if depth == 1 => {
382 let next = self.stream.peek_ahead(position + 1).kind;
383 return next == LeftParen
384 || (next == Dot
385 && self.stream.peek_ahead(position + 2).kind == Identifier
386 && self.stream.peek_ahead(position + 3).kind == LeftParen);
387 }
388 RightAngleBracket => depth -= 1,
389 LeftParen => {
390 let mut paren_depth = 1;
391 position += 1;
392 while paren_depth > 0 {
393 if position > MAX_LOOKAHEAD {
394 return false;
395 }
396 match self.stream.peek_ahead(position).kind {
397 LeftParen => paren_depth += 1,
398 RightParen => paren_depth -= 1,
399 EOF => return false,
400 _ => {}
401 }
402 position += 1;
403 }
404 continue;
405 }
406 EOF | Plus | Minus | Star | Slash | Percent | EqualDouble | NotEqual
407 | AmpersandDouble | PipeDouble | Semicolon | LeftCurlyBrace | RightCurlyBrace
408 | LeftSquareBracket | RightSquareBracket => return false,
409 _ => {}
410 }
411 position += 1;
412 }
413 }
414
415 fn has_block_after_struct(&self) -> bool {
416 let mut depth = 1;
417 let mut i = 0;
418 while depth > 0 {
419 i += 1;
420 if i > MAX_LOOKAHEAD {
421 return false;
422 }
423 let token = self.stream.peek_ahead(i);
424 match token.kind {
425 LeftCurlyBrace => depth += 1,
426 RightCurlyBrace => depth -= 1,
427 EOF => return false,
428 _ => {}
429 }
430 }
431 let after = self.stream.peek_ahead(i + 1);
432 matches!(
433 after.kind,
434 LeftCurlyBrace
435 | RightParen
436 | EqualDouble
437 | NotEqual
438 | LeftAngleBracket
439 | RightAngleBracket
440 | LessThanOrEqual
441 | GreaterThanOrEqual
442 | AmpersandDouble
443 | PipeDouble
444 | Plus
445 | Minus
446 | Star
447 | Slash
448 | Percent
449 )
450 }
451
452 fn is_struct_instantiation(&self) -> bool {
453 if self.previous_token.kind != Identifier {
454 return false;
455 }
456
457 let is_uppercase = self
458 .previous_token
459 .text
460 .starts_with(|c: char| c.is_uppercase());
461 let first_ahead = self.stream.peek_ahead(1);
462
463 if first_ahead.kind == DotDot {
464 return true;
465 }
466
467 if first_ahead.kind == RightCurlyBrace {
468 if self.in_control_flow_header {
469 return is_uppercase && self.has_block_after_struct();
470 }
471 return is_uppercase;
472 }
473
474 if first_ahead.kind == Identifier {
475 let second_ahead = self.stream.peek_ahead(2);
476 return match second_ahead.kind {
477 Colon => self.stream.peek_ahead(3).kind != Colon,
478 Comma | RightCurlyBrace => {
479 if self.in_control_flow_header {
480 is_uppercase && self.has_block_after_struct()
481 } else {
482 is_uppercase
483 }
484 }
485 _ => false,
486 };
487 }
488
489 false
490 }
491
492 fn enter_recursion(&mut self) -> bool {
493 if self.depth >= MAX_DEPTH {
494 let span = self.span_from_token(self.current_token());
495 self.track_error_at(span, "too deeply nested", "Reduce nesting depth");
496 return false;
497 }
498 self.depth += 1;
499 true
500 }
501
502 fn leave_recursion(&mut self) {
503 self.depth -= 1;
504 }
505
506 fn too_many_errors(&self) -> bool {
507 self.errors.len() >= MAX_ERRORS
508 }
509
510 fn position(&self) -> u32 {
511 self.current_token().byte_offset
512 }
513
514 fn at_sync_point(&self) -> bool {
515 matches!(
516 self.current_token().kind,
517 Semicolon
518 | RightCurlyBrace
519 | RightParen
520 | RightSquareBracket
521 | Comma
522 | Function
523 | Struct
524 | Enum
525 | Const
526 | Impl
527 | Interface
528 | Type
529 | Import
530 )
531 }
532
533 fn can_start_annotation(&self) -> bool {
534 matches!(self.current_token().kind, Identifier | Function | LeftParen)
535 }
536
537 fn at_item_boundary(&self) -> bool {
538 matches!(
539 self.current_token().kind,
540 Let | Function | Struct | Enum | Impl | Interface | Type | Const | Import
541 )
542 }
543
544 fn at_match_arm_terminator(&self) -> bool {
545 self.at_eof() || self.is(Comma) || self.is(RightCurlyBrace) || self.at_item_boundary()
546 }
547
548 fn resync_on_error(&mut self) {
549 if !self.at_eof() {
550 self.next();
551 }
552
553 while !self.at_sync_point() && !self.at_eof() {
554 self.next();
555 }
556 }
557
558 fn track_error(
559 &mut self,
560 label: impl Into<std::string::String>,
561 help: impl Into<std::string::String>,
562 ) {
563 let current = self.current_token();
564 let span = ast::Span::new(self.file_id, current.byte_offset, current.byte_length);
565 self.track_error_at(span, label, help);
566 }
567
568 fn track_error_at(
569 &mut self,
570 span: ast::Span,
571 label: impl Into<std::string::String>,
572 help: impl Into<std::string::String>,
573 ) {
574 if self.too_many_errors() {
575 return;
576 }
577 let error = ParseError::new("Syntax error", span, label.into())
578 .with_parse_code("syntax_error")
579 .with_help(help.into());
580
581 self.errors.push(error);
582 }
583
584 fn error_import_alias_after_path(&mut self, span: ast::Span, alias: &str, path: &str) {
585 if self.too_many_errors() {
586 return;
587 }
588 let error = ParseError::new("Syntax error", span, "import alias goes before the path")
589 .with_parse_code("import_alias_position")
590 .with_help(format!(
591 "Use Go-style alias syntax: `import {alias} \"{path}\"`"
592 ));
593
594 self.errors.push(error);
595 }
596
597 fn error_match_arm_missing_comma(&mut self, span: ast::Span) {
598 if self.too_many_errors() {
599 return;
600 }
601 let error = ParseError::new("Syntax error", span, "missing comma after match arm")
602 .with_parse_code("match_arm_missing_comma")
603 .with_help("Match arms must be separated by commas, even when the body is a block.");
604
605 self.errors.push(error);
606 }
607
608 pub(super) fn error_map_literal_not_supported(&mut self, span: ast::Span) {
609 if self.too_many_errors() {
610 return;
611 }
612 let error = ParseError::new("Invalid `Map` initialization", span, "invalid syntax")
613 .with_parse_code("invalid_map_initialization")
614 .with_help("To initialize a `Map`, use `Map.new<K, V>()` then `m[key] = value`");
615
616 self.errors.push(error);
617 }
618
619 pub(super) fn error_missing_initializer(&mut self, span: ast::Span) {
620 if self.too_many_errors() {
621 return;
622 }
623 let error = ParseError::new(
624 "Missing initializer",
625 span,
626 "annotated binding needs a value",
627 )
628 .with_parse_code("missing_initializer")
629 .with_help("Bindings must be initialized");
630
631 self.errors.push(error);
632 }
633
634 fn track_ensure_error(&mut self, expected_token: TokenKind) {
635 if self.too_many_errors() {
636 return;
637 }
638 let current = self.current_token();
639
640 let error_code = match expected_token {
641 Semicolon => "missing_semicolon",
642 RightCurlyBrace => "unclosed_block",
643 _ => "unexpected_token",
644 };
645
646 let span = ast::Span::new(self.file_id, current.byte_offset, current.byte_length);
647 let error = ParseError::new("Syntax error", span, format!("expected {}", expected_token))
648 .with_parse_code(error_code);
649
650 self.errors.push(error);
651 }
652
653 fn close_brace_span(&mut self, start: Token<'source>, error_anchor: Token<'source>) -> Span {
654 if self.is(RightCurlyBrace) {
655 let close = self.current_token();
656 self.next();
657 let end = close.byte_offset + close.byte_length;
658 Span::new(
659 self.file_id,
660 start.byte_offset,
661 end.saturating_sub(start.byte_offset),
662 )
663 } else {
664 self.error_unclosed_block(&error_anchor);
665 self.span_from_tokens(start)
666 }
667 }
668
669 fn error_unclosed_block(&mut self, open_brace: &Token) {
670 let span = ast::Span::new(self.file_id, open_brace.byte_offset, open_brace.byte_length);
671 let error = ParseError::new("Unclosed block", span, "opening brace here")
672 .with_parse_code("unclosed_block")
673 .with_help("Add a closing `}`");
674
675 self.errors.push(error);
676 }
677
678 fn error_tuple_arity(&mut self, arity: usize, span: Span) {
679 let help = if arity == 0 {
680 "Use `()` for unit type".to_string()
681 } else if arity == 1 {
682 "Use the type directly without wrapping in a tuple".to_string()
683 } else {
684 "For >5 elements, use a struct with named fields".to_string()
685 };
686
687 let error = ParseError::new(
688 "Invalid tuple",
689 span,
690 format!("{}-element tuple not allowed", arity),
691 )
692 .with_parse_code("tuple_element_count")
693 .with_help(help);
694
695 self.errors.push(error);
696 }
697
698 fn error_duplicate_field_in_pattern(
699 &mut self,
700 field_name: &str,
701 first_span: Span,
702 second_span: Span,
703 ) {
704 let error = ParseError::new(
705 "Duplicate field",
706 first_span,
707 format!("first use of `{}`", field_name),
708 )
709 .with_span_label(second_span, "used again")
710 .with_parse_code("duplicate_field_in_pattern")
711 .with_help("Remove the duplicate binding");
712
713 self.errors.push(error);
714 }
715
716 fn error_duplicate_impl_parent(&mut self, first_span: Span, second_span: Span) {
717 let error = ParseError::new("Duplicate impl", first_span, "first use")
718 .with_span_label(second_span, "used again")
719 .with_parse_code("duplicate_impl_parent")
720 .with_help("Remove the duplicate parent");
721
722 self.errors.push(error);
723 }
724
725 fn error_duplicate_struct_field(&mut self, name: &str, first_span: Span, second_span: Span) {
726 let error = ParseError::new("Duplicate field", first_span, "first defined")
727 .with_span_label(second_span, "defined again")
728 .with_parse_code("duplicate_struct_field")
729 .with_help(format!("Remove the duplicate field `{}`", name));
730
731 self.errors.push(error);
732 }
733
734 fn error_duplicate_enum_variant(&mut self, name: &str, first_span: Span, second_span: Span) {
735 let error = ParseError::new("Duplicate variant", first_span, "first defined")
736 .with_span_label(second_span, "defined again")
737 .with_parse_code("duplicate_enum_variant")
738 .with_help(format!("Remove the duplicate variant `{}`", name));
739
740 self.errors.push(error);
741 }
742
743 fn error_duplicate_interface_method(
744 &mut self,
745 name: &str,
746 first_span: Span,
747 second_span: Span,
748 ) {
749 let error = ParseError::new("Duplicate method", first_span, "first defined")
750 .with_span_label(second_span, "defined again")
751 .with_parse_code("duplicate_interface_method")
752 .with_help(format!("Remove the duplicate method `{}`", name));
753
754 self.errors.push(error);
755 }
756
757 fn error_float_pattern_not_allowed(&mut self, span: Span, float_text: &str) {
758 let error = ParseError::new("Invalid pattern", span, "float literal not allowed here")
759 .with_parse_code("float_pattern")
760 .with_help(format!(
761 "Use a guard instead: `x if x == {} =>`",
762 float_text
763 ));
764
765 self.errors.push(error);
766 }
767
768 fn error_uppercase_binding(&mut self, span: Span) {
769 let error = ParseError::new("Invalid binding name", span, "uppercase not allowed here")
770 .with_parse_code("uppercase_binding")
771 .with_help("Lowercase the binding");
772
773 self.errors.push(error);
774 }
775
776 fn error_detached_doc_comment(&mut self, span: Span) {
777 let error = ParseError::new("Unattached doc comment", span, "is detached")
778 .with_parse_code("detached_doc_comment")
779 .with_help("Place the doc comment on the line immediately above a symbol definition");
780
781 self.errors.push(error);
782 }
783
784 fn error_interface_method_with_type_parameters(&mut self, span: Span, count: usize) {
785 let label = if count == 1 {
786 "type parameter not allowed"
787 } else {
788 "type parameters not allowed"
789 };
790 let error = ParseError::new("Invalid interface method", span, label)
791 .with_parse_code("interface_method_with_type_parameters")
792 .with_help(
793 "Interface methods cannot have type parameters, because Go interfaces do not support generic methods",
794 );
795
796 self.errors.push(error);
797 }
798
799 pub(crate) fn parse_integer_text(&mut self, text: &str) -> ast::Literal {
800 self.parse_integer_text_with(text, false)
801 }
802
803 pub(crate) fn parse_integer_text_with(
804 &mut self,
805 text: &str,
806 preserve_decimal_text: bool,
807 ) -> ast::Literal {
808 let clean = if text.contains('_') {
809 std::borrow::Cow::Owned(text.replace('_', ""))
810 } else {
811 std::borrow::Cow::Borrowed(text)
812 };
813
814 let (n, is_decimal) = if clean.starts_with("0x") || clean.starts_with("0X") {
815 let value = u64::from_str_radix(&clean[2..], 16).unwrap_or_else(|_| {
816 self.track_error(
817 format!("hex literal '{text}' is too large"),
818 "Maximum value is `0xFFFFFFFFFFFFFFFF`.",
819 );
820 0
821 });
822 (value, false)
823 } else if clean.starts_with("0o") || clean.starts_with("0O") {
824 let value = u64::from_str_radix(&clean[2..], 8).unwrap_or_else(|_| {
825 self.track_error(
826 format!("octal literal '{text}' is too large"),
827 "Maximum value is `0o1777777777777777777777`.",
828 );
829 0
830 });
831 (value, false)
832 } else if clean.starts_with("0b") || clean.starts_with("0B") {
833 let value = u64::from_str_radix(&clean[2..], 2).unwrap_or_else(|_| {
834 self.track_error(
835 format!("binary literal '{text}' is too large"),
836 "Value must fit in 64 bits.",
837 );
838 0
839 });
840 (value, false)
841 } else if clean.len() > 1
842 && clean.starts_with('0')
843 && clean.chars().skip(1).all(|c| c.is_ascii_digit())
844 {
845 let value = u64::from_str_radix(&clean[1..], 8).unwrap_or_else(|_| {
846 self.track_error(
847 format!("octal literal '{text}' is too large"),
848 "Maximum value is `01777777777777777777777`.",
849 );
850 0
851 });
852 (value, false)
853 } else {
854 let value = clean.parse().unwrap_or_else(|_| {
855 self.track_error(
856 format!("integer literal '{text}' is too large"),
857 "Maximum value is `18446744073709551615`.",
858 );
859 0
860 });
861 (value, true)
862 };
863
864 let original_text = if is_decimal && !preserve_decimal_text {
865 None
866 } else {
867 Some(text.to_string())
868 };
869
870 ast::Literal::Integer {
871 value: n,
872 text: original_text,
873 }
874 }
875
876 fn unexpected_token(&mut self, ctx: &str) -> ast::Expression {
877 let token = self.current_token();
878 let token_descriptor = if token.text.is_empty() {
879 format!("{:?}", token.kind)
880 } else {
881 format!("`{}`", token.text)
882 };
883
884 let span = ast::Span::new(self.file_id, token.byte_offset, token.byte_length);
885
886 let (label, error_code, help) = match ctx {
887 "expr" => (
888 format!("expected expression, found {}", token_descriptor),
889 "expected_expression",
890 "Check your syntax.",
891 ),
892 "pattern" => (
893 format!("unexpected {} in pattern", token_descriptor),
894 "invalid_pattern",
895 "Patterns include literals, variables, and destructuring.",
896 ),
897 "literal" => (
898 format!("expected literal, found {}", token_descriptor),
899 "expected_literal",
900 "Literals include numbers, strings, characters, and booleans.",
901 ),
902 "top_item" if token.text == "trait" => (
903 format!("unexpected {}", token_descriptor),
904 "trait_unsupported",
905 "Lisette uses `interface` with Go-style structural typing. Types automatically satisfy interfaces if they have the required methods.",
906 ),
907 "top_item" if token.text == "use" => (
908 "unexpected syntax for import".to_string(),
909 "use_unsupported",
910 "Use `import` instead of `use` for imports: `import \"module/path\"`",
911 ),
912 "top_item" => (
913 "expected declaration".to_string(),
914 "expected_declaration",
915 "At the top level of a file, Lisette expects `fn`, `struct`, `enum`, `interface`, `import`, or `type`.",
916 ),
917 _ => (
918 format!("unexpected {}", token_descriptor),
919 "unexpected_token",
920 "Check your syntax.",
921 ),
922 };
923
924 let error = ParseError::new("Syntax error", span, label)
925 .with_parse_code(error_code)
926 .with_help(help);
927
928 if !self.too_many_errors() {
929 self.errors.push(error);
930 }
931
932 self.resync_on_error();
933
934 ast::Expression::Unit {
935 ty: Type::uninferred(),
936 span,
937 }
938 }
939}
940
941struct TokenStream<'source> {
942 tokens: Vec<Token<'source>>,
943 position: usize,
944}
945
946impl<'source> TokenStream<'source> {
947 fn new(tokens: Vec<Token<'source>>) -> Self {
948 Self {
949 tokens,
950 position: 0,
951 }
952 }
953
954 fn peek(&self) -> Token<'source> {
955 self.tokens
956 .get(self.position)
957 .copied()
958 .unwrap_or_else(|| Token {
959 kind: TokenKind::EOF,
960 text: "",
961 byte_offset: self
962 .tokens
963 .last()
964 .map(|t| t.byte_offset + t.byte_length)
965 .unwrap_or(0),
966 byte_length: 0,
967 })
968 }
969
970 fn peek_ahead(&self, n: usize) -> Token<'source> {
971 self.tokens
972 .get(self.position + n)
973 .copied()
974 .unwrap_or_else(|| Token {
975 kind: TokenKind::EOF,
976 text: "",
977 byte_offset: self
978 .tokens
979 .last()
980 .map(|t| t.byte_offset + t.byte_length)
981 .unwrap_or(0),
982 byte_length: 0,
983 })
984 }
985
986 fn consume(&mut self) -> Token<'source> {
987 let token = self.peek();
988 if self.position < self.tokens.len() {
989 self.position += 1;
990 }
991 token
992 }
993}