1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
#![recursion_limit = "512"]
#![cfg_attr(feature = "const",
            feature(const_generics,
                    const_panic,
                    const_evaluatable_checked,
))]
#![cfg_attr(feature = "const", allow(incomplete_features))]
//! # TyLisp: a domain-specific language for type-system calculations
//!
//! > This crate is still **experimental** and likely to undergo significant
//! > changes.
//! > It is developed primarily to support the needs of `memquery`.
//!
//! `tylisp` is a programming language embedded in Rust's macro and
//! type systems.
//! Its main purpose is to ease the burden of writing extremely-generic
//! Rust code that comes from a proliferation of trait bounds.
//! Because `tylisp` calculations occur entirely within the type system,
//! any object that the used operations support will automatically be
//! accepted.
//!
//! `tylisp` has two distinct operating modes:
//! * `Eval` mode will calculate a type, but doesn't provide any means to
//!   produce a value of that type.  It's mostly used to convey extra
//!   context informtion that can help select one of several implementations.
//! * `Calc` additionally provides a mechanism to apply the same actions to
//!   runtime values, which will produce a result value of the specified type.
//!   This allows us to define overloaded functions that have a return type
//!   dependent on argument types.
//! 
//! ## Semver Hazards
//! Writing code this way poses unique challenges to versioning.
//! All of the Rust functions defined via `tylisp` expose their internal
//! algorithm in the public API.
//! Thus, any change to the internal workings of a function have the
//! potential to break downstream code.
//! 
//! Best practice is to specify traditional trait bounds that constrain
//! the input and output types, and manually verify that the `tylisp`
//! code always satisfies these constraints.

#[cfg(not(feature = "const"))] pub use ::typenum_uuid::uuid_new_v4;
#[cfg(feature = "const")] pub use ::typenum_uuid::uuid_new_v4_literal;
#[cfg(feature = "const")] #[macro_export] macro_rules! uuid_new_v4 {
    ($($x:tt)*) => {
        $crate::ConstId<{$crate::uuid_new_v4_literal!()}>
    }
}

pub struct ConstId<const ID:u128>;

use typenum as tn; // 1.12.0;
pub use typenum;
#[cfg(frunk)] pub use frunk::{HNil,HCons};
#[cfg(not(frunk))]
#[derive(Debug,Default,Copy,Clone,Eq,PartialEq,Ord,PartialOrd,Hash)]
pub struct HNil;

#[cfg(not(frunk))]
#[derive(Debug,Default,Copy,Clone,Eq,PartialEq,Ord,PartialOrd,Hash)]
pub struct HCons<H,T> { pub head: H, pub tail: T }

#[macro_use]
pub mod macros;
pub mod engine;
pub mod ops;
pub mod marker_traits;

#[derive(Debug)]
pub struct Quote<T>(T);

// Required for any argument to Is
#[cfg(feature = "const")]
pub trait LispId:WithConstId { type Id; }
#[cfg(not(feature = "const"))]
pub trait LispId { type Id; }

pub trait WithConstId { const ID: u128; }

impl<T:LispId<Id=ConstId<ID>>, const ID:u128> WithConstId for T {
    const ID:u128 = ID;
}

#[cfg(feature="const")]
impl<T:LispId<Id=ConstId<ID>> + ?Sized, const ID:u128> LispId for std::marker::PhantomData<T> {
    type Id = T::Id;
}

#[cfg(not(feature="const"))]
impl<T:LispId + ?Sized> LispId for std::marker::PhantomData<T> {
    type Id = T::Id;
}

literal!{
              HNil;
          tn::True;
          tn::False;
          tn::Z0;
          tn::UTerm;
    {H,L} tn::UInt<H,L>
}