use enum_downcast::EnumDowncast;
use crate::display_tree::display_grouped;
use crate::prelude::*;
pub mod statements;
#[derive(Clone, Debug, PartialEq)]
pub struct Statement {
pub value: StatementEnum,
pub span: Span,
}
impl Statement {
pub fn expect<T>(&self) -> Result<T>
where
StatementEnum: enum_downcast::AsVariant<T>,
T: Clone,
{
match self.value.enum_downcast_ref::<T>() {
Some(statement) => Ok(statement.clone()),
None => Err(Error::new(
{
let expected = std::any::type_name::<T>().split("::").last().unwrap();
let actual = self.value.name();
ErrorType::UnexpectedStatement {
expected: expected.to_string(),
actual,
}
},
self.span.clone(),
)),
}
}
pub(crate) fn get_display_tree(&self) -> Vec<(Location, String)> {
match &self.value {
StatementEnum::Expression(expression) => display_grouped(
"expression",
self.span.clone(),
expression.expression.get_display_tree(),
),
StatementEnum::Break(_) => vec![(self.span.get_start(), "break".to_string())],
StatementEnum::Continue(_) => vec![(self.span.get_start(), "continue".to_string())],
StatementEnum::Return(return_statement) => match &return_statement.expression {
Some(expression) => {
display_grouped("return", self.span.clone(), expression.get_display_tree())
}
None => vec![(self.span.get_start(), "return".to_string())],
},
StatementEnum::Ev(ev_statement) => match &ev_statement.expression {
Some(expression) => {
display_grouped("ev", self.span.clone(), expression.get_display_tree())
}
None => vec![(self.span.get_start(), "ev".to_string())],
},
StatementEnum::Declaration(declaration) => display_grouped(
if declaration.is_const { "const" } else { "let" },
self.span.clone(),
[(
declaration.identifier.span().get_start(),
declaration.identifier.to_string(),
)]
.into_iter()
.chain(declaration.expression.get_display_tree())
.collect(),
),
StatementEnum::Exit(exit_statement) => match &exit_statement.expression {
Some(expression) => {
display_grouped("exit", self.span.clone(), expression.get_display_tree())
}
None => vec![(self.span.get_start(), "exit".to_string())],
},
}
}
}
impl std::fmt::Display for Statement {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "'{}' at {}", self.value, self.span)
}
}
pub trait ExpectStatement {
fn expect_statement(self, span: &Span) -> Result<Statement>;
}
impl ExpectStatement for Option<Statement> {
fn expect_statement(self, span: &Span) -> Result<Statement> {
match self {
Some(statement) => Ok(statement),
None => Err(Error::new(ErrorType::ExpectedStatement, span.clone())),
}
}
}
#[derive(Clone, Debug, EnumDowncast, PartialEq)]
pub enum StatementEnum {
Expression(statements::Expression),
Break(statements::Break),
Continue(statements::Continue),
Return(statements::Return),
Ev(statements::Ev),
Exit(statements::Exit),
Declaration(statements::Declaration),
}
impl StatementEnum {
pub fn name(&self) -> String {
use StatementEnum::*;
match self {
Expression(_) => "Expression",
Break(_) => "Break",
Continue(_) => "Continue",
Return(_) => "Return",
Ev(_) => "Ev",
Exit(_) => "Exit",
Declaration(_) => "Declaration",
}
.to_string()
}
}
impl std::fmt::Display for StatementEnum {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
use StatementEnum::*;
match self {
Expression(expression) => write!(f, "{};", expression.expression.value),
Break(_) => write!(f, "break;"),
Continue(_) => write!(f, "continue;"),
Return(return_statement) => match &return_statement.expression {
Some(expression) => write!(f, "return {};", expression),
None => write!(f, "return;"),
},
Ev(ev_statement) => match &ev_statement.expression {
Some(expression) => write!(f, "-> {};", expression),
None => write!(f, "->;"),
},
Exit(exit_statement) => match &exit_statement.expression {
Some(expression) => write!(f, "exit {};", expression),
None => write!(f, "exit;"),
},
Declaration(declaration) => write!(
f,
"{} {} = {}",
if declaration.is_const { "const" } else { "let" },
declaration.identifier,
declaration.expression
),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn expect_test() {
let statement = Statement {
value: statements::Expression {
expression: Box::new(Expression {
value: expressions::Number { value: 0.0 }.into(),
span: Span::new((1, 1, 0), "test"),
}),
}
.into(),
span: Span::new((1, 1, 0), "test"),
};
assert!(statement.expect::<statements::Break>().is_err());
let expression_statement = statement.expect::<statements::Expression>().unwrap();
let number_expression = expression_statement
.expression
.expect::<expressions::Number>()
.unwrap();
assert_eq!(number_expression.value, 0.0);
}
}