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(); 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 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 = ¶ms[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(); 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.error_impl_interface_embed(keyword_span);
793 self.parse_interface_parent(item_doc, &mut seen_parents, &mut parents);
794 }
795
796 Identifier if self.current_token().text == "embed" => {
797 self.next();
798 self.parse_interface_parent(item_doc, &mut seen_parents, &mut parents);
799 }
800
801 _ => {
802 if let Some((_, span)) = item_doc {
803 self.error_detached_doc_comment(span);
804 }
805 self.track_error(
806 "expected `fn` or `embed`",
807 "Only method signatures (`fn`) and embeddings (`embed`) are allowed in interfaces.",
808 );
809 self.next();
810 }
811 }
812 }
813
814 self.ensure(RightCurlyBrace);
815
816 Expression::Interface {
817 doc,
818 name,
819 name_span,
820 generics,
821 parents,
822 method_signatures,
823 visibility: Visibility::Private,
824 span: self.span_from_tokens(start),
825 }
826 }
827
828 fn parse_interface_parent(
829 &mut self,
830 item_doc: Option<(std::string::String, Span)>,
831 seen_parents: &mut Vec<(EcoString, Span)>,
832 parents: &mut Vec<ParentInterface>,
833 ) {
834 if let Some((_, span)) = item_doc {
835 self.error_detached_doc_comment(span);
836 }
837
838 let parent_start = self.current_token();
839 let annotation = self.parse_annotation();
840 let parent_span = self.span_from_tokens(parent_start);
841
842 if let Annotation::Constructor { name, .. } = &annotation {
843 if let Some((_, first_span)) = seen_parents.iter().find(|(n, _)| n == name.as_str()) {
844 self.error_duplicate_embed_parent(*first_span, parent_span);
845 } else {
846 seen_parents.push((name.clone(), parent_span));
847 }
848 }
849
850 parents.push(ParentInterface {
851 annotation,
852 ty: Type::uninferred(),
853 span: parent_span,
854 });
855 self.advance_if(Semicolon);
856 }
857}