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 fn track_ensure_error(&mut self, expected_token: TokenKind) {
609 if self.too_many_errors() {
610 return;
611 }
612 let current = self.current_token();
613
614 let error_code = match expected_token {
615 Semicolon => "missing_semicolon",
616 RightCurlyBrace => "unclosed_block",
617 _ => "unexpected_token",
618 };
619
620 let span = ast::Span::new(self.file_id, current.byte_offset, current.byte_length);
621 let error = ParseError::new("Syntax error", span, format!("expected {}", expected_token))
622 .with_parse_code(error_code);
623
624 self.errors.push(error);
625 }
626
627 fn close_brace_span(&mut self, start: Token<'source>, error_anchor: Token<'source>) -> Span {
628 if self.is(RightCurlyBrace) {
629 let close = self.current_token();
630 self.next();
631 let end = close.byte_offset + close.byte_length;
632 Span::new(
633 self.file_id,
634 start.byte_offset,
635 end.saturating_sub(start.byte_offset),
636 )
637 } else {
638 self.error_unclosed_block(&error_anchor);
639 self.span_from_tokens(start)
640 }
641 }
642
643 fn error_unclosed_block(&mut self, open_brace: &Token) {
644 let span = ast::Span::new(self.file_id, open_brace.byte_offset, open_brace.byte_length);
645 let error = ParseError::new("Unclosed block", span, "opening brace here")
646 .with_parse_code("unclosed_block")
647 .with_help("Add a closing `}`");
648
649 self.errors.push(error);
650 }
651
652 fn error_tuple_arity(&mut self, arity: usize, span: Span) {
653 let help = if arity == 0 {
654 "Use `()` for unit type".to_string()
655 } else if arity == 1 {
656 "Use the type directly without wrapping in a tuple".to_string()
657 } else {
658 "For >5 elements, use a struct with named fields".to_string()
659 };
660
661 let error = ParseError::new(
662 "Invalid tuple",
663 span,
664 format!("{}-element tuple not allowed", arity),
665 )
666 .with_parse_code("tuple_element_count")
667 .with_help(help);
668
669 self.errors.push(error);
670 }
671
672 fn error_duplicate_field_in_pattern(
673 &mut self,
674 field_name: &str,
675 first_span: Span,
676 second_span: Span,
677 ) {
678 let error = ParseError::new(
679 "Duplicate field",
680 first_span,
681 format!("first use of `{}`", field_name),
682 )
683 .with_span_label(second_span, "used again")
684 .with_parse_code("duplicate_field_in_pattern")
685 .with_help("Remove the duplicate binding");
686
687 self.errors.push(error);
688 }
689
690 fn error_duplicate_impl_parent(&mut self, first_span: Span, second_span: Span) {
691 let error = ParseError::new("Duplicate impl", first_span, "first use")
692 .with_span_label(second_span, "used again")
693 .with_parse_code("duplicate_impl_parent")
694 .with_help("Remove the duplicate parent");
695
696 self.errors.push(error);
697 }
698
699 fn error_duplicate_struct_field(&mut self, name: &str, first_span: Span, second_span: Span) {
700 let error = ParseError::new("Duplicate field", first_span, "first defined")
701 .with_span_label(second_span, "defined again")
702 .with_parse_code("duplicate_struct_field")
703 .with_help(format!("Remove the duplicate field `{}`", name));
704
705 self.errors.push(error);
706 }
707
708 fn error_duplicate_enum_variant(&mut self, name: &str, first_span: Span, second_span: Span) {
709 let error = ParseError::new("Duplicate variant", first_span, "first defined")
710 .with_span_label(second_span, "defined again")
711 .with_parse_code("duplicate_enum_variant")
712 .with_help(format!("Remove the duplicate variant `{}`", name));
713
714 self.errors.push(error);
715 }
716
717 fn error_duplicate_interface_method(
718 &mut self,
719 name: &str,
720 first_span: Span,
721 second_span: Span,
722 ) {
723 let error = ParseError::new("Duplicate method", first_span, "first defined")
724 .with_span_label(second_span, "defined again")
725 .with_parse_code("duplicate_interface_method")
726 .with_help(format!("Remove the duplicate method `{}`", name));
727
728 self.errors.push(error);
729 }
730
731 fn error_float_pattern_not_allowed(&mut self, span: Span, float_text: &str) {
732 let error = ParseError::new("Invalid pattern", span, "float literal not allowed here")
733 .with_parse_code("float_pattern")
734 .with_help(format!(
735 "Use a guard instead: `x if x == {} =>`",
736 float_text
737 ));
738
739 self.errors.push(error);
740 }
741
742 fn error_uppercase_binding(&mut self, span: Span) {
743 let error = ParseError::new("Invalid binding name", span, "uppercase not allowed here")
744 .with_parse_code("uppercase_binding")
745 .with_help("Lowercase the binding");
746
747 self.errors.push(error);
748 }
749
750 fn error_detached_doc_comment(&mut self, span: Span) {
751 let error = ParseError::new("Unattached doc comment", span, "is detached")
752 .with_parse_code("detached_doc_comment")
753 .with_help("Place the doc comment on the line immediately above a symbol definition");
754
755 self.errors.push(error);
756 }
757
758 fn error_interface_method_with_type_parameters(&mut self, span: Span, count: usize) {
759 let label = if count == 1 {
760 "type parameter not allowed"
761 } else {
762 "type parameters not allowed"
763 };
764 let error = ParseError::new("Invalid interface method", span, label)
765 .with_parse_code("interface_method_with_type_parameters")
766 .with_help(
767 "Interface methods cannot have type parameters, because Go interfaces do not support generic methods",
768 );
769
770 self.errors.push(error);
771 }
772
773 pub(crate) fn parse_integer_text(&mut self, text: &str) -> ast::Literal {
774 self.parse_integer_text_with(text, false)
775 }
776
777 pub(crate) fn parse_integer_text_with(
778 &mut self,
779 text: &str,
780 preserve_decimal_text: bool,
781 ) -> ast::Literal {
782 let clean = if text.contains('_') {
783 std::borrow::Cow::Owned(text.replace('_', ""))
784 } else {
785 std::borrow::Cow::Borrowed(text)
786 };
787
788 let (n, is_decimal) = if clean.starts_with("0x") || clean.starts_with("0X") {
789 let value = u64::from_str_radix(&clean[2..], 16).unwrap_or_else(|_| {
790 self.track_error(
791 format!("hex literal '{text}' is too large"),
792 "Maximum value is `0xFFFFFFFFFFFFFFFF`.",
793 );
794 0
795 });
796 (value, false)
797 } else if clean.starts_with("0o") || clean.starts_with("0O") {
798 let value = u64::from_str_radix(&clean[2..], 8).unwrap_or_else(|_| {
799 self.track_error(
800 format!("octal literal '{text}' is too large"),
801 "Maximum value is `0o1777777777777777777777`.",
802 );
803 0
804 });
805 (value, false)
806 } else if clean.starts_with("0b") || clean.starts_with("0B") {
807 let value = u64::from_str_radix(&clean[2..], 2).unwrap_or_else(|_| {
808 self.track_error(
809 format!("binary literal '{text}' is too large"),
810 "Value must fit in 64 bits.",
811 );
812 0
813 });
814 (value, false)
815 } else if clean.len() > 1
816 && clean.starts_with('0')
817 && clean.chars().skip(1).all(|c| c.is_ascii_digit())
818 {
819 let value = u64::from_str_radix(&clean[1..], 8).unwrap_or_else(|_| {
820 self.track_error(
821 format!("octal literal '{text}' is too large"),
822 "Maximum value is `01777777777777777777777`.",
823 );
824 0
825 });
826 (value, false)
827 } else {
828 let value = clean.parse().unwrap_or_else(|_| {
829 self.track_error(
830 format!("integer literal '{text}' is too large"),
831 "Maximum value is `18446744073709551615`.",
832 );
833 0
834 });
835 (value, true)
836 };
837
838 let original_text = if is_decimal && !preserve_decimal_text {
839 None
840 } else {
841 Some(text.to_string())
842 };
843
844 ast::Literal::Integer {
845 value: n,
846 text: original_text,
847 }
848 }
849
850 fn unexpected_token(&mut self, ctx: &str) -> ast::Expression {
851 let token = self.current_token();
852 let token_descriptor = if token.text.is_empty() {
853 format!("{:?}", token.kind)
854 } else {
855 format!("`{}`", token.text)
856 };
857
858 let span = ast::Span::new(self.file_id, token.byte_offset, token.byte_length);
859
860 let (label, error_code, help) = match ctx {
861 "expr" => (
862 format!("expected expression, found {}", token_descriptor),
863 "expected_expression",
864 "Check your syntax.",
865 ),
866 "pattern" => (
867 format!("unexpected {} in pattern", token_descriptor),
868 "invalid_pattern",
869 "Patterns include literals, variables, and destructuring.",
870 ),
871 "literal" => (
872 format!("expected literal, found {}", token_descriptor),
873 "expected_literal",
874 "Literals include numbers, strings, characters, and booleans.",
875 ),
876 "top_item" if token.text == "trait" => (
877 format!("unexpected {}", token_descriptor),
878 "trait_unsupported",
879 "Lisette uses `interface` with Go-style structural typing. Types automatically satisfy interfaces if they have the required methods.",
880 ),
881 "top_item" if token.text == "use" => (
882 "unexpected syntax for import".to_string(),
883 "use_unsupported",
884 "Use `import` instead of `use` for imports: `import \"module/path\"`",
885 ),
886 "top_item" => (
887 "expected declaration".to_string(),
888 "expected_declaration",
889 "At the top level of a file, Lisette expects `fn`, `struct`, `enum`, `interface`, `import`, or `type`.",
890 ),
891 _ => (
892 format!("unexpected {}", token_descriptor),
893 "unexpected_token",
894 "Check your syntax.",
895 ),
896 };
897
898 let error = ParseError::new("Syntax error", span, label)
899 .with_parse_code(error_code)
900 .with_help(help);
901
902 if !self.too_many_errors() {
903 self.errors.push(error);
904 }
905
906 self.resync_on_error();
907
908 ast::Expression::Unit {
909 ty: Type::uninferred(),
910 span,
911 }
912 }
913}
914
915struct TokenStream<'source> {
916 tokens: Vec<Token<'source>>,
917 position: usize,
918}
919
920impl<'source> TokenStream<'source> {
921 fn new(tokens: Vec<Token<'source>>) -> Self {
922 Self {
923 tokens,
924 position: 0,
925 }
926 }
927
928 fn peek(&self) -> Token<'source> {
929 self.tokens
930 .get(self.position)
931 .copied()
932 .unwrap_or_else(|| Token {
933 kind: TokenKind::EOF,
934 text: "",
935 byte_offset: self
936 .tokens
937 .last()
938 .map(|t| t.byte_offset + t.byte_length)
939 .unwrap_or(0),
940 byte_length: 0,
941 })
942 }
943
944 fn peek_ahead(&self, n: usize) -> Token<'source> {
945 self.tokens
946 .get(self.position + n)
947 .copied()
948 .unwrap_or_else(|| Token {
949 kind: TokenKind::EOF,
950 text: "",
951 byte_offset: self
952 .tokens
953 .last()
954 .map(|t| t.byte_offset + t.byte_length)
955 .unwrap_or(0),
956 byte_length: 0,
957 })
958 }
959
960 fn consume(&mut self) -> Token<'source> {
961 let token = self.peek();
962 if self.position < self.tokens.len() {
963 self.position += 1;
964 }
965 token
966 }
967}