use std::sync::Arc;
use gramatika::{Parse, ParseStreamer, Span, Spanned};
use crate::{
common::{AttributeList, TypeDecl},
parser::ErrorRecoveringParseStream,
stmt::BlockStmt,
token::{brace, keyword, operator, punct, Token, TokenKind},
ParseStream,
};
use super::Decl;
#[derive(Clone, DebugLisp)]
pub struct FunctionDecl {
pub attributes: Option<AttributeList>,
pub storage: Token,
pub storage_modifier: Option<Token>,
pub name: Token,
pub params: Arc<[Decl]>,
pub return_ty: Option<TypeDecl>,
pub body: BlockStmt,
}
#[derive(Clone, DebugLisp)]
pub struct ParamDecl {
pub attributes: Option<AttributeList>,
pub name: Token,
pub ty: TypeDecl,
}
impl Parse for FunctionDecl {
type Stream = ParseStream;
fn parse(input: &mut Self::Stream) -> gramatika::Result<Self> {
let storage = input.consume(keyword![fn])?;
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::function)?;
input.consume(brace!["("])?;
let params = input.parse_seq_with_finisher(
|input| !input.check(brace![")"]),
|input, param| {
if !input.check(brace![")"]) {
input.consume(punct![,])?;
}
Ok(Decl::Param(param))
},
)?;
input.consume(brace![")"])?;
let return_ty = if input.check(operator![->]) {
Some(input.parse::<TypeDecl>()?)
} else {
None
};
let body = input.parse::<BlockStmt>()?;
Ok(Self {
attributes: None,
storage,
storage_modifier,
name,
params: params.into(),
return_ty,
body,
})
}
}
impl Spanned for FunctionDecl {
fn span(&self) -> Span {
self.storage.span().through(self.body.brace_close.span())
}
}
impl Parse for ParamDecl {
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_kind(TokenKind::Ident)?;
let ty = input.parse::<TypeDecl>()?;
Ok(Self {
attributes,
name,
ty,
})
}
}
impl Spanned for ParamDecl {
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();
span_start.through(self.ty.span())
}
}