Skip to main content

lisette_syntax/parse/
definitions.rs

1use ecow::EcoString;
2
3use super::Parser;
4use crate::ast::{
5    Annotation, Attribute, AttributeArg, EnumFieldDefinition, EnumVariant, Expression, Generic,
6    ParentInterface, Span, StructFieldDefinition, StructKind, VariantFields, Visibility,
7};
8use crate::lex::Token;
9use crate::lex::TokenKind::*;
10use crate::parse::error::ParseError;
11use crate::types::Type;
12
13impl<'source> Parser<'source> {
14    pub(crate) fn parse_attributes(&mut self) -> Vec<Attribute> {
15        let mut attributes = vec![];
16        loop {
17            self.advance_if(Semicolon);
18            if !self.is(Hash) {
19                break;
20            }
21            if let Some(attribute) = self.parse_attribute() {
22                attributes.push(attribute);
23            }
24        }
25        attributes
26    }
27
28    fn parse_attribute(&mut self) -> Option<Attribute> {
29        let start = self.current_token();
30        self.ensure(Hash);
31
32        if !self.is(LeftSquareBracket) {
33            self.track_error("expected `[` after `#`", "Add `[` to start the attribute");
34            return None;
35        }
36        self.next();
37
38        if !self.is(Identifier) {
39            self.track_error(
40                "expected attribute name",
41                "Add an attribute name like `json` or `db`",
42            );
43            while self.is_not(RightSquareBracket) && !self.at_eof() {
44                self.next();
45            }
46            self.advance_if(RightSquareBracket);
47            return None;
48        }
49
50        let name = self.read_identifier();
51        let args = if self.advance_if(LeftParen) {
52            self.parse_attribute_args()
53        } else {
54            vec![]
55        };
56
57        if !self.advance_if(RightSquareBracket) {
58            self.track_error("expected `]`", "Add `]` to close the attribute");
59        }
60
61        Some(Attribute {
62            name: name.to_string(),
63            args,
64            span: self.span_from_tokens(start),
65        })
66    }
67
68    fn parse_attribute_args(&mut self) -> Vec<AttributeArg> {
69        let mut args = vec![];
70
71        while self.is_not(RightParen) && !self.at_eof() {
72            if let Some(arg) = self.parse_attribute_arg() {
73                args.push(arg);
74            }
75
76            if !self.advance_if(Comma) {
77                break;
78            }
79        }
80
81        self.ensure(RightParen);
82        args
83    }
84
85    fn parse_attribute_arg(&mut self) -> Option<AttributeArg> {
86        if self.advance_if(Bang) {
87            if self.is(Identifier) {
88                return Some(AttributeArg::NegatedFlag(
89                    self.read_identifier().to_string(),
90                ));
91            } else {
92                self.track_error(
93                    "expected identifier after `!`",
94                    "Add an identifier like `omitempty` after `!`",
95                );
96                return None;
97            }
98        }
99
100        if self.is(Identifier) {
101            return Some(AttributeArg::Flag(self.read_identifier().to_string()));
102        }
103
104        if self.is(String) {
105            let token = self.current_token();
106            self.next();
107            let text = token.text;
108            let value = if text.len() >= 2 {
109                &text[1..text.len() - 1]
110            } else {
111                text
112            };
113            return Some(AttributeArg::String(value.to_string()));
114        }
115
116        if self.is(Backtick) {
117            let token = self.current_token();
118            self.next();
119            let text = token.text;
120            let value = if text.len() >= 2 {
121                &text[1..text.len() - 1]
122            } else {
123                text
124            };
125            return Some(AttributeArg::Raw(value.to_string()));
126        }
127
128        self.track_error(
129            "expected attribute argument",
130            "Add a flag (e.g. `omitempty`), string (e.g. `\"name\"`), or raw tag (e.g. `` `json:\"name\"` ``)",
131        );
132        None
133    }
134
135    pub fn parse_enum_definition(
136        &mut self,
137        doc: Option<std::string::String>,
138        attributes: Vec<Attribute>,
139    ) -> Expression {
140        let start = self.current_token();
141
142        self.ensure(Enum);
143
144        let name_token = self.current_token();
145        let name_span = Span::new(self.file_id, name_token.byte_offset, name_token.byte_length);
146        let name = self.read_identifier();
147        let generics = self.parse_generics();
148
149        let underlying_start = self.current_token();
150        if self.advance_if(Colon) {
151            let _ = self.parse_annotation();
152            let underlying_span = Span::new(
153                self.file_id,
154                underlying_start.byte_offset,
155                underlying_start.byte_length,
156            );
157            let error = ParseError::new(
158                "Enum with underlying type",
159                underlying_span,
160                "enums cannot have an underlying type",
161            )
162            .with_parse_code("enum_underlying_type")
163            .with_help(
164                "Remove the `: type` annotation. To model a Go defined primitive type, use a named primitive type such as `pub struct Weekday(int)` with package-level constants.",
165            );
166            self.errors.push(error);
167        }
168
169        self.ensure(LeftCurlyBrace);
170
171        self.parse_regular_enum_body(doc, attributes, name, name_span, generics, start)
172    }
173
174    fn parse_regular_enum_body(
175        &mut self,
176        doc: Option<std::string::String>,
177        attributes: Vec<Attribute>,
178        name: EcoString,
179        name_span: Span,
180        generics: Vec<Generic>,
181        start: Token<'source>,
182    ) -> Expression {
183        let mut variants = vec![];
184        let mut seen_variants: Vec<(EcoString, Span)> = vec![];
185
186        while self.is_not(RightCurlyBrace) {
187            let start_position = self.stream.position;
188
189            let variant_doc = self.collect_doc_comments().map(|(text, _)| text);
190            if let Some(variant) = self.parse_enum_variant_with_doc(variant_doc) {
191                if let Some((_, first_span)) =
192                    seen_variants.iter().find(|(n, _)| n == &variant.name)
193                {
194                    self.error_duplicate_enum_variant(
195                        &variant.name,
196                        *first_span,
197                        variant.name_span,
198                    );
199                } else {
200                    seen_variants.push((variant.name.clone(), variant.name_span));
201                }
202                variants.push(variant);
203            }
204            self.expect_comma_or(RightCurlyBrace);
205            self.ensure_progress(start_position, RightCurlyBrace);
206        }
207
208        self.ensure(RightCurlyBrace);
209
210        Expression::Enum {
211            doc,
212            attributes,
213            name,
214            name_span,
215            generics,
216            variants,
217            visibility: Visibility::Private,
218            span: self.span_from_tokens(start),
219        }
220    }
221
222    fn parse_enum_variant_with_doc(
223        &mut self,
224        doc: Option<std::string::String>,
225    ) -> Option<EnumVariant> {
226        if self.is_not(Identifier) {
227            self.track_error(
228                "expected variant name",
229                "Variant names must be identifiers.",
230            );
231            return None;
232        }
233
234        let name_token = self.current_token();
235        let name_span = Span::new(self.file_id, name_token.byte_offset, name_token.byte_length);
236        let name = self.read_identifier();
237
238        if self.is(Equal) {
239            let eq_token = self.current_token();
240            let eq_span = Span::new(self.file_id, eq_token.byte_offset, eq_token.byte_length);
241            let error = ParseError::new(
242                "Assigned enum variant",
243                eq_span,
244                "enum variants cannot have assigned values",
245            )
246            .with_parse_code("enum_assigned_variant")
247            .with_help(
248                "Lisette enums are sum types, not Go const groups. To model a Go defined primitive type, use a named primitive type such as `pub struct Weekday(int)` with package-level constants.",
249            );
250            self.errors.push(error);
251            self.next(); // consume `=`
252            self.skip_assigned_variant_value();
253        }
254
255        let fields = self.parse_enum_variant_fields();
256
257        Some(EnumVariant {
258            doc,
259            name,
260            name_span,
261            fields,
262        })
263    }
264
265    fn skip_assigned_variant_value(&mut self) {
266        self.advance_if(Minus);
267        if self.is(Integer) || self.is(String) {
268            self.next();
269        }
270    }
271
272    fn parse_enum_variant_fields(&mut self) -> VariantFields {
273        if self.advance_if(LeftParen) {
274            return self.parse_tuple_variant_fields();
275        }
276
277        if self.advance_if(LeftCurlyBrace) {
278            return self.parse_struct_variant_fields();
279        }
280
281        VariantFields::Unit
282    }
283
284    fn parse_tuple_variant_fields(&mut self) -> VariantFields {
285        let mut fields = vec![];
286
287        loop {
288            if self.at_eof()
289                || self.is(RightParen)
290                || self.is(RightCurlyBrace)
291                || !self.can_start_annotation()
292            {
293                break;
294            }
295
296            let field = EnumFieldDefinition {
297                name: format!("field{}", fields.len()).into(),
298                name_span: Span::dummy(),
299                annotation: self.parse_annotation(),
300                ty: Type::uninferred(),
301            };
302
303            fields.push(field);
304
305            self.expect_comma_or(RightParen);
306        }
307
308        self.ensure(RightParen);
309
310        VariantFields::Tuple(fields)
311    }
312
313    fn parse_struct_variant_fields(&mut self) -> VariantFields {
314        let mut fields = vec![];
315        let mut seen_fields: Vec<(EcoString, Span)> = vec![];
316
317        loop {
318            if self.at_eof()
319                || self.is(RightCurlyBrace)
320                || self.at_item_boundary()
321                || self.is_not(Identifier)
322            {
323                break;
324            }
325
326            let name_token = self.current_token();
327            let name_span = Span::new(self.file_id, name_token.byte_offset, name_token.byte_length);
328            let name = self.read_identifier();
329            self.ensure(Colon);
330            let annotation = self.parse_annotation();
331
332            if let Some((_, first_span)) = seen_fields.iter().find(|(n, _)| n == &name) {
333                self.error_duplicate_struct_field(&name, *first_span, name_span);
334            } else {
335                seen_fields.push((name.clone(), name_span));
336            }
337
338            let field = EnumFieldDefinition {
339                name,
340                name_span,
341                annotation,
342                ty: Type::uninferred(),
343            };
344
345            fields.push(field);
346
347            self.expect_comma_or(RightCurlyBrace);
348        }
349
350        self.ensure(RightCurlyBrace);
351
352        VariantFields::Struct(fields)
353    }
354
355    pub fn parse_struct_definition(
356        &mut self,
357        doc: Option<std::string::String>,
358        attributes: Vec<Attribute>,
359    ) -> Expression {
360        let start = self.current_token();
361
362        self.ensure(Struct);
363
364        let name_token = self.current_token();
365        let name_span = Span::new(self.file_id, name_token.byte_offset, name_token.byte_length);
366        let name = self.read_identifier();
367        let generics = self.parse_generics();
368
369        if self.is(LeftParen) {
370            return self.parse_tuple_struct(doc, attributes, name, name_span, generics, start);
371        }
372
373        self.parse_named_struct(doc, attributes, name, name_span, generics, start)
374    }
375
376    fn parse_named_struct(
377        &mut self,
378        doc: Option<std::string::String>,
379        attributes: Vec<Attribute>,
380        name: EcoString,
381        name_span: Span,
382        generics: Vec<Generic>,
383        start: Token<'source>,
384    ) -> Expression {
385        let mut fields = vec![];
386        let mut seen_fields: Vec<(EcoString, Span)> = vec![];
387
388        self.ensure(LeftCurlyBrace);
389
390        while self.is_not(RightCurlyBrace) {
391            let start_position = self.stream.position;
392
393            let field_attributes = self.parse_attributes();
394            let field_doc = self.collect_doc_comments().map(|(text, _)| text);
395            if let Some(field) = self.parse_struct_field_with_doc(field_doc, field_attributes) {
396                if let Some((_, first_span)) = seen_fields.iter().find(|(n, _)| n == &field.name) {
397                    self.error_duplicate_struct_field(&field.name, *first_span, field.name_span);
398                } else {
399                    seen_fields.push((field.name.clone(), field.name_span));
400                }
401                fields.push(field);
402            }
403            self.expect_comma_or(RightCurlyBrace);
404            self.ensure_progress(start_position, RightCurlyBrace);
405        }
406
407        self.ensure(RightCurlyBrace);
408
409        Expression::Struct {
410            doc,
411            attributes,
412            name,
413            name_span,
414            generics,
415            fields,
416            kind: StructKind::Record,
417            visibility: Visibility::Private,
418            span: self.span_from_tokens(start),
419        }
420    }
421
422    fn parse_tuple_struct(
423        &mut self,
424        doc: Option<std::string::String>,
425        attributes: Vec<Attribute>,
426        name: EcoString,
427        name_span: Span,
428        generics: Vec<Generic>,
429        start: Token<'source>,
430    ) -> Expression {
431        self.ensure(LeftParen);
432
433        let mut fields = vec![];
434        let mut index = 0;
435
436        while self.is_not(RightParen) {
437            if self.at_eof() || self.at_item_boundary() || !self.can_start_annotation() {
438                break;
439            }
440
441            let field_start = self.current_token();
442            let annotation = self.parse_annotation();
443            let field_span = self.span_from_tokens(field_start);
444
445            fields.push(StructFieldDefinition {
446                doc: None,
447                attributes: vec![],
448                name: format!("_{}", index).into(),
449                name_span: field_span,
450                annotation,
451                visibility: Visibility::Private,
452                ty: Type::uninferred(),
453                embedded: false,
454            });
455
456            index += 1;
457            self.expect_comma_or(RightParen);
458        }
459
460        self.ensure(RightParen);
461
462        Expression::Struct {
463            doc,
464            attributes,
465            name,
466            name_span,
467            generics,
468            fields,
469            kind: StructKind::Tuple,
470            visibility: Visibility::Private,
471            span: self.span_from_tokens(start),
472        }
473    }
474
475    fn parse_struct_field_with_doc(
476        &mut self,
477        doc: Option<std::string::String>,
478        attributes: Vec<Attribute>,
479    ) -> Option<StructFieldDefinition> {
480        let visibility = if self.advance_if(Pub) {
481            Visibility::Public
482        } else {
483            Visibility::Private
484        };
485
486        if self.is(Mut) {
487            self.track_error(
488                "fields cannot be marked `mut`",
489                "Fields cannot be marked `mut`; mutability applies to bindings (`let mut x = ...`).",
490            );
491            self.next();
492        }
493
494        // `embed T`, but not a field named `embed` (`embed: T`).
495        if self.is(Identifier)
496            && self.current_token().text == "embed"
497            && self.stream.peek_ahead(1).kind != Colon
498        {
499            if visibility.is_public() {
500                self.track_error(
501                    "embedded field cannot be `pub`",
502                    "An embedded field takes no `pub`; its visibility derives from the embedded type.",
503                );
504            }
505            return self.parse_embedded_field(doc, attributes);
506        }
507
508        if self.is_not(Identifier) {
509            self.track_error("expected field name", "Field names must be identifiers.");
510            return None;
511        }
512
513        let name_token = self.current_token();
514        let name_span = Span::new(self.file_id, name_token.byte_offset, name_token.byte_length);
515        let name = self.read_identifier();
516
517        self.ensure(Colon);
518
519        Some(StructFieldDefinition {
520            doc,
521            attributes,
522            visibility,
523            name,
524            name_span,
525            annotation: self.parse_annotation(),
526            ty: Type::uninferred(),
527            embedded: false,
528        })
529    }
530
531    fn parse_embedded_field(
532        &mut self,
533        doc: Option<std::string::String>,
534        attributes: Vec<Attribute>,
535    ) -> Option<StructFieldDefinition> {
536        let start = self.current_token();
537        self.ensure(Identifier);
538
539        let annotation = self.parse_annotation();
540        let span = self.span_from_tokens(start);
541
542        let Some(name) = Self::embedded_field_name(&annotation) else {
543            self.track_error_at(
544                span,
545                "expected a named type after `embed`",
546                "`embed` requires a named type; a function or tuple type cannot be embedded.",
547            );
548            return None;
549        };
550
551        if let Some(attribute) = attributes.first() {
552            self.track_error_at(
553                attribute.span,
554                "embedded field cannot have attributes",
555                "An embedded field takes no attributes; its serialization follows Go's anonymous-field inlining.",
556            );
557        }
558
559        Some(StructFieldDefinition {
560            doc,
561            attributes: vec![],
562            visibility: Visibility::Private,
563            name,
564            name_span: span,
565            annotation,
566            ty: Type::uninferred(),
567            embedded: true,
568        })
569    }
570
571    fn embedded_field_name(annotation: &Annotation) -> Option<EcoString> {
572        let mut current = annotation;
573        loop {
574            let Annotation::Constructor { name, params, .. } = current else {
575                return None;
576            };
577            let segment = name.rsplit('.').next().unwrap_or(name);
578            if (segment == "Option" || segment == "Ref") && params.len() == 1 {
579                current = &params[0];
580                continue;
581            }
582            return Some(segment.into());
583        }
584    }
585
586    pub fn parse_const_definition(&mut self, doc: Option<std::string::String>) -> Expression {
587        let start = self.current_token();
588
589        self.ensure(Const);
590
591        let identifier_token = self.current_token();
592        let identifier_span = Span::new(
593            self.file_id,
594            identifier_token.byte_offset,
595            identifier_token.byte_length,
596        );
597        let identifier = self.read_identifier();
598        let annotation = if self.advance_if(Colon) {
599            Some(self.parse_annotation())
600        } else {
601            None
602        };
603
604        let expression = if self.advance_if(Equal) {
605            self.parse_expression()
606        } else {
607            Expression::NoOp
608        };
609
610        Expression::Const {
611            doc,
612            identifier,
613            identifier_span,
614            annotation,
615            expression: expression.into(),
616            visibility: Visibility::Private,
617            ty: Type::uninferred(),
618            span: self.span_from_tokens(start),
619        }
620    }
621
622    pub fn parse_var_declaration(&mut self, doc: Option<std::string::String>) -> Expression {
623        let start = self.current_token();
624
625        self.ensure(Var);
626
627        let name_token = self.current_token();
628        let name_span = Span::new(self.file_id, name_token.byte_offset, name_token.byte_length);
629        let name = self.read_identifier();
630
631        if self.advance_if(Equal) {
632            return self.recover_var_initializer(start);
633        }
634
635        self.ensure(Colon);
636        let annotation = self.parse_annotation();
637
638        if self.advance_if(Equal) {
639            return self.recover_var_initializer(start);
640        }
641
642        Expression::VariableDeclaration {
643            doc,
644            name,
645            name_span,
646            annotation,
647            visibility: Visibility::Private,
648            ty: Type::uninferred(),
649            span: self.span_from_tokens(start),
650        }
651    }
652
653    fn recover_var_initializer(&mut self, start: Token<'source>) -> Expression {
654        self.parse_expression();
655        self.error_var_initializer(self.span_from_token(start));
656
657        Expression::Unit {
658            ty: Type::uninferred(),
659            span: self.span_from_tokens(start),
660        }
661    }
662
663    pub fn parse_impl_block(&mut self) -> Expression {
664        let start = self.current_token();
665
666        self.ensure(Impl);
667
668        let generics = self.parse_generics();
669
670        let receiver = self.parse_annotation(); // e.g. List<T>
671
672        let (receiver_name, annotation) = match &receiver {
673            Annotation::Constructor { name, .. } => (name.clone(), receiver),
674            _ => {
675                self.track_error("expected `impl` receiver", "Use `impl TypeName { ... }`.");
676                ("".into(), Annotation::Unknown)
677            }
678        };
679
680        if self.is(For) {
681            self.track_error(
682                "invalid syntax",
683                "Lisette types satisfy interfaces automatically by having the required methods. Use `impl Type { ... }` to add methods.",
684            );
685            self.next();
686            self.parse_annotation();
687        }
688
689        let mut methods = vec![];
690
691        self.ensure(LeftCurlyBrace);
692
693        while self.is_not(RightCurlyBrace) {
694            self.advance_if(Semicolon);
695            if self.is(RightCurlyBrace) {
696                break;
697            }
698
699            let method_doc = self.collect_doc_comments();
700            let method_attrs = self.parse_attributes();
701            let is_public = self.advance_if(Pub);
702
703            if self.is(Function) {
704                let method = self.parse_function(method_doc.map(|(text, _)| text), method_attrs);
705                let method = if is_public {
706                    method.set_public()
707                } else {
708                    method
709                };
710                methods.push(method);
711            } else {
712                if let Some((_, span)) = method_doc {
713                    self.error_detached_doc_comment(span);
714                }
715                self.track_error(
716                    "expected `fn` in impl block",
717                    "Only functions are allowed in `impl` blocks.",
718                );
719                self.next();
720            }
721        }
722
723        self.ensure(RightCurlyBrace);
724
725        Expression::ImplBlock {
726            annotation,
727            methods,
728            receiver_name,
729            generics,
730            ty: Type::uninferred(),
731            span: self.span_from_tokens(start),
732        }
733    }
734
735    pub fn parse_interface_definition(&mut self, doc: Option<std::string::String>) -> Expression {
736        let start = self.current_token();
737
738        self.ensure(Interface);
739
740        let name_token = self.current_token();
741        let name_span = Span::new(self.file_id, name_token.byte_offset, name_token.byte_length);
742        let name = self.read_identifier();
743
744        let generics = self.parse_generics();
745
746        let mut parents = vec![];
747        let mut seen_parents: Vec<(EcoString, Span)> = vec![];
748        let mut method_signatures = vec![];
749        let mut seen_methods: Vec<(EcoString, Span)> = vec![];
750
751        self.ensure(LeftCurlyBrace);
752
753        while self.is_not(RightCurlyBrace) {
754            self.advance_if(Semicolon);
755            if self.is(RightCurlyBrace) {
756                break;
757            }
758
759            let item_doc = self.collect_doc_comments();
760            let method_attrs = self.parse_attributes();
761            if !self.is(Function)
762                && let Some(attribute) = method_attrs.first()
763            {
764                self.error_misplaced_attribute(attribute.span);
765            }
766            match self.current_token().kind {
767                Function => {
768                    let method =
769                        self.parse_interface_method(item_doc.map(|(text, _)| text), method_attrs);
770                    if let Expression::Function {
771                        ref name,
772                        ref name_span,
773                        ..
774                    } = method
775                    {
776                        if let Some((_, first_span)) = seen_methods.iter().find(|(n, _)| n == name)
777                        {
778                            self.error_duplicate_interface_method(name, *first_span, *name_span);
779                        } else {
780                            seen_methods.push((name.clone(), *name_span));
781                        }
782                    }
783                    method_signatures.push(method);
784                    self.advance_if(Semicolon);
785                }
786
787                Impl => {
788                    let keyword = self.current_token();
789                    let keyword_span =
790                        Span::new(self.file_id, keyword.byte_offset, keyword.byte_length);
791                    self.ensure(Impl);
792                    self.parse_interface_parent(
793                        item_doc,
794                        Some(keyword_span),
795                        &mut seen_parents,
796                        &mut parents,
797                    );
798                }
799
800                Identifier if self.current_token().text == "embed" => {
801                    self.next();
802                    self.parse_interface_parent(item_doc, None, &mut seen_parents, &mut parents);
803                }
804
805                _ => {
806                    if let Some((_, span)) = item_doc {
807                        self.error_detached_doc_comment(span);
808                    }
809                    self.track_error(
810                        "expected `fn` or `embed`",
811                        "Only method signatures (`fn`) and embeddings (`embed`) are allowed in interfaces.",
812                    );
813                    self.next();
814                }
815            }
816        }
817
818        self.ensure(RightCurlyBrace);
819
820        Expression::Interface {
821            doc,
822            name,
823            name_span,
824            generics,
825            parents,
826            method_signatures,
827            visibility: Visibility::Private,
828            span: self.span_from_tokens(start),
829        }
830    }
831
832    fn parse_interface_parent(
833        &mut self,
834        item_doc: Option<(std::string::String, Span)>,
835        impl_keyword_span: Option<Span>,
836        seen_parents: &mut Vec<(EcoString, Span)>,
837        parents: &mut Vec<ParentInterface>,
838    ) {
839        if let Some((_, span)) = item_doc {
840            self.error_detached_doc_comment(span);
841        }
842
843        let parent_start = self.current_token();
844        let annotation = self.parse_annotation();
845        let parent_span = self.span_from_tokens(parent_start);
846
847        if let Annotation::Constructor { name, .. } = &annotation {
848            if let Some((_, first_span)) = seen_parents.iter().find(|(n, _)| n == name.as_str()) {
849                self.error_duplicate_impl_parent(*first_span, parent_span);
850            } else {
851                seen_parents.push((name.clone(), parent_span));
852            }
853        }
854
855        parents.push(ParentInterface {
856            annotation,
857            ty: Type::uninferred(),
858            span: parent_span,
859            impl_keyword_span,
860        });
861        self.advance_if(Semicolon);
862    }
863}