use itertools::Itertools;
use crate::ir::{
expressions::Expr, Guard, InputReference, LocalFreqRef, OutputReference, Stmt, StreamReference,
WindowReference,
};
pub trait StmtFormatter {
type Return;
fn skip(&self) -> Self::Return;
fn seq(&self, inner: Vec<Stmt>) -> Self::Return;
fn parallel(&self, inner: Vec<Stmt>) -> Self::Return;
fn shift(&self, sr: StreamReference) -> Self::Return;
fn input(&self, sr: InputReference) -> Self::Return;
fn spawn(
&self,
sr: OutputReference,
with: Option<Vec<Expr>>,
local_frequencies: Vec<LocalFreqRef>,
windows: Vec<WindowReference>,
) -> Self::Return;
fn eval(&self, sr: OutputReference, with: Expr, idx: usize) -> Self::Return;
fn close(
&self,
sr: OutputReference,
local_frequencies: Vec<LocalFreqRef>,
windows: Vec<WindowReference>,
) -> Self::Return;
fn r#if(&self, guard: Guard, cons: Stmt, alt: Option<Stmt>) -> Self::Return;
fn iterate(&self, sr: Vec<OutputReference>, inner: Stmt) -> Self::Return;
fn assign(
&self,
sr: Vec<OutputReference>,
parameter_expr: Vec<Expr>,
inner: Stmt,
) -> Self::Return;
fn stmt(&self, stmt: Stmt) -> Self::Return {
match stmt {
Stmt::Skip => self.skip(),
Stmt::Seq(stmts) => self.seq(stmts),
Stmt::Parallel(stmts) => self.parallel(stmts),
Stmt::Shift(sr) => self.shift(sr),
Stmt::Input(sr) => self.input(sr),
Stmt::Spawn {
sr,
with,
local_frequencies,
windows,
} => self.spawn(sr, with, local_frequencies, windows),
Stmt::Eval { sr, with, idx } => self.eval(sr, with, idx),
Stmt::Close {
sr,
local_frequencies,
windows,
} => self.close(sr, local_frequencies, windows),
Stmt::If(if_stmt) => {
let (guard, cons, alt) = if_stmt.destruct();
self.r#if(guard, cons, alt)
}
Stmt::Iterate { sr, stmt } => self.iterate(sr, *stmt),
Stmt::Assign {
parameter_expr,
sr,
stmt,
} => self.assign(sr, parameter_expr, *stmt),
}
}
}
pub trait DefaultStmtFormatter
where
Self: StmtFormatter<Return = String>,
{
fn skip(&self) -> String {
"".into()
}
fn shift(&self, sr: StreamReference) -> String;
fn input(&self, sr: InputReference) -> String;
fn spawn(
&self,
sr: OutputReference,
with: Option<Vec<Expr>>,
local_frequencies: Vec<LocalFreqRef>,
windows: Vec<WindowReference>,
) -> String;
fn eval(&self, sr: OutputReference, with: Expr, idx: usize) -> String;
fn close(
&self,
sr: OutputReference,
local_frequencies: Vec<LocalFreqRef>,
windows: Vec<WindowReference>,
) -> String;
fn r#if(&self, guard: Guard, cons: Stmt, alt: Option<Stmt>) -> String;
fn iterate(&self, sr: Vec<OutputReference>, inner: Stmt) -> String;
fn assign(&self, sr: Vec<OutputReference>, parameter_expr: Vec<Expr>, inner: Stmt) -> String;
fn seq(&self, inner: Vec<Stmt>) -> String {
inner.into_iter().map(|stmt| self.stmt(stmt)).join("\n")
}
fn parallel(&self, inner: Vec<Stmt>) -> String {
<Self as DefaultStmtFormatter>::seq(self, inner)
}
}
impl<F: DefaultStmtFormatter> StmtFormatter for F {
type Return = String;
fn skip(&self) -> Self::Return {
<Self as DefaultStmtFormatter>::skip(self)
}
fn seq(&self, inner: Vec<Stmt>) -> Self::Return {
<Self as DefaultStmtFormatter>::seq(self, inner)
}
fn parallel(&self, inner: Vec<Stmt>) -> Self::Return {
<Self as DefaultStmtFormatter>::parallel(self, inner)
}
fn shift(&self, sr: StreamReference) -> Self::Return {
<Self as DefaultStmtFormatter>::shift(self, sr)
}
fn input(&self, sr: InputReference) -> Self::Return {
<Self as DefaultStmtFormatter>::input(self, sr)
}
fn spawn(
&self,
sr: OutputReference,
with: Option<Vec<Expr>>,
local_frequencies: Vec<LocalFreqRef>,
windows: Vec<WindowReference>,
) -> Self::Return {
<Self as DefaultStmtFormatter>::spawn(self, sr, with, local_frequencies, windows)
}
fn eval(&self, sr: OutputReference, with: Expr, idx: usize) -> Self::Return {
<Self as DefaultStmtFormatter>::eval(self, sr, with, idx)
}
fn close(
&self,
sr: OutputReference,
local_frequencies: Vec<LocalFreqRef>,
windows: Vec<WindowReference>,
) -> Self::Return {
<Self as DefaultStmtFormatter>::close(self, sr, local_frequencies, windows)
}
fn r#if(&self, guard: Guard, cons: Stmt, alt: Option<Stmt>) -> Self::Return {
<Self as DefaultStmtFormatter>::r#if(self, guard, cons, alt)
}
fn iterate(&self, sr: Vec<OutputReference>, inner: Stmt) -> Self::Return {
<Self as DefaultStmtFormatter>::iterate(self, sr, inner)
}
fn assign(
&self,
sr: Vec<OutputReference>,
parameter_expr: Vec<Expr>,
inner: Stmt,
) -> Self::Return {
<Self as DefaultStmtFormatter>::assign(self, sr, parameter_expr, inner)
}
}