use ecow::EcoString;
use super::Parser;
use crate::ast::{
Annotation, Attribute, AttributeArg, EnumFieldDefinition, EnumVariant, Expression, Generic,
ParentInterface, Span, StructFieldDefinition, StructKind, VariantFields, Visibility,
};
use crate::lex::Token;
use crate::lex::TokenKind::*;
use crate::parse::error::ParseError;
use crate::types::Type;
impl<'source> Parser<'source> {
pub(crate) fn parse_attributes(&mut self) -> Vec<Attribute> {
let mut attributes = vec![];
loop {
self.advance_if(Semicolon);
if !self.is(Hash) {
break;
}
if let Some(attribute) = self.parse_attribute() {
attributes.push(attribute);
}
}
attributes
}
fn parse_attribute(&mut self) -> Option<Attribute> {
let start = self.current_token();
self.ensure(Hash);
if !self.is(LeftSquareBracket) {
self.track_error("expected `[` after `#`", "Add `[` to start the attribute");
return None;
}
self.next();
if !self.is(Identifier) {
self.track_error(
"expected attribute name",
"Add an attribute name like `json` or `db`",
);
while self.is_not(RightSquareBracket) && !self.at_eof() {
self.next();
}
self.advance_if(RightSquareBracket);
return None;
}
let name = self.read_identifier();
let args = if self.advance_if(LeftParen) {
self.parse_attribute_args()
} else {
vec![]
};
if !self.advance_if(RightSquareBracket) {
self.track_error("expected `]`", "Add `]` to close the attribute");
}
Some(Attribute {
name: name.to_string(),
args,
span: self.span_from_tokens(start),
})
}
fn parse_attribute_args(&mut self) -> Vec<AttributeArg> {
let mut args = vec![];
while self.is_not(RightParen) && !self.at_eof() {
if let Some(arg) = self.parse_attribute_arg() {
args.push(arg);
}
if !self.advance_if(Comma) {
break;
}
}
self.ensure(RightParen);
args
}
fn parse_attribute_arg(&mut self) -> Option<AttributeArg> {
if self.advance_if(Bang) {
if self.is(Identifier) {
return Some(AttributeArg::NegatedFlag(
self.read_identifier().to_string(),
));
} else {
self.track_error(
"expected identifier after `!`",
"Add an identifier like `omitempty` after `!`",
);
return None;
}
}
if self.is(Identifier) {
return Some(AttributeArg::Flag(self.read_identifier().to_string()));
}
if self.is(String) {
let token = self.current_token();
self.next();
let text = token.text;
let value = if text.len() >= 2 {
&text[1..text.len() - 1]
} else {
text
};
return Some(AttributeArg::String(value.to_string()));
}
if self.is(Backtick) {
let token = self.current_token();
self.next();
let text = token.text;
let value = if text.len() >= 2 {
&text[1..text.len() - 1]
} else {
text
};
return Some(AttributeArg::Raw(value.to_string()));
}
self.track_error(
"expected attribute argument",
"Add a flag (e.g. `omitempty`), string (e.g. `\"name\"`), or raw tag (e.g. `` `json:\"name\"` ``)",
);
None
}
pub fn parse_enum_definition(
&mut self,
doc: Option<std::string::String>,
attributes: Vec<Attribute>,
) -> Expression {
let start = self.current_token();
self.ensure(Enum);
let name_token = self.current_token();
let name_span = Span::new(self.file_id, name_token.byte_offset, name_token.byte_length);
let name = self.read_identifier();
let generics = self.parse_generics();
let underlying_start = self.current_token();
if self.advance_if(Colon) {
let _ = self.parse_annotation();
let underlying_span = Span::new(
self.file_id,
underlying_start.byte_offset,
underlying_start.byte_length,
);
let error = ParseError::new(
"Enum with underlying type",
underlying_span,
"enums cannot have an underlying type",
)
.with_parse_code("enum_underlying_type")
.with_help(
"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.",
);
self.errors.push(error);
}
self.ensure(LeftCurlyBrace);
self.parse_regular_enum_body(doc, attributes, name, name_span, generics, start)
}
fn parse_regular_enum_body(
&mut self,
doc: Option<std::string::String>,
attributes: Vec<Attribute>,
name: EcoString,
name_span: Span,
generics: Vec<Generic>,
start: Token<'source>,
) -> Expression {
let mut variants = vec![];
let mut seen_variants: Vec<(EcoString, Span)> = vec![];
while self.is_not(RightCurlyBrace) {
let start_position = self.stream.position;
let variant_doc = self.collect_doc_comments().map(|(text, _)| text);
if let Some(variant) = self.parse_enum_variant_with_doc(variant_doc) {
if let Some((_, first_span)) =
seen_variants.iter().find(|(n, _)| n == &variant.name)
{
self.error_duplicate_enum_variant(
&variant.name,
*first_span,
variant.name_span,
);
} else {
seen_variants.push((variant.name.clone(), variant.name_span));
}
variants.push(variant);
}
self.expect_comma_or(RightCurlyBrace);
self.ensure_progress(start_position, RightCurlyBrace);
}
self.ensure(RightCurlyBrace);
Expression::Enum {
doc,
attributes,
name,
name_span,
generics,
variants,
visibility: Visibility::Private,
span: self.span_from_tokens(start),
}
}
fn parse_enum_variant_with_doc(
&mut self,
doc: Option<std::string::String>,
) -> Option<EnumVariant> {
if self.is_not(Identifier) {
self.track_error(
"expected variant name",
"Variant names must be identifiers.",
);
return None;
}
let name_token = self.current_token();
let name_span = Span::new(self.file_id, name_token.byte_offset, name_token.byte_length);
let name = self.read_identifier();
if self.is(Equal) {
let eq_token = self.current_token();
let eq_span = Span::new(self.file_id, eq_token.byte_offset, eq_token.byte_length);
let error = ParseError::new(
"Assigned enum variant",
eq_span,
"enum variants cannot have assigned values",
)
.with_parse_code("enum_assigned_variant")
.with_help(
"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.",
);
self.errors.push(error);
self.next(); self.skip_assigned_variant_value();
}
let fields = self.parse_enum_variant_fields();
Some(EnumVariant {
doc,
name,
name_span,
fields,
})
}
fn skip_assigned_variant_value(&mut self) {
self.advance_if(Minus);
if self.is(Integer) || self.is(String) {
self.next();
}
}
fn parse_enum_variant_fields(&mut self) -> VariantFields {
if self.advance_if(LeftParen) {
return self.parse_tuple_variant_fields();
}
if self.advance_if(LeftCurlyBrace) {
return self.parse_struct_variant_fields();
}
VariantFields::Unit
}
fn parse_tuple_variant_fields(&mut self) -> VariantFields {
let mut fields = vec![];
loop {
if self.at_eof()
|| self.is(RightParen)
|| self.is(RightCurlyBrace)
|| !self.can_start_annotation()
{
break;
}
let field = EnumFieldDefinition {
name: format!("field{}", fields.len()).into(),
name_span: Span::dummy(),
annotation: self.parse_annotation(),
ty: Type::uninferred(),
};
fields.push(field);
self.expect_comma_or(RightParen);
}
self.ensure(RightParen);
VariantFields::Tuple(fields)
}
fn parse_struct_variant_fields(&mut self) -> VariantFields {
let mut fields = vec![];
let mut seen_fields: Vec<(EcoString, Span)> = vec![];
loop {
if self.at_eof()
|| self.is(RightCurlyBrace)
|| self.at_item_boundary()
|| self.is_not(Identifier)
{
break;
}
let name_token = self.current_token();
let name_span = Span::new(self.file_id, name_token.byte_offset, name_token.byte_length);
let name = self.read_identifier();
self.ensure(Colon);
let annotation = self.parse_annotation();
if let Some((_, first_span)) = seen_fields.iter().find(|(n, _)| n == &name) {
self.error_duplicate_struct_field(&name, *first_span, name_span);
} else {
seen_fields.push((name.clone(), name_span));
}
let field = EnumFieldDefinition {
name,
name_span,
annotation,
ty: Type::uninferred(),
};
fields.push(field);
self.expect_comma_or(RightCurlyBrace);
}
self.ensure(RightCurlyBrace);
VariantFields::Struct(fields)
}
pub fn parse_struct_definition(
&mut self,
doc: Option<std::string::String>,
attributes: Vec<Attribute>,
) -> Expression {
let start = self.current_token();
self.ensure(Struct);
let name_token = self.current_token();
let name_span = Span::new(self.file_id, name_token.byte_offset, name_token.byte_length);
let name = self.read_identifier();
let generics = self.parse_generics();
if self.is(LeftParen) {
return self.parse_tuple_struct(doc, attributes, name, name_span, generics, start);
}
self.parse_named_struct(doc, attributes, name, name_span, generics, start)
}
fn parse_named_struct(
&mut self,
doc: Option<std::string::String>,
attributes: Vec<Attribute>,
name: EcoString,
name_span: Span,
generics: Vec<Generic>,
start: Token<'source>,
) -> Expression {
let mut fields = vec![];
let mut seen_fields: Vec<(EcoString, Span)> = vec![];
self.ensure(LeftCurlyBrace);
while self.is_not(RightCurlyBrace) {
let start_position = self.stream.position;
let field_attributes = self.parse_attributes();
let field_doc = self.collect_doc_comments().map(|(text, _)| text);
if let Some(field) = self.parse_struct_field_with_doc(field_doc, field_attributes) {
if let Some((_, first_span)) = seen_fields.iter().find(|(n, _)| n == &field.name) {
self.error_duplicate_struct_field(&field.name, *first_span, field.name_span);
} else {
seen_fields.push((field.name.clone(), field.name_span));
}
fields.push(field);
}
self.expect_comma_or(RightCurlyBrace);
self.ensure_progress(start_position, RightCurlyBrace);
}
self.ensure(RightCurlyBrace);
Expression::Struct {
doc,
attributes,
name,
name_span,
generics,
fields,
kind: StructKind::Record,
visibility: Visibility::Private,
span: self.span_from_tokens(start),
}
}
fn parse_tuple_struct(
&mut self,
doc: Option<std::string::String>,
attributes: Vec<Attribute>,
name: EcoString,
name_span: Span,
generics: Vec<Generic>,
start: Token<'source>,
) -> Expression {
self.ensure(LeftParen);
let mut fields = vec![];
let mut index = 0;
while self.is_not(RightParen) {
if self.at_eof() || self.at_item_boundary() || !self.can_start_annotation() {
break;
}
let field_start = self.current_token();
let annotation = self.parse_annotation();
let field_span = self.span_from_tokens(field_start);
fields.push(StructFieldDefinition {
doc: None,
attributes: vec![],
name: format!("_{}", index).into(),
name_span: field_span,
annotation,
visibility: Visibility::Private,
ty: Type::uninferred(),
embedded: false,
});
index += 1;
self.expect_comma_or(RightParen);
}
self.ensure(RightParen);
Expression::Struct {
doc,
attributes,
name,
name_span,
generics,
fields,
kind: StructKind::Tuple,
visibility: Visibility::Private,
span: self.span_from_tokens(start),
}
}
fn parse_struct_field_with_doc(
&mut self,
doc: Option<std::string::String>,
attributes: Vec<Attribute>,
) -> Option<StructFieldDefinition> {
let visibility = if self.advance_if(Pub) {
Visibility::Public
} else {
Visibility::Private
};
if self.is(Mut) {
self.track_error(
"fields cannot be marked `mut`",
"Fields cannot be marked `mut`; mutability applies to bindings (`let mut x = ...`).",
);
self.next();
}
if self.is(Identifier)
&& self.current_token().text == "embed"
&& self.stream.peek_ahead(1).kind != Colon
{
if visibility.is_public() {
self.track_error(
"embedded field cannot be `pub`",
"An embedded field takes no `pub`; its visibility derives from the embedded type.",
);
}
return self.parse_embedded_field(doc, attributes);
}
if self.is_not(Identifier) {
self.track_error("expected field name", "Field names must be identifiers.");
return None;
}
let name_token = self.current_token();
let name_span = Span::new(self.file_id, name_token.byte_offset, name_token.byte_length);
let name = self.read_identifier();
self.ensure(Colon);
Some(StructFieldDefinition {
doc,
attributes,
visibility,
name,
name_span,
annotation: self.parse_annotation(),
ty: Type::uninferred(),
embedded: false,
})
}
fn parse_embedded_field(
&mut self,
doc: Option<std::string::String>,
attributes: Vec<Attribute>,
) -> Option<StructFieldDefinition> {
let start = self.current_token();
self.ensure(Identifier);
let annotation = self.parse_annotation();
let span = self.span_from_tokens(start);
let Some(name) = Self::embedded_field_name(&annotation) else {
self.track_error_at(
span,
"expected a named type after `embed`",
"`embed` requires a named type; a function or tuple type cannot be embedded.",
);
return None;
};
if let Some(attribute) = attributes.first() {
self.track_error_at(
attribute.span,
"embedded field cannot have attributes",
"An embedded field takes no attributes; its serialization follows Go's anonymous-field inlining.",
);
}
Some(StructFieldDefinition {
doc,
attributes: vec![],
visibility: Visibility::Private,
name,
name_span: span,
annotation,
ty: Type::uninferred(),
embedded: true,
})
}
fn embedded_field_name(annotation: &Annotation) -> Option<EcoString> {
let mut current = annotation;
loop {
let Annotation::Constructor { name, params, .. } = current else {
return None;
};
let segment = name.rsplit('.').next().unwrap_or(name);
if (segment == "Option" || segment == "Ref") && params.len() == 1 {
current = ¶ms[0];
continue;
}
return Some(segment.into());
}
}
pub fn parse_const_definition(&mut self, doc: Option<std::string::String>) -> Expression {
let start = self.current_token();
self.ensure(Const);
let identifier_token = self.current_token();
let identifier_span = Span::new(
self.file_id,
identifier_token.byte_offset,
identifier_token.byte_length,
);
let identifier = self.read_identifier();
let annotation = if self.advance_if(Colon) {
Some(self.parse_annotation())
} else {
None
};
let expression = if self.advance_if(Equal) {
self.parse_expression()
} else {
Expression::NoOp
};
Expression::Const {
doc,
identifier,
identifier_span,
annotation,
expression: expression.into(),
visibility: Visibility::Private,
ty: Type::uninferred(),
span: self.span_from_tokens(start),
}
}
pub fn parse_var_declaration(&mut self, doc: Option<std::string::String>) -> Expression {
let start = self.current_token();
self.ensure(Var);
let name_token = self.current_token();
let name_span = Span::new(self.file_id, name_token.byte_offset, name_token.byte_length);
let name = self.read_identifier();
if self.advance_if(Equal) {
return self.recover_var_initializer(start);
}
self.ensure(Colon);
let annotation = self.parse_annotation();
if self.advance_if(Equal) {
return self.recover_var_initializer(start);
}
Expression::VariableDeclaration {
doc,
name,
name_span,
annotation,
visibility: Visibility::Private,
ty: Type::uninferred(),
span: self.span_from_tokens(start),
}
}
fn recover_var_initializer(&mut self, start: Token<'source>) -> Expression {
self.parse_expression();
self.error_var_initializer(self.span_from_token(start));
Expression::Unit {
ty: Type::uninferred(),
span: self.span_from_tokens(start),
}
}
pub fn parse_impl_block(&mut self) -> Expression {
let start = self.current_token();
self.ensure(Impl);
let generics = self.parse_generics();
let receiver = self.parse_annotation();
let (receiver_name, annotation) = match &receiver {
Annotation::Constructor { name, .. } => (name.clone(), receiver),
_ => {
self.track_error("expected `impl` receiver", "Use `impl TypeName { ... }`.");
("".into(), Annotation::Unknown)
}
};
if self.is(For) {
self.track_error(
"invalid syntax",
"Lisette types satisfy interfaces automatically by having the required methods. Use `impl Type { ... }` to add methods.",
);
self.next();
self.parse_annotation();
}
let mut methods = vec![];
self.ensure(LeftCurlyBrace);
while self.is_not(RightCurlyBrace) {
self.advance_if(Semicolon);
if self.is(RightCurlyBrace) {
break;
}
let method_doc = self.collect_doc_comments();
let method_attrs = self.parse_attributes();
let is_public = self.advance_if(Pub);
if self.is(Function) {
let method = self.parse_function(method_doc.map(|(text, _)| text), method_attrs);
let method = if is_public {
method.set_public()
} else {
method
};
methods.push(method);
} else {
if let Some((_, span)) = method_doc {
self.error_detached_doc_comment(span);
}
self.track_error(
"expected `fn` in impl block",
"Only functions are allowed in `impl` blocks.",
);
self.next();
}
}
self.ensure(RightCurlyBrace);
Expression::ImplBlock {
annotation,
methods,
receiver_name,
generics,
ty: Type::uninferred(),
span: self.span_from_tokens(start),
}
}
pub fn parse_interface_definition(&mut self, doc: Option<std::string::String>) -> Expression {
let start = self.current_token();
self.ensure(Interface);
let name_token = self.current_token();
let name_span = Span::new(self.file_id, name_token.byte_offset, name_token.byte_length);
let name = self.read_identifier();
let generics = self.parse_generics();
let mut parents = vec![];
let mut seen_parents: Vec<(EcoString, Span)> = vec![];
let mut method_signatures = vec![];
let mut seen_methods: Vec<(EcoString, Span)> = vec![];
self.ensure(LeftCurlyBrace);
while self.is_not(RightCurlyBrace) {
self.advance_if(Semicolon);
if self.is(RightCurlyBrace) {
break;
}
let item_doc = self.collect_doc_comments();
let method_attrs = self.parse_attributes();
if !self.is(Function)
&& let Some(attribute) = method_attrs.first()
{
self.error_misplaced_attribute(attribute.span);
}
match self.current_token().kind {
Function => {
let method =
self.parse_interface_method(item_doc.map(|(text, _)| text), method_attrs);
if let Expression::Function {
ref name,
ref name_span,
..
} = method
{
if let Some((_, first_span)) = seen_methods.iter().find(|(n, _)| n == name)
{
self.error_duplicate_interface_method(name, *first_span, *name_span);
} else {
seen_methods.push((name.clone(), *name_span));
}
}
method_signatures.push(method);
self.advance_if(Semicolon);
}
Impl => {
let keyword = self.current_token();
let keyword_span =
Span::new(self.file_id, keyword.byte_offset, keyword.byte_length);
self.ensure(Impl);
self.error_impl_interface_embed(keyword_span);
self.parse_interface_parent(item_doc, &mut seen_parents, &mut parents);
}
Identifier if self.current_token().text == "embed" => {
self.next();
self.parse_interface_parent(item_doc, &mut seen_parents, &mut parents);
}
_ => {
if let Some((_, span)) = item_doc {
self.error_detached_doc_comment(span);
}
self.track_error(
"expected `fn` or `embed`",
"Only method signatures (`fn`) and embeddings (`embed`) are allowed in interfaces.",
);
self.next();
}
}
}
self.ensure(RightCurlyBrace);
Expression::Interface {
doc,
name,
name_span,
generics,
parents,
method_signatures,
visibility: Visibility::Private,
span: self.span_from_tokens(start),
}
}
fn parse_interface_parent(
&mut self,
item_doc: Option<(std::string::String, Span)>,
seen_parents: &mut Vec<(EcoString, Span)>,
parents: &mut Vec<ParentInterface>,
) {
if let Some((_, span)) = item_doc {
self.error_detached_doc_comment(span);
}
let parent_start = self.current_token();
let annotation = self.parse_annotation();
let parent_span = self.span_from_tokens(parent_start);
if let Annotation::Constructor { name, .. } = &annotation {
if let Some((_, first_span)) = seen_parents.iter().find(|(n, _)| n == name.as_str()) {
self.error_duplicate_embed_parent(*first_span, parent_span);
} else {
seen_parents.push((name.clone(), parent_span));
}
}
parents.push(ParentInterface {
annotation,
ty: Type::uninferred(),
span: parent_span,
});
self.advance_if(Semicolon);
}
}