Skip to content
This repository has been archived by the owner on Dec 10, 2024. It is now read-only.

Commit

Permalink
feat: add tailcall optimization
Browse files Browse the repository at this point in the history
  • Loading branch information
aripiprazole committed Nov 22, 2023
1 parent 4778b25 commit 51baa65
Show file tree
Hide file tree
Showing 3 changed files with 79 additions and 9 deletions.
55 changes: 47 additions & 8 deletions src/eval.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
use std::{
collections::VecDeque,
convert::Infallible,
ops::{ControlFlow, FromResidual, Try},
sync::{Arc, RwLock},
};

Expand Down Expand Up @@ -32,27 +34,64 @@ pub enum Value {
pub struct Environment {
pub global: Value,
pub expanded: bool,
pub frames: VecDeque<Frame>,
pub frames: Arc<VecDeque<Frame>>,
}

pub enum Trampoline<T> {
pub enum Trampoline<T, E = Expr> {
Done(T),
Raise(String),
Continue(Box<dyn FnOnce() -> Trampoline<T>>),
Raise(E),
Continue(Box<dyn Fn() -> Trampoline<T>>),
}

pub fn eval(expr: Expr, _: Environment) -> Trampoline<Expr> {
pub fn eval(expr: Expr, environment: Environment) -> Trampoline<Value> {

Check failure on line 46 in src/eval.rs

View workflow job for this annotation

GitHub Actions / 👁️‍🗨️ Lint check (nightly)

parameter is only used in recursion
match expr {
Expr::Fun(_) => todo!(),
Expr::List(_) => todo!(),
Expr::Apply(_) => todo!(),
Expr::Def(_) => todo!(),
Expr::DefMacro(_) => todo!(),
Expr::Quote(_) => todo!(),
Expr::Recur(_) => todo!(),
Expr::Deref(_) => todo!(),
Expr::Deref(deref) => {
let value = eval(deref.value()?, environment)?;

Check failure on line 55 in src/eval.rs

View workflow job for this annotation

GitHub Actions / 👁️‍🗨️ Lint check (nightly)

unused variable: `value`

Check warning on line 55 in src/eval.rs

View workflow job for this annotation

GitHub Actions / 👁️‍🗨️ Compilation check (nightly)

unused variable: `value`

Check warning on line 55 in src/eval.rs

View workflow job for this annotation

GitHub Actions / 🧪 Test check (nightly)

unused variable: `value`

todo!()
}
Expr::Atomic(_) => todo!(),
Expr::Set(_) => todo!(),
Expr::Literal(_) => Trampoline::Done(expr),

// Bridges one-to-one from Value, to Expr, and back to Value.
Expr::Fun(fun) => Trampoline::Done(Value::Fun(fun)),
Expr::Literal(literal) => Trampoline::Done(Value::Literal(literal)),
}
}

impl<T> Try for Trampoline<T, Expr> {
type Output = T;
type Residual = Result<Infallible, Expr>;

fn from_output(output: Self::Output) -> Self {
Trampoline::Done(output)
}

fn branch(self) -> ControlFlow<Self::Residual, Self::Output> {
let mut value: Trampoline<T, Expr> = self;
loop {
match value {
Trampoline::Done(done) => return ControlFlow::Continue(done),
Trampoline::Raise(error) => return ControlFlow::Break(Err(error)),
Trampoline::Continue(f) => {
value = f();
}
}
}
}
}

impl<T, E, F: From<E>> FromResidual<Result<Infallible, E>> for Trampoline<T, F> {
fn from_residual(residual: Result<Infallible, E>) -> Self {
match residual {
Err(error) => Trampoline::Raise(From::from(error)),
_ => unreachable!(),
}
}
}
1 change: 1 addition & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
#![feature(box_patterns)]
#![feature(try_trait_v2)]

use std::fmt::Display;

Expand Down
32 changes: 31 additions & 1 deletion src/semantic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ define_ast!(Expr, {
Atomic, // (atomic* 123)
Set, // (set* a 123)
DefMacro, // (defmacro* a (fun (a b) (+ a b))
Quote, // '(fun (a b) (+ a b))
Quote, // '(fun* (a b) (+ a b))
Literal // 123 | "bla" | :bla | bla
});

Expand Down Expand Up @@ -239,6 +239,8 @@ pub mod quote {

/// Literal expression construct, it's a literal value.
pub mod literal {
use std::fmt::Display;

use super::*;

impl ExpressionKind for Literal {
Expand All @@ -247,7 +249,17 @@ pub mod literal {
}
}

impl From<String> for Expr {
fn from(value: String) -> Self {
Expr::Literal(Literal(Term::String(value)))
}
}

impl Expr {
pub fn new_keyword(keyword: impl Display) -> Self {
Expr::Literal(Literal(Term::Atom(keyword.to_string())))
}

/// Expects a string literal and returns it's value.
pub fn string(&self) -> Result<String> {
match self {
Expand Down Expand Up @@ -288,6 +300,24 @@ pub enum SemanticError {
ExpectedQuoteExpression,
}

impl From<SemanticError> for Expr {
fn from(value: SemanticError) -> Self {
match value {
SemanticError::InvalidExpression => Expr::new_keyword("error/invalid-expression"),
SemanticError::InvalidList => Expr::new_keyword("error/invalid-list"),
SemanticError::InvalidArguments => Expr::new_keyword("error/invalid-arguments"),
SemanticError::MissingParameters => Expr::new_keyword("error/missing-parameters"),
SemanticError::MissingBody => Expr::new_keyword("error/missing-body"),
SemanticError::MissingHead => Expr::new_keyword("error/missing-head"),
SemanticError::ExpectedString => Expr::new_keyword("error/expected-string"),
SemanticError::ExpectedVectorWithSize(_) => Expr::new_keyword("error/expected-vector"),
SemanticError::ExpectedQuoteExpression => {
Expr::new_keyword("error/expected-quote-expression")
}
}
}
}

fn assert_length(list: Vec<Term>, length: usize) -> Result<Vec<Term>, SemanticError> {
if list.len() != length {
Err(SemanticError::ExpectedVectorWithSize(length))
Expand Down

0 comments on commit 51baa65

Please sign in to comment.