use std::{borrow::Cow, mem};
use nom::Input;
use super::{
SExprError, SExprLocation, SExprResult, Span, expect_char, read_faces,
read_ident, read_integer, read_params, read_span_prefix, read_word,
skip_ws, validate_containment, validate_sibling_order
};
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}
};
struct Frame<'src>
{
form: Form<'src>,
span: SourceSpan,
prev: SourceSpan,
mark: Span<'src>
}
enum Form<'src>
{
Function
{
parameters: Option<Vec<Parameter<'src>>>
},
Binary
{
operator: Operator,
left: Option<Expression<'src>>
},
Neg,
Group,
StandardDice
{
count: Option<Expression<'src>>
},
CustomDice,
Drop
{
direction: Direction,
after_keyword: Span<'src>,
dice: Option<DiceExpression<'src>>
},
Range
{
start: Option<Expression<'src>>
},
Binding
{
name: &'src str,
name_span: SourceSpan
}
}
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
enum Operator
{
Add,
Sub,
Mul,
Div,
Mod,
Exp
}
impl Operator
{
fn apply<'src>(
self,
left: Expression<'src>,
right: Expression<'src>,
span: SourceSpan
) -> Expression<'src>
{
let left = Box::new(left);
let right = Box::new(right);
Expression::Arithmetic(match self
{
Operator::Add =>
{
ArithmeticExpression::Add(Add { left, right, span })
},
Operator::Sub =>
{
ArithmeticExpression::Sub(Sub { left, right, span })
},
Operator::Mul =>
{
ArithmeticExpression::Mul(Mul { left, right, span })
},
Operator::Div =>
{
ArithmeticExpression::Div(Div { left, right, span })
},
Operator::Mod =>
{
ArithmeticExpression::Mod(Mod { left, right, span })
},
Operator::Exp =>
{
ArithmeticExpression::Exp(Exp { left, right, span })
},
})
}
}
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
enum Direction
{
Lowest,
Highest
}
impl Direction
{
fn apply<'src>(
self,
dice: DiceExpression<'src>,
drop: Option<Expression<'src>>,
span: SourceSpan
) -> Expression<'src>
{
let dice = Box::new(dice);
let drop = drop.map(Box::new);
Expression::Dice(match self
{
Direction::Lowest =>
{
DiceExpression::DropLowest(DropLowest { dice, drop, span })
},
Direction::Highest =>
{
DiceExpression::DropHighest(DropHighest { dice, drop, span })
},
})
}
}
enum Step<'src>
{
Read(Span<'src>),
Deliver(Span<'src>, Expression<'src>),
Finish(Span<'src>, Function<'src>)
}
type Outcome<'src> = Result<Step<'src>, nom::Err<SExprError>>;
#[cfg_attr(doc, aquamarine::aquamarine)]
pub(super) 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 mut stack = Vec::new();
let mut step = await_child(
&mut stack,
Form::Function { parameters },
span,
last_param_span,
input
);
loop
{
step = match step
{
Step::Read(input) => read(input, &mut stack)?,
Step::Deliver(input, child) =>
{
let frame = stack.pop().expect("stack must not be empty");
deliver(frame, child, input, &mut stack)?
},
Step::Finish(input, function) => return Ok((input, function))
}
}
}
fn read<'src>(input: Span<'src>, stack: &mut Vec<Frame<'src>>)
-> Outcome<'src>
{
let (input, _) = skip_ws(input)?;
if let Some(parent) = stack.last_mut()
{
parent.mark = input;
}
let (input, span) = read_span_prefix(input)?;
let (input, _) = skip_ws(input)?;
match input.fragment().chars().next()
{
Some('(') => open(input.take_from(1), span, stack),
Some(c) if c == '-' || c.is_ascii_digit() =>
{
let (input, value) = read_integer(input)?;
Ok(Step::Deliver(
input,
Expression::Constant(Constant { value, span })
))
},
Some(_) =>
{
let (input, name) = read_ident(input)?;
Ok(Step::Deliver(
input,
Expression::Variable(Variable {
name: Cow::Borrowed(name),
span
})
))
},
None => Err(nom::Err::Failure(SExprError::ExpectedExpression {
location: SExprLocation::of(input)
}))
}
}
fn open<'src>(
input: Span<'src>,
span: SourceSpan,
stack: &mut Vec<Frame<'src>>
) -> Outcome<'src>
{
let (input, _) = skip_ws(input)?;
let kw_mark = input;
let (mut input, kw) = read_word(input)?;
let mut prev = SourceSpan::default();
let binary = |operator| Form::Binary {
operator,
left: None
};
let form = match *kw.fragment()
{
"add" => binary(Operator::Add),
"sub" => binary(Operator::Sub),
"mul" => binary(Operator::Mul),
"div" => binary(Operator::Div),
"mod" => binary(Operator::Mod),
"exp" => binary(Operator::Exp),
"neg" => Form::Neg,
"group" => Form::Group,
"standard-dice" => Form::StandardDice { count: None },
"custom-dice" => Form::CustomDice,
"drop-lowest" => Form::Drop {
direction: Direction::Lowest,
after_keyword: input,
dice: None
},
"drop-highest" => Form::Drop {
direction: Direction::Highest,
after_keyword: input,
dice: None
},
"range" => Form::Range { start: None },
"binding" =>
{
let (rest, _) = skip_ws(input)?;
let name_mark = rest;
let (rest, name_span) = read_span_prefix(rest)?;
validate_containment(span, name_span, name_mark)?;
let (rest, name) = read_ident(rest)?;
input = rest;
prev = name_span;
Form::Binding { name, name_span }
},
"function" =>
{
return Err(nom::Err::Failure(SExprError::NestedFunctionKeyword {
location: SExprLocation::of(kw_mark)
}))
},
keyword =>
{
return Err(nom::Err::Failure(SExprError::UnknownKeyword {
keyword: keyword.to_string(),
location: SExprLocation::of(kw_mark)
}))
},
};
Ok(await_child(stack, form, span, prev, input))
}
fn deliver<'src>(
frame: Frame<'src>,
child: Expression<'src>,
input: Span<'src>,
stack: &mut Vec<Frame<'src>>
) -> Outcome<'src>
{
let Frame {
form,
span,
prev,
mark
} = frame;
let child_span = child.span();
validate_containment(span, child_span, mark)?;
validate_sibling_order(prev, child_span, mark)?;
match form
{
Form::Function { parameters } =>
{
let (input, _) = expect_char(')')(input)?;
let (input, _) = skip_ws(input)?;
Ok(Step::Finish(
input,
Function {
parameters,
body: child,
span
}
))
},
Form::Binary {
operator,
left: None
} => Ok(await_child(
stack,
Form::Binary {
operator,
left: Some(child)
},
span,
child_span,
input
)),
Form::Binary {
operator,
left: Some(left)
} => close(input, operator.apply(left, child, span)),
Form::Neg => close(
input,
Expression::Arithmetic(ArithmeticExpression::Neg(Neg {
operand: Box::new(child),
span
}))
),
Form::Group => close(
input,
Expression::Group(Group {
expression: Box::new(child),
span
})
),
Form::StandardDice { count: None } => Ok(await_child(
stack,
Form::StandardDice { count: Some(child) },
span,
child_span,
input
)),
Form::StandardDice { count: Some(count) } => close(
input,
Expression::Dice(DiceExpression::Standard(StandardDice {
count: Box::new(count),
faces: Box::new(child),
span
}))
),
Form::CustomDice =>
{
let (input, faces) = read_faces(input)?;
close(
input,
Expression::Dice(DiceExpression::Custom(CustomDice {
count: Box::new(child),
faces,
span
}))
)
},
Form::Drop {
direction,
after_keyword,
dice: None
} =>
{
let Some(dice) = into_dice(child)
else
{
return Err(nom::Err::Failure(
SExprError::ExpectedDiceExpression {
location: SExprLocation::of(after_keyword)
}
))
};
let (probe, _) = skip_ws(input)?;
if probe.fragment().starts_with(')')
{
close(input, direction.apply(dice, None, span))
}
else
{
Ok(await_child(
stack,
Form::Drop {
direction,
after_keyword,
dice: Some(dice)
},
span,
child_span,
input
))
}
},
Form::Drop {
direction,
dice: Some(dice),
..
} => close(input, direction.apply(dice, Some(child), span)),
Form::Range { start: None } => Ok(await_child(
stack,
Form::Range { start: Some(child) },
span,
child_span,
input
)),
Form::Range { start: Some(start) } => close(
input,
Expression::Range(Range {
start: Box::new(start),
end: Box::new(child),
span
})
),
Form::Binding { name, name_span } => close(
input,
Expression::Binding(Binding {
name: Cow::Borrowed(name),
name_span,
expression: Box::new(child),
span
})
)
}
}
fn await_child<'src>(
stack: &mut Vec<Frame<'src>>,
form: Form<'src>,
span: SourceSpan,
prev: SourceSpan,
input: Span<'src>
) -> Step<'src>
{
stack.push(Frame {
form,
span,
prev,
mark: input
});
Step::Read(input)
}
fn close<'src>(input: Span<'src>, expression: Expression<'src>)
-> Outcome<'src>
{
let (input, _) = expect_char(')')(input)?;
Ok(Step::Deliver(input, expression))
}
fn into_dice(mut expression: Expression<'_>) -> Option<DiceExpression<'_>>
{
match &mut expression
{
Expression::Dice(dice) =>
{
let placeholder = DiceExpression::Custom(CustomDice {
count: Box::new(Expression::Constant(Constant {
value: 0,
span: SourceSpan::SYNTHETIC
})),
faces: Vec::new(),
span: SourceSpan::SYNTHETIC
});
Some(mem::replace(dice, placeholder))
},
_ => None
}
}