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 self.ensure(Colon);
632 let annotation = self.parse_annotation();
633
634 Expression::VariableDeclaration {
635 doc,
636 name,
637 name_span,
638 annotation,
639 visibility: Visibility::Private,
640 ty: Type::uninferred(),
641 span: self.span_from_tokens(start),
642 }
643 }
644
645 pub fn parse_impl_block(&mut self) -> Expression {
646 let start = self.current_token();
647
648 self.ensure(Impl);
649
650 let generics = self.parse_generics();
651
652 let receiver = self.parse_annotation(); let (receiver_name, annotation) = match &receiver {
655 Annotation::Constructor { name, .. } => (name.clone(), receiver),
656 _ => {
657 self.track_error("expected `impl` receiver", "Use `impl TypeName { ... }`.");
658 ("".into(), Annotation::Unknown)
659 }
660 };
661
662 if self.is(For) {
663 self.track_error(
664 "invalid syntax",
665 "Lisette types satisfy interfaces automatically by having the required methods. Use `impl Type { ... }` to add methods.",
666 );
667 self.next();
668 self.parse_annotation();
669 }
670
671 let mut methods = vec![];
672
673 self.ensure(LeftCurlyBrace);
674
675 while self.is_not(RightCurlyBrace) {
676 self.advance_if(Semicolon);
677 if self.is(RightCurlyBrace) {
678 break;
679 }
680
681 let method_doc = self.collect_doc_comments();
682 let method_attrs = self.parse_attributes();
683 let is_public = self.advance_if(Pub);
684
685 if self.is(Function) {
686 let method = self.parse_function(method_doc.map(|(text, _)| text), method_attrs);
687 let method = if is_public {
688 method.set_public()
689 } else {
690 method
691 };
692 methods.push(method);
693 } else {
694 if let Some((_, span)) = method_doc {
695 self.error_detached_doc_comment(span);
696 }
697 self.track_error(
698 "expected `fn` in impl block",
699 "Only functions are allowed in `impl` blocks.",
700 );
701 self.next();
702 }
703 }
704
705 self.ensure(RightCurlyBrace);
706
707 Expression::ImplBlock {
708 annotation,
709 methods,
710 receiver_name,
711 generics,
712 ty: Type::uninferred(),
713 span: self.span_from_tokens(start),
714 }
715 }
716
717 pub fn parse_interface_definition(&mut self, doc: Option<std::string::String>) -> Expression {
718 let start = self.current_token();
719
720 self.ensure(Interface);
721
722 let name_token = self.current_token();
723 let name_span = Span::new(self.file_id, name_token.byte_offset, name_token.byte_length);
724 let name = self.read_identifier();
725
726 let generics = self.parse_generics();
727
728 let mut parents = vec![];
729 let mut seen_parents: Vec<(EcoString, Span)> = vec![];
730 let mut method_signatures = vec![];
731 let mut seen_methods: Vec<(EcoString, Span)> = vec![];
732
733 self.ensure(LeftCurlyBrace);
734
735 while self.is_not(RightCurlyBrace) {
736 self.advance_if(Semicolon);
737 if self.is(RightCurlyBrace) {
738 break;
739 }
740
741 let item_doc = self.collect_doc_comments();
742 let method_attrs = self.parse_attributes();
743 if !self.is(Function)
744 && let Some(attribute) = method_attrs.first()
745 {
746 self.error_misplaced_attribute(attribute.span);
747 }
748 match self.current_token().kind {
749 Function => {
750 let method =
751 self.parse_interface_method(item_doc.map(|(text, _)| text), method_attrs);
752 if let Expression::Function {
753 ref name,
754 ref name_span,
755 ..
756 } = method
757 {
758 if let Some((_, first_span)) = seen_methods.iter().find(|(n, _)| n == name)
759 {
760 self.error_duplicate_interface_method(name, *first_span, *name_span);
761 } else {
762 seen_methods.push((name.clone(), *name_span));
763 }
764 }
765 method_signatures.push(method);
766 self.advance_if(Semicolon);
767 }
768
769 Impl => {
770 let keyword = self.current_token();
771 let keyword_span =
772 Span::new(self.file_id, keyword.byte_offset, keyword.byte_length);
773 self.ensure(Impl);
774 self.parse_interface_parent(
775 item_doc,
776 Some(keyword_span),
777 &mut seen_parents,
778 &mut parents,
779 );
780 }
781
782 Identifier if self.current_token().text == "embed" => {
783 self.next();
784 self.parse_interface_parent(item_doc, None, &mut seen_parents, &mut parents);
785 }
786
787 _ => {
788 if let Some((_, span)) = item_doc {
789 self.error_detached_doc_comment(span);
790 }
791 self.track_error(
792 "expected `fn` or `embed`",
793 "Only method signatures (`fn`) and embeddings (`embed`) are allowed in interfaces.",
794 );
795 self.next();
796 }
797 }
798 }
799
800 self.ensure(RightCurlyBrace);
801
802 Expression::Interface {
803 doc,
804 name,
805 name_span,
806 generics,
807 parents,
808 method_signatures,
809 visibility: Visibility::Private,
810 span: self.span_from_tokens(start),
811 }
812 }
813
814 fn parse_interface_parent(
815 &mut self,
816 item_doc: Option<(std::string::String, Span)>,
817 impl_keyword_span: Option<Span>,
818 seen_parents: &mut Vec<(EcoString, Span)>,
819 parents: &mut Vec<ParentInterface>,
820 ) {
821 if let Some((_, span)) = item_doc {
822 self.error_detached_doc_comment(span);
823 }
824
825 let parent_start = self.current_token();
826 let annotation = self.parse_annotation();
827 let parent_span = self.span_from_tokens(parent_start);
828
829 if let Annotation::Constructor { name, .. } = &annotation {
830 if let Some((_, first_span)) = seen_parents.iter().find(|(n, _)| n == name.as_str()) {
831 self.error_duplicate_impl_parent(*first_span, parent_span);
832 } else {
833 seen_parents.push((name.clone(), parent_span));
834 }
835 }
836
837 parents.push(ParentInterface {
838 annotation,
839 ty: Type::uninferred(),
840 span: parent_span,
841 impl_keyword_span,
842 });
843 self.advance_if(Semicolon);
844 }
845}