use std::{
error::Error,
fmt::{self, Display, Formatter, Write},
ops::Deref
};
use crate::ast::{
Add, ArithmeticExpression, Constant, CustomDice, DiceExpression, Div,
DropHighest, DropLowest, Exp, Expression, Function, Group, Mod, Mul, Neg,
Range, StandardDice, Sub, Variable
};
pub trait SExpressible
{
fn write_s_expr(
&self,
f: &mut dyn Write,
remaining_space: usize,
options: SExpressibleOptions
) -> fmt::Result;
fn size_s_expr(&self) -> 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
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct SExpressibleOptions
{
pub indent: usize,
pub tab_width: usize,
pub soft_limit: usize
}
impl SExpressibleOptions
{
#[inline]
pub fn new(indent: usize, tab_width: usize, soft_limit: usize) -> Self
{
Self {
indent,
tab_width,
soft_limit
}
}
#[inline]
pub fn increase_indent(&self) -> Self
{
Self {
indent: self.indent + 1,
tab_width: self.tab_width,
soft_limit: self.soft_limit
}
}
pub fn available_space(&self) -> usize
{
self.soft_limit - self.indent * self.tab_width
}
}
impl Default for SExpressibleOptions
{
fn default() -> Self
{
Self {
indent: 0,
tab_width: 4,
soft_limit: 80
}
}
}
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) -> usize { (*self).size_s_expr() }
}
impl SExpressible for Option<Vec<&str>>
{
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) -> usize
{
match self
{
Some(vec) => (&vec[..]).size_s_expr(),
None => 2
}
}
}
impl SExpressible for &[&str]
{
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()
{
write!(f, "[")?;
for (i, item) in self.iter().enumerate()
{
if i > 0
{
write!(f, " ")?;
}
write_ident(f, item)?;
}
write!(f, "]")
}
else
{
write!(f, "[")?;
for item in self.iter()
{
write!(f, "\n{}", "\t".repeat(options.indent + 1))?;
write_ident(f, item)?;
}
write!(f, "\n{}]", "\t".repeat(options.indent))
}
}
fn size_s_expr(&self) -> usize
{
self.iter().copied().map(ident_size).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()
{
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) -> usize
{
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.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) -> usize
{
3 + self.0.as_ref().len() + self.1.size_s_expr()
}
}
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.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) -> usize
{
5 + self.0.as_ref().len() + self.1.size_s_expr() + self.2.size_s_expr()
}
}
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
{
let keyword = "function";
write!(f, "({}", keyword)?;
let remaining_space = remaining_space - 9;
let params = &self.parameters;
let space_needed = params.size_s_expr();
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();
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) -> usize
{
12 + self.parameters.size_s_expr() + self.body.size_s_expr()
}
}
impl SExpressible for Group<'_>
{
fn write_s_expr(
&self,
f: &mut dyn Write,
remaining_space: usize,
options: SExpressibleOptions
) -> fmt::Result
{
self.expression.write_s_expr(f, remaining_space, options)
}
fn size_s_expr(&self) -> usize { self.expression.size_s_expr() }
}
impl SExpressible for Constant
{
fn write_s_expr(
&self,
f: &mut dyn Write,
_remaining_space: usize,
_options: SExpressibleOptions
) -> fmt::Result
{
write!(f, "{}", self.0)
}
fn size_s_expr(&self) -> usize
{
if self.0 == 0
{
1
}
else
{
format!("{}", self.0).len()
}
}
}
impl SExpressible for Variable<'_>
{
fn write_s_expr(
&self,
f: &mut dyn Write,
_remaining_space: usize,
_options: SExpressibleOptions
) -> fmt::Result
{
write_ident(f, self.0)
}
fn size_s_expr(&self) -> usize { ident_size(self.0) }
}
impl SExpressible for Range<'_>
{
fn write_s_expr(
&self,
f: &mut dyn Write,
remaining_space: usize,
options: SExpressibleOptions
) -> fmt::Result
{
("range", self.start.deref(), self.end.deref()).write_s_expr(
f,
remaining_space,
options
)
}
fn size_s_expr(&self) -> usize
{
("range", self.start.deref(), self.end.deref()).size_s_expr()
}
}
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::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) -> usize
{
match self
{
Expression::Group(group) => group.size_s_expr(),
Expression::Constant(constant) => constant.size_s_expr(),
Expression::Variable(variable) => variable.size_s_expr(),
Expression::Range(range) => range.size_s_expr(),
Expression::Dice(dice) => dice.size_s_expr(),
Expression::Arithmetic(arithmetic) => arithmetic.size_s_expr()
}
}
}
impl SExpressible for StandardDice<'_>
{
fn write_s_expr(
&self,
f: &mut dyn Write,
remaining_spaces: usize,
options: SExpressibleOptions
) -> fmt::Result
{
("standard-dice", self.count.deref(), self.faces.deref()).write_s_expr(
f,
remaining_spaces,
options
)
}
fn size_s_expr(&self) -> usize
{
("standard-dice", self.count.deref(), self.faces.deref()).size_s_expr()
}
}
impl SExpressible for CustomDice<'_>
{
fn write_s_expr(
&self,
f: &mut dyn Write,
remaining_spaces: usize,
options: SExpressibleOptions
) -> fmt::Result
{
("custom-dice", self.count.deref(), self.faces.deref()).write_s_expr(
f,
remaining_spaces,
options
)
}
fn size_s_expr(&self) -> usize
{
("custom-dice", self.count.deref(), self.faces.deref()).size_s_expr()
}
}
impl SExpressible for DropLowest<'_>
{
fn write_s_expr(
&self,
f: &mut dyn Write,
remaining_spaces: usize,
options: SExpressibleOptions
) -> fmt::Result
{
(
"drop-lowest",
self.dice.deref(),
self.drop.as_ref().map_or_else(
|| &Expression::Constant(Constant(1)),
|drop| drop.deref()
)
)
.write_s_expr(f, remaining_spaces, options)
}
fn size_s_expr(&self) -> usize
{
(
"drop-lowest",
self.dice.deref(),
self.drop.as_ref().map_or_else(
|| &Expression::Constant(Constant(1)),
|drop| drop.deref()
)
)
.size_s_expr()
}
}
impl SExpressible for DropHighest<'_>
{
fn write_s_expr(
&self,
f: &mut dyn Write,
remaining_spaces: usize,
options: SExpressibleOptions
) -> fmt::Result
{
(
"drop-highest",
self.dice.deref(),
self.drop.as_ref().map_or_else(
|| &Expression::Constant(Constant(1)),
|drop| drop.deref()
)
)
.write_s_expr(f, remaining_spaces, options)
}
fn size_s_expr(&self) -> usize
{
(
"drop-highest",
self.dice.deref(),
self.drop.as_ref().map_or_else(
|| &Expression::Constant(Constant(1)),
|drop| drop.deref()
)
)
.size_s_expr()
}
}
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) -> usize
{
match self
{
DiceExpression::Standard(dice) => dice.size_s_expr(),
DiceExpression::Custom(dice) => dice.size_s_expr(),
DiceExpression::DropLowest(drop) => drop.size_s_expr(),
DiceExpression::DropHighest(drop) => drop.size_s_expr()
}
}
}
impl SExpressible for Add<'_>
{
fn write_s_expr(
&self,
f: &mut dyn Write,
remaining_space: usize,
options: SExpressibleOptions
) -> fmt::Result
{
("add", self.left.deref(), self.right.deref()).write_s_expr(
f,
remaining_space,
options
)
}
fn size_s_expr(&self) -> usize
{
("add", self.left.deref(), self.right.deref()).size_s_expr()
}
}
impl SExpressible for Sub<'_>
{
fn write_s_expr(
&self,
f: &mut dyn Write,
remaining_space: usize,
options: SExpressibleOptions
) -> fmt::Result
{
("sub", self.left.deref(), self.right.deref()).write_s_expr(
f,
remaining_space,
options
)
}
fn size_s_expr(&self) -> usize
{
("sub", self.left.deref(), self.right.deref()).size_s_expr()
}
}
impl SExpressible for Mul<'_>
{
fn write_s_expr(
&self,
f: &mut dyn Write,
remaining_space: usize,
options: SExpressibleOptions
) -> fmt::Result
{
("mul", self.left.deref(), self.right.deref()).write_s_expr(
f,
remaining_space,
options
)
}
fn size_s_expr(&self) -> usize
{
("mul", self.left.deref(), self.right.deref()).size_s_expr()
}
}
impl SExpressible for Div<'_>
{
fn write_s_expr(
&self,
f: &mut dyn Write,
remaining_space: usize,
options: SExpressibleOptions
) -> fmt::Result
{
("div", self.left.deref(), self.right.deref()).write_s_expr(
f,
remaining_space,
options
)
}
fn size_s_expr(&self) -> usize
{
("div", self.left.deref(), self.right.deref()).size_s_expr()
}
}
impl SExpressible for Mod<'_>
{
fn write_s_expr(
&self,
f: &mut dyn Write,
remaining_space: usize,
options: SExpressibleOptions
) -> fmt::Result
{
("mod", self.left.deref(), self.right.deref()).write_s_expr(
f,
remaining_space,
options
)
}
fn size_s_expr(&self) -> usize
{
("mod", self.left.deref(), self.right.deref()).size_s_expr()
}
}
impl SExpressible for Exp<'_>
{
fn write_s_expr(
&self,
f: &mut dyn Write,
remaining_space: usize,
options: SExpressibleOptions
) -> fmt::Result
{
("exp", self.left.deref(), self.right.deref()).write_s_expr(
f,
remaining_space,
options
)
}
fn size_s_expr(&self) -> usize
{
("exp", self.left.deref(), self.right.deref()).size_s_expr()
}
}
impl SExpressible for Neg<'_>
{
fn write_s_expr(
&self,
f: &mut dyn Write,
remaining_space: usize,
options: SExpressibleOptions
) -> fmt::Result
{
("neg", self.operand.deref()).write_s_expr(f, remaining_space, options)
}
fn size_s_expr(&self) -> usize
{
("neg", self.operand.deref()).size_s_expr()
}
}
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) -> usize
{
match self
{
ArithmeticExpression::Add(add) => add.size_s_expr(),
ArithmeticExpression::Sub(sub) => sub.size_s_expr(),
ArithmeticExpression::Mul(mul) => mul.size_s_expr(),
ArithmeticExpression::Div(div) => div.size_s_expr(),
ArithmeticExpression::Mod(r#mod) => r#mod.size_s_expr(),
ArithmeticExpression::Exp(exp) => exp.size_s_expr(),
ArithmeticExpression::Neg(neg) => neg.size_s_expr()
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct SExprError
{
pub message: String,
pub offset: usize
}
impl Display for SExprError
{
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result
{
write!(
f,
"S-expression error at byte {}: {}",
self.offset, self.message
)
}
}
impl Error for SExprError {}
struct SExprReader<'src>
{
input: &'src str,
pos: usize
}
impl<'src> SExprReader<'src>
{
fn new(input: &'src str) -> Self { Self { input, pos: 0 } }
fn remaining(&self) -> &'src str { &self.input[self.pos..] }
fn skip_whitespace(&mut self)
{
while self.pos < self.input.len()
&& self.input.as_bytes()[self.pos].is_ascii_whitespace()
{
self.pos += 1;
}
}
fn next_char(&mut self) -> Option<char>
{
let c = self.remaining().chars().next()?;
self.pos += c.len_utf8();
Some(c)
}
fn peek(&self) -> Option<char> { self.remaining().chars().next() }
fn expect_char(&mut self, expected: char) -> Result<(), SExprError>
{
self.skip_whitespace();
match self.next_char()
{
Some(c) if c == expected => Ok(()),
Some(c) => Err(SExprError {
message: format!("expected '{}', found '{}'", expected, c),
offset: self.pos - c.len_utf8()
}),
None => Err(SExprError {
message: format!("expected '{}', found end of input", expected),
offset: self.pos
})
}
}
fn read_word(&mut self) -> Result<&'src str, SExprError>
{
self.skip_whitespace();
let start = self.pos;
while self.pos < self.input.len()
{
let c = self.input.as_bytes()[self.pos];
if c.is_ascii_whitespace()
|| c == b'(' || c == b')'
|| c == b'[' || c == b']'
{
break;
}
self.pos += 1;
}
if self.pos == start
{
Err(SExprError {
message: "expected a word".to_string(),
offset: self.pos
})
}
else
{
Ok(&self.input[start..self.pos])
}
}
fn read_ident(&mut self) -> Result<&'src str, SExprError>
{
self.skip_whitespace();
if self.peek() == Some('"')
{
self.next_char(); let start = self.pos;
while self.pos < self.input.len()
&& self.input.as_bytes()[self.pos] != b'"'
{
self.pos += 1;
}
if self.pos >= self.input.len()
{
return Err(SExprError {
message: "unterminated quoted identifier".to_string(),
offset: start - 1
});
}
let ident = &self.input[start..self.pos];
self.pos += 1; Ok(ident)
}
else
{
self.read_word()
}
}
fn read_integer(&mut self) -> Result<i32, SExprError>
{
let word = self.read_word()?;
word.parse::<i32>().map_err(|e| SExprError {
message: format!("invalid integer '{}': {}", word, e),
offset: self.pos - word.len()
})
}
fn read_params(&mut self) -> Result<Option<Vec<&'src str>>, SExprError>
{
self.skip_whitespace();
self.expect_char('[')?;
let mut params = Vec::new();
loop
{
self.skip_whitespace();
if self.peek() == Some(']')
{
self.next_char();
break;
}
params.push(self.read_ident()?);
}
if params.is_empty()
{
Ok(None)
}
else
{
Ok(Some(params))
}
}
fn read_faces(&mut self) -> Result<Vec<i32>, SExprError>
{
self.skip_whitespace();
self.expect_char('[')?;
let mut faces = Vec::new();
loop
{
self.skip_whitespace();
if self.peek() == Some(']')
{
self.next_char();
break;
}
faces.push(self.read_integer()?);
}
if faces.is_empty()
{
Err(SExprError {
message: "custom dice faces list must not be empty".to_string(),
offset: self.pos
})
}
else
{
Ok(faces)
}
}
fn read_expr(&mut self) -> Result<Expression<'src>, SExprError>
{
self.skip_whitespace();
match self.peek()
{
Some('(') =>
{
self.next_char();
self.skip_whitespace();
let keyword = self.read_word()?;
let expr = match keyword
{
"add" => self.read_binary(|l, r| {
Expression::Arithmetic(ArithmeticExpression::Add(Add {
left: Box::new(l),
right: Box::new(r)
}))
})?,
"sub" => self.read_binary(|l, r| {
Expression::Arithmetic(ArithmeticExpression::Sub(Sub {
left: Box::new(l),
right: Box::new(r)
}))
})?,
"mul" => self.read_binary(|l, r| {
Expression::Arithmetic(ArithmeticExpression::Mul(Mul {
left: Box::new(l),
right: Box::new(r)
}))
})?,
"div" => self.read_binary(|l, r| {
Expression::Arithmetic(ArithmeticExpression::Div(Div {
left: Box::new(l),
right: Box::new(r)
}))
})?,
"mod" => self.read_binary(|l, r| {
Expression::Arithmetic(ArithmeticExpression::Mod(Mod {
left: Box::new(l),
right: Box::new(r)
}))
})?,
"exp" => self.read_binary(|l, r| {
Expression::Arithmetic(ArithmeticExpression::Exp(Exp {
left: Box::new(l),
right: Box::new(r)
}))
})?,
"neg" =>
{
let operand = self.read_expr()?;
Expression::Arithmetic(ArithmeticExpression::Neg(Neg {
operand: Box::new(operand)
}))
},
"standard-dice" =>
{
let count = self.read_expr()?;
let faces = self.read_expr()?;
Expression::Dice(DiceExpression::Standard(
StandardDice {
count: Box::new(count),
faces: Box::new(faces)
}
))
},
"custom-dice" =>
{
let count = self.read_expr()?;
let faces = self.read_faces()?;
Expression::Dice(DiceExpression::Custom(CustomDice {
count: Box::new(count),
faces
}))
},
"drop-lowest" =>
{
let dice = self.read_dice_expr()?;
let drop = self.read_expr()?;
Expression::Dice(DiceExpression::DropLowest(
DropLowest {
dice: Box::new(dice),
drop: Some(Box::new(drop))
}
))
},
"drop-highest" =>
{
let dice = self.read_dice_expr()?;
let drop = self.read_expr()?;
Expression::Dice(DiceExpression::DropHighest(
DropHighest {
dice: Box::new(dice),
drop: Some(Box::new(drop))
}
))
},
"range" =>
{
let start = self.read_expr()?;
let end = self.read_expr()?;
Expression::Range(Range {
start: Box::new(start),
end: Box::new(end)
})
},
"function" =>
{
return Err(SExprError {
message: "unexpected 'function' inside expression"
.to_string(),
offset: self.pos - keyword.len()
});
},
_ =>
{
return Err(SExprError {
message: format!("unknown keyword '{}'", keyword),
offset: self.pos - keyword.len()
});
}
};
self.expect_char(')')?;
Ok(expr)
},
Some(c) if c == '-' || c.is_ascii_digit() =>
{
let value = self.read_integer()?;
Ok(Expression::Constant(Constant(value)))
},
Some(_) =>
{
let name = self.read_ident()?;
Ok(Expression::Variable(Variable(name)))
},
None => Err(SExprError {
message: "expected expression, found end of input".to_string(),
offset: self.pos
})
}
}
fn read_dice_expr(&mut self) -> Result<DiceExpression<'src>, SExprError>
{
let expr = self.read_expr()?;
match expr
{
Expression::Dice(d) => Ok(d),
_ => Err(SExprError {
message: "expected dice expression as first argument to \
drop-lowest/drop-highest"
.to_string(),
offset: self.pos
})
}
}
fn read_binary(
&mut self,
f: impl FnOnce(Expression<'src>, Expression<'src>) -> Expression<'src>
) -> Result<Expression<'src>, SExprError>
{
let left = self.read_expr()?;
let right = self.read_expr()?;
Ok(f(left, right))
}
fn read_function(&mut self) -> Result<Function<'src>, SExprError>
{
self.skip_whitespace();
self.expect_char('(')?;
self.skip_whitespace();
let keyword = self.read_word()?;
if keyword != "function"
{
return Err(SExprError {
message: format!("expected 'function', found '{}'", keyword),
offset: self.pos - keyword.len()
});
}
let parameters = self.read_params()?;
let body = self.read_expr()?;
self.expect_char(')')?;
self.skip_whitespace();
if self.pos != self.input.len()
{
return Err(SExprError {
message: format!("trailing input: '{}'", self.remaining()),
offset: self.pos
});
}
Ok(Function { parameters, body })
}
}
pub fn read_s_expr(input: &str) -> Result<Function<'_>, SExprError>
{
SExprReader::new(input).read_function()
}