tylisp 0.1.0

A domain-specific language for expressing complex type bounds
Documentation
pub mod list;
pub mod logic;
pub mod rc;
pub mod arith;
#[cfg(feature = "const")] pub mod set;

mod if_impl;
pub use if_impl::{If,Cond};

use crate::{LispId, engine::*};
use typenum as tn;

use std::default::Default;

/// TyLisp operator that compares two values for identity
///
/// Accepts two parameters that both implement `LispId`.
/// Returns `True` if both ids are equal and `False` otherwise.
///
/// Warning: the current implementation of `LispId` only considers
/// the base type, so Foo<A> might compare equal to Foo<B>.
/// This behavior is subject to change in the future.

#[derive(Debug,Default)]
pub struct Is;
defun!{ Is {} }

#[cfg(not(feature = "const"))]
impl<L,R> FunCall< sexpr!{L, R} > for Is
where L: LispId,
      R: LispId,
      L::Id: tn::IsEqual<R::Id>,
      <L::Id as tn::IsEqual<R::Id>>::Output: Default,
{
    type Result = <L::Id as tn::IsEqual<R::Id>>::Output;
}

#[cfg(feature = "const")]
impl<L,R> FunCall< sexpr!{L, R} > for Is
where L:LispId,
      R:LispId,
      IdIsEqual<L::Id, R::Id>: AsTypeBool
{
    type Result = <IdIsEqual<L::Id, R::Id> as AsTypeBool>::TypeBool;
}

pub struct IdIsEqual<L,R>(std::marker::PhantomData<(L,R)>);

#[cfg(feature = "const")]
impl<const LID:u128, const RID:u128> AsTypeBool
for IdIsEqual<crate::ConstId<LID>, crate::ConstId<RID>>
where ConstBool<{LID == RID}>: AsTypeBool {
    type TypeBool = <ConstBool<{LID == RID}> as AsTypeBool>::TypeBool;
}

pub trait AsTypeBool {
    type TypeBool: tn::marker_traits::Bit;
}

impl AsTypeBool for ConstBool<true> {
    type TypeBool = tn::True;
}

impl AsTypeBool for ConstBool<false> {
    type TypeBool = tn::False;
}

pub struct ConstBool<const X:bool>;

impl<T> FunCalc<T> for Is where Is: FunCall<T>, <Is as FunCall<T>>::Result: Default {
    type Result = <Is as FunCall<T>>::Result;
    #[inline(always)]
    fn calc(self, _:T)->Self::Result { Default::default() }
}

#[derive(Debug,Default)]
pub struct IsNot;
defun_nocalc!{() IsNot {
    (L,R) {_:L, _:R} => {logic::Not, {Is, @L, @R}};
}}

#[test]
fn test_is() {
    #[derive(Debug,Default)] struct A;
    #[derive(Debug,Default)] struct B;
    literal!{A; B}

    let _: eval!{If, {Is, A, B}, A, B } = B;
    let _: eval!{If, {Is, tn::True, tn::True}, A, B} = A;
    let _: eval!{If, {Is, A, A}, A, B} = A;
    let _: eval!{If, {Is, B, B}, A, B} = A;

    fn assert_b<X:LispId>() where sexpr!{If, {Is, @X, B}, A, B}: Eval<Result = A> {
    }

    assert_b::<B>();
}

#[test]
fn test_is_not() {
    #[derive(Debug,Default)] struct A;
    #[derive(Debug,Default)] struct B;
    literal!{A; B}

    let _: eval!{If, {IsNot, A, B}, A, B } = A;
    let _: eval!{If, {IsNot, tn::True, tn::True}, A, B} = B;
}

#[test]
fn test_partial() {
    #[derive(Debug,Default)] struct A;
    #[derive(Debug,Default)] struct B;
    literal!{A; B}

    let _: eval!{If, {{Partial, Is, A}, B}, A, B } = B;
    let _: eval!{If, {Is, tn::True, tn::True}, A, B} = A;
}
/// Function that accepts any number of arguments and returns the first
///
/// Useful to specify a constant value where a function call is expected

#[derive(Debug,Default)]
pub struct Ret;
literal!{Ret}

impl Call for Ret { type Conv=cc::Func; }
impl<T,U> FunCall < sexpr!{T; U} > for Ret {
    type Result = T;
}

impl<T,U> FunCalc < sexpr!{T; U} > for Ret {
    type Result = T;
    fn calc(self, sexpr_pat!{x:T; _:U}: sexpr!{T; U})-> T {
        x
    }    
}

/// Constructs a partial evaluation of a tylisp function.
///
/// This allows you to capture some state inside a tylisp callable:
/// ```raw
/// {{Partial, Func, A, B}, C, D}
/// ```
/// is equivalent to:
/// ```raw
/// {Func, A, B, C, D}
/// ```
#[derive(Debug,Default)]
pub struct Partial;
defmacro!{Partial {
    (Prefix) {; Prefix} => {Ret, @PartialImpl<Prefix>};
}}
impl<Prefix, QPrefix>
SynCalc< Prefix, QPrefix > for Partial
{
    type Result = PartialImpl<Prefix>;
    #[inline(always)]
    fn syn_calc(self, _:QPrefix)->Self::Result {
        PartialImpl(std::marker::PhantomData)
    }
}

/// Tylisp macro to perform `evaĺ`-only calculations in a `calc` context
///
/// Takes one argument, which is evaluated in an `eval` context.
/// Returns the evaluation result wrapped in a `PhantomData` object.
#[derive(Debug,Default)]
pub struct Phantom;
defmacro!{Phantom {}}

impl<Arg:Eval> SynCall<sexpr!{Arg}> for Phantom {
    type Result = std::marker::PhantomData<Arg::Result>;
}

impl<Arg:Eval,QArg> SynCalc<sexpr!{Arg},QArg> for Phantom {
    type Result = std::marker::PhantomData<Arg::Result>;
    #[inline(always)]
    fn syn_calc(self, _:QArg)->Self::Result { std::marker::PhantomData }
}



#[derive(Debug,Default)]
pub struct Quote;
defun_nocalc!{() Quote {
    (Arg) {_:Arg} => {Ret, @crate::Quote<Arg>};
}}


#[derive(Debug)]
pub struct PartialImpl<Prefix>(std::marker::PhantomData<Prefix>);
defun_nocalc!{(Prefix) PartialImpl<Prefix> {}}

impl<T> Default for PartialImpl<T> {
    #[inline(always)]
    fn default()->Self { PartialImpl(std::marker::PhantomData) }
}

impl<T> From<()> for PartialImpl<T> {
    #[inline(always)]
    fn from(_:())->Self { Default::default() }
}

impl<Prefix, Tail> FunCall< Tail > for PartialImpl<Prefix>
where sexpr!{list::Concat, @Prefix, {list::Map, Quote, @Tail}}: Eval,
    eval!{list::Concat, @Prefix, {list::Map, Quote, @Tail}}: Eval
{
    type Result = <eval!{list::Concat, @Prefix, {list::Map, Quote, @Tail}} as Eval>::Result;
}