tylisp 0.1.0

A domain-specific language for expressing complex type bounds
Documentation
use crate::{Quote,HNil,HCons};

pub fn calc<Expr,Quotes>(q:Quotes)->Expr::Result
where Expr:Calc<Quotes> {
    <Expr as Calc<Quotes>>::calc(q)
}

/// An s-expression that is well-formed lisp code
pub trait Eval {
    type Result;
}

/// An s-expression that can calculate a runtime value, given the provided
/// `Quotes`.
///
/// `Quotes` should have the same structure as `Self`, but items
pub trait Calc<Quotes> {
    type Result;
    fn calc(q:Quotes)->Self::Result;
}

impl<T> Eval for Quote<T> {
    type Result = T;
}

impl<T:Eval> Eval for std::marker::PhantomData<T> {
    type Result = T::Result;
}

impl<T,U> Calc<U> for std::marker::PhantomData<T> {
    type Result = Self;
    #[inline(always)]
    fn calc(_:U)->Self { std::marker::PhantomData }
}

impl<Src: Into<Dest>, Dest> Calc<Src> for Quote<Dest> {
    type Result = Dest;
    #[inline(always)]
    fn calc(t:Src)->Dest { t.into() }
}

/// A list where every item is individually evaluable
pub trait EvalList {
    type Result;
}
macro_rules! eval_list { ($exprs:ty) => { <$exprs as EvalList>::Result }}

pub trait CalcList<Quotes> {
    type Result;
    fn calc_list(q:Quotes)->Self::Result;
}

impl EvalList for HNil {
    type Result = HNil;
}

impl CalcList<HNil> for HNil {
    type Result = HNil;
    #[inline(always)]
    fn calc_list(_:HNil)->HNil { HNil }
}

impl<H:Eval, T:EvalList> EvalList for HCons<H,T> {
    type Result = HCons<H::Result, eval_list!{T}>;
}

impl<QH, QT, H, T> CalcList<HCons<QH,QT>> for HCons<H,T>
where H: Calc<QH>,
      T: CalcList< QT > {
    type Result = HCons<H::Result, T::Result>;
    #[inline(always)]
    fn calc_list(q: HCons<QH,QT>) -> HCons<H::Result, T::Result> {
        HCons { head: H::calc(q.head),
                tail: T::calc_list(q.tail)
        }
    }
}

impl<H:Eval, T> Eval for HCons<H,T>
where H::Result: Call,
     <H::Result as Call>::Conv: CallImpl<H::Result, T> {
    type Result = <<H::Result as Call>::Conv as CallImpl<H::Result, T>>::Result;
}

impl<H:Calc<QH>, T, QH, QT> Calc<HCons<QH,QT>> for HCons<H,T>
where H::Result: Call,
     <H::Result as Call>::Conv: CalcImpl<H::Result, T, QT>,
{
    type Result = <<H::Result as Call>::Conv as CalcImpl<H::Result, T, QT>>::Result;
    #[inline(always)]
    fn calc(q:HCons<QH, QT>)->Self::Result {
        let func = H::calc(q.head);
        <H::Result as Call>::Conv::calc_impl(func, q.tail)
    }
}

/// Calling Conventions
pub mod cc {
    /// Use FunCall trait
    pub struct Func;
    
    /// Use SynCall trait
    pub struct Syntax;
}

/// A callable lisp value.
///
/// When the evaluator process a list, the head of that list should evaluate
/// to something that implements `Call`.
/// The `Conv` associated type describes the calling convention that should be
/// used for this callsite, and it controls the rest of processing, including
/// whether or not the rest of the arguments are evaluated at all.
pub trait Call {
    /// Which calling convention is expected?
    ///
    /// Should be one of the types in the `cc` module.
    type Conv;
}

/// A function whose arguments are pre-evaluated
///
/// Note that it is acceptable to arbitrarily constrain this impl, and
/// also to provide multiple impls for the same type, as long as they do
/// not overlap.
///
/// `Args` is an `hlist` of all the arguments passed to the function.
pub trait FunCall< Args >: Call<Conv=cc::Func> {
    type Result;
}

pub trait FunCalc< Args >: Sized {
    type Result;
    fn calc(self, args:Args)->Self::Result;
}

/// A function that takes raw syntax as an argument
///
/// NB: Unlike traditional Lisp macros, this returns a *Value*, not more syntax
pub trait SynCall< Args >: Call<Conv=cc::Syntax> {
    type Result;
}

/// A hack to work around a long-standing bug in the type system, where
/// associated types aren't considered appropriately distinct
pub trait CallImpl<F, Args> {
    type Result;
}

pub trait CalcImpl<F, Args, Q> {
    type Result;
    fn calc_impl(func:F, q:Q)->Self::Result;
}

impl<F, Args:EvalList> CallImpl<F, Args> for cc::Func
where F:FunCall< eval_list!{Args} > {
    type Result = <F as FunCall< eval_list!{Args}>>::Result;
}

impl<F, Args:CalcList<Q>, Q> CalcImpl<F, Args, Q> for cc::Func
where F: FunCalc< Args::Result > {
    type Result = <F as FunCalc< Args::Result >>::Result;
    #[inline(always)]
    fn calc_impl(func:F, q:Q)->Self::Result {
        let args = Args::calc_list(q);
        func.calc(args)
    }
}

impl<F, Args> CallImpl<F, Args> for cc::Syntax
where F:SynCall< Args > {
    type Result = <F as SynCall< Args>>::Result;
}

impl<F, Args, Q> CalcImpl<F, Args, Q> for cc::Syntax
where F:SynCalc< Args, Q > {
    type Result = <F as SynCalc<Args, Q>>::Result;
    #[inline(always)]
    fn calc_impl(func:F, quotes:Q)->Self::Result {
        func.syn_calc(quotes)
    }
}

pub trait SynCalc< Args, Q > {
    type Result;
    fn syn_calc(self, quotes:Q) -> Self::Result;
}