use std::fmt::{self, Formatter};
use super::{
Add, ArithmeticExpression, Binding, Constant, CustomDice, DiceExpression,
Div, DropHighest, DropLowest, Exp, Expression, Function, Group, Mod, Mul,
Neg, Parameter, Range, StandardDice, Sub, Variable
};
#[derive(Copy, Clone)]
pub(super) enum Piece<'a>
{
Text(&'static str),
Name(&'a str),
Integer(i32),
Faces(&'a [i32]),
Parameters(&'a [Parameter<'a>]),
Expression(&'a Expression<'a>),
Dice(&'a DiceExpression<'a>)
}
pub(super) trait Render
{
fn schedule<'a>(&'a self, stack: &mut Vec<Piece<'a>>);
}
fn schedule<'a>(stack: &mut Vec<Piece<'a>>, pieces: &[Piece<'a>])
{
stack.extend(pieces.iter().rev());
}
pub(super) fn render(
root: &(impl Render + ?Sized),
f: &mut Formatter<'_>
) -> fmt::Result
{
let mut stack = Vec::new();
root.schedule(&mut stack);
while let Some(piece) = stack.pop()
{
match piece
{
Piece::Text(text) | Piece::Name(text) => f.write_str(text)?,
Piece::Integer(value) => write!(f, "{}", value)?,
Piece::Faces(faces) =>
{
write_separated(f, faces, |f, face| write!(f, "{}", face))?
},
Piece::Parameters(parameters) =>
{
write_separated(f, parameters, |f, parameter| {
write!(f, "{{{}}}", parameter.name)
})?
},
Piece::Expression(expression) => expression.schedule(&mut stack),
Piece::Dice(dice) => dice.schedule(&mut stack)
}
}
Ok(())
}
fn write_separated<T>(
f: &mut Formatter<'_>,
items: &[T],
mut write_item: impl FnMut(&mut Formatter<'_>, &T) -> fmt::Result
) -> fmt::Result
{
for (i, item) in items.iter().enumerate()
{
if i > 0
{
f.write_str(", ")?;
}
write_item(f, item)?;
}
Ok(())
}
impl Render for Function<'_>
{
fn schedule<'a>(&'a self, stack: &mut Vec<Piece<'a>>)
{
match &self.parameters
{
Some(parameters) => schedule(
stack,
&[
Piece::Parameters(parameters),
Piece::Text(": "),
Piece::Expression(&self.body)
]
),
None => schedule(stack, &[Piece::Expression(&self.body)])
}
}
}
impl Render for Parameter<'_>
{
fn schedule<'a>(&'a self, stack: &mut Vec<Piece<'a>>)
{
schedule(
stack,
&[Piece::Text("{"), Piece::Name(&self.name), Piece::Text("}")]
);
}
}
impl Render for Group<'_>
{
fn schedule<'a>(&'a self, stack: &mut Vec<Piece<'a>>)
{
schedule(
stack,
&[
Piece::Text("("),
Piece::Expression(&self.expression),
Piece::Text(")")
]
);
}
}
impl Render for Constant
{
fn schedule<'a>(&'a self, stack: &mut Vec<Piece<'a>>)
{
stack.push(Piece::Integer(self.value));
}
}
impl Render for Variable<'_>
{
fn schedule<'a>(&'a self, stack: &mut Vec<Piece<'a>>)
{
schedule(
stack,
&[Piece::Text("{"), Piece::Name(&self.name), Piece::Text("}")]
);
}
}
impl Render for Binding<'_>
{
fn schedule<'a>(&'a self, stack: &mut Vec<Piece<'a>>)
{
schedule(
stack,
&[
Piece::Text("{"),
Piece::Name(&self.name),
Piece::Text("}@("),
Piece::Expression(&self.expression),
Piece::Text(")")
]
);
}
}
impl Render for Range<'_>
{
fn schedule<'a>(&'a self, stack: &mut Vec<Piece<'a>>)
{
schedule(
stack,
&[
Piece::Text("["),
Piece::Expression(&self.start),
Piece::Text(":"),
Piece::Expression(&self.end),
Piece::Text("]")
]
);
}
}
impl Render for Expression<'_>
{
fn schedule<'a>(&'a self, stack: &mut Vec<Piece<'a>>)
{
match self
{
Expression::Group(group) => group.schedule(stack),
Expression::Constant(constant) => constant.schedule(stack),
Expression::Variable(variable) => variable.schedule(stack),
Expression::Binding(binding) => binding.schedule(stack),
Expression::Range(range) => range.schedule(stack),
Expression::Dice(dice) => dice.schedule(stack),
Expression::Arithmetic(arithmetic) => arithmetic.schedule(stack)
}
}
}
impl Render for StandardDice<'_>
{
fn schedule<'a>(&'a self, stack: &mut Vec<Piece<'a>>)
{
schedule(
stack,
&[
Piece::Expression(&self.count),
Piece::Text("D"),
Piece::Expression(&self.faces)
]
);
}
}
impl Render for CustomDice<'_>
{
fn schedule<'a>(&'a self, stack: &mut Vec<Piece<'a>>)
{
schedule(
stack,
&[
Piece::Expression(&self.count),
Piece::Text("D["),
Piece::Faces(&self.faces),
Piece::Text("]")
]
);
}
}
impl Render for DropLowest<'_>
{
fn schedule<'a>(&'a self, stack: &mut Vec<Piece<'a>>)
{
schedule_drop(stack, &self.dice, " drop lowest", self.drop.as_deref());
}
}
impl Render for DropHighest<'_>
{
fn schedule<'a>(&'a self, stack: &mut Vec<Piece<'a>>)
{
schedule_drop(stack, &self.dice, " drop highest", self.drop.as_deref());
}
}
fn schedule_drop<'a>(
stack: &mut Vec<Piece<'a>>,
dice: &'a DiceExpression<'a>,
keywords: &'static str,
drop: Option<&'a Expression<'a>>
)
{
if let Some(drop) = drop
{
schedule(stack, &[Piece::Text(" "), Piece::Expression(drop)]);
}
schedule(stack, &[Piece::Dice(dice), Piece::Text(keywords)]);
}
impl Render for DiceExpression<'_>
{
fn schedule<'a>(&'a self, stack: &mut Vec<Piece<'a>>)
{
match self
{
DiceExpression::Standard(dice) => dice.schedule(stack),
DiceExpression::Custom(dice) => dice.schedule(stack),
DiceExpression::DropLowest(clause) => clause.schedule(stack),
DiceExpression::DropHighest(clause) => clause.schedule(stack)
}
}
}
impl Render for Add<'_>
{
fn schedule<'a>(&'a self, stack: &mut Vec<Piece<'a>>)
{
schedule_binary(stack, &self.left, " + ", &self.right);
}
}
impl Render for Sub<'_>
{
fn schedule<'a>(&'a self, stack: &mut Vec<Piece<'a>>)
{
schedule_binary(stack, &self.left, " - ", &self.right);
}
}
impl Render for Mul<'_>
{
fn schedule<'a>(&'a self, stack: &mut Vec<Piece<'a>>)
{
schedule_binary(stack, &self.left, " * ", &self.right);
}
}
impl Render for Div<'_>
{
fn schedule<'a>(&'a self, stack: &mut Vec<Piece<'a>>)
{
schedule_binary(stack, &self.left, " / ", &self.right);
}
}
impl Render for Mod<'_>
{
fn schedule<'a>(&'a self, stack: &mut Vec<Piece<'a>>)
{
schedule_binary(stack, &self.left, " % ", &self.right);
}
}
impl Render for Exp<'_>
{
fn schedule<'a>(&'a self, stack: &mut Vec<Piece<'a>>)
{
schedule_binary(stack, &self.left, " ^ ", &self.right);
}
}
fn schedule_binary<'a>(
stack: &mut Vec<Piece<'a>>,
left: &'a Expression<'a>,
operator: &'static str,
right: &'a Expression<'a>
)
{
schedule(
stack,
&[
Piece::Expression(left),
Piece::Text(operator),
Piece::Expression(right)
]
);
}
impl Render for Neg<'_>
{
fn schedule<'a>(&'a self, stack: &mut Vec<Piece<'a>>)
{
schedule(stack, &[Piece::Text("-"), Piece::Expression(&self.operand)]);
}
}
impl Render for ArithmeticExpression<'_>
{
fn schedule<'a>(&'a self, stack: &mut Vec<Piece<'a>>)
{
match self
{
ArithmeticExpression::Add(add) => add.schedule(stack),
ArithmeticExpression::Sub(sub) => sub.schedule(stack),
ArithmeticExpression::Mul(mul) => mul.schedule(stack),
ArithmeticExpression::Div(div) => div.schedule(stack),
ArithmeticExpression::Mod(r#mod) => r#mod.schedule(stack),
ArithmeticExpression::Exp(exp) => exp.schedule(stack),
ArithmeticExpression::Neg(neg) => neg.schedule(stack)
}
}
}