#![deny(clippy::undocumented_unsafe_blocks)]
use std::{
fmt::{self, Debug, Formatter},
hash::{Hash, Hasher},
mem::{self, ManuallyDrop},
ptr
};
use super::{
Add, ArithmeticExpression, Binding, Constant, CustomDice, DiceExpression,
Div, DropHighest, DropLowest, Exp, Expression, Group, Mod, Mul, Neg, Range,
StandardDice, Sub, Variable,
traversal::{self, Node}
};
use crate::span::SourceSpan;
impl Debug for Expression<'_>
{
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result
{
traversal::debug(Node::Expression(self), f)
}
}
impl Debug for DiceExpression<'_>
{
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result
{
traversal::debug(Node::Dice(self), f)
}
}
impl PartialEq for Expression<'_>
{
fn eq(&self, other: &Self) -> bool
{
traversal::eq(Node::Expression(self), Node::Expression(other))
}
}
impl PartialEq for DiceExpression<'_>
{
fn eq(&self, other: &Self) -> bool
{
traversal::eq(Node::Dice(self), Node::Dice(other))
}
}
impl Eq for Expression<'_> {}
impl Eq for DiceExpression<'_> {}
impl Hash for Expression<'_>
{
fn hash<H: Hasher>(&self, state: &mut H)
{
traversal::hash(Node::Expression(self), state)
}
}
impl Hash for DiceExpression<'_>
{
fn hash<H: Hasher>(&self, state: &mut H)
{
traversal::hash(Node::Dice(self), state)
}
}
impl Clone for Expression<'_>
{
fn clone(&self) -> Self
{
match self
{
Expression::Constant(constant) => Expression::Constant(*constant),
Expression::Variable(variable) =>
{
Expression::Variable(variable.clone())
},
_ =>
{
let mut cloner = Cloner::new(Task::Expression(self), false);
cloner.run();
cloner.pop_expression()
}
}
}
}
impl Clone for DiceExpression<'_>
{
fn clone(&self) -> Self
{
let mut cloner = Cloner::new(Task::Dice(self), false);
cloner.run();
cloner.pop_dice()
}
}
pub(super) fn untether<'src>(expression: &Expression<'src>)
-> Expression<'src>
{
let mut cloner = Cloner::new(Task::Expression(expression), true);
cloner.run();
cloner.pop_expression()
}
pub(super) fn untether_dice<'src>(
dice: &DiceExpression<'src>
) -> DiceExpression<'src>
{
let mut cloner = Cloner::new(Task::Dice(dice), true);
cloner.run();
cloner.pop_dice()
}
enum Task<'a, 'src>
{
Expression(&'a Expression<'src>),
Dice(&'a DiceExpression<'src>),
BuildExpression(&'a Expression<'src>),
BuildDice(&'a DiceExpression<'src>)
}
struct Cloner<'a, 'src>
{
untether: bool,
tasks: Vec<Task<'a, 'src>>,
expressions: Vec<Expression<'src>>,
dice: Vec<DiceExpression<'src>>
}
impl<'a, 'src> Cloner<'a, 'src>
{
fn new(root: Task<'a, 'src>, untether: bool) -> Self
{
Cloner {
untether,
tasks: vec![root],
expressions: Vec::new(),
dice: Vec::new()
}
}
fn span(&self, span: SourceSpan) -> SourceSpan
{
if self.untether
{
SourceSpan::default()
}
else
{
span
}
}
fn run(&mut self)
{
while let Some(task) = self.tasks.pop()
{
match task
{
Task::Expression(expression) =>
{
self.visit_expression(expression)
},
Task::Dice(dice) => self.visit_dice(dice),
Task::BuildExpression(expression) =>
{
let copy = self.build_expression(expression);
self.expressions.push(copy);
},
Task::BuildDice(dice) =>
{
let copy = self.build_dice(dice);
self.dice.push(copy);
}
}
}
}
fn visit_expression(&mut self, expression: &'a Expression<'src>)
{
self.tasks.push(Task::BuildExpression(expression));
match expression
{
Expression::Constant(constant) =>
{
self.tasks.pop();
self.expressions.push(Expression::Constant(Constant {
span: self.span(constant.span),
..*constant
}));
},
Expression::Variable(variable) =>
{
self.tasks.pop();
self.expressions.push(Expression::Variable(Variable {
name: variable.name.clone(),
span: self.span(variable.span)
}));
},
Expression::Group(group) =>
{
self.tasks.push(Task::Expression(&group.expression))
},
Expression::Binding(binding) =>
{
self.tasks.push(Task::Expression(&binding.expression))
},
Expression::Range(range) =>
{
self.tasks.push(Task::Expression(&range.end));
self.tasks.push(Task::Expression(&range.start));
},
Expression::Dice(dice) => self.tasks.push(Task::Dice(dice)),
Expression::Arithmetic(arithmetic) => match arithmetic
{
ArithmeticExpression::Add(Add { left, right, .. })
| ArithmeticExpression::Sub(Sub { left, right, .. })
| ArithmeticExpression::Mul(Mul { left, right, .. })
| ArithmeticExpression::Div(Div { left, right, .. })
| ArithmeticExpression::Mod(Mod { left, right, .. })
| ArithmeticExpression::Exp(Exp { left, right, .. }) =>
{
self.tasks.push(Task::Expression(right));
self.tasks.push(Task::Expression(left));
},
ArithmeticExpression::Neg(neg) =>
{
self.tasks.push(Task::Expression(&neg.operand))
},
}
}
}
fn visit_dice(&mut self, dice: &'a DiceExpression<'src>)
{
self.tasks.push(Task::BuildDice(dice));
match dice
{
DiceExpression::Standard(standard) =>
{
self.tasks.push(Task::Expression(&standard.faces));
self.tasks.push(Task::Expression(&standard.count));
},
DiceExpression::Custom(custom) =>
{
self.tasks.push(Task::Expression(&custom.count))
},
DiceExpression::DropLowest(DropLowest { dice, drop, .. })
| DiceExpression::DropHighest(DropHighest { dice, drop, .. }) =>
{
if let Some(drop) = drop
{
self.tasks.push(Task::Expression(drop));
}
self.tasks.push(Task::Dice(dice));
}
}
}
fn build_expression(
&mut self,
expression: &'a Expression<'src>
) -> Expression<'src>
{
match expression
{
Expression::Constant(_) | Expression::Variable(_) =>
{
unreachable!("leaves are cloned without a build")
},
Expression::Group(group) => Expression::Group(Group {
expression: Box::new(self.pop_expression()),
span: self.span(group.span)
}),
Expression::Binding(binding) => Expression::Binding(Binding {
name: binding.name.clone(),
name_span: self.span(binding.name_span),
expression: Box::new(self.pop_expression()),
span: self.span(binding.span)
}),
Expression::Range(range) =>
{
let (start, end) = self.pop_expression_pair();
Expression::Range(Range {
start,
end,
span: self.span(range.span)
})
},
Expression::Dice(_) => Expression::Dice(self.pop_dice()),
Expression::Arithmetic(arithmetic) =>
{
Expression::Arithmetic(self.build_arithmetic(arithmetic))
},
}
}
fn build_arithmetic(
&mut self,
arithmetic: &'a ArithmeticExpression<'src>
) -> ArithmeticExpression<'src>
{
if let ArithmeticExpression::Neg(neg) = arithmetic
{
return ArithmeticExpression::Neg(Neg {
operand: Box::new(self.pop_expression()),
span: self.span(neg.span)
})
}
let (left, right) = self.pop_expression_pair();
match arithmetic
{
ArithmeticExpression::Add(add) => ArithmeticExpression::Add(Add {
left,
right,
span: self.span(add.span)
}),
ArithmeticExpression::Sub(sub) => ArithmeticExpression::Sub(Sub {
left,
right,
span: self.span(sub.span)
}),
ArithmeticExpression::Mul(mul) => ArithmeticExpression::Mul(Mul {
left,
right,
span: self.span(mul.span)
}),
ArithmeticExpression::Div(div) => ArithmeticExpression::Div(Div {
left,
right,
span: self.span(div.span)
}),
ArithmeticExpression::Mod(r#mod) =>
{
ArithmeticExpression::Mod(Mod {
left,
right,
span: self.span(r#mod.span)
})
},
ArithmeticExpression::Exp(exp) => ArithmeticExpression::Exp(Exp {
left,
right,
span: self.span(exp.span)
}),
ArithmeticExpression::Neg(_) => unreachable!()
}
}
fn build_dice(
&mut self,
dice: &'a DiceExpression<'src>
) -> DiceExpression<'src>
{
match dice
{
DiceExpression::Standard(standard) =>
{
let (count, faces) = self.pop_expression_pair();
DiceExpression::Standard(StandardDice {
count,
faces,
span: self.span(standard.span)
})
},
DiceExpression::Custom(custom) =>
{
DiceExpression::Custom(CustomDice {
count: Box::new(self.pop_expression()),
faces: custom.faces.clone(),
span: self.span(custom.span)
})
},
DiceExpression::DropLowest(clause) =>
{
let drop = clause
.drop
.as_ref()
.map(|_| Box::new(self.pop_expression()));
DiceExpression::DropLowest(DropLowest {
dice: Box::new(self.pop_dice()),
drop,
span: self.span(clause.span)
})
},
DiceExpression::DropHighest(clause) =>
{
let drop = clause
.drop
.as_ref()
.map(|_| Box::new(self.pop_expression()));
DiceExpression::DropHighest(DropHighest {
dice: Box::new(self.pop_dice()),
drop,
span: self.span(clause.span)
})
}
}
}
fn pop_expression(&mut self) -> Expression<'src>
{
self.expressions
.pop()
.expect("the child must be cloned before its parent")
}
fn pop_expression_pair(
&mut self
) -> (Box<Expression<'src>>, Box<Expression<'src>>)
{
let second = self.pop_expression();
let first = self.pop_expression();
(Box::new(first), Box::new(second))
}
fn pop_dice(&mut self) -> DiceExpression<'src>
{
self.dice
.pop()
.expect("the child must be cloned before its parent")
}
}
impl Drop for Expression<'_>
{
fn drop(&mut self)
{
let mut orphans = Vec::new();
detach_children(self, &mut orphans);
drop_orphans(orphans);
}
}
impl Drop for DiceExpression<'_>
{
fn drop(&mut self)
{
let mut orphans = Vec::new();
detach_dice_children(self, &mut orphans);
drop_orphans(orphans);
}
}
const PLACEHOLDER: Expression<'static> = Expression::Constant(Constant {
value: 0,
span: SourceSpan::SYNTHETIC
});
fn drop_orphans(mut orphans: Vec<Expression<'_>>)
{
while let Some(mut orphan) = orphans.pop()
{
detach_children(&mut orphan, &mut orphans);
}
}
fn detach<'src>(
slot: &mut Expression<'src>,
orphans: &mut Vec<Expression<'src>>
)
{
if !matches!(slot, Expression::Constant(_) | Expression::Variable(_))
{
orphans.push(mem::replace(slot, PLACEHOLDER));
}
}
fn detach_children<'src>(
expression: &mut Expression<'src>,
orphans: &mut Vec<Expression<'src>>
)
{
match expression
{
Expression::Constant(_) | Expression::Variable(_) =>
{},
Expression::Group(group) => detach(&mut group.expression, orphans),
Expression::Binding(binding) =>
{
detach(&mut binding.expression, orphans)
},
Expression::Range(range) =>
{
detach(&mut range.start, orphans);
detach(&mut range.end, orphans);
},
Expression::Dice(dice) => detach_dice_children(dice, orphans),
Expression::Arithmetic(arithmetic) => match arithmetic
{
ArithmeticExpression::Add(Add { left, right, .. })
| ArithmeticExpression::Sub(Sub { left, right, .. })
| ArithmeticExpression::Mul(Mul { left, right, .. })
| ArithmeticExpression::Div(Div { left, right, .. })
| ArithmeticExpression::Mod(Mod { left, right, .. })
| ArithmeticExpression::Exp(Exp { left, right, .. }) =>
{
detach(left, orphans);
detach(right, orphans);
},
ArithmeticExpression::Neg(neg) => detach(&mut neg.operand, orphans)
}
}
}
fn detach_dice_children<'src>(
dice: &mut DiceExpression<'src>,
orphans: &mut Vec<Expression<'src>>
)
{
match dice
{
DiceExpression::Standard(standard) =>
{
detach(&mut standard.count, orphans);
detach(&mut standard.faces, orphans);
},
DiceExpression::Custom(custom) => detach(&mut custom.count, orphans),
DiceExpression::DropLowest(DropLowest { dice, drop, .. })
| DiceExpression::DropHighest(DropHighest { dice, drop, .. }) =>
{
if let Some(drop) = drop.take()
{
orphans.push(*drop);
}
match **dice
{
DiceExpression::Standard(_) | DiceExpression::Custom(_) =>
{
detach_dice_children(dice, orphans)
},
DiceExpression::DropLowest(_)
| DiceExpression::DropHighest(_) => promote_leaf(dice, orphans)
}
}
}
}
#[cfg_attr(doc, aquamarine::aquamarine)]
fn promote_leaf<'src>(
slot: &mut DiceExpression<'src>,
orphans: &mut Vec<Expression<'src>>
)
{
let mut cursor = &mut *slot;
loop
{
match cursor
{
DiceExpression::DropLowest(DropLowest { dice, drop, .. })
| DiceExpression::DropHighest(DropHighest { dice, drop, .. }) =>
{
if let Some(drop) = drop.take()
{
orphans.push(*drop);
}
cursor = &mut **dice;
},
leaf
@ (DiceExpression::Standard(_) | DiceExpression::Custom(_)) =>
{
detach_dice_children(leaf, orphans);
break
}
}
}
let mut clause = unsafe { ptr::read(slot) };
let leaf = loop
{
let undropped = ManuallyDrop::new(clause);
let dice = match &*undropped
{
DiceExpression::DropLowest(payload) =>
{
let DropLowest {
dice,
drop: _,
span: _
} = unsafe { ptr::read(payload) };
dice
},
DiceExpression::DropHighest(payload) =>
{
let DropHighest {
dice,
drop: _,
span: _
} = unsafe { ptr::read(payload) };
dice
},
DiceExpression::Standard(_) | DiceExpression::Custom(_) =>
{
break ManuallyDrop::into_inner(undropped)
},
};
clause = *dice;
};
unsafe { ptr::write(slot, leaf) };
}