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            });
454
455            index += 1;
456            self.expect_comma_or(RightParen);
457        }
458
459        self.ensure(RightParen);
460
461        Expression::Struct {
462            doc,
463            attributes,
464            name,
465            name_span,
466            generics,
467            fields,
468            kind: StructKind::Tuple,
469            visibility: Visibility::Private,
470            span: self.span_from_tokens(start),
471        }
472    }
473
474    fn parse_struct_field_with_doc(
475        &mut self,
476        doc: Option<std::string::String>,
477        attributes: Vec<Attribute>,
478    ) -> Option<StructFieldDefinition> {
479        let visibility = if self.advance_if(Pub) {
480            Visibility::Public
481        } else {
482            Visibility::Private
483        };
484
485        if self.is(Mut) {
486            self.track_error(
487                "fields cannot be marked `mut`",
488                "Fields cannot be marked `mut`; mutability applies to bindings (`let mut x = ...`).",
489            );
490            self.next();
491        }
492
493        if self.is_not(Identifier) {
494            self.track_error("expected field name", "Field names must be identifiers.");
495            return None;
496        }
497
498        let name_token = self.current_token();
499        let name_span = Span::new(self.file_id, name_token.byte_offset, name_token.byte_length);
500        let name = self.read_identifier();
501
502        self.ensure(Colon);
503
504        Some(StructFieldDefinition {
505            doc,
506            attributes,
507            visibility,
508            name,
509            name_span,
510            annotation: self.parse_annotation(),
511            ty: Type::uninferred(),
512        })
513    }
514
515    pub fn parse_const_definition(&mut self, doc: Option<std::string::String>) -> Expression {
516        let start = self.current_token();
517
518        self.ensure(Const);
519
520        let identifier_token = self.current_token();
521        let identifier_span = Span::new(
522            self.file_id,
523            identifier_token.byte_offset,
524            identifier_token.byte_length,
525        );
526        let identifier = self.read_identifier();
527        let annotation = if self.advance_if(Colon) {
528            Some(self.parse_annotation())
529        } else {
530            None
531        };
532
533        let expression = if self.advance_if(Equal) {
534            self.parse_expression()
535        } else {
536            Expression::NoOp
537        };
538
539        Expression::Const {
540            doc,
541            identifier,
542            identifier_span,
543            annotation,
544            expression: expression.into(),
545            visibility: Visibility::Private,
546            ty: Type::uninferred(),
547            span: self.span_from_tokens(start),
548        }
549    }
550
551    pub fn parse_var_declaration(&mut self, doc: Option<std::string::String>) -> Expression {
552        let start = self.current_token();
553
554        self.ensure(Var);
555
556        let name_token = self.current_token();
557        let name_span = Span::new(self.file_id, name_token.byte_offset, name_token.byte_length);
558        let name = self.read_identifier();
559
560        self.ensure(Colon);
561        let annotation = self.parse_annotation();
562
563        Expression::VariableDeclaration {
564            doc,
565            name,
566            name_span,
567            annotation,
568            visibility: Visibility::Private,
569            ty: Type::uninferred(),
570            span: self.span_from_tokens(start),
571        }
572    }
573
574    pub fn parse_impl_block(&mut self) -> Expression {
575        let start = self.current_token();
576
577        self.ensure(Impl);
578
579        let generics = self.parse_generics();
580
581        let receiver = self.parse_annotation(); // e.g. List<T>
582
583        let (receiver_name, annotation) = match &receiver {
584            Annotation::Constructor { name, .. } => (name.clone(), receiver),
585            _ => {
586                self.track_error("expected `impl` receiver", "Use `impl TypeName { ... }`.");
587                ("".into(), Annotation::Unknown)
588            }
589        };
590
591        if self.is(For) {
592            self.track_error(
593                "invalid syntax",
594                "Lisette types satisfy interfaces automatically by having the required methods. Use `impl Type { ... }` to add methods.",
595            );
596            self.next();
597            self.parse_annotation();
598        }
599
600        let mut methods = vec![];
601
602        self.ensure(LeftCurlyBrace);
603
604        while self.is_not(RightCurlyBrace) {
605            self.advance_if(Semicolon);
606            if self.is(RightCurlyBrace) {
607                break;
608            }
609
610            let method_doc = self.collect_doc_comments();
611            let method_attrs = self.parse_attributes();
612            let is_public = self.advance_if(Pub);
613
614            if self.is(Function) {
615                let method = self.parse_function(method_doc.map(|(text, _)| text), method_attrs);
616                let method = if is_public {
617                    method.set_public()
618                } else {
619                    method
620                };
621                methods.push(method);
622            } else {
623                if let Some((_, span)) = method_doc {
624                    self.error_detached_doc_comment(span);
625                }
626                self.track_error(
627                    "expected `fn` in impl block",
628                    "Only functions are allowed in `impl` blocks.",
629                );
630                self.next();
631            }
632        }
633
634        self.ensure(RightCurlyBrace);
635
636        Expression::ImplBlock {
637            annotation,
638            methods,
639            receiver_name,
640            generics,
641            ty: Type::uninferred(),
642            span: self.span_from_tokens(start),
643        }
644    }
645
646    pub fn parse_interface_definition(&mut self, doc: Option<std::string::String>) -> Expression {
647        let start = self.current_token();
648
649        self.ensure(Interface);
650
651        let name_token = self.current_token();
652        let name_span = Span::new(self.file_id, name_token.byte_offset, name_token.byte_length);
653        let name = self.read_identifier();
654
655        let generics = self.parse_generics();
656
657        let mut parents = vec![];
658        let mut seen_parents: Vec<(EcoString, Span)> = vec![];
659        let mut method_signatures = vec![];
660        let mut seen_methods: Vec<(EcoString, Span)> = vec![];
661
662        self.ensure(LeftCurlyBrace);
663
664        while self.is_not(RightCurlyBrace) {
665            self.advance_if(Semicolon);
666            if self.is(RightCurlyBrace) {
667                break;
668            }
669
670            let item_doc = self.collect_doc_comments();
671            let method_attrs = self.parse_attributes();
672            if !self.is(Function)
673                && let Some(attribute) = method_attrs.first()
674            {
675                self.error_misplaced_attribute(attribute.span);
676            }
677            match self.current_token().kind {
678                Function => {
679                    let method =
680                        self.parse_interface_method(item_doc.map(|(text, _)| text), method_attrs);
681                    if let Expression::Function {
682                        ref name,
683                        ref name_span,
684                        ..
685                    } = method
686                    {
687                        if let Some((_, first_span)) = seen_methods.iter().find(|(n, _)| n == name)
688                        {
689                            self.error_duplicate_interface_method(name, *first_span, *name_span);
690                        } else {
691                            seen_methods.push((name.clone(), *name_span));
692                        }
693                    }
694                    method_signatures.push(method);
695                    self.advance_if(Semicolon);
696                }
697
698                Impl => {
699                    if let Some((_, span)) = item_doc {
700                        self.error_detached_doc_comment(span);
701                    }
702                    self.ensure(Impl);
703
704                    let parent_start = self.current_token();
705                    let annotation = self.parse_annotation();
706                    let parent_span = self.span_from_tokens(parent_start);
707
708                    if let Annotation::Constructor { name, .. } = &annotation {
709                        if let Some((_, first_span)) =
710                            seen_parents.iter().find(|(n, _)| n == name.as_str())
711                        {
712                            self.error_duplicate_impl_parent(*first_span, parent_span);
713                        } else {
714                            seen_parents.push((name.clone(), parent_span));
715                        }
716                    }
717
718                    parents.push(ParentInterface {
719                        annotation,
720                        ty: Type::uninferred(),
721                        span: parent_span,
722                    });
723                    self.advance_if(Semicolon);
724                }
725
726                _ => {
727                    if let Some((_, span)) = item_doc {
728                        self.error_detached_doc_comment(span);
729                    }
730                    self.track_error(
731                        "expected `fn` or `impl`",
732                        "Only functions and `impl` blocks are allowed in interfaces.",
733                    );
734                    self.next();
735                }
736            }
737        }
738
739        self.ensure(RightCurlyBrace);
740
741        Expression::Interface {
742            doc,
743            name,
744            name_span,
745            generics,
746            parents,
747            method_signatures,
748            visibility: Visibility::Private,
749            span: self.span_from_tokens(start),
750        }
751    }
752}