Skip to main content

lisette_syntax/parse/
mod.rs

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; // 0 is <
414        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}