use crate::class::Class;
use crate::declaration::VariableDeclaration;
use crate::expression::Expression;
use crate::function::Function;
use crate::identifier::Identifier;
use crate::pattern::Pattern;
use crate::span::Spanned;
pub type Statement = Spanned<StatementKind>;
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum StatementKind {
Block {
body: Vec<Statement>,
},
Empty,
Debugger,
Expression {
expression: Expression,
},
If {
test: Expression,
consequent: Box<Statement>,
alternate: Option<Box<Statement>>,
},
Switch {
discriminant: Expression,
cases: Vec<SwitchCase>,
},
For {
init: Option<ForInit>,
test: Option<Expression>,
update: Option<Expression>,
body: Box<Statement>,
},
ForIn {
left: ForLeft,
right: Expression,
body: Box<Statement>,
},
ForOf {
left: ForLeft,
right: Expression,
body: Box<Statement>,
is_await: bool,
},
While {
test: Expression,
body: Box<Statement>,
},
DoWhile {
body: Box<Statement>,
test: Expression,
},
Return {
argument: Option<Expression>,
},
Throw {
argument: Expression,
},
Try {
block: Vec<Statement>,
handler: Option<CatchClause>,
finalizer: Option<Vec<Statement>>,
},
Break {
label: Option<Identifier>,
},
Continue {
label: Option<Identifier>,
},
Labeled {
label: Identifier,
body: Box<Statement>,
},
VariableDeclaration(VariableDeclaration),
FunctionDeclaration(Function),
ClassDeclaration(Class),
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct SwitchCase {
test: Option<Expression>,
consequent: Vec<Statement>,
}
impl SwitchCase {
#[must_use]
pub fn new(test: Option<Expression>, consequent: Vec<Statement>) -> Self {
Self { test, consequent }
}
#[must_use]
pub fn test(&self) -> Option<&Expression> {
self.test.as_ref()
}
#[must_use]
pub fn consequent(&self) -> &[Statement] {
&self.consequent
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct CatchClause {
param: Option<Pattern>,
body: Vec<Statement>,
}
impl CatchClause {
#[must_use]
pub fn new(param: Option<Pattern>, body: Vec<Statement>) -> Self {
Self { param, body }
}
#[must_use]
pub fn param(&self) -> Option<&Pattern> {
self.param.as_ref()
}
#[must_use]
pub fn body(&self) -> &[Statement] {
&self.body
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum ForInit {
Declaration(VariableDeclaration),
Expression(Expression),
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum ForLeft {
Declaration(VariableDeclaration),
Pattern(Pattern),
}
impl std::fmt::Display for StatementKind {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Block { body } => write_block(f, body),
Self::Empty => f.write_str(";"),
Self::Debugger => f.write_str("debugger;"),
Self::Expression { expression } => write!(f, "{expression};"),
Self::If {
test,
consequent,
alternate,
} => write_if(f, test, consequent, alternate.as_deref()),
Self::Switch {
discriminant,
cases,
} => write_switch(f, discriminant, cases),
Self::For {
init,
test,
update,
body,
} => write_for(f, init.as_ref(), test.as_ref(), update.as_ref(), body),
Self::ForIn { left, right, body } => write!(f, "for ({left} in {right}) {body}"),
Self::ForOf {
left,
right,
body,
is_await,
} => write_for_of(f, left, right, body, *is_await),
Self::While { test, body } => write!(f, "while ({test}) {body}"),
Self::DoWhile { body, test } => write!(f, "do {body} while ({test});"),
Self::Return { argument } => write_return(f, argument.as_ref()),
Self::Throw { argument } => write!(f, "throw {argument};"),
Self::Try {
block,
handler,
finalizer,
} => write_try(f, block, handler.as_ref(), finalizer.as_ref()),
Self::Break { label } => write_break_continue(f, "break", label.as_ref()),
Self::Continue { label } => write_break_continue(f, "continue", label.as_ref()),
Self::Labeled { label, body } => write!(f, "{label}: {body}"),
Self::VariableDeclaration(decl) => write!(f, "{decl};"),
Self::FunctionDeclaration(func) => write!(f, "{func}"),
Self::ClassDeclaration(class) => write!(f, "{class}"),
}
}
}
fn write_block(f: &mut std::fmt::Formatter<'_>, body: &[Statement]) -> std::fmt::Result {
let lines = body
.iter()
.map(|s| format!(" {s}"))
.collect::<Vec<_>>()
.join("\n");
if body.is_empty() {
f.write_str("{}")
} else {
write!(f, "{{\n{lines}\n}}")
}
}
fn write_if(
f: &mut std::fmt::Formatter<'_>,
test: &Expression,
consequent: &Statement,
alternate: Option<&Statement>,
) -> std::fmt::Result {
write!(f, "if ({test}) {consequent}")?;
match alternate {
Some(alt) => write!(f, " else {alt}"),
None => Ok(()),
}
}
fn write_switch(
f: &mut std::fmt::Formatter<'_>,
discriminant: &Expression,
cases: &[SwitchCase],
) -> std::fmt::Result {
write!(f, "switch ({discriminant}) {{ ")?;
let body = cases
.iter()
.map(|c| match c.test() {
Some(t) => format!("case {t}: ..."),
None => "default: ...".to_owned(),
})
.collect::<Vec<_>>()
.join(" ");
write!(f, "{body} }}")
}
fn write_for(
f: &mut std::fmt::Formatter<'_>,
init: Option<&ForInit>,
test: Option<&Expression>,
update: Option<&Expression>,
body: &Statement,
) -> std::fmt::Result {
let init_str = init.map_or(String::new(), |i| format!("{i}"));
let test_str = test.map_or(String::new(), |t| format!("{t}"));
let update_str = update.map_or(String::new(), |u| format!("{u}"));
write!(f, "for ({init_str}; {test_str}; {update_str}) {body}")
}
fn write_for_of(
f: &mut std::fmt::Formatter<'_>,
left: &ForLeft,
right: &Expression,
body: &Statement,
is_await: bool,
) -> std::fmt::Result {
let keyword = if is_await { "for await" } else { "for" };
write!(f, "{keyword} ({left} of {right}) {body}")
}
fn write_return(
f: &mut std::fmt::Formatter<'_>,
argument: Option<&Expression>,
) -> std::fmt::Result {
match argument {
Some(arg) => write!(f, "return {arg};"),
None => f.write_str("return;"),
}
}
fn write_try(
f: &mut std::fmt::Formatter<'_>,
block: &[Statement],
handler: Option<&CatchClause>,
finalizer: Option<&Vec<Statement>>,
) -> std::fmt::Result {
write!(f, "try {{ {} stmts }}", block.len())?;
if let Some(h) = handler {
match h.param() {
Some(p) => write!(f, " catch ({p}) {{ ... }}")?,
None => write!(f, " catch {{ ... }}")?,
}
}
if let Some(fin) = finalizer {
write!(f, " finally {{ {} stmts }}", fin.len())?;
}
Ok(())
}
fn write_break_continue(
f: &mut std::fmt::Formatter<'_>,
keyword: &str,
label: Option<&Identifier>,
) -> std::fmt::Result {
match label {
Some(l) => write!(f, "{keyword} {l};"),
None => write!(f, "{keyword};"),
}
}
impl std::fmt::Display for ForInit {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Declaration(decl) => write!(f, "{decl}"),
Self::Expression(expr) => write!(f, "{expr}"),
}
}
}
impl std::fmt::Display for ForLeft {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Declaration(decl) => write!(f, "{decl}"),
Self::Pattern(pat) => write!(f, "{pat}"),
}
}
}