use std::{
fmt::{self, Debug, Formatter, Write},
hash::{Hash, Hasher}
};
use super::{
Add, ArithmeticExpression, Constant, DiceExpression, Div, Exp, Expression,
Mod, Mul, Sub, Variable
};
use crate::span::SourceSpan;
#[derive(Copy, Clone)]
pub(super) enum Node<'a, 'src>
{
Expression(&'a Expression<'src>),
Dice(&'a DiceExpression<'src>)
}
#[derive(Copy, Clone, PartialEq, Eq, Hash)]
pub(super) enum Event<'a, 'src>
{
Struct(&'static str),
Tuple(&'static str),
Field(&'static str),
Leaf(Leaf<'a, 'src>),
Unit(&'static str),
Close
}
#[derive(Copy, Clone, PartialEq, Eq, Hash)]
pub(super) enum Leaf<'a, 'src>
{
Span(SourceSpan),
Name(&'a str),
Constant(Constant),
Variable(&'a Variable<'src>),
Faces(&'a [i32])
}
impl Leaf<'_, '_>
{
fn as_debug(&self) -> &dyn Debug
{
match self
{
Leaf::Span(span) => span,
Leaf::Name(name) => name,
Leaf::Constant(constant) => constant,
Leaf::Variable(variable) => variable,
Leaf::Faces(faces) => faces
}
}
}
#[derive(Copy, Clone)]
enum Item<'a, 'src>
{
Event(Event<'a, 'src>),
Node(Node<'a, 'src>)
}
pub(super) struct Events<'a, 'src>
{
stack: Vec<Item<'a, 'src>>
}
impl<'a, 'src> Events<'a, 'src>
{
pub(super) fn new(root: Node<'a, 'src>) -> Self
{
Events {
stack: vec![Item::Node(root)]
}
}
fn schedule(&mut self, items: &[Item<'a, 'src>])
{
self.stack.extend(items.iter().rev());
}
fn expand(&mut self, node: Node<'a, 'src>)
{
match node
{
Node::Expression(expression) => self.expand_expression(expression),
Node::Dice(dice) => self.expand_dice(dice)
}
}
fn expand_expression(&mut self, expression: &'a Expression<'src>)
{
match expression
{
Expression::Group(group) => self.schedule(&[
tuple("Group"),
structure("Group"),
field("expression"),
expression_node(&group.expression),
field("span"),
span(group.span),
close(),
close()
]),
Expression::Constant(constant) => self.schedule(&[
tuple("Constant"),
leaf(Leaf::Constant(*constant)),
close()
]),
Expression::Variable(variable) => self.schedule(&[
tuple("Variable"),
leaf(Leaf::Variable(variable)),
close()
]),
Expression::Binding(binding) => self.schedule(&[
tuple("Binding"),
structure("Binding"),
field("name"),
leaf(Leaf::Name(&binding.name)),
field("name_span"),
span(binding.name_span),
field("expression"),
expression_node(&binding.expression),
field("span"),
span(binding.span),
close(),
close()
]),
Expression::Range(range) => self.schedule(&[
tuple("Range"),
structure("Range"),
field("start"),
expression_node(&range.start),
field("end"),
expression_node(&range.end),
field("span"),
span(range.span),
close(),
close()
]),
Expression::Dice(dice) =>
{
self.schedule(&[tuple("Dice"), dice_node(dice), close()])
},
Expression::Arithmetic(arithmetic) =>
{
self.expand_arithmetic(arithmetic)
},
}
}
fn expand_arithmetic(&mut self, arithmetic: &'a ArithmeticExpression<'src>)
{
match arithmetic
{
ArithmeticExpression::Add(Add { left, right, span })
| ArithmeticExpression::Sub(Sub { left, right, span })
| ArithmeticExpression::Mul(Mul { left, right, span })
| ArithmeticExpression::Div(Div { left, right, span })
| ArithmeticExpression::Mod(Mod { left, right, span })
| ArithmeticExpression::Exp(Exp { left, right, span }) =>
{
let name = match arithmetic
{
ArithmeticExpression::Add(_) => "Add",
ArithmeticExpression::Sub(_) => "Sub",
ArithmeticExpression::Mul(_) => "Mul",
ArithmeticExpression::Div(_) => "Div",
ArithmeticExpression::Mod(_) => "Mod",
ArithmeticExpression::Exp(_) => "Exp",
ArithmeticExpression::Neg(_) => unreachable!()
};
self.schedule(&[
tuple("Arithmetic"),
tuple(name),
structure(name),
field("left"),
expression_node(left),
field("right"),
expression_node(right),
field("span"),
self::span(*span),
close(),
close(),
close()
])
},
ArithmeticExpression::Neg(neg) => self.schedule(&[
tuple("Arithmetic"),
tuple("Neg"),
structure("Neg"),
field("operand"),
expression_node(&neg.operand),
field("span"),
span(neg.span),
close(),
close(),
close()
])
}
}
fn expand_dice(&mut self, dice: &'a DiceExpression<'src>)
{
let (variant, name, dice, drop, span) = match dice
{
DiceExpression::Standard(standard) =>
{
return self.schedule(&[
tuple("Standard"),
structure("StandardDice"),
field("count"),
expression_node(&standard.count),
field("faces"),
expression_node(&standard.faces),
field("span"),
span(standard.span),
close(),
close()
])
},
DiceExpression::Custom(custom) =>
{
return self.schedule(&[
tuple("Custom"),
structure("CustomDice"),
field("count"),
expression_node(&custom.count),
field("faces"),
leaf(Leaf::Faces(&custom.faces)),
field("span"),
span(custom.span),
close(),
close()
])
},
DiceExpression::DropLowest(clause) => (
"DropLowest",
"DropLowest",
&clause.dice,
&clause.drop,
clause.span
),
DiceExpression::DropHighest(clause) => (
"DropHighest",
"DropHighest",
&clause.dice,
&clause.drop,
clause.span
)
};
self.schedule(&[field("span"), self::span(span), close(), close()]);
match drop
{
None => self.schedule(&[Item::Event(Event::Unit("None"))]),
Some(drop) =>
{
self.schedule(&[tuple("Some"), expression_node(drop), close()])
},
}
self.schedule(&[
tuple(variant),
structure(name),
field("dice"),
dice_node(dice),
field("drop")
]);
}
}
impl<'a, 'src> Iterator for Events<'a, 'src>
{
type Item = Event<'a, 'src>;
fn next(&mut self) -> Option<Self::Item>
{
loop
{
match self.stack.pop()?
{
Item::Event(event) => return Some(event),
Item::Node(node) => self.expand(node)
}
}
}
}
fn structure<'a, 'src>(name: &'static str) -> Item<'a, 'src>
{
Item::Event(Event::Struct(name))
}
fn tuple<'a, 'src>(name: &'static str) -> Item<'a, 'src>
{
Item::Event(Event::Tuple(name))
}
fn field<'a, 'src>(name: &'static str) -> Item<'a, 'src>
{
Item::Event(Event::Field(name))
}
fn close<'a, 'src>() -> Item<'a, 'src> { Item::Event(Event::Close) }
fn leaf<'a, 'src>(leaf: Leaf<'a, 'src>) -> Item<'a, 'src>
{
Item::Event(Event::Leaf(leaf))
}
fn span<'a, 'src>(span: SourceSpan) -> Item<'a, 'src> { leaf(Leaf::Span(span)) }
fn expression_node<'a, 'src>(expression: &'a Expression<'src>)
-> Item<'a, 'src>
{
Item::Node(Node::Expression(expression))
}
fn dice_node<'a, 'src>(dice: &'a DiceExpression<'src>) -> Item<'a, 'src>
{
Item::Node(Node::Dice(dice))
}
pub(super) fn debug(root: Node<'_, '_>, f: &mut Formatter<'_>) -> fmt::Result
{
let pretty = f.alternate();
let mut open = Vec::<Container>::new();
for event in Events::new(root)
{
match event
{
Event::Struct(name) | Event::Tuple(name) =>
{
begin_value(f, &mut open, pretty)?;
f.write_str(name)?;
open.push(Container {
is_struct: matches!(event, Event::Struct(_)),
has_fields: false
});
},
Event::Unit(name) =>
{
begin_value(f, &mut open, pretty)?;
f.write_str(name)?;
end_value(f, &open, pretty)?;
},
Event::Leaf(leaf) =>
{
begin_value(f, &mut open, pretty)?;
if pretty
{
let mut indenter = Indenter {
f,
levels: open.len(),
on_newline: false
};
write!(indenter, "{:#?}", leaf.as_debug())?;
}
else
{
leaf.as_debug().fmt(f)?;
}
end_value(f, &open, pretty)?;
},
Event::Field(name) =>
{
let levels = open.len();
let container = open
.last_mut()
.expect("a field must belong to an open struct");
if pretty
{
if !container.has_fields
{
f.write_str(" {\n")?;
}
indent(f, levels)?;
}
else
{
f.write_str(
if container.has_fields { ", " } else { " { " }
)?;
}
container.has_fields = true;
f.write_str(name)?;
f.write_str(": ")?;
},
Event::Close =>
{
let container = open
.pop()
.expect("a close must match an open struct or tuple");
if container.has_fields
{
if pretty
{
indent(f, open.len())?;
}
f.write_str(match (container.is_struct, pretty)
{
(true, true) => "}",
(true, false) => " }",
(false, _) => ")"
})?;
}
end_value(f, &open, pretty)?;
}
}
}
Ok(())
}
struct Container
{
is_struct: bool,
has_fields: bool
}
fn begin_value(
f: &mut Formatter<'_>,
open: &mut [Container],
pretty: bool
) -> fmt::Result
{
let levels = open.len();
match open.last_mut()
{
Some(container) if !container.is_struct =>
{
if pretty
{
if !container.has_fields
{
f.write_str("(\n")?;
}
indent(f, levels)?;
}
else
{
f.write_str(if container.has_fields { ", " } else { "(" })?;
}
container.has_fields = true;
Ok(())
},
_ => Ok(())
}
}
fn end_value(
f: &mut Formatter<'_>,
open: &[Container],
pretty: bool
) -> fmt::Result
{
if pretty && !open.is_empty()
{
f.write_str(",\n")?;
}
Ok(())
}
fn indent(f: &mut impl Write, levels: usize) -> fmt::Result
{
const SPACES: &str =
" ";
let mut remaining = 4 * levels;
while remaining > 0
{
let chunk = remaining.min(SPACES.len());
f.write_str(&SPACES[..chunk])?;
remaining -= chunk;
}
Ok(())
}
struct Indenter<'f, 'b>
{
f: &'f mut Formatter<'b>,
levels: usize,
on_newline: bool
}
impl Write for Indenter<'_, '_>
{
fn write_str(&mut self, s: &str) -> fmt::Result
{
for line in s.split_inclusive('\n')
{
if self.on_newline
{
indent(self.f, self.levels)?;
}
self.on_newline = line.ends_with('\n');
self.f.write_str(line)?;
}
Ok(())
}
}
pub(super) fn eq(left: Node<'_, '_>, right: Node<'_, '_>) -> bool
{
Events::new(left).eq(Events::new(right))
}
pub(super) fn hash<H: Hasher>(root: Node<'_, '_>, state: &mut H)
{
Events::new(root).for_each(|event| event.hash(state));
}