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