use alloc::{borrow::Cow, boxed::Box, vec::Vec};
use super::{AstParser, core::ParserCore, tuple::TupleParser};
use crate::{
ast::{
AnonStructExpr, BoolExpr, ErrorExpr, Expr, FieldsBody, Ident, NumberExpr, NumberKind,
OptionExpr, OptionValue, StructBody, StructExpr, StructField, Trivia, TupleBody,
},
error::{Error, Span},
token::{Token, TokenKind},
};
pub(super) trait StructFieldParser<'a>: ParserCore<'a> {
fn parse_fields_body(
&mut self,
open_paren: Token<'a>,
leading: Trivia<'a>,
errors: &mut Vec<Error>,
) -> Expr<'a>;
fn parse_ident_expr(&mut self, errors: &mut Vec<Error>) -> Expr<'a>;
fn parse_some(&mut self, some_tok: Token<'a>, errors: &mut Vec<Error>) -> Expr<'a>;
fn parse_struct_or_variant(
&mut self,
name_tok: &Token<'a>,
errors: &mut Vec<Error>,
) -> Expr<'a>;
fn parse_struct_body_contents(
&mut self,
open_paren: Token<'a>,
leading: Trivia<'a>,
errors: &mut Vec<Error>,
) -> StructBody<'a>;
fn ident_token_to_expr(
&mut self,
tok: Token<'a>,
pre_body: Trivia<'a>,
errors: &mut Vec<Error>,
) -> Expr<'a>;
fn parse_struct_fields_from_first(
&mut self,
leading: Trivia<'a>,
first_name: &Token<'a>,
pre_colon: Trivia<'a>,
errors: &mut Vec<Error>,
) -> (Vec<StructField<'a>>, Trivia<'a>);
fn make_error_field(
&mut self,
error_span: Span,
leading: Trivia<'a>,
errors: &mut Vec<Error>,
) -> (StructField<'a>, Trivia<'a>, bool);
}
impl<'a> StructFieldParser<'a> for AstParser<'a> {
#[allow(clippy::too_many_lines)]
fn parse_fields_body(
&mut self,
open_paren: Token<'a>,
mut leading: Trivia<'a>,
errors: &mut Vec<Error>,
) -> Expr<'a> {
let mut fields = Vec::new();
loop {
match self.peek_kind() {
TokenKind::RParen | TokenKind::Eof => break,
TokenKind::Ident => {}
_ => {
let error_span = self.peek_span();
let (field, new_leading, has_comma) =
self.make_error_field(error_span, leading, errors);
fields.push(field);
leading = new_leading;
if !has_comma {
break;
}
continue;
}
}
let name_tok = self.next_token();
let pre_colon = self.collect_leading_trivia();
let colon = if self.peek_kind() == TokenKind::Colon {
self.next_token().span
} else {
errors.push(Self::error(
name_tok.span,
Self::expected("`:` after map key", Some("struct field")),
));
Self::span_at_end(&name_tok.span)
};
let post_colon = self.collect_leading_trivia();
let value = match self.peek_kind() {
TokenKind::Comma | TokenKind::RParen => {
let error_span = Self::span_at_end(&colon);
let error_kind = Self::expected("value", Some("struct field"));
errors.push(Self::error(error_span, error_kind.clone()));
Expr::Error(ErrorExpr {
span: error_span,
error: Error::with_span(error_kind, error_span),
})
}
_ => self.parse_expr_lossy(errors),
};
let trailing = self.collect_leading_trivia();
let (comma, has_comma) = self.consume_comma();
fields.push(StructField {
leading,
name: Ident {
span: name_tok.span,
name: Cow::Borrowed(name_tok.text),
},
pre_colon,
colon,
post_colon,
value,
trailing,
comma,
});
leading = self.collect_leading_trivia();
if !has_comma {
if matches!(self.peek_kind(), TokenKind::Ident) {
errors.push(Self::error(
self.peek_span(),
Self::expected("comma", Some("struct")),
));
} else {
break;
}
}
}
let trailing = if fields.is_empty() {
leading
} else {
self.collect_leading_trivia()
};
let close_paren = self.consume_closing(
TokenKind::RParen,
Self::expected("closing `)` or `}`", Some("struct")),
errors,
);
Expr::AnonStruct(AnonStructExpr {
span: Span::between(&open_paren.span, &close_paren),
open_paren: open_paren.span,
leading: Trivia::empty(),
fields,
trailing,
close_paren,
})
}
fn parse_ident_expr(&mut self, errors: &mut Vec<Error>) -> Expr<'a> {
let ident_tok = self.next_token();
debug_assert_eq!(ident_tok.kind, TokenKind::Ident);
match ident_tok.text {
"true" => Expr::Bool(BoolExpr {
span: ident_tok.span,
value: true,
}),
"false" => Expr::Bool(BoolExpr {
span: ident_tok.span,
value: false,
}),
"None" => Expr::Option(Box::new(OptionExpr {
span: ident_tok.span,
value: None,
})),
"Some" => self.parse_some(ident_tok, errors),
"inf" | "NaN" => Expr::Number(NumberExpr {
span: ident_tok.span,
raw: Cow::Borrowed(ident_tok.text),
kind: NumberKind::SpecialFloat,
}),
_ => self.parse_struct_or_variant(&ident_tok, errors),
}
}
fn parse_some(&mut self, some_tok: Token<'a>, errors: &mut Vec<Error>) -> Expr<'a> {
if self.peek_kind() != TokenKind::LParen {
let err = Self::error(some_tok.span, Self::expected("`Some` or `None`", None));
return self.error_expr_from(err, errors);
}
let open_paren = self.next_token();
let leading = self.collect_leading_trivia();
let expr = self.parse_expr_lossy(errors);
let trailing = self.collect_leading_trivia();
let close_paren = self.consume_closing(
TokenKind::RParen,
Self::expected("closing `)`", Some("option")),
errors,
);
Expr::Option(Box::new(OptionExpr {
span: Span::between(&some_tok.span, &close_paren),
value: Some(OptionValue {
open_paren: open_paren.span,
leading,
expr,
trailing,
close_paren,
}),
}))
}
fn parse_struct_or_variant(
&mut self,
name_tok: &Token<'a>,
errors: &mut Vec<Error>,
) -> Expr<'a> {
let name = Ident {
span: name_tok.span,
name: Cow::Borrowed(name_tok.text),
};
let pre_body = self.collect_leading_trivia();
let body = match self.peek_kind() {
TokenKind::LParen => {
let open_paren = self.next_token();
let leading = self.collect_leading_trivia();
match self.try_parse_empty_tuple_body(&open_paren.span, leading) {
Ok(tuple_body) => Some(StructBody::Tuple(tuple_body)),
Err(leading) => {
Some(self.parse_struct_body_contents(open_paren, leading, errors))
}
}
}
_ => None,
};
let span = if let Some(ref b) = body {
let end_span = match b {
StructBody::Tuple(t) => &t.close_paren,
StructBody::Fields(f) => &f.close_brace,
};
Span::between(&name_tok.span, end_span)
} else {
name_tok.span
};
Expr::Struct(StructExpr {
span,
name,
pre_body,
body,
})
}
fn parse_struct_body_contents(
&mut self,
open_paren: Token<'a>,
leading: Trivia<'a>,
errors: &mut Vec<Error>,
) -> StructBody<'a> {
if self.peek_kind() == TokenKind::Ident {
let first_tok = self.next_token();
let post_ident_trivia = self.collect_leading_trivia();
if self.peek_kind() == TokenKind::Colon {
let (fields, trailing) = self.parse_struct_fields_from_first(
leading,
&first_tok,
post_ident_trivia,
errors,
);
let close_paren = self.consume_closing(
TokenKind::RParen,
Self::expected("closing `)` or `}`", Some("struct")),
errors,
);
StructBody::Fields(FieldsBody {
open_brace: open_paren.span,
leading: Trivia::empty(),
fields,
trailing,
close_brace: close_paren,
})
} else {
let first_expr = self.ident_token_to_expr(first_tok, post_ident_trivia, errors);
let (elements, trailing) =
self.parse_tuple_elements_from_first(leading, first_expr, errors);
let close_paren = self.consume_closing(
TokenKind::RParen,
Self::expected("closing `)` or `}`", Some("struct")),
errors,
);
StructBody::Tuple(TupleBody {
open_paren: open_paren.span,
leading: Trivia::empty(),
elements,
trailing,
close_paren,
})
}
} else {
let elements = self.parse_tuple_elements(leading, errors);
let trailing = self.collect_leading_trivia();
let close_paren = self.consume_closing(
TokenKind::RParen,
Self::expected("closing `)` or `}`", Some("struct")),
errors,
);
StructBody::Tuple(TupleBody {
open_paren: open_paren.span,
leading: Trivia::empty(),
elements,
trailing,
close_paren,
})
}
}
fn ident_token_to_expr(
&mut self,
tok: Token<'a>,
pre_body: Trivia<'a>,
errors: &mut Vec<Error>,
) -> Expr<'a> {
match tok.text {
"true" => Expr::Bool(BoolExpr {
span: tok.span,
value: true,
}),
"false" => Expr::Bool(BoolExpr {
span: tok.span,
value: false,
}),
"None" => Expr::Option(Box::new(OptionExpr {
span: tok.span,
value: None,
})),
"Some" => self.parse_some(tok, errors),
"inf" | "NaN" => Expr::Number(NumberExpr {
span: tok.span,
raw: Cow::Borrowed(tok.text),
kind: NumberKind::SpecialFloat,
}),
_ => {
let name = Ident {
span: tok.span,
name: Cow::Borrowed(tok.text),
};
let body = match self.peek_kind() {
TokenKind::LParen => {
let open_paren = self.next_token();
let leading = self.collect_leading_trivia();
match self.try_parse_empty_tuple_body(&open_paren.span, leading) {
Ok(tuple_body) => Some(StructBody::Tuple(tuple_body)),
Err(leading) => {
Some(self.parse_struct_body_contents(open_paren, leading, errors))
}
}
}
_ => None,
};
let span = if let Some(ref b) = body {
let end_span = match b {
StructBody::Tuple(t) => &t.close_paren,
StructBody::Fields(f) => &f.close_brace,
};
Span::between(&tok.span, end_span)
} else {
tok.span
};
Expr::Struct(StructExpr {
span,
name,
pre_body,
body,
})
}
}
}
#[allow(clippy::too_many_lines)]
fn parse_struct_fields_from_first(
&mut self,
leading: Trivia<'a>,
first_name: &Token<'a>,
pre_colon: Trivia<'a>,
errors: &mut Vec<Error>,
) -> (Vec<StructField<'a>>, Trivia<'a>) {
let mut fields = Vec::new();
let colon_tok = self.next_token();
debug_assert_eq!(colon_tok.kind, TokenKind::Colon);
let post_colon = self.collect_leading_trivia();
let value = self.parse_expr_inner(errors);
let trailing = self.collect_leading_trivia();
let (comma, has_comma) = self.consume_comma();
fields.push(StructField {
leading,
name: Ident {
span: first_name.span,
name: Cow::Borrowed(first_name.text),
},
pre_colon,
colon: colon_tok.span,
post_colon,
value,
trailing,
comma,
});
if !has_comma {
return (fields, self.collect_leading_trivia());
}
let mut leading = self.collect_leading_trivia();
loop {
match self.peek_kind() {
TokenKind::RParen | TokenKind::Eof => break,
TokenKind::Ident => {}
_ => {
let error_span = self.peek_span();
let (field, new_leading, has_comma) =
self.make_error_field(error_span, leading, errors);
fields.push(field);
leading = new_leading;
if !has_comma {
break;
}
continue;
}
}
let name_tok = self.next_token();
let pre_colon = self.collect_leading_trivia();
let colon = if self.peek_kind() == TokenKind::Colon {
self.next_token().span
} else {
errors.push(Self::error(
name_tok.span,
Self::expected("`:` after map key", Some("struct field")),
));
Self::span_at_end(&name_tok.span)
};
let post_colon = self.collect_leading_trivia();
let value = match self.peek_kind() {
TokenKind::Comma | TokenKind::RParen => {
let error_span = Self::span_at_end(&colon);
let error_kind = Self::expected("value", Some("struct field"));
errors.push(Self::error(error_span, error_kind.clone()));
Expr::Error(ErrorExpr {
span: error_span,
error: Error::with_span(error_kind, error_span),
})
}
_ => self.parse_expr_inner(errors),
};
let trailing = self.collect_leading_trivia();
let (comma, has_comma) = self.consume_comma();
fields.push(StructField {
leading,
name: Ident {
span: name_tok.span,
name: Cow::Borrowed(name_tok.text),
},
pre_colon,
colon,
post_colon,
value,
trailing,
comma,
});
leading = self.collect_leading_trivia();
if !has_comma {
if matches!(self.peek_kind(), TokenKind::Ident) {
errors.push(Self::error(
self.peek_span(),
Self::expected("comma", Some("struct")),
));
} else {
break;
}
}
}
(fields, leading)
}
fn make_error_field(
&mut self,
error_span: Span,
leading: Trivia<'a>,
errors: &mut Vec<Error>,
) -> (StructField<'a>, Trivia<'a>, bool) {
errors.push(Self::error(error_span, Self::expected("identifier", None)));
self.recover_until(&[TokenKind::Comma, TokenKind::RParen]);
let trailing = self.collect_leading_trivia();
let (comma, has_comma) = self.consume_comma();
let field = StructField {
leading,
name: Ident {
span: error_span,
name: Cow::Borrowed(""),
},
pre_colon: Trivia::empty(),
colon: Self::span_at_end(&error_span),
post_colon: Trivia::empty(),
value: Expr::Error(ErrorExpr {
span: error_span,
error: Error::with_span(Self::expected("identifier", None), error_span),
}),
trailing,
comma,
};
let new_leading = self.collect_leading_trivia();
(field, new_leading, has_comma)
}
}