use std::fmt::{self, Write};
use super::{
Layout, SExpressible, SExpressibleOptions, ident_size, layout,
span_prefix_size, write_ident, write_newline, write_span_prefix
};
use crate::{
ast::{
Add, ArithmeticExpression, Binding, Constant, CustomDice,
DiceExpression, Div, DropHighest, DropLowest, Exp, Expression,
Function, Group, Mod, Mul, Neg, Parameter, Range, StandardDice, Sub,
Variable
},
span::SourceSpan
};
#[derive(Copy, Clone)]
pub(super) enum Shape<'a>
{
Constant(&'a Constant),
Variable(&'a Variable<'a>),
Pair
{
span: SourceSpan,
keyword: &'static str,
child: Child<'a>
},
Triple
{
span: SourceSpan,
keyword: &'static str,
first: Child<'a>,
second: Part<'a>
},
Binding
{
span: SourceSpan,
name_span: SourceSpan,
name: &'a str,
expression: &'a Expression<'a>
},
Function
{
span: SourceSpan,
parameters: &'a Option<Vec<Parameter<'a>>>,
body: &'a Expression<'a>
},
Transparent(&'a Expression<'a>)
}
#[derive(Copy, Clone)]
pub(super) enum Child<'a>
{
Expression(&'a Expression<'a>),
Dice(&'a DiceExpression<'a>)
}
#[derive(Copy, Clone)]
pub(super) enum Part<'a>
{
Child(Child<'a>),
Faces(&'a [i32])
}
pub(super) trait Shaped
{
fn shape<'a>(&'a self, options: SExpressibleOptions) -> Shape<'a>;
}
impl<'a> Child<'a>
{
fn shape(self, options: SExpressibleOptions) -> Shape<'a>
{
let shape = match self
{
Child::Expression(expression) => expression.shape(options),
Child::Dice(dice) => dice.shape(options)
};
resolve(shape, options)
}
}
fn resolve<'a>(mut shape: Shape<'a>, options: SExpressibleOptions)
-> Shape<'a>
{
while let Shape::Transparent(expression) = shape
{
shape = expression.shape(options);
}
shape
}
impl<'a> Shape<'a>
{
fn own_size(&self, options: SExpressibleOptions) -> usize
{
match *self
{
Shape::Constant(constant) => constant.size_s_expr(options),
Shape::Variable(variable) => variable.size_s_expr(options),
Shape::Pair { span, keyword, .. } =>
{
span_prefix_size(span, options) + 3 + keyword.len()
},
Shape::Triple {
span,
keyword,
second,
..
} =>
{
span_prefix_size(span, options)
+ 4
+ keyword.len()
+ match second
{
Part::Child(_) => 0,
Part::Faces(faces) => faces.size_s_expr(options)
}
},
Shape::Binding {
span,
name_span,
name,
..
} =>
{
span_prefix_size(span, options)
+ 2
+ "binding".len()
+ 2
+ span_prefix_size(name_span, options)
+ ident_size(name)
},
Shape::Function {
span, parameters, ..
} =>
{
span_prefix_size(span, options)
+ 12
+ parameters.size_s_expr(options)
},
Shape::Transparent(_) =>
{
unreachable!("transparent groups are resolved first")
}
}
}
fn children(&self) -> [Option<Child<'a>>; 2]
{
match *self
{
Shape::Constant(_) | Shape::Variable(_) => [None, None],
Shape::Pair { child, .. } => [Some(child), None],
Shape::Triple { first, second, .. } => [
Some(first),
match second
{
Part::Child(child) => Some(child),
Part::Faces(_) => None
}
],
Shape::Binding { expression, .. } =>
{
[Some(Child::Expression(expression)), None]
},
Shape::Function { body, .. } =>
{
[Some(Child::Expression(body)), None]
},
Shape::Transparent(expression) =>
{
[Some(Child::Expression(expression)), None]
},
}
}
}
pub(super) fn size(
root: &(impl Shaped + ?Sized),
options: SExpressibleOptions
) -> usize
{
let mut total = 0;
let mut pending = vec![resolve(root.shape(options), options)];
while let Some(shape) = pending.pop()
{
total += shape.own_size(options);
pending.extend(
shape
.children()
.into_iter()
.flatten()
.map(|child| child.shape(options))
);
}
total
}
enum SizeTask<'a>
{
Enter
{
shape: Shape<'a>,
parent: Option<usize>
},
Exit
{
index: usize,
parent: Option<usize>
}
}
fn sizes(root: Shape<'_>, options: SExpressibleOptions) -> Vec<usize>
{
let mut sizes = Vec::new();
let mut tasks = vec![SizeTask::Enter {
shape: root,
parent: None
}];
while let Some(task) = tasks.pop()
{
match task
{
SizeTask::Enter { shape, parent } =>
{
let index = sizes.len();
sizes.push(shape.own_size(options));
tasks.push(SizeTask::Exit { index, parent });
let [first, second] = shape.children();
for child in [second, first].into_iter().flatten()
{
tasks.push(SizeTask::Enter {
shape: child.shape(options),
parent: Some(index)
});
}
},
SizeTask::Exit { index, parent } =>
{
if let Some(parent) = parent
{
sizes[parent] += sizes[index];
}
}
}
}
sizes
}
enum Item<'a>
{
Text(&'static str),
Newline(usize),
Child
{
child: Child<'a>,
remaining_space: usize,
indent: usize
},
Faces
{
faces: &'a [i32],
remaining_space: usize,
indent: usize
}
}
struct Writer<'a, 'f>
{
f: &'f mut dyn Write,
options: SExpressibleOptions,
sizes: Vec<usize>,
next: usize,
stack: Vec<Item<'a>>
}
#[cfg_attr(doc, aquamarine::aquamarine)]
pub(super) fn write(
root: &(impl Shaped + ?Sized),
f: &mut dyn Write,
remaining_space: usize,
options: SExpressibleOptions
) -> fmt::Result
{
let root = resolve(root.shape(options), options);
let mut writer = Writer {
f,
options,
sizes: sizes(root, options),
next: 0,
stack: Vec::new()
};
writer.expand(root, remaining_space, options.indent)?;
writer.run()
}
impl<'a> Writer<'a, '_>
{
fn at(&self, indent: usize) -> SExpressibleOptions
{
SExpressibleOptions {
indent,
..self.options
}
}
fn schedule(&mut self, items: impl DoubleEndedIterator<Item = Item<'a>>)
{
self.stack.extend(items.rev());
}
fn run(&mut self) -> fmt::Result
{
while let Some(item) = self.stack.pop()
{
match item
{
Item::Text(text) => self.f.write_str(text)?,
Item::Newline(indent) => write_newline(self.f, indent)?,
Item::Child {
child,
remaining_space,
indent
} =>
{
let shape = child.shape(self.options);
self.expand(shape, remaining_space, indent)?
},
Item::Faces {
faces,
remaining_space,
indent
} =>
{
let options = self.at(indent);
faces.write_s_expr(self.f, remaining_space, options)?
}
}
}
Ok(())
}
fn expand(
&mut self,
shape: Shape<'a>,
remaining_space: usize,
indent: usize
) -> fmt::Result
{
let size = self.sizes[self.next];
self.next += 1;
let options = self.at(indent);
match shape
{
Shape::Constant(constant) =>
{
constant.write_s_expr(self.f, remaining_space, options)
},
Shape::Variable(variable) =>
{
variable.write_s_expr(self.f, remaining_space, options)
},
Shape::Pair {
span,
keyword,
child
} =>
{
let (remaining_space, size) =
self.begin_form(span, keyword, remaining_space, size)?;
let (remaining_space, indent) =
match layout(remaining_space, size, options)
{
Layout::Inline =>
{
self.f.write_str(" ")?;
(usize::MAX, indent)
},
Layout::Wrapped(options) =>
{
write_newline(self.f, options.indent)?;
(options.available_space(), options.indent)
}
};
self.schedule(
[
Item::Child {
child,
remaining_space,
indent
},
Item::Text(")")
]
.into_iter()
);
Ok(())
},
Shape::Triple {
span,
keyword,
first,
second
} =>
{
let (remaining_space, size) =
self.begin_form(span, keyword, remaining_space, size)?;
let (remaining_space, indent, separator) =
match layout(remaining_space, size, options)
{
Layout::Inline =>
{
self.f.write_str(" ")?;
(usize::MAX, indent, Item::Text(" "))
},
Layout::Wrapped(options) =>
{
write_newline(self.f, options.indent)?;
(
options.available_space(),
options.indent,
Item::Newline(options.indent)
)
}
};
let second = match second
{
Part::Child(child) => Item::Child {
child,
remaining_space,
indent
},
Part::Faces(faces) => Item::Faces {
faces,
remaining_space,
indent
}
};
self.schedule(
[
Item::Child {
child: first,
remaining_space,
indent
},
separator,
second,
Item::Text(")")
]
.into_iter()
);
Ok(())
},
Shape::Binding {
span,
name_span,
name,
expression
} =>
{
let body_size = size - shape.own_size(options);
write_span_prefix(self.f, span, options)?;
let remaining_space = remaining_space
.saturating_sub(span_prefix_size(span, options));
let keyword = "binding";
write!(self.f, "({}", keyword)?;
let remaining_space =
remaining_space.saturating_sub(1 + keyword.len());
let name_size =
span_prefix_size(name_span, options) + ident_size(name);
let space_needed =
1 + name_size + 1 + body_size + 1 + options.indent;
let (remaining_space, indent) = if remaining_space
>= space_needed
{
self.f.write_str(" ")?;
write_span_prefix(self.f, name_span, options)?;
write_ident(self.f, name)?;
self.f.write_str(" ")?;
(usize::MAX, indent)
}
else
{
let options = options.increase_indent();
write_newline(self.f, options.indent)?;
write_span_prefix(self.f, name_span, options)?;
write_ident(self.f, name)?;
write_newline(self.f, options.indent)?;
(options.available_space(), options.indent)
};
self.schedule(
[
Item::Child {
child: Child::Expression(expression),
remaining_space,
indent
},
Item::Text(")")
]
.into_iter()
);
Ok(())
},
Shape::Function {
span,
parameters,
body
} =>
{
let body_size = size - shape.own_size(options);
self.begin_function(
span,
parameters,
body,
body_size,
remaining_space,
options
)
},
Shape::Transparent(_) =>
{
unreachable!("transparent groups are resolved first")
}
}
}
fn begin_form(
&mut self,
span: SourceSpan,
keyword: &str,
remaining_space: usize,
size: usize
) -> Result<(usize, usize), fmt::Error>
{
write_span_prefix(self.f, span, self.options)?;
let prefix_size = span_prefix_size(span, self.options);
write!(self.f, "({}", keyword)?;
Ok((
remaining_space.saturating_sub(prefix_size),
size - prefix_size
))
}
fn begin_function(
&mut self,
span: SourceSpan,
parameters: &'a Option<Vec<Parameter<'a>>>,
body: &'a Expression<'a>,
body_size: usize,
remaining_space: usize,
options: SExpressibleOptions
) -> fmt::Result
{
write_span_prefix(self.f, span, options)?;
let remaining_space =
remaining_space.saturating_sub(span_prefix_size(span, options));
let keyword = "function";
write!(self.f, "({}", keyword)?;
let remaining_space = remaining_space.saturating_sub(9);
let params_size = parameters.size_s_expr(options);
let space_needed = 1 + params_size;
let remaining_space = if remaining_space >= space_needed
{
self.f.write_str(" ")?;
parameters.write_s_expr(self.f, usize::MAX, options)?;
remaining_space - space_needed
}
else
{
let options = options.increase_indent();
write_newline(self.f, options.indent)?;
let available_space = options.available_space();
parameters.write_s_expr(self.f, available_space, options)?;
let is_one_line = parameters.as_ref().is_none_or(Vec::is_empty)
|| available_space >= params_size + options.indent;
available_space
.saturating_sub(if is_one_line { params_size } else { 1 })
};
let space_needed = 1 + body_size + 1 + options.indent;
let (remaining_space, indent) = if remaining_space >= space_needed
{
self.f.write_str(" ")?;
(usize::MAX, options.indent)
}
else
{
let options = options.increase_indent();
write_newline(self.f, options.indent)?;
(options.available_space(), options.indent)
};
self.schedule(
[
Item::Child {
child: Child::Expression(body),
remaining_space,
indent
},
Item::Text(")")
]
.into_iter()
);
Ok(())
}
}
impl Shaped for Function<'_>
{
fn shape<'a>(&'a self, _options: SExpressibleOptions) -> Shape<'a>
{
Shape::Function {
span: self.span,
parameters: &self.parameters,
body: &self.body
}
}
}
impl Shaped for Group<'_>
{
fn shape<'a>(&'a self, options: SExpressibleOptions) -> Shape<'a>
{
if options.with_groups
{
pair(self.span, "group", Child::Expression(&self.expression))
}
else
{
Shape::Transparent(&self.expression)
}
}
}
impl Shaped for Constant
{
fn shape<'a>(&'a self, _options: SExpressibleOptions) -> Shape<'a>
{
Shape::Constant(self)
}
}
impl Shaped for Variable<'_>
{
fn shape<'a>(&'a self, _options: SExpressibleOptions) -> Shape<'a>
{
Shape::Variable(self)
}
}
impl Shaped for Range<'_>
{
fn shape<'a>(&'a self, _options: SExpressibleOptions) -> Shape<'a>
{
triple(self.span, "range", &self.start, &self.end)
}
}
impl Shaped for Binding<'_>
{
fn shape<'a>(&'a self, _options: SExpressibleOptions) -> Shape<'a>
{
Shape::Binding {
span: self.span,
name_span: self.name_span,
name: &self.name,
expression: &self.expression
}
}
}
impl Shaped for Expression<'_>
{
fn shape<'a>(&'a self, options: SExpressibleOptions) -> Shape<'a>
{
match self
{
Expression::Group(group) => group.shape(options),
Expression::Constant(constant) => constant.shape(options),
Expression::Variable(variable) => variable.shape(options),
Expression::Binding(binding) => binding.shape(options),
Expression::Range(range) => range.shape(options),
Expression::Dice(dice) => dice.shape(options),
Expression::Arithmetic(arithmetic) => arithmetic.shape(options)
}
}
}
impl Shaped for StandardDice<'_>
{
fn shape<'a>(&'a self, _options: SExpressibleOptions) -> Shape<'a>
{
triple(self.span, "standard-dice", &self.count, &self.faces)
}
}
impl Shaped for CustomDice<'_>
{
fn shape<'a>(&'a self, _options: SExpressibleOptions) -> Shape<'a>
{
Shape::Triple {
span: self.span,
keyword: "custom-dice",
first: Child::Expression(&self.count),
second: Part::Faces(&self.faces)
}
}
}
impl Shaped for DropLowest<'_>
{
fn shape<'a>(&'a self, _options: SExpressibleOptions) -> Shape<'a>
{
drop_clause(self.span, "drop-lowest", &self.dice, self.drop.as_deref())
}
}
impl Shaped for DropHighest<'_>
{
fn shape<'a>(&'a self, _options: SExpressibleOptions) -> Shape<'a>
{
drop_clause(self.span, "drop-highest", &self.dice, self.drop.as_deref())
}
}
impl Shaped for DiceExpression<'_>
{
fn shape<'a>(&'a self, options: SExpressibleOptions) -> Shape<'a>
{
match self
{
DiceExpression::Standard(dice) => dice.shape(options),
DiceExpression::Custom(dice) => dice.shape(options),
DiceExpression::DropLowest(clause) => clause.shape(options),
DiceExpression::DropHighest(clause) => clause.shape(options)
}
}
}
impl Shaped for Add<'_>
{
fn shape<'a>(&'a self, _options: SExpressibleOptions) -> Shape<'a>
{
triple(self.span, "add", &self.left, &self.right)
}
}
impl Shaped for Sub<'_>
{
fn shape<'a>(&'a self, _options: SExpressibleOptions) -> Shape<'a>
{
triple(self.span, "sub", &self.left, &self.right)
}
}
impl Shaped for Mul<'_>
{
fn shape<'a>(&'a self, _options: SExpressibleOptions) -> Shape<'a>
{
triple(self.span, "mul", &self.left, &self.right)
}
}
impl Shaped for Div<'_>
{
fn shape<'a>(&'a self, _options: SExpressibleOptions) -> Shape<'a>
{
triple(self.span, "div", &self.left, &self.right)
}
}
impl Shaped for Mod<'_>
{
fn shape<'a>(&'a self, _options: SExpressibleOptions) -> Shape<'a>
{
triple(self.span, "mod", &self.left, &self.right)
}
}
impl Shaped for Exp<'_>
{
fn shape<'a>(&'a self, _options: SExpressibleOptions) -> Shape<'a>
{
triple(self.span, "exp", &self.left, &self.right)
}
}
impl Shaped for Neg<'_>
{
fn shape<'a>(&'a self, _options: SExpressibleOptions) -> Shape<'a>
{
pair(self.span, "neg", Child::Expression(&self.operand))
}
}
impl Shaped for ArithmeticExpression<'_>
{
fn shape<'a>(&'a self, options: SExpressibleOptions) -> Shape<'a>
{
match self
{
ArithmeticExpression::Add(add) => add.shape(options),
ArithmeticExpression::Sub(sub) => sub.shape(options),
ArithmeticExpression::Mul(mul) => mul.shape(options),
ArithmeticExpression::Div(div) => div.shape(options),
ArithmeticExpression::Mod(r#mod) => r#mod.shape(options),
ArithmeticExpression::Exp(exp) => exp.shape(options),
ArithmeticExpression::Neg(neg) => neg.shape(options)
}
}
}
fn pair<'a>(
span: SourceSpan,
keyword: &'static str,
child: Child<'a>
) -> Shape<'a>
{
Shape::Pair {
span,
keyword,
child
}
}
fn triple<'a>(
span: SourceSpan,
keyword: &'static str,
first: &'a Expression<'a>,
second: &'a Expression<'a>
) -> Shape<'a>
{
Shape::Triple {
span,
keyword,
first: Child::Expression(first),
second: Part::Child(Child::Expression(second))
}
}
fn drop_clause<'a>(
span: SourceSpan,
keyword: &'static str,
dice: &'a DiceExpression<'a>,
drop: Option<&'a Expression<'a>>
) -> Shape<'a>
{
match drop
{
Some(drop) => Shape::Triple {
span,
keyword,
first: Child::Dice(dice),
second: Part::Child(Child::Expression(drop))
},
None => pair(span, keyword, Child::Dice(dice))
}
}