tylisp/
engine.rs

1use crate::{Quote,HNil,HCons};
2
3pub fn calc<Expr,Quotes>(q:Quotes)->Expr::Result
4where Expr:Calc<Quotes> {
5    <Expr as Calc<Quotes>>::calc(q)
6}
7
8/// An s-expression that is well-formed lisp code
9pub trait Eval {
10    type Result;
11}
12
13/// An s-expression that can calculate a runtime value, given the provided
14/// `Quotes`.
15///
16/// `Quotes` should have the same structure as `Self`, but items
17pub trait Calc<Quotes> {
18    type Result;
19    fn calc(q:Quotes)->Self::Result;
20}
21
22impl<T> Eval for Quote<T> {
23    type Result = T;
24}
25
26impl<T:Eval> Eval for std::marker::PhantomData<T> {
27    type Result = T::Result;
28}
29
30impl<T,U> Calc<U> for std::marker::PhantomData<T> {
31    type Result = Self;
32    #[inline(always)]
33    fn calc(_:U)->Self { std::marker::PhantomData }
34}
35
36impl<Src: Into<Dest>, Dest> Calc<Src> for Quote<Dest> {
37    type Result = Dest;
38    #[inline(always)]
39    fn calc(t:Src)->Dest { t.into() }
40}
41
42/// A list where every item is individually evaluable
43pub trait EvalList {
44    type Result;
45}
46macro_rules! eval_list { ($exprs:ty) => { <$exprs as EvalList>::Result }}
47
48pub trait CalcList<Quotes> {
49    type Result;
50    fn calc_list(q:Quotes)->Self::Result;
51}
52
53impl EvalList for HNil {
54    type Result = HNil;
55}
56
57impl CalcList<HNil> for HNil {
58    type Result = HNil;
59    #[inline(always)]
60    fn calc_list(_:HNil)->HNil { HNil }
61}
62
63impl<H:Eval, T:EvalList> EvalList for HCons<H,T> {
64    type Result = HCons<H::Result, eval_list!{T}>;
65}
66
67impl<QH, QT, H, T> CalcList<HCons<QH,QT>> for HCons<H,T>
68where H: Calc<QH>,
69      T: CalcList< QT > {
70    type Result = HCons<H::Result, T::Result>;
71    #[inline(always)]
72    fn calc_list(q: HCons<QH,QT>) -> HCons<H::Result, T::Result> {
73        HCons { head: H::calc(q.head),
74                tail: T::calc_list(q.tail)
75        }
76    }
77}
78
79impl<H:Eval, T> Eval for HCons<H,T>
80where H::Result: Call,
81     <H::Result as Call>::Conv: CallImpl<H::Result, T> {
82    type Result = <<H::Result as Call>::Conv as CallImpl<H::Result, T>>::Result;
83}
84
85impl<H:Calc<QH>, T, QH, QT> Calc<HCons<QH,QT>> for HCons<H,T>
86where H::Result: Call,
87     <H::Result as Call>::Conv: CalcImpl<H::Result, T, QT>,
88{
89    type Result = <<H::Result as Call>::Conv as CalcImpl<H::Result, T, QT>>::Result;
90    #[inline(always)]
91    fn calc(q:HCons<QH, QT>)->Self::Result {
92        let func = H::calc(q.head);
93        <H::Result as Call>::Conv::calc_impl(func, q.tail)
94    }
95}
96
97/// Calling Conventions
98pub mod cc {
99    /// Use FunCall trait
100    pub struct Func;
101    
102    /// Use SynCall trait
103    pub struct Syntax;
104}
105
106/// A callable lisp value.
107///
108/// When the evaluator process a list, the head of that list should evaluate
109/// to something that implements `Call`.
110/// The `Conv` associated type describes the calling convention that should be
111/// used for this callsite, and it controls the rest of processing, including
112/// whether or not the rest of the arguments are evaluated at all.
113pub trait Call {
114    /// Which calling convention is expected?
115    ///
116    /// Should be one of the types in the `cc` module.
117    type Conv;
118}
119
120/// A function whose arguments are pre-evaluated
121///
122/// Note that it is acceptable to arbitrarily constrain this impl, and
123/// also to provide multiple impls for the same type, as long as they do
124/// not overlap.
125///
126/// `Args` is an `hlist` of all the arguments passed to the function.
127pub trait FunCall< Args >: Call<Conv=cc::Func> {
128    type Result;
129}
130
131pub trait FunCalc< Args >: Sized {
132    type Result;
133    fn calc(self, args:Args)->Self::Result;
134}
135
136/// A function that takes raw syntax as an argument
137///
138/// NB: Unlike traditional Lisp macros, this returns a *Value*, not more syntax
139pub trait SynCall< Args >: Call<Conv=cc::Syntax> {
140    type Result;
141}
142
143/// A hack to work around a long-standing bug in the type system, where
144/// associated types aren't considered appropriately distinct
145pub trait CallImpl<F, Args> {
146    type Result;
147}
148
149pub trait CalcImpl<F, Args, Q> {
150    type Result;
151    fn calc_impl(func:F, q:Q)->Self::Result;
152}
153
154impl<F, Args:EvalList> CallImpl<F, Args> for cc::Func
155where F:FunCall< eval_list!{Args} > {
156    type Result = <F as FunCall< eval_list!{Args}>>::Result;
157}
158
159impl<F, Args:CalcList<Q>, Q> CalcImpl<F, Args, Q> for cc::Func
160where F: FunCalc< Args::Result > {
161    type Result = <F as FunCalc< Args::Result >>::Result;
162    #[inline(always)]
163    fn calc_impl(func:F, q:Q)->Self::Result {
164        let args = Args::calc_list(q);
165        func.calc(args)
166    }
167}
168
169impl<F, Args> CallImpl<F, Args> for cc::Syntax
170where F:SynCall< Args > {
171    type Result = <F as SynCall< Args>>::Result;
172}
173
174impl<F, Args, Q> CalcImpl<F, Args, Q> for cc::Syntax
175where F:SynCalc< Args, Q > {
176    type Result = <F as SynCalc<Args, Q>>::Result;
177    #[inline(always)]
178    fn calc_impl(func:F, quotes:Q)->Self::Result {
179        func.syn_calc(quotes)
180    }
181}
182
183pub trait SynCalc< Args, Q > {
184    type Result;
185    fn syn_calc(self, quotes:Q) -> Self::Result;
186}