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