use std::sync::Arc;
use gramatika::{Parse, ParseStreamer, Span, Spanned};
use crate::{
common::{AttributeList, TypeDecl},
parser::ErrorRecoveringParseStream,
token::{brace, keyword, operator, punct, Token, TokenKind},
ParseStream,
};
use super::Decl;
#[derive(Clone, DebugLisp)]
pub struct StructDecl {
pub attributes: Option<AttributeList>,
pub storage: Token,
pub storage_modifier: Option<Token>,
pub name: Token,
pub body: StructBody,
}
#[derive(Clone, DebugLisp)]
pub struct StructBody {
pub open_brace: Token,
pub fields: Arc<[Decl]>,
pub close_brace: Token,
}
#[derive(Clone, DebugLisp)]
pub struct FieldDecl {
pub attributes: Option<AttributeList>,
pub name: Token,
pub ty: TypeDecl,
pub separator: Option<Token>,
}
impl Parse for StructDecl {
type Stream = ParseStream;
fn parse(input: &mut Self::Stream) -> gramatika::Result<Self> {
let storage = input.consume(keyword![struct])?;
let storage_modifier = if input.check(operator![<]) {
input.consume(operator![<])?;
let modifier = input.consume_kind(TokenKind::Keyword)?;
input.consume(operator![>])?;
Some(modifier)
} else {
None
};
let name = input.consume_as(TokenKind::Ident, Token::struct_)?;
let body = input.parse()?;
Ok(Self {
attributes: None,
storage,
storage_modifier,
name,
body,
})
}
}
impl Spanned for StructDecl {
fn span(&self) -> Span {
self.storage.span().through(self.body.span())
}
}
impl Parse for StructBody {
type Stream = ParseStream;
fn parse(input: &mut Self::Stream) -> gramatika::Result<Self> {
let open_brace = input.consume(brace!["{"])?;
let fields = input.parse_seq_with_finisher::<FieldDecl, _>(
|input| !input.check(brace!["}"]),
|_, field| Ok(Decl::Field(field)),
)?;
let close_brace = input.consume(brace!["}"])?;
Ok(Self {
open_brace,
fields: fields.into(),
close_brace,
})
}
}
impl Spanned for StructBody {
fn span(&self) -> Span {
self.open_brace.span().through(self.close_brace.span())
}
}
impl Parse for FieldDecl {
type Stream = ParseStream;
fn parse(input: &mut Self::Stream) -> gramatika::Result<Self> {
let attributes = if input.check(punct!["@"]) {
Some(input.parse::<AttributeList>()?)
} else {
None
};
let name = input.consume_as(TokenKind::Ident, Token::field)?;
let ty = input.parse()?;
let separator = if !input.check(brace!("}")) {
Some(input.consume(punct![,])?)
} else {
None
};
Ok(Self {
attributes,
name,
ty,
separator,
})
}
}
impl Spanned for FieldDecl {
fn span(&self) -> Span {
let span_start = self
.attributes
.as_ref()
.and_then(|attrs| attrs.attributes.first().map(|attr| &attr.at_sign))
.unwrap_or(&self.name)
.span();
let span_end = match &self.separator {
Some(sep) => sep.span(),
None => self.ty.span(),
};
span_start.through(span_end)
}
}