mod reader;
mod writer;
use std::{
borrow::Cow,
error::Error,
fmt::{self, Display, Formatter, Write}
};
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
},
parser::is_canonical_name,
span::SourceSpan
};
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 }
}
enum Layout
{
Inline,
Wrapped(SExpressibleOptions)
}
fn layout(
remaining_space: usize,
size: usize,
options: SExpressibleOptions
) -> Layout
{
if remaining_space >= size + options.indent
{
Layout::Inline
}
else
{
Layout::Wrapped(options.increase_indent())
}
}
fn write_newline(f: &mut dyn Write, indent: usize) -> fmt::Result
{
const TABS: &str = "\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\
\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t";
f.write_char('\n')?;
let mut remaining = indent;
while remaining > 0
{
let chunk = remaining.min(TABS.len());
f.write_str(&TABS[..chunk])?;
remaining -= chunk;
}
Ok(())
}
#[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) + options.indent
{
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_newline(f, options.indent + 1)?;
write_span_prefix(f, item.span, options)?;
write_ident(f, &item.name)?;
}
write_newline(f, options.indent)?;
write!(f, "]")
}
}
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) + options.indent
{
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_newline(f, options.indent + 1)?;
write!(f, "{}", item)?;
}
write_newline(f, options.indent)?;
write!(f, "]")
}
}
fn size_s_expr(&self, _options: SExpressibleOptions) -> usize
{
if self.is_empty()
{
return 2
}
self.iter()
.map(|&x| {
x.unsigned_abs()
.checked_ilog10()
.map_or(0, |log| log 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 (remaining_space, options) =
match layout(remaining_space, self.size_s_expr(options), options)
{
Layout::Inline =>
{
write!(f, " ")?;
(usize::MAX, options)
},
Layout::Wrapped(options) =>
{
write_newline(f, options.indent)?;
(options.available_space(), options)
}
};
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;
match layout(remaining_space, self.size_s_expr(options), options)
{
Layout::Inline =>
{
write!(f, " ")?;
sub1.write_s_expr(f, usize::MAX, options)?;
write!(f, " ")?;
sub2.write_s_expr(f, usize::MAX, options)?;
},
Layout::Wrapped(options) =>
{
write_newline(f, options.indent)?;
sub1.write_s_expr(f, options.available_space(), options)?;
write_newline(f, 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 write_ident(f: &mut dyn Write, ident: &str) -> fmt::Result
{
write!(f, "{{{}}}", ident)
}
fn ident_size(ident: &str) -> usize { ident.len() + 2 }
impl SExpressible for Function<'_>
{
fn write_s_expr(
&self,
f: &mut dyn Write,
remaining_space: usize,
options: SExpressibleOptions
) -> fmt::Result
{
writer::write(self, f, remaining_space, options)
}
fn size_s_expr(&self, options: SExpressibleOptions) -> usize
{
writer::size(self, options)
}
}
impl SExpressible for Group<'_>
{
fn write_s_expr(
&self,
f: &mut dyn Write,
remaining_space: usize,
options: SExpressibleOptions
) -> fmt::Result
{
writer::write(self, f, remaining_space, options)
}
fn size_s_expr(&self, options: SExpressibleOptions) -> usize
{
writer::size(self, 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
{
writer::write(self, f, remaining_space, options)
}
fn size_s_expr(&self, options: SExpressibleOptions) -> usize
{
writer::size(self, options)
}
}
impl SExpressible for Binding<'_>
{
fn write_s_expr(
&self,
f: &mut dyn Write,
remaining_space: usize,
options: SExpressibleOptions
) -> fmt::Result
{
writer::write(self, f, remaining_space, options)
}
fn size_s_expr(&self, options: SExpressibleOptions) -> usize
{
writer::size(self, options)
}
}
impl SExpressible for Expression<'_>
{
fn write_s_expr(
&self,
f: &mut dyn Write,
remaining_space: usize,
options: SExpressibleOptions
) -> fmt::Result
{
writer::write(self, f, remaining_space, options)
}
fn size_s_expr(&self, options: SExpressibleOptions) -> usize
{
writer::size(self, options)
}
}
impl SExpressible for StandardDice<'_>
{
fn write_s_expr(
&self,
f: &mut dyn Write,
remaining_space: usize,
options: SExpressibleOptions
) -> fmt::Result
{
writer::write(self, f, remaining_space, options)
}
fn size_s_expr(&self, options: SExpressibleOptions) -> usize
{
writer::size(self, options)
}
}
impl SExpressible for CustomDice<'_>
{
fn write_s_expr(
&self,
f: &mut dyn Write,
remaining_space: usize,
options: SExpressibleOptions
) -> fmt::Result
{
writer::write(self, f, remaining_space, options)
}
fn size_s_expr(&self, options: SExpressibleOptions) -> usize
{
writer::size(self, options)
}
}
impl SExpressible for DropLowest<'_>
{
fn write_s_expr(
&self,
f: &mut dyn Write,
remaining_space: usize,
options: SExpressibleOptions
) -> fmt::Result
{
writer::write(self, f, remaining_space, options)
}
fn size_s_expr(&self, options: SExpressibleOptions) -> usize
{
writer::size(self, options)
}
}
impl SExpressible for DropHighest<'_>
{
fn write_s_expr(
&self,
f: &mut dyn Write,
remaining_space: usize,
options: SExpressibleOptions
) -> fmt::Result
{
writer::write(self, f, remaining_space, options)
}
fn size_s_expr(&self, options: SExpressibleOptions) -> usize
{
writer::size(self, options)
}
}
impl SExpressible for DiceExpression<'_>
{
fn write_s_expr(
&self,
f: &mut dyn Write,
remaining_space: usize,
options: SExpressibleOptions
) -> fmt::Result
{
writer::write(self, f, remaining_space, options)
}
fn size_s_expr(&self, options: SExpressibleOptions) -> usize
{
writer::size(self, options)
}
}
impl SExpressible for Add<'_>
{
fn write_s_expr(
&self,
f: &mut dyn Write,
remaining_space: usize,
options: SExpressibleOptions
) -> fmt::Result
{
writer::write(self, f, remaining_space, options)
}
fn size_s_expr(&self, options: SExpressibleOptions) -> usize
{
writer::size(self, options)
}
}
impl SExpressible for Sub<'_>
{
fn write_s_expr(
&self,
f: &mut dyn Write,
remaining_space: usize,
options: SExpressibleOptions
) -> fmt::Result
{
writer::write(self, f, remaining_space, options)
}
fn size_s_expr(&self, options: SExpressibleOptions) -> usize
{
writer::size(self, options)
}
}
impl SExpressible for Mul<'_>
{
fn write_s_expr(
&self,
f: &mut dyn Write,
remaining_space: usize,
options: SExpressibleOptions
) -> fmt::Result
{
writer::write(self, f, remaining_space, options)
}
fn size_s_expr(&self, options: SExpressibleOptions) -> usize
{
writer::size(self, options)
}
}
impl SExpressible for Div<'_>
{
fn write_s_expr(
&self,
f: &mut dyn Write,
remaining_space: usize,
options: SExpressibleOptions
) -> fmt::Result
{
writer::write(self, f, remaining_space, options)
}
fn size_s_expr(&self, options: SExpressibleOptions) -> usize
{
writer::size(self, options)
}
}
impl SExpressible for Mod<'_>
{
fn write_s_expr(
&self,
f: &mut dyn Write,
remaining_space: usize,
options: SExpressibleOptions
) -> fmt::Result
{
writer::write(self, f, remaining_space, options)
}
fn size_s_expr(&self, options: SExpressibleOptions) -> usize
{
writer::size(self, options)
}
}
impl SExpressible for Exp<'_>
{
fn write_s_expr(
&self,
f: &mut dyn Write,
remaining_space: usize,
options: SExpressibleOptions
) -> fmt::Result
{
writer::write(self, f, remaining_space, options)
}
fn size_s_expr(&self, options: SExpressibleOptions) -> usize
{
writer::size(self, options)
}
}
impl SExpressible for Neg<'_>
{
fn write_s_expr(
&self,
f: &mut dyn Write,
remaining_space: usize,
options: SExpressibleOptions
) -> fmt::Result
{
writer::write(self, f, remaining_space, options)
}
fn size_s_expr(&self, options: SExpressibleOptions) -> usize
{
writer::size(self, options)
}
}
impl SExpressible for ArithmeticExpression<'_>
{
fn write_s_expr(
&self,
f: &mut dyn Write,
remaining_space: usize,
options: SExpressibleOptions
) -> fmt::Result
{
writer::write(self, f, remaining_space, options)
}
fn size_s_expr(&self, options: SExpressibleOptions) -> usize
{
writer::size(self, options)
}
}
pub fn read_s_expr(input: &str) -> Result<Function<'_>, SExprError>
{
let span = Span::new(input);
let (rest, function) = reader::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
{
pub(crate) 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
},
UnterminatedIdent
{
location: SExprLocation
},
InvalidIdent
{
text: String,
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::UnterminatedIdent { location }
| Self::InvalidIdent { 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::UnterminatedIdent { .. } =>
{
write!(f, "unterminated identifier")
},
Self::InvalidIdent { text, .. } =>
{
write!(f, "invalid identifier {:?}", text)
},
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
}
}
pub(crate) type Span<'src> = LocatedSpan<&'src str>;
pub(crate) type SExprResult<'src, T> = IResult<Span<'src>, T, SExprError>;
pub(crate) fn skip_ws(input: Span<'_>) -> SExprResult<'_, ()>
{
let (input, _) = take_while(|c: char| c.is_ascii_whitespace())(input)?;
Ok((input, ()))
}
pub(crate) 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)
}))
}
}
}
pub(crate) 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))
}
}
pub(crate) fn read_ident(input: Span<'_>) -> SExprResult<'_, &str>
{
let (input, _) = skip_ws(input)?;
let mark = input;
let (after_brace, _) = expect_char('{')(input)?;
match after_brace.fragment().find('}')
{
Some(pos) =>
{
let ident = *after_brace.take(pos).fragment();
if !is_canonical_name(ident)
{
return Err(nom::Err::Failure(SExprError::InvalidIdent {
text: ident.to_string(),
location: SExprLocation::of(mark)
}));
}
let rest = after_brace.take_from(pos + 1);
Ok((rest, ident))
},
None => Err(nom::Err::Failure(SExprError::UnterminatedIdent {
location: SExprLocation::of(mark)
}))
}
}
pub(crate) 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)
}))
}
}
pub(crate) 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()))
}
}
pub(crate) 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)
}))
}
}
pub(crate) 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)
}))
}
}
pub(crate) 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: Cow::Borrowed(name),
span
});
cur = next;
}
}
}
let result = if params.is_empty()
{
None
}
else
{
Some(params)
};
Ok((cur, (result, prev_sibling)))
}
pub(crate) 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))
}
}