use crate::parser::{Backend, Binary, Parser, Unary};
use crate::{Context, Error, ErrorKind, Evaluator, Number, Span};
#[derive(Debug, Clone, Copy)]
pub struct Instruction<'i, N>(Code<'i, N>);
impl<N: Default> Default for Instruction<'_, N> {
fn default() -> Self {
Self(Code::Number(N::default()))
}
}
#[derive(Debug, Clone, Copy)]
enum Code<'i, N> {
Number(N),
Variable(&'i str, Span),
Call {
name: &'i str,
count: usize,
span: Span,
},
Unary(Unary, Span),
Binary(Binary, Span),
Logic {
or: bool,
target: usize,
condition: Span,
span: Span,
},
Truth(Span),
JumpUnless(usize, Span),
Jump(usize),
}
#[derive(Debug, Clone, Copy)]
pub struct Program<'b, 'i, N, const STACK: usize = 32> {
code: &'b [Instruction<'i, N>],
stack: usize,
}
impl<N: Number, const STACK: usize> Program<'_, '_, N, STACK> {
pub const fn len(&self) -> usize {
self.code.len()
}
pub const fn is_empty(&self) -> bool {
self.code.is_empty()
}
pub const fn stack(&self) -> usize {
self.stack
}
pub fn evaluate<C: Context<N> + ?Sized>(&self, context: &C) -> Result<N, Error> {
let mut stack = Stack::<N, STACK>::new();
let mut position = 0;
while let Some(Instruction(code)) = self.code.get(position) {
position += 1;
match *code {
Code::Number(value) => stack.push(value),
Code::Variable(name, span) => match context.variable(name) {
Some(value) => stack.push(value),
None => return Err(Error::new(ErrorKind::UnknownVariable, span)),
},
Code::Call { name, count, span } => {
let result = match context.call(name, stack.top(count)) {
Some(result) => result.map_err(|kind| Error::new(kind, span))?,
None => {
let name = Span::new(span.start, span.start + name.len());
return Err(Error::new(ErrorKind::UnknownFunction, name));
}
};
stack.drop(count);
stack.push(result);
}
Code::Unary(operator, span) => {
let operand = stack.pop();
stack.push(at(operator.apply(operand), span)?);
}
Code::Binary(operator, span) => {
let right = stack.pop();
let left = stack.pop();
stack.push(at(operator.apply(left, right), span)?);
}
Code::Logic {
or,
target,
condition,
span,
} => {
if !at(stack.pop().is_zero(), condition)? == or {
stack.push(at(N::from_bool(or), span)?);
position = target;
}
}
Code::Truth(span) => {
let truth = !at(stack.pop().is_zero(), span)?;
stack.push(at(N::from_bool(truth), span)?);
}
Code::JumpUnless(target, span) => {
if at(stack.pop().is_zero(), span)? {
position = target;
}
}
Code::Jump(target) => position = target,
}
}
Ok(stack.pop())
}
}
fn at<T>(result: Result<T, ErrorKind>, span: Span) -> Result<T, Error> {
result.map_err(|kind| Error::new(kind, span))
}
struct Stack<N, const STACK: usize> {
values: [N; STACK],
length: usize,
}
impl<N: Number, const STACK: usize> Stack<N, STACK> {
fn new() -> Self {
Self {
values: [N::default(); STACK],
length: 0,
}
}
fn push(&mut self, value: N) {
if let Some(slot) = self.values.get_mut(self.length) {
*slot = value;
self.length += 1;
}
}
fn pop(&mut self) -> N {
self.length = self.length.saturating_sub(1);
self.values.get(self.length).copied().unwrap_or_default()
}
fn top(&self, count: usize) -> &[N] {
self.values
.get(self.length.saturating_sub(count)..self.length)
.unwrap_or_default()
}
fn drop(&mut self, count: usize) {
self.length = self.length.saturating_sub(count);
}
}
struct Compilation<'b, 'i, N> {
code: &'b mut [Instruction<'i, N>],
length: usize,
depth: usize,
stack: usize,
capacity: usize,
barrier: usize,
}
impl<'i, N> Compilation<'_, 'i, N> {
fn emit(
&mut self,
code: Code<'i, N>,
pops: usize,
pushes: usize,
span: Span,
) -> Result<usize, Error> {
let slot = self
.code
.get_mut(self.length)
.ok_or(Error::new(ErrorKind::TooLong, span))?;
*slot = Instruction(code);
self.depth = self.depth.saturating_sub(pops) + pushes;
if self.depth > self.capacity {
return Err(Error::new(ErrorKind::TooLong, span));
}
self.stack = self.stack.max(self.depth);
self.length += 1;
Ok(self.length - 1)
}
fn constants<const COUNT: usize>(&self) -> Option<[N; COUNT]>
where
N: Copy + Default,
{
let start = self.length.checked_sub(COUNT)?;
if start < self.barrier {
return None;
}
let mut values = [N::default(); COUNT];
for (value, Instruction(code)) in values.iter_mut().zip(self.code.get(start..self.length)?)
{
match code {
Code::Number(number) => *value = *number,
_ => return None,
}
}
Some(values)
}
fn fold(&mut self, count: usize, value: N, span: Span) -> Result<(), Error> {
self.length -= count;
self.depth = self.depth.saturating_sub(count);
self.emit(Code::Number(value), 0, 1, span).map(drop)
}
fn land(&mut self, position: usize, span: Span) {
let next = self.length;
self.barrier = next;
if let Some(Instruction(code)) = self.code.get_mut(position) {
match code {
Code::Logic {
target, span: end, ..
} => {
*target = next;
*end = span;
}
Code::JumpUnless(target, _) | Code::Jump(target) => *target = next,
_ => {}
}
}
}
}
impl<'i, N: Number> Backend<'i> for Compilation<'_, 'i, N> {
type Value = ();
type Branch = usize;
fn number(&mut self, literal: &'i str, span: Span) -> Result<(), Error> {
let value = N::parse(literal).ok_or(Error::new(ErrorKind::InvalidNumber, span))?;
self.emit(Code::Number(value), 0, 1, span).map(drop)
}
fn variable(&mut self, name: &'i str, span: Span) -> Result<(), Error> {
self.emit(Code::Variable(name, span), 0, 1, span).map(drop)
}
fn call(&mut self, name: &'i str, _: Span, arguments: &[()], span: Span) -> Result<(), Error> {
let count = arguments.len();
self.emit(Code::Call { name, count, span }, count, 1, span)
.map(drop)
}
fn unary(&mut self, operator: Unary, _: (), span: Span) -> Result<(), Error> {
if let Some(Ok(value)) = self.constants().map(|[operand]| operator.apply(operand)) {
return self.fold(1, value, span);
}
self.emit(Code::Unary(operator, span), 1, 1, span).map(drop)
}
fn binary(&mut self, operator: Binary, _: (), _: (), span: Span) -> Result<(), Error> {
if let Some(Ok(value)) = self
.constants()
.map(|[left, right]| operator.apply(left, right))
{
return self.fold(2, value, span);
}
self.emit(Code::Binary(operator, span), 2, 1, span)
.map(drop)
}
fn logic(&mut self, or: bool, _: (), span: Span) -> Result<usize, Error> {
let code = Code::Logic {
or,
target: 0,
condition: span,
span,
};
self.emit(code, 1, 0, span)
}
fn logic_end(&mut self, _: bool, branch: usize, _: (), span: Span) -> Result<(), Error> {
self.emit(Code::Truth(span), 1, 1, span)?;
self.land(branch, span);
Ok(())
}
fn condition(&mut self, _: (), span: Span) -> Result<usize, Error> {
self.emit(Code::JumpUnless(0, span), 1, 0, span)
}
fn otherwise(&mut self, branch: usize, span: Span) -> Result<usize, Error> {
let jump = self.emit(Code::Jump(0), 0, 0, span)?;
self.land(branch, span);
self.depth = self.depth.saturating_sub(1);
Ok(jump)
}
fn conditional_end(&mut self, branch: usize, _: (), _: ()) {
self.land(branch, Span::new(0, 0));
}
}
impl<C: ?Sized, const ARGUMENTS: usize> Evaluator<'_, C, ARGUMENTS> {
pub fn compile<'b, 'i, N: Number, const STACK: usize>(
&self,
input: &'i str,
code: &'b mut [Instruction<'i, N>],
) -> Result<Program<'b, 'i, N, STACK>, Error> {
let compilation = Compilation {
code,
length: 0,
depth: 0,
stack: 0,
capacity: STACK,
barrier: 0,
};
let ((), compilation) = Parser::<_, ARGUMENTS>::parse(input, self.syntax(), compilation)?;
let code: &'b [Instruction<'i, N>] = compilation.code;
Ok(Program {
code: code.get(..compilation.length).unwrap_or_default(),
stack: compilation.stack,
})
}
}
pub fn compile<'b, 'i, N: Number, const STACK: usize>(
input: &'i str,
code: &'b mut [Instruction<'i, N>],
) -> Result<Program<'b, 'i, N, STACK>, Error> {
Evaluator::<()>::new(&()).compile(input, code)
}