use std::{
error::Error,
fmt::{self, Display, Formatter, Write},
ops::Deref
};
use nom::{
IResult, Input,
bytes::complete::take_while,
error::{ErrorKind, ParseError as NomParseError}
};
use nom_locate::LocatedSpan;
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, Spanned}
};
pub trait SExpressible
{
fn write_s_expr(
&self,
f: &mut dyn Write,
remaining_space: usize,
options: SExpressibleOptions
) -> fmt::Result;
fn size_s_expr(&self, options: SExpressibleOptions) -> usize;
fn to_s_expr(&self, options: SExpressibleOptions) -> String
{
let mut buffer = String::new();
self.write_s_expr(&mut buffer, options.soft_limit, options)
.unwrap();
buffer
}
}
fn write_span_prefix(
f: &mut dyn Write,
span: SourceSpan,
options: SExpressibleOptions
) -> fmt::Result
{
if options.with_spans && span != SourceSpan::SYNTHETIC
{
write!(f, "^[{} {}] ", span.start, span.end)
}
else
{
Ok(())
}
}
fn span_prefix_size(span: SourceSpan, options: SExpressibleOptions) -> usize
{
if options.with_spans && span != SourceSpan::SYNTHETIC
{
5 + decimal_digits(span.start) + decimal_digits(span.end)
}
else
{
0
}
}
fn decimal_digits(n: usize) -> usize
{
if n == 0 { 1 } else { n.ilog10() as usize + 1 }
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct SExpressibleOptions
{
pub indent: usize,
pub tab_width: usize,
pub soft_limit: usize,
pub with_spans: bool,
pub with_groups: bool
}
impl SExpressibleOptions
{
#[inline]
pub fn new(indent: usize, tab_width: usize, soft_limit: usize) -> Self
{
Self {
indent,
tab_width,
soft_limit,
with_spans: false,
with_groups: false
}
}
#[inline]
pub fn increase_indent(&self) -> Self
{
Self {
indent: self.indent + 1,
tab_width: self.tab_width,
soft_limit: self.soft_limit,
with_spans: self.with_spans,
with_groups: self.with_groups
}
}
#[inline]
pub fn with_spans(mut self, value: bool) -> Self
{
self.with_spans = value;
self
}
#[inline]
pub fn with_groups(mut self, value: bool) -> Self
{
self.with_groups = value;
self
}
pub fn available_space(&self) -> usize
{
self.soft_limit.saturating_sub(self.indent * self.tab_width)
}
}
impl Default for SExpressibleOptions
{
fn default() -> Self
{
Self {
indent: 0,
tab_width: 4,
soft_limit: 80,
with_spans: false,
with_groups: false
}
}
}
impl<T> SExpressible for &T
where
T: SExpressible
{
#[inline]
fn write_s_expr(
&self,
f: &mut dyn Write,
remaining_space: usize,
options: SExpressibleOptions
) -> fmt::Result
{
(*self).write_s_expr(f, remaining_space, options)
}
#[inline]
fn size_s_expr(&self, options: SExpressibleOptions) -> usize
{
(*self).size_s_expr(options)
}
}
impl SExpressible for Option<Vec<Parameter<'_>>>
{
fn write_s_expr(
&self,
f: &mut dyn Write,
remaining_space: usize,
options: SExpressibleOptions
) -> fmt::Result
{
match self
{
Some(vec) => (&vec[..]).write_s_expr(f, remaining_space, options),
None => write!(f, "[]")
}
}
fn size_s_expr(&self, options: SExpressibleOptions) -> usize
{
match self
{
Some(vec) => (&vec[..]).size_s_expr(options),
None => 2
}
}
}
impl SExpressible for &[Parameter<'_>]
{
fn write_s_expr(
&self,
f: &mut dyn Write,
remaining_space: usize,
options: SExpressibleOptions
) -> fmt::Result
{
if self.is_empty()
{
write!(f, "[]")
}
else if remaining_space >= self.size_s_expr(options)
{
write!(f, "[")?;
for (i, item) in self.iter().enumerate()
{
if i > 0
{
write!(f, " ")?;
}
write_span_prefix(f, item.span, options)?;
write_ident(f, item.name)?;
}
write!(f, "]")
}
else
{
write!(f, "[")?;
for item in self.iter()
{
write!(f, "\n{}", "\t".repeat(options.indent + 1))?;
write_span_prefix(f, item.span, options)?;
write_ident(f, item.name)?;
}
write!(f, "\n{}]", "\t".repeat(options.indent))
}
}
fn size_s_expr(&self, options: SExpressibleOptions) -> usize
{
if self.is_empty()
{
return 2
}
self.iter()
.map(|p| span_prefix_size(p.span, options) + ident_size(p.name))
.sum::<usize>()
+ self.len()
+ 1
}
}
impl SExpressible for &[i32]
{
fn write_s_expr(
&self,
f: &mut dyn Write,
remaining_space: usize,
options: SExpressibleOptions
) -> fmt::Result
{
if self.is_empty()
{
write!(f, "[]")
}
else if remaining_space >= self.size_s_expr(options)
{
write!(f, "[")?;
for (i, item) in self.iter().enumerate()
{
if i > 0
{
write!(f, " ")?;
}
write!(f, "{}", item)?;
}
write!(f, "]")
}
else
{
write!(f, "[")?;
for item in self.iter()
{
write!(f, "\n{}{}", "\t".repeat(options.indent + 1), item)?;
}
write!(f, "\n{}]", "\t".repeat(options.indent))
}
}
fn size_s_expr(&self, _options: SExpressibleOptions) -> usize
{
if self.is_empty()
{
return 2
}
self.iter()
.map(|&x| {
(x.abs() as f64).log10().floor() as usize
+ if x < 0 { 1 } else { 0 }
+ 1
})
.sum::<usize>()
+ self.len()
+ 1
}
}
impl<A, B> SExpressible for (A, B)
where
A: AsRef<str>,
B: SExpressible
{
fn write_s_expr(
&self,
f: &mut dyn Write,
remaining_space: usize,
options: SExpressibleOptions
) -> fmt::Result
{
let keyword = self.0.as_ref();
write!(f, "({}", keyword)?;
let sub = &self.1;
let space_needed = self.size_s_expr(options) + options.indent;
let remaining_space = if remaining_space >= space_needed
{
write!(f, " ")?;
usize::MAX
}
else
{
let options = options.increase_indent();
write!(f, "\n{}", "\t".repeat(options.indent))?;
options.available_space()
};
sub.write_s_expr(f, remaining_space, options)?;
write!(f, ")")
}
fn size_s_expr(&self, options: SExpressibleOptions) -> usize
{
3 + self.0.as_ref().len() + self.1.size_s_expr(options)
}
}
impl<A, B, C> SExpressible for (A, B, C)
where
A: AsRef<str>,
B: SExpressible,
C: SExpressible
{
fn write_s_expr(
&self,
f: &mut dyn Write,
remaining_space: usize,
options: SExpressibleOptions
) -> fmt::Result
{
let keyword = self.0.as_ref();
write!(f, "({}", keyword)?;
let sub1 = &self.1;
let sub2 = &self.2;
let space_needed = self.size_s_expr(options) + options.indent;
if remaining_space >= space_needed
{
write!(f, " ")?;
sub1.write_s_expr(f, usize::MAX, options)?;
write!(f, " ")?;
sub2.write_s_expr(f, usize::MAX, options)?;
}
else
{
let options = options.increase_indent();
write!(f, "\n{}", "\t".repeat(options.indent))?;
sub1.write_s_expr(f, options.available_space(), options)?;
write!(f, "\n{}", "\t".repeat(options.indent))?;
sub2.write_s_expr(f, options.available_space(), options)?;
};
write!(f, ")")
}
fn size_s_expr(&self, options: SExpressibleOptions) -> usize
{
4 + self.0.as_ref().len()
+ self.1.size_s_expr(options)
+ self.2.size_s_expr(options)
}
}
fn needs_quoting(ident: &str) -> bool
{
ident.contains(|c: char| {
c.is_whitespace() || matches!(c, '(' | ')' | '[' | ']' | '"')
})
}
fn write_ident(f: &mut dyn Write, ident: &str) -> fmt::Result
{
if needs_quoting(ident)
{
write!(f, "\"{}\"", ident)
}
else
{
write!(f, "{}", ident)
}
}
fn ident_size(ident: &str) -> usize
{
if needs_quoting(ident)
{
ident.len() + 2
}
else
{
ident.len()
}
}
impl SExpressible for Function<'_>
{
fn write_s_expr(
&self,
f: &mut dyn Write,
remaining_space: usize,
options: SExpressibleOptions
) -> fmt::Result
{
write_span_prefix(f, self.span, options)?;
let remaining_space = remaining_space
.saturating_sub(span_prefix_size(self.span, options));
let keyword = "function";
write!(f, "({}", keyword)?;
let remaining_space = remaining_space.saturating_sub(9);
let params = &self.parameters;
let space_needed = params.size_s_expr(options);
let remaining_space = if remaining_space >= space_needed
{
write!(f, " ")?;
params.write_s_expr(f, remaining_space, options)?;
remaining_space - space_needed
}
else
{
let options = options.increase_indent();
write!(f, "\n{}", "\t".repeat(options.indent))?;
params.write_s_expr(f, options.available_space(), options)?;
options.available_space()
};
let body = &self.body;
let space_needed = body.size_s_expr(options);
if remaining_space >= space_needed
{
write!(f, " ")?;
body.write_s_expr(f, remaining_space, options)?;
}
else
{
let options = options.increase_indent();
write!(f, "\n{}", "\t".repeat(options.indent))?;
body.write_s_expr(f, options.available_space(), options)?;
};
write!(f, ")")
}
fn size_s_expr(&self, options: SExpressibleOptions) -> usize
{
span_prefix_size(self.span, options)
+ 12 + self.parameters.size_s_expr(options)
+ self.body.size_s_expr(options)
}
}
impl SExpressible for Group<'_>
{
fn write_s_expr(
&self,
f: &mut dyn Write,
remaining_space: usize,
options: SExpressibleOptions
) -> fmt::Result
{
if options.with_groups
{
write_span_prefix(f, self.span, options)?;
let remaining_space = remaining_space
.saturating_sub(span_prefix_size(self.span, options));
("group", self.expression.deref()).write_s_expr(
f,
remaining_space,
options
)
}
else
{
self.expression.write_s_expr(f, remaining_space, options)
}
}
fn size_s_expr(&self, options: SExpressibleOptions) -> usize
{
if options.with_groups
{
span_prefix_size(self.span, options)
+ ("group", self.expression.deref()).size_s_expr(options)
}
else
{
self.expression.size_s_expr(options)
}
}
}
impl SExpressible for Constant
{
fn write_s_expr(
&self,
f: &mut dyn Write,
_remaining_space: usize,
options: SExpressibleOptions
) -> fmt::Result
{
write_span_prefix(f, self.span, options)?;
write!(f, "{}", self.value)
}
fn size_s_expr(&self, options: SExpressibleOptions) -> usize
{
let body = if self.value == 0
{
1
}
else
{
format!("{}", self.value).len()
};
span_prefix_size(self.span, options) + body
}
}
impl SExpressible for Variable<'_>
{
fn write_s_expr(
&self,
f: &mut dyn Write,
_remaining_space: usize,
options: SExpressibleOptions
) -> fmt::Result
{
write_span_prefix(f, self.span, options)?;
write_ident(f, self.name)
}
fn size_s_expr(&self, options: SExpressibleOptions) -> usize
{
span_prefix_size(self.span, options) + ident_size(self.name)
}
}
impl SExpressible for Range<'_>
{
fn write_s_expr(
&self,
f: &mut dyn Write,
remaining_space: usize,
options: SExpressibleOptions
) -> fmt::Result
{
write_span_prefix(f, self.span, options)?;
let remaining_space = remaining_space
.saturating_sub(span_prefix_size(self.span, options));
("range", self.start.deref(), self.end.deref()).write_s_expr(
f,
remaining_space,
options
)
}
fn size_s_expr(&self, options: SExpressibleOptions) -> usize
{
span_prefix_size(self.span, options)
+ ("range", self.start.deref(), self.end.deref())
.size_s_expr(options)
}
}
impl SExpressible for Binding<'_>
{
fn write_s_expr(
&self,
f: &mut dyn Write,
remaining_space: usize,
options: SExpressibleOptions
) -> fmt::Result
{
write_span_prefix(f, self.span, options)?;
let remaining_space = remaining_space
.saturating_sub(span_prefix_size(self.span, options));
let keyword = "binding";
write!(f, "({}", keyword)?;
let remaining_space = remaining_space.saturating_sub(1 + keyword.len());
let name_size =
span_prefix_size(self.name_span, options) + ident_size(self.name);
let body = self.expression.deref();
let body_size = body.size_s_expr(options);
let space_needed = name_size + 1 + body_size + 1 + options.indent;
if remaining_space >= space_needed
{
write!(f, " ")?;
write_span_prefix(f, self.name_span, options)?;
write_ident(f, self.name)?;
write!(f, " ")?;
body.write_s_expr(f, usize::MAX, options)?;
}
else
{
let options = options.increase_indent();
write!(f, "\n{}", "\t".repeat(options.indent))?;
write_span_prefix(f, self.name_span, options)?;
write_ident(f, self.name)?;
write!(f, "\n{}", "\t".repeat(options.indent))?;
body.write_s_expr(f, options.available_space(), options)?;
}
write!(f, ")")
}
fn size_s_expr(&self, options: SExpressibleOptions) -> usize
{
span_prefix_size(self.span, options)
+ 2 + "binding".len()
+ 2 + span_prefix_size(self.name_span, options)
+ ident_size(self.name)
+ self.expression.size_s_expr(options)
}
}
impl SExpressible for Expression<'_>
{
fn write_s_expr(
&self,
f: &mut dyn Write,
remaining_space: usize,
options: SExpressibleOptions
) -> fmt::Result
{
match self
{
Expression::Group(group) =>
{
group.write_s_expr(f, remaining_space, options)
},
Expression::Constant(constant) =>
{
constant.write_s_expr(f, remaining_space, options)
},
Expression::Variable(variable) =>
{
variable.write_s_expr(f, remaining_space, options)
},
Expression::Binding(binding) =>
{
binding.write_s_expr(f, remaining_space, options)
},
Expression::Range(range) =>
{
range.write_s_expr(f, remaining_space, options)
},
Expression::Dice(dice) =>
{
dice.write_s_expr(f, remaining_space, options)
},
Expression::Arithmetic(arithmetic) =>
{
arithmetic.write_s_expr(f, remaining_space, options)
},
}
}
fn size_s_expr(&self, options: SExpressibleOptions) -> usize
{
match self
{
Expression::Group(group) => group.size_s_expr(options),
Expression::Constant(constant) => constant.size_s_expr(options),
Expression::Variable(variable) => variable.size_s_expr(options),
Expression::Binding(binding) => binding.size_s_expr(options),
Expression::Range(range) => range.size_s_expr(options),
Expression::Dice(dice) => dice.size_s_expr(options),
Expression::Arithmetic(arithmetic) =>
{
arithmetic.size_s_expr(options)
},
}
}
}
impl SExpressible for StandardDice<'_>
{
fn write_s_expr(
&self,
f: &mut dyn Write,
remaining_spaces: usize,
options: SExpressibleOptions
) -> fmt::Result
{
write_span_prefix(f, self.span, options)?;
let remaining_spaces = remaining_spaces
.saturating_sub(span_prefix_size(self.span, options));
("standard-dice", self.count.deref(), self.faces.deref()).write_s_expr(
f,
remaining_spaces,
options
)
}
fn size_s_expr(&self, options: SExpressibleOptions) -> usize
{
span_prefix_size(self.span, options)
+ ("standard-dice", self.count.deref(), self.faces.deref())
.size_s_expr(options)
}
}
impl SExpressible for CustomDice<'_>
{
fn write_s_expr(
&self,
f: &mut dyn Write,
remaining_spaces: usize,
options: SExpressibleOptions
) -> fmt::Result
{
write_span_prefix(f, self.span, options)?;
let remaining_spaces = remaining_spaces
.saturating_sub(span_prefix_size(self.span, options));
("custom-dice", self.count.deref(), self.faces.deref()).write_s_expr(
f,
remaining_spaces,
options
)
}
fn size_s_expr(&self, options: SExpressibleOptions) -> usize
{
span_prefix_size(self.span, options)
+ ("custom-dice", self.count.deref(), self.faces.deref())
.size_s_expr(options)
}
}
impl SExpressible for DropLowest<'_>
{
fn write_s_expr(
&self,
f: &mut dyn Write,
remaining_spaces: usize,
options: SExpressibleOptions
) -> fmt::Result
{
write_span_prefix(f, self.span, options)?;
let remaining_spaces = remaining_spaces
.saturating_sub(span_prefix_size(self.span, options));
match self.drop.as_ref()
{
Some(drop) => ("drop-lowest", self.dice.deref(), drop.deref())
.write_s_expr(f, remaining_spaces, options),
None => ("drop-lowest", self.dice.deref()).write_s_expr(
f,
remaining_spaces,
options
)
}
}
fn size_s_expr(&self, options: SExpressibleOptions) -> usize
{
span_prefix_size(self.span, options)
+ match self.drop.as_ref()
{
Some(drop) => ("drop-lowest", self.dice.deref(), drop.deref())
.size_s_expr(options),
None => ("drop-lowest", self.dice.deref()).size_s_expr(options)
}
}
}
impl SExpressible for DropHighest<'_>
{
fn write_s_expr(
&self,
f: &mut dyn Write,
remaining_spaces: usize,
options: SExpressibleOptions
) -> fmt::Result
{
write_span_prefix(f, self.span, options)?;
let remaining_spaces = remaining_spaces
.saturating_sub(span_prefix_size(self.span, options));
match self.drop.as_ref()
{
Some(drop) => ("drop-highest", self.dice.deref(), drop.deref())
.write_s_expr(f, remaining_spaces, options),
None => ("drop-highest", self.dice.deref()).write_s_expr(
f,
remaining_spaces,
options
)
}
}
fn size_s_expr(&self, options: SExpressibleOptions) -> usize
{
span_prefix_size(self.span, options)
+ match self.drop.as_ref()
{
Some(drop) => ("drop-highest", self.dice.deref(), drop.deref())
.size_s_expr(options),
None => ("drop-highest", self.dice.deref()).size_s_expr(options)
}
}
}
impl SExpressible for DiceExpression<'_>
{
fn write_s_expr(
&self,
f: &mut dyn Write,
remaining_space: usize,
options: SExpressibleOptions
) -> fmt::Result
{
match self
{
DiceExpression::Standard(dice) =>
{
dice.write_s_expr(f, remaining_space, options)
},
DiceExpression::Custom(dice) =>
{
dice.write_s_expr(f, remaining_space, options)
},
DiceExpression::DropLowest(drop) =>
{
drop.write_s_expr(f, remaining_space, options)
},
DiceExpression::DropHighest(drop) =>
{
drop.write_s_expr(f, remaining_space, options)
},
}
}
fn size_s_expr(&self, options: SExpressibleOptions) -> usize
{
match self
{
DiceExpression::Standard(dice) => dice.size_s_expr(options),
DiceExpression::Custom(dice) => dice.size_s_expr(options),
DiceExpression::DropLowest(drop) => drop.size_s_expr(options),
DiceExpression::DropHighest(drop) => drop.size_s_expr(options)
}
}
}
impl SExpressible for Add<'_>
{
fn write_s_expr(
&self,
f: &mut dyn Write,
remaining_space: usize,
options: SExpressibleOptions
) -> fmt::Result
{
write_span_prefix(f, self.span, options)?;
let remaining_space = remaining_space
.saturating_sub(span_prefix_size(self.span, options));
("add", self.left.deref(), self.right.deref()).write_s_expr(
f,
remaining_space,
options
)
}
fn size_s_expr(&self, options: SExpressibleOptions) -> usize
{
span_prefix_size(self.span, options)
+ ("add", self.left.deref(), self.right.deref())
.size_s_expr(options)
}
}
impl SExpressible for Sub<'_>
{
fn write_s_expr(
&self,
f: &mut dyn Write,
remaining_space: usize,
options: SExpressibleOptions
) -> fmt::Result
{
write_span_prefix(f, self.span, options)?;
let remaining_space = remaining_space
.saturating_sub(span_prefix_size(self.span, options));
("sub", self.left.deref(), self.right.deref()).write_s_expr(
f,
remaining_space,
options
)
}
fn size_s_expr(&self, options: SExpressibleOptions) -> usize
{
span_prefix_size(self.span, options)
+ ("sub", self.left.deref(), self.right.deref())
.size_s_expr(options)
}
}
impl SExpressible for Mul<'_>
{
fn write_s_expr(
&self,
f: &mut dyn Write,
remaining_space: usize,
options: SExpressibleOptions
) -> fmt::Result
{
write_span_prefix(f, self.span, options)?;
let remaining_space = remaining_space
.saturating_sub(span_prefix_size(self.span, options));
("mul", self.left.deref(), self.right.deref()).write_s_expr(
f,
remaining_space,
options
)
}
fn size_s_expr(&self, options: SExpressibleOptions) -> usize
{
span_prefix_size(self.span, options)
+ ("mul", self.left.deref(), self.right.deref())
.size_s_expr(options)
}
}
impl SExpressible for Div<'_>
{
fn write_s_expr(
&self,
f: &mut dyn Write,
remaining_space: usize,
options: SExpressibleOptions
) -> fmt::Result
{
write_span_prefix(f, self.span, options)?;
let remaining_space = remaining_space
.saturating_sub(span_prefix_size(self.span, options));
("div", self.left.deref(), self.right.deref()).write_s_expr(
f,
remaining_space,
options
)
}
fn size_s_expr(&self, options: SExpressibleOptions) -> usize
{
span_prefix_size(self.span, options)
+ ("div", self.left.deref(), self.right.deref())
.size_s_expr(options)
}
}
impl SExpressible for Mod<'_>
{
fn write_s_expr(
&self,
f: &mut dyn Write,
remaining_space: usize,
options: SExpressibleOptions
) -> fmt::Result
{
write_span_prefix(f, self.span, options)?;
let remaining_space = remaining_space
.saturating_sub(span_prefix_size(self.span, options));
("mod", self.left.deref(), self.right.deref()).write_s_expr(
f,
remaining_space,
options
)
}
fn size_s_expr(&self, options: SExpressibleOptions) -> usize
{
span_prefix_size(self.span, options)
+ ("mod", self.left.deref(), self.right.deref())
.size_s_expr(options)
}
}
impl SExpressible for Exp<'_>
{
fn write_s_expr(
&self,
f: &mut dyn Write,
remaining_space: usize,
options: SExpressibleOptions
) -> fmt::Result
{
write_span_prefix(f, self.span, options)?;
let remaining_space = remaining_space
.saturating_sub(span_prefix_size(self.span, options));
("exp", self.left.deref(), self.right.deref()).write_s_expr(
f,
remaining_space,
options
)
}
fn size_s_expr(&self, options: SExpressibleOptions) -> usize
{
span_prefix_size(self.span, options)
+ ("exp", self.left.deref(), self.right.deref())
.size_s_expr(options)
}
}
impl SExpressible for Neg<'_>
{
fn write_s_expr(
&self,
f: &mut dyn Write,
remaining_space: usize,
options: SExpressibleOptions
) -> fmt::Result
{
write_span_prefix(f, self.span, options)?;
let remaining_space = remaining_space
.saturating_sub(span_prefix_size(self.span, options));
("neg", self.operand.deref()).write_s_expr(f, remaining_space, options)
}
fn size_s_expr(&self, options: SExpressibleOptions) -> usize
{
span_prefix_size(self.span, options)
+ ("neg", self.operand.deref()).size_s_expr(options)
}
}
impl SExpressible for ArithmeticExpression<'_>
{
fn write_s_expr(
&self,
f: &mut dyn Write,
remaining_space: usize,
options: SExpressibleOptions
) -> fmt::Result
{
match self
{
ArithmeticExpression::Add(add) =>
{
add.write_s_expr(f, remaining_space, options)
},
ArithmeticExpression::Sub(sub) =>
{
sub.write_s_expr(f, remaining_space, options)
},
ArithmeticExpression::Mul(mul) =>
{
mul.write_s_expr(f, remaining_space, options)
},
ArithmeticExpression::Div(div) =>
{
div.write_s_expr(f, remaining_space, options)
},
ArithmeticExpression::Mod(r#mod) =>
{
r#mod.write_s_expr(f, remaining_space, options)
},
ArithmeticExpression::Exp(exp) =>
{
exp.write_s_expr(f, remaining_space, options)
},
ArithmeticExpression::Neg(neg) =>
{
neg.write_s_expr(f, remaining_space, options)
},
}
}
fn size_s_expr(&self, options: SExpressibleOptions) -> usize
{
match self
{
ArithmeticExpression::Add(add) => add.size_s_expr(options),
ArithmeticExpression::Sub(sub) => sub.size_s_expr(options),
ArithmeticExpression::Mul(mul) => mul.size_s_expr(options),
ArithmeticExpression::Div(div) => div.size_s_expr(options),
ArithmeticExpression::Mod(r#mod) => r#mod.size_s_expr(options),
ArithmeticExpression::Exp(exp) => exp.size_s_expr(options),
ArithmeticExpression::Neg(neg) => neg.size_s_expr(options)
}
}
}
pub fn read_s_expr(input: &str) -> Result<Function<'_>, SExprError>
{
let span = Span::new(input);
let (rest, function) = read_function(span).map_err(|e| match e
{
nom::Err::Error(e) | nom::Err::Failure(e) => e,
nom::Err::Incomplete(_) => unreachable!()
})?;
if !rest.fragment().is_empty()
{
return Err(SExprError::TrailingInput {
text: rest.fragment().to_string(),
location: SExprLocation::of(rest)
})
}
Ok(function)
}
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
pub struct SExprLocation
{
pub offset: usize,
pub line: u32,
pub column: usize
}
impl SExprLocation
{
fn of(span: Span<'_>) -> Self
{
Self {
offset: span.location_offset(),
line: span.location_line(),
column: span.get_utf8_column()
}
}
}
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub enum SExprError
{
Syntax
{
description: String,
location: SExprLocation
},
ExpectedChar
{
expected: char,
found: Option<char>,
location: SExprLocation
},
ExpectedWord
{
location: SExprLocation
},
UnterminatedQuotedIdent
{
location: SExprLocation
},
InvalidInteger
{
text: String,
reason: String,
location: SExprLocation
},
InvalidByteOffset
{
text: String,
reason: String,
location: SExprLocation
},
UnrecognizedSpanShape
{
found: Option<char>,
location: SExprLocation
},
InvertedSpan
{
start: usize,
end: usize,
location: SExprLocation
},
ChildSpanEscapesParent
{
child: SourceSpan,
parent: SourceSpan,
location: SExprLocation
},
SiblingSpanOutOfOrder
{
prev: SourceSpan,
next: SourceSpan,
location: SExprLocation
},
FacelessCustomDice
{
location: SExprLocation
},
ExpectedDiceExpression
{
location: SExprLocation
},
ExpectedTopLevelFunction
{
found: String,
location: SExprLocation
},
NestedFunctionKeyword
{
location: SExprLocation
},
UnknownKeyword
{
keyword: String,
location: SExprLocation
},
ExpectedExpression
{
location: SExprLocation
},
TrailingInput
{
text: String,
location: SExprLocation
}
}
impl SExprError
{
pub fn location(&self) -> SExprLocation
{
match self
{
Self::Syntax { location, .. }
| Self::ExpectedChar { location, .. }
| Self::ExpectedWord { location }
| Self::UnterminatedQuotedIdent { location }
| Self::InvalidInteger { location, .. }
| Self::InvalidByteOffset { location, .. }
| Self::UnrecognizedSpanShape { location, .. }
| Self::InvertedSpan { location, .. }
| Self::ChildSpanEscapesParent { location, .. }
| Self::SiblingSpanOutOfOrder { location, .. }
| Self::FacelessCustomDice { location }
| Self::ExpectedDiceExpression { location }
| Self::ExpectedTopLevelFunction { location, .. }
| Self::NestedFunctionKeyword { location }
| Self::UnknownKeyword { location, .. }
| Self::ExpectedExpression { location }
| Self::TrailingInput { location, .. } => *location
}
}
}
impl Display for SExprError
{
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result
{
let loc = self.location();
write!(
f,
"S-expression error at line {}, column {} (byte {}): ",
loc.line, loc.column, loc.offset
)?;
match self
{
Self::Syntax { description, .. } =>
{
write!(f, "syntax error ({})", description)
},
Self::ExpectedChar {
expected,
found: None,
..
} => write!(f, "expected '{}', found end of input", expected),
Self::ExpectedChar {
expected,
found: Some(c),
..
} => write!(f, "expected '{}', found '{}'", expected, c),
Self::ExpectedWord { .. } => write!(f, "expected a word"),
Self::UnterminatedQuotedIdent { .. } =>
{
write!(f, "unterminated quoted identifier")
},
Self::InvalidInteger { text, reason, .. } =>
{
write!(f, "invalid integer '{}': {}", text, reason)
},
Self::InvalidByteOffset { text, reason, .. } =>
{
write!(f, "invalid byte offset '{}': {}", text, reason)
},
Self::UnrecognizedSpanShape { found: None, .. } =>
{
write!(f, "expected '[' after '^', found end of input")
},
Self::UnrecognizedSpanShape { found: Some(c), .. } => write!(
f,
"expected '[' after '^' (only '^[start end]' span metadata \
is supported), found '{}'",
c
),
Self::InvertedSpan { start, end, .. } =>
{
write!(f, "span start {} exceeds end {}", start, end)
},
Self::ChildSpanEscapesParent { child, parent, .. } => write!(
f,
"child span {}..{} escapes parent span {}..{}",
child.start, child.end, parent.start, parent.end
),
Self::SiblingSpanOutOfOrder { prev, next, .. } => write!(
f,
"sibling span {}..{} overlaps or precedes prior sibling span \
{}..{}",
next.start, next.end, prev.start, prev.end
),
Self::FacelessCustomDice { .. } =>
{
write!(f, "custom dice faces list must not be empty")
},
Self::ExpectedDiceExpression { .. } => write!(
f,
"expected dice expression as first argument to \
drop-lowest/drop-highest"
),
Self::ExpectedTopLevelFunction { found, .. } =>
{
write!(f, "expected 'function', found '{}'", found)
},
Self::NestedFunctionKeyword { .. } =>
{
write!(f, "unexpected 'function' inside expression")
},
Self::UnknownKeyword { keyword, .. } =>
{
write!(f, "unknown keyword '{}'", keyword)
},
Self::ExpectedExpression { .. } =>
{
write!(f, "expected expression, found end of input")
},
Self::TrailingInput { text, .. } =>
{
write!(f, "trailing input: '{}'", text)
}
}
}
}
impl Error for SExprError {}
impl<'src> NomParseError<Span<'src>> for SExprError
{
fn from_error_kind(input: Span<'src>, kind: ErrorKind) -> Self
{
Self::Syntax {
description: format!("nom::{:?}", kind),
location: SExprLocation::of(input)
}
}
fn append(_input: Span<'src>, _kind: ErrorKind, other: Self) -> Self
{
other
}
}
type Span<'src> = LocatedSpan<&'src str>;
type SExprResult<'src, T> = IResult<Span<'src>, T, SExprError>;
fn skip_ws(input: Span<'_>) -> SExprResult<'_, ()>
{
let (input, _) = take_while(|c: char| c.is_ascii_whitespace())(input)?;
Ok((input, ()))
}
fn expect_char<'src>(
expected: char
) -> impl FnMut(Span<'src>) -> SExprResult<'src, ()>
{
move |input| {
let (input, _) = skip_ws(input)?;
match input.fragment().chars().next()
{
Some(c) if c == expected =>
{
let len = c.len_utf8();
Ok((input.take_from(len), ()))
},
Some(c) => Err(nom::Err::Failure(SExprError::ExpectedChar {
expected,
found: Some(c),
location: SExprLocation::of(input)
})),
None => Err(nom::Err::Failure(SExprError::ExpectedChar {
expected,
found: None,
location: SExprLocation::of(input)
}))
}
}
}
fn read_word(input: Span<'_>) -> SExprResult<'_, Span<'_>>
{
let (input, _) = skip_ws(input)?;
let mark = input;
let (rest, word) = take_while(|c: char| {
!c.is_ascii_whitespace() && !matches!(c, '(' | ')' | '[' | ']')
})(input)?;
if word.fragment().is_empty()
{
Err(nom::Err::Failure(SExprError::ExpectedWord {
location: SExprLocation::of(mark)
}))
}
else
{
Ok((rest, word))
}
}
fn read_ident(input: Span<'_>) -> SExprResult<'_, &str>
{
let (input, _) = skip_ws(input)?;
match input.fragment().chars().next()
{
Some('"') =>
{
let mark = input;
let after_quote = input.take_from(1);
match after_quote.fragment().find('"')
{
Some(pos) =>
{
let ident = after_quote.take(pos);
let rest = after_quote.take_from(pos + 1);
Ok((rest, *ident.fragment()))
},
None => Err(nom::Err::Failure(
SExprError::UnterminatedQuotedIdent {
location: SExprLocation::of(mark)
}
))
}
},
_ =>
{
let (rest, word) = read_word(input)?;
Ok((rest, *word.fragment()))
}
}
}
fn read_integer(input: Span<'_>) -> SExprResult<'_, i32>
{
let (rest, word) = read_word(input)?;
match word.fragment().parse::<i32>()
{
Ok(value) => Ok((rest, value)),
Err(e) => Err(nom::Err::Failure(SExprError::InvalidInteger {
text: word.fragment().to_string(),
reason: e.to_string(),
location: SExprLocation::of(word)
}))
}
}
fn read_usize(input: Span<'_>) -> SExprResult<'_, usize>
{
let (rest, word) = read_word(input)?;
match word.fragment().parse::<usize>()
{
Ok(value) => Ok((rest, value)),
Err(e) => Err(nom::Err::Failure(SExprError::InvalidByteOffset {
text: word.fragment().to_string(),
reason: e.to_string(),
location: SExprLocation::of(word)
}))
}
}
fn read_span_prefix(input: Span<'_>) -> SExprResult<'_, SourceSpan>
{
let (input, _) = skip_ws(input)?;
match input.fragment().chars().next()
{
Some('^') =>
{
let caret_mark = input;
let after_caret = input.take_from(1);
match after_caret.fragment().chars().next()
{
Some('[') =>
{
let after_bracket = after_caret.take_from(1);
let (input, start) = read_usize(after_bracket)?;
let (input, end) = read_usize(input)?;
let (input, _) = expect_char(']')(input)?;
if start > end
{
Err(nom::Err::Failure(SExprError::InvertedSpan {
start,
end,
location: SExprLocation::of(caret_mark)
}))
}
else
{
Ok((input, SourceSpan { start, end }))
}
},
Some(c) =>
{
Err(nom::Err::Failure(SExprError::UnrecognizedSpanShape {
found: Some(c),
location: SExprLocation::of(after_caret)
}))
},
None =>
{
Err(nom::Err::Failure(SExprError::UnrecognizedSpanShape {
found: None,
location: SExprLocation::of(after_caret)
}))
},
}
},
_ => Ok((input, SourceSpan::default()))
}
}
fn validate_containment(
parent: SourceSpan,
child: SourceSpan,
at: Span<'_>
) -> Result<(), nom::Err<SExprError>>
{
if parent == SourceSpan::SYNTHETIC || child == SourceSpan::SYNTHETIC
{
return Ok(())
}
if parent.start <= child.start && child.end <= parent.end
{
Ok(())
}
else
{
Err(nom::Err::Failure(SExprError::ChildSpanEscapesParent {
child,
parent,
location: SExprLocation::of(at)
}))
}
}
fn validate_sibling_order(
prev: SourceSpan,
next: SourceSpan,
at: Span<'_>
) -> Result<(), nom::Err<SExprError>>
{
if prev == SourceSpan::SYNTHETIC || next == SourceSpan::SYNTHETIC
{
return Ok(())
}
if prev.end <= next.start
{
Ok(())
}
else
{
Err(nom::Err::Failure(SExprError::SiblingSpanOutOfOrder {
prev,
next,
location: SExprLocation::of(at)
}))
}
}
fn read_function(input: Span<'_>) -> SExprResult<'_, Function<'_>>
{
let (input, span) = read_span_prefix(input)?;
let (input, _) = expect_char('(')(input)?;
let (input, _) = skip_ws(input)?;
let kw_mark = input;
let (input, kw) = read_word(input)?;
let keyword = *kw.fragment();
if keyword != "function"
{
return Err(nom::Err::Failure(SExprError::ExpectedTopLevelFunction {
found: keyword.to_string(),
location: SExprLocation::of(kw_mark)
}))
}
let (input, (parameters, last_param_span)) = read_params(input, span)?;
let (input, body) = read_child(input, span, last_param_span)?;
let (input, _) = expect_char(')')(input)?;
let (input, _) = skip_ws(input)?;
Ok((
input,
Function {
parameters,
body,
span
}
))
}
fn read_params<'src>(
input: Span<'src>,
parent: SourceSpan
) -> SExprResult<'src, (Option<Vec<Parameter<'src>>>, SourceSpan)>
{
let (input, _) = expect_char('[')(input)?;
let mut params: Vec<Parameter<'src>> = Vec::new();
let mut prev_sibling = SourceSpan::default();
let mut cur = input;
loop
{
let (next, _) = skip_ws(cur)?;
match next.fragment().chars().next()
{
Some(']') =>
{
cur = next.take_from(1);
break
},
_ =>
{
let mark = next;
let (next, span) = read_span_prefix(next)?;
let (next, name) = read_ident(next)?;
validate_containment(parent, span, mark)?;
validate_sibling_order(prev_sibling, span, mark)?;
prev_sibling = span;
params.push(Parameter { name, span });
cur = next;
}
}
}
let result = if params.is_empty()
{
None
}
else
{
Some(params)
};
Ok((cur, (result, prev_sibling)))
}
fn read_faces(input: Span<'_>) -> SExprResult<'_, Vec<i32>>
{
let (input, _) = expect_char('[')(input)?;
let mut faces = Vec::new();
let mut cur = input;
loop
{
let (next, _) = skip_ws(cur)?;
match next.fragment().chars().next()
{
Some(']') =>
{
cur = next.take_from(1);
break
},
_ =>
{
let (next, value) = read_integer(next)?;
faces.push(value);
cur = next;
}
}
}
if faces.is_empty()
{
Err(nom::Err::Failure(SExprError::FacelessCustomDice {
location: SExprLocation::of(cur)
}))
}
else
{
Ok((cur, faces))
}
}
fn read_child<'src>(
input: Span<'src>,
parent: SourceSpan,
prev_sibling: SourceSpan
) -> SExprResult<'src, Expression<'src>>
{
let (input, _) = skip_ws(input)?;
let mark = input;
let (input, expr) = read_expr(input)?;
let child = expr.span();
validate_containment(parent, child, mark)?;
validate_sibling_order(prev_sibling, child, mark)?;
Ok((input, expr))
}
fn read_dice_child<'src>(
input: Span<'src>,
parent: SourceSpan,
prev_sibling: SourceSpan
) -> SExprResult<'src, DiceExpression<'src>>
{
let mark = input;
let (input, expr) = read_child(input, parent, prev_sibling)?;
match expr
{
Expression::Dice(d) => Ok((input, d)),
_ => Err(nom::Err::Failure(SExprError::ExpectedDiceExpression {
location: SExprLocation::of(mark)
}))
}
}
fn read_optional_drop<'src>(
input: Span<'src>,
parent: SourceSpan,
prev_sibling: SourceSpan
) -> SExprResult<'src, Option<Box<Expression<'src>>>>
{
let (probe, _) = skip_ws(input)?;
if probe.fragment().starts_with(')')
{
return Ok((input, None))
}
let (input, drop) = read_child(input, parent, prev_sibling)?;
Ok((input, Some(Box::new(drop))))
}
fn read_binary<'src, F>(
input: Span<'src>,
parent: SourceSpan,
f: F
) -> SExprResult<'src, Expression<'src>>
where
F: FnOnce(Expression<'src>, Expression<'src>) -> Expression<'src>
{
let (input, left) = read_child(input, parent, SourceSpan::default())?;
let left_span = left.span();
let (input, right) = read_child(input, parent, left_span)?;
Ok((input, f(left, right)))
}
fn read_expr(input: Span<'_>) -> SExprResult<'_, Expression<'_>>
{
let (input, span) = read_span_prefix(input)?;
let (input, _) = skip_ws(input)?;
match input.fragment().chars().next()
{
Some('(') =>
{
let input = input.take_from(1);
let (input, _) = skip_ws(input)?;
let kw_mark = input;
let (input, kw) = read_word(input)?;
let keyword = *kw.fragment();
let (input, expr) = match keyword
{
"add" => read_binary(input, span, |l, r| {
Expression::Arithmetic(ArithmeticExpression::Add(Add {
left: Box::new(l),
right: Box::new(r),
span
}))
}),
"sub" => read_binary(input, span, |l, r| {
Expression::Arithmetic(ArithmeticExpression::Sub(Sub {
left: Box::new(l),
right: Box::new(r),
span
}))
}),
"mul" => read_binary(input, span, |l, r| {
Expression::Arithmetic(ArithmeticExpression::Mul(Mul {
left: Box::new(l),
right: Box::new(r),
span
}))
}),
"div" => read_binary(input, span, |l, r| {
Expression::Arithmetic(ArithmeticExpression::Div(Div {
left: Box::new(l),
right: Box::new(r),
span
}))
}),
"mod" => read_binary(input, span, |l, r| {
Expression::Arithmetic(ArithmeticExpression::Mod(Mod {
left: Box::new(l),
right: Box::new(r),
span
}))
}),
"exp" => read_binary(input, span, |l, r| {
Expression::Arithmetic(ArithmeticExpression::Exp(Exp {
left: Box::new(l),
right: Box::new(r),
span
}))
}),
"neg" =>
{
let (input, operand) =
read_child(input, span, SourceSpan::default())?;
Ok((
input,
Expression::Arithmetic(ArithmeticExpression::Neg(
Neg {
operand: Box::new(operand),
span
}
))
))
},
"group" =>
{
let (input, inner) =
read_child(input, span, SourceSpan::default())?;
Ok((
input,
Expression::Group(Group {
expression: Box::new(inner),
span
})
))
},
"standard-dice" =>
{
let (input, count) =
read_child(input, span, SourceSpan::default())?;
let count_span = count.span();
let (input, faces) = read_child(input, span, count_span)?;
Ok((
input,
Expression::Dice(DiceExpression::Standard(
StandardDice {
count: Box::new(count),
faces: Box::new(faces),
span
}
))
))
},
"custom-dice" =>
{
let (input, count) =
read_child(input, span, SourceSpan::default())?;
let (input, faces) = read_faces(input)?;
Ok((
input,
Expression::Dice(DiceExpression::Custom(CustomDice {
count: Box::new(count),
faces,
span
}))
))
},
"drop-lowest" =>
{
let (input, dice) =
read_dice_child(input, span, SourceSpan::default())?;
let dice_span = dice.span();
let (input, drop) =
read_optional_drop(input, span, dice_span)?;
Ok((
input,
Expression::Dice(DiceExpression::DropLowest(
DropLowest {
dice: Box::new(dice),
drop,
span
}
))
))
},
"drop-highest" =>
{
let (input, dice) =
read_dice_child(input, span, SourceSpan::default())?;
let dice_span = dice.span();
let (input, drop) =
read_optional_drop(input, span, dice_span)?;
Ok((
input,
Expression::Dice(DiceExpression::DropHighest(
DropHighest {
dice: Box::new(dice),
drop,
span
}
))
))
},
"range" =>
{
let (input, start) =
read_child(input, span, SourceSpan::default())?;
let start_span = start.span();
let (input, end) = read_child(input, span, start_span)?;
Ok((
input,
Expression::Range(Range {
start: Box::new(start),
end: Box::new(end),
span
})
))
},
"binding" =>
{
let (input, name_span) = read_span_prefix(input)?;
let (input, name) = read_ident(input)?;
let (input, expression) =
read_child(input, span, name_span)?;
Ok((
input,
Expression::Binding(Binding {
name,
name_span,
expression: Box::new(expression),
span
})
))
},
"function" =>
{
Err(nom::Err::Failure(SExprError::NestedFunctionKeyword {
location: SExprLocation::of(kw_mark)
}))
},
_ => Err(nom::Err::Failure(SExprError::UnknownKeyword {
keyword: keyword.to_string(),
location: SExprLocation::of(kw_mark)
}))
}?;
let (input, _) = expect_char(')')(input)?;
Ok((input, expr))
},
Some(c) if c == '-' || c.is_ascii_digit() =>
{
let (input, value) = read_integer(input)?;
Ok((input, Expression::Constant(Constant { value, span })))
},
Some(_) =>
{
let (input, name) = read_ident(input)?;
Ok((input, Expression::Variable(Variable { name, span })))
},
None => Err(nom::Err::Failure(SExprError::ExpectedExpression {
location: SExprLocation::of(input)
}))
}
}