tylisp/
macros.rs

1
2/// sexprs, the building blocks of everything Lisp
3///
4/// This crate uses an unusal sexpression syntax in order to be Rust-compatible:
5/// * Curly braces `{}` are used for delimiters instead of parentheses
6/// * Terms are separated by commas instead of whitespace
7/// * The quote character is `@`
8/// * Atoms are arbitrary Rust types
9/// * The final comma can be replaced by `;` to suppress the automatic `HNil`
10#[macro_export]
11macro_rules! sexpr {
12    // The empty s-expression is `HNil`
13    { $(;)? } => {
14        $crate::HNil
15    };
16
17
18    // A leading semicolon parses a single term
19    { ; { $($head:tt)* } } => {
20        $crate::sexpr!{ $($head)* }
21    };
22
23    { ; @ {$($head:tt)*} $(= $val:expr)? } => {
24        $crate::Quote< $crate::sexpr!{ $($head)* } >
25    };
26
27    { ; @ $head:ty $(= $val:expr)? } => {
28        $crate::Quote< $head >
29    };
30
31    { ; $head:ty } => { $head };
32
33
34    // Two terms separated by a semicolon are a pair
35    { { $($head:tt)* } ; $($tail:tt)+ } => {
36        $crate::HCons< $crate::sexpr!{$($head)*},
37                       $crate::sexpr!{;$($tail)*} >
38    };
39
40    { @ {$($head:tt)*} $(= $val:expr)? ; $($tail:tt)+ } => {
41        $crate::HCons< $crate::Quote< $crate::sexpr!{$($head)*}>,
42                       $crate::sexpr!{;$($tail)*} >
43    };
44
45    { @ $head:ty $(= $val:expr)?; $($tail:tt)+ } => {
46        $crate::HCons< $crate::Quote< $head >,
47                       $crate::sexpr!{;$($tail)*} >
48    };
49
50    { $head:ty ; $($tail:tt)+ } => {
51        $crate::HCons< $head,
52                       $crate::sexpr!{;$($tail)*} >
53    };
54
55    
56    // A term followed by a comma is the head of a list
57    { { $($head:tt)* } $(, $($tail:tt)*)? } => {
58        $crate::HCons< $crate::sexpr!{$($head)*},
59                       $crate::sexpr!{$($($tail)*)?} >
60    };
61
62    { @ {$($head:tt)*} $(= $val:expr)? $(, $($tail:tt)*)? } => {
63        $crate::HCons< $crate::Quote< $crate::sexpr!{ $($head)* } >,
64                       $crate::sexpr!{$($($tail)*)?} >
65    };
66
67    { @ $head:ty $(= $val:expr)? $(, $($tail:tt)*)? } => {
68        $crate::HCons< $crate::Quote< $head >,
69                       $crate::sexpr!{$($($tail)*)?} >
70    };
71
72    { $head:ty $(, $($tail:tt)*)? } => {
73        $crate::HCons< $head,
74                       $crate::sexpr!{$($($tail)*)?} >
75    };
76}
77
78#[macro_export]
79macro_rules! sexpr_quoted_types {
80    // The empty s-expression is `HNil`
81    { $(;)? } => {
82        $crate::HNil
83    };
84
85
86    // A leading semicolon parses a single term
87    { ; { $($head:tt)* } } => { () };
88    { ; @ {$($head:tt)*}=$val:expr } => {
89        $crate::sexpr!{$($head:tt)*} = $val
90    };
91    { ; @ {$($head:tt)*} } => { () };
92    { ; @ $head:ty=$val:expr } => {
93        $head = $val
94    };
95    { ; @ $head:ty } => { () };
96    { ; $head:ty } => { () };
97
98
99    // Two terms separated by a semicolon are a pair
100    { { $($head:tt)* } ; $($tail:tt)+ } => {
101        $crate::HCons < $crate::sexpr_quoted_types!{$($head)*},
102                        $crate::sexpr_quoted_types!{;$($tail)*} >
103    };
104
105    { @ {$($head:tt)*} = $val:expr ; $($tail:tt)+ } => {
106        $crate::HCons < $crate::sexpr!{$($head:tt)*},
107                        $crate::sexpr_quoted_types!{;$($tail)*} >
108    };
109    { @ {$($head:tt)*} ; $($tail:tt)+ } => {
110        $crate::HCons < (),
111                        $crate::sexpr_quoted_types!{;$($tail)*} >
112    };
113
114    { @ $head:ty = $val:expr; $($tail:tt)+ } => {
115        $crate::HCons< $head,
116                       $crate::sexpr_quoted_types!{;$($tail)*} >
117    };
118    { @ $head:ty; $($tail:tt)+ } => {
119        $crate::HCons< (),
120                       $crate::sexpr_quoted_types!{;$($tail)*} >
121    };
122
123    { $head:ty ; $($tail:tt)+ } => {
124        $crate::HCons< (),
125                       $crate::sexpr_quoted_types!{;$($tail)*} >
126    };
127
128    
129    // A term followed by a comma is the head of a list
130    { { $($head:tt)* } $(, $($tail:tt)*)? } => {
131        $crate::HCons< $crate::sexpr_quoted_types!{$($head)*},
132                       $crate::sexpr_quoted_types!{$($($tail)*)?} >
133    };
134
135    { @ {$($head:tt)*} = $val:expr $(, $($tail:tt)*)? } => {
136        $crate::HCons< $crate::sexpr!{$($head:tt)*},
137                       $crate::sexpr_quoted_types!{$($($tail)*)?} >
138    };
139
140    { @ {$($head:tt)*} $(, $($tail:tt)*)? } => {
141        $crate::HCons< (),
142                       $crate::sexpr_quoted_types!{$($($tail)*)?} >
143    };
144
145    { @ $head:ty = $val:expr $(, $($tail:tt)*)? } => {
146        $crate::HCons< $head,
147                       $crate::sexpr_quoted_types!{$($($tail)*)?} >
148    };
149
150    { @ $head:ty $(, $($tail:tt)*)? } => {
151        $crate::HCons< (),
152                       $crate::sexpr_quoted_types!{$($($tail)*)?} >
153    };
154
155    { $head:ty $(, $($tail:tt)*)? } => {
156        $crate::HCons< (),
157                       $crate::sexpr_quoted_types!{$($($tail)*)?} >
158    };
159}
160
161
162/// Internal macro to extract quoted expressions from an `sexpr`
163#[macro_export]
164macro_rules! sexpr_quoted_vals {
165    // The empty s-expression is `HNil`
166    { $(;)? } => {
167        $crate::HNil
168    };
169
170
171    // A leading semicolon parses a single term
172    { ; { $($head:tt)* } } => { () };
173    { ; @ {$($head:tt)*}=$val:expr } => {
174        {let _x:$crate::sexpr!{$($head:tt)*} = $val; _x}
175    };
176    { ; @ {$($head:tt)*} } => { () };
177    { ; @ $head:ty=$val:expr } => {
178        {let _x:$head = $val; _x}
179    };
180    { ; @ $head:ty } => { () };
181    { ; $head:ty } => { () };
182
183
184    // Two terms separated by a semicolon are a pair
185    { { $($head:tt)* } ; $($tail:tt)+ } => {
186        $crate::HCons { head : $crate::sexpr_quoted_vals!{$($head)*},
187                        tail : $crate::sexpr_quoted_vals!{;$($tail)*} }
188    };
189
190    { @ {$($head:tt)*} = $val:expr ; $($tail:tt)+ } => {
191        $crate::HCons { head : {let _x:$crate::sexpr!{$($head:tt)*} = $val; _x},
192                        tail : $crate::sexpr_quoted_vals!{;$($tail)*} }
193    };
194    { @ {$($head:tt)*} ; $($tail:tt)+ } => {
195        $crate::HCons { head: (),
196                        tail: $crate::sexpr_quoted_vals!{;$($tail)*} }
197    };
198
199    { @ $head:ty = $val:expr; $($tail:tt)+ } => {
200        $crate::HCons{ head: {let _x:$head = $val; _x},
201                       tail: $crate::sexpr_quoted_vals!{;$($tail)*} }
202    };
203    { @ $head:ty; $($tail:tt)+ } => {
204        $crate::HCons{ head: (),
205                       tail: $crate::sexpr_quoted_vals!{;$($tail)*} }
206    };
207
208    { $head:ty ; $($tail:tt)+ } => {
209        $crate::HCons{ head: (),
210                       tail: $crate::sexpr_quoted_vals!{;$($tail)*} }
211    };
212
213    
214    // A term followed by a comma is the head of a list
215    { { $($head:tt)* } $(, $($tail:tt)*)? } => {
216        $crate::HCons{ head: $crate::sexpr_quoted_vals!{$($head)*},
217                       tail: $crate::sexpr_quoted_vals!{$($($tail)*)?} }
218    };
219
220    { @ {$($head:tt)*} = $val:expr $(, $($tail:tt)*)? } => {
221        $crate::HCons{ head: {let _x:$crate::sexpr!{$($head:tt)*} = $val; _x},
222                       tail: $crate::sexpr_quoted_vals!{$($($tail)*)?} }
223    };
224
225    { @ {$($head:tt)*} $(, $($tail:tt)*)? } => {
226        $crate::HCons{ head: (),
227                       tail: $crate::sexpr_quoted_vals!{$($($tail)*)?} }
228    };
229
230    { @ $head:ty = $val:expr $(, $($tail:tt)*)? } => {
231        $crate::HCons{ head: {let _x:$head = $val; _x},
232                       tail: $crate::sexpr_quoted_vals!{$($($tail)*)?} }
233    };
234
235    { @ $head:ty $(, $($tail:tt)*)? } => {
236        $crate::HCons{ head: (),
237                       tail: $crate::sexpr_quoted_vals!{$($($tail)*)?} }
238    };
239
240    { $head:ty $(, $($tail:tt)*)? } => {
241        $crate::HCons{ head: (),
242                       tail: $crate::sexpr_quoted_vals!{$($($tail)*)?} }
243    };
244}
245
246/// Build an sexpr type from expressions.
247#[macro_export]
248macro_rules! sexpr_val {
249    // The empty s-expression is `HNil`
250    { $(;)? } => {
251        $crate::HNil
252    };
253
254    // A leading semicolon parses a single term
255    { ; @{ $($head:tt)* } } => { $crate::sexpr_val!{$($head)*} };
256    { ; $head:expr } => { $head };
257   
258    { @{ $($head:tt)* } ; $($tail:tt)* } => {
259        $crate::HCons{ head: $crate::sexpr_val!{$($head)*},
260                       tail: $crate::sexpr_val!{;$($tail)*} }
261    };
262    { $head:expr ; $($tail:tt)* } => {
263        $crate::HCons{ head: $head,
264                       tail: $crate::sexpr_val!{;$($tail)*} }
265    };
266
267    { @{ $($head:tt)* } $(, $($tail:tt)*)? } => {
268        $crate::HCons{ head: $crate::sexpr_val!{$($head)*},
269                       tail: $crate::sexpr_val!{$($($tail)*)?} }
270    };
271    { $head:expr $(, $($tail:tt)*)? } => {
272        $crate::HCons{ head: $head,
273                       tail: $crate::sexpr_val!{$($($tail)*)?} }
274    };
275}
276
277
278/// Build an sexpr type from expressions.
279#[macro_export]
280macro_rules! sexpr_pat_ty {
281    // The empty s-expression is `HNil`
282    { $(;)? } => {
283        $crate::HNil
284    };
285
286    // A leading semicolon parses a single term
287    { ; { $($head:tt)* } } => { $crate::sexpr_pat_ty!{$($head)*} };
288    { ; $head:tt : $ty:ty } => { $ty };
289   
290    { { $($head:tt)* } ; $($tail:tt)* } => {
291        $crate::HCons< $crate::sexpr_pat_ty!{$($head)*},
292                       $crate::sexpr_pat_ty!{;$($tail)*} >
293    };
294    { $head:tt : $ty:ty; $($tail:tt)* } => {
295        $crate::HCons< $ty,
296                       $crate::sexpr_pat_ty!{;$($tail)*} >
297    };
298
299    { { $($head:tt)* } $(, $($tail:tt)*)? } => {
300        $crate::HCons< $crate::sexpr_pat_ty!{$($head)*},
301                       $crate::sexpr_pat_ty!{$($($tail)*)?} >
302    };
303    { $head:tt : $ty:ty $(, $($tail:tt)*)? } => {
304        $crate::HCons< $ty,
305                       $crate::sexpr_pat_ty!{$($($tail)*)?} >
306    };
307}
308
309/// Build an sexpr type from expressions.
310#[macro_export]
311macro_rules! sexpr_pat {
312    // The empty s-expression is `HNil`
313    { $(;)? } => {
314        $crate::HNil
315    };
316
317    // A leading semicolon parses a single term
318    { ; { $($head:tt)* } } => { $crate::sexpr_pat!{$($head)*} };
319    { ; $head:tt : $ty:ty } => { $head };
320   
321    { { $($head:tt)* } ; $($tail:tt)* } => {
322        $crate::HCons{ head: $crate::sexpr_pat!{$($head)*},
323                       tail: $crate::sexpr_pat!{;$($tail)*} }
324    };
325    { $head:tt : $ty:ty; $($tail:tt)* } => {
326        $crate::HCons{ head: $head,
327                       tail: $crate::sexpr_pat!{;$($tail)*} }
328    };
329
330    { { $($head:tt)* } $(, $($tail:tt)*)? } => {
331        $crate::HCons{ head: $crate::sexpr_pat!{$($head)*},
332                       tail: $crate::sexpr_pat!{$($($tail)*)?} }
333    };
334    { $head:tt : $ty:ty $(, $($tail:tt)*)? } => {
335        $crate::HCons{ head: $head,
336                       tail: $crate::sexpr_pat!{$($($tail)*)?} }
337    };
338}
339
340/// Shorthand for resolving a lisp expression into its resultant type
341#[macro_export]
342macro_rules! eval { ($($expr:tt)*) => { <$crate::sexpr!{$($expr)*} as $crate::engine::Eval>::Result } }
343
344#[macro_export]
345macro_rules! partial { ($($expr:tt)*) => { $crate::ops::PartialImpl<$crate::sexpr!{$($expr)*}> }}
346
347#[macro_export]
348macro_rules! calc { ($($expr:tt)*) => {
349    $crate::engine::calc::<$crate::sexpr!{$($expr)*}, $crate::sexpr_quoted_types!{$($expr)*}>(
350        $crate::sexpr_quoted_vals!{$($expr)*}
351    )
352}}
353
354#[macro_export]
355macro_rules! calc_ty { ($($expr:tt)*) => {
356    <$crate::sexpr!{$($expr)*} as $crate::engine::Calc<$crate::sexpr_quoted_types!{$($expr)*}>>::Result
357}}
358
359#[macro_export]
360macro_rules! calc_bound { ($($expr:tt)*) => {
361    $crate::engine::Calc<$crate::sexpr_quoted_types!{$($expr)*}>
362}}
363
364/// Define types as lisp literals, which evaluate to themselves
365#[macro_export]
366macro_rules! literal {
367    { $(
368        $({$($gen:tt)*})? $ty:ty
369    );+ } => { $(
370        impl$(<$($gen)*>)? $crate::engine::Eval for $ty { type Result = Self; }
371        impl<Q,$($($gen)*)?> $crate::engine::Calc<Q> for $ty where Self: ::std::default::Default {
372            type Result = Self;
373            #[inline(always)]
374            fn calc(_:Q)->Self { ::std::default::Default::default() }
375        }
376        impl$(<$($gen)*>)? $crate::LispId for $ty { type Id = $crate::uuid_new_v4!(| $crate::typenum); }
377    )* }
378}
379
380#[cfg(feature="const")]
381#[macro_export]
382macro_rules! literal_with_id {
383    { $(
384        $({$($gen:tt)*})? $ty:ty : $id:literal
385    );+ } => { $(
386        impl$(<$($gen)*>)? $crate::engine::Eval for $ty { type Result = Self; }
387        impl<Q,$($($gen)*)?> $crate::engine::Calc<Q> for $ty where Self: ::std::default::Default {
388            type Result = Self;
389            #[inline(always)]
390            fn calc(_:Q)->Self { ::std::default::Default::default() }
391        }
392        impl$(<$($gen)*>)? $crate::LispId for $ty {
393            type Id = $crate::ConstId<$id>;
394        }
395    )* }
396}
397
398#[macro_export]
399macro_rules! non_calc_literal {
400    { $(
401        $({$($gen:tt)*})? $ty:ty
402    );+ } => { $(
403        impl$(<$($gen)+>)? $crate::engine::Eval for $ty { type Result = Self; }
404        impl$(<$($gen)+>)? $crate::LispId for $ty { type Id = $crate::uuid_new_v4!(| $crate::typenum); }
405    )* }
406}
407
408/// Setup a new function, and some match arms for implementation
409///
410/// This is really a cross between a macro and a traditional Lisp
411/// function.  It takes evaluated arguments as input (which means
412/// bounds can easily be placed on the result), but builds syntax
413/// that, when evaluated, produces the correct result.
414///
415/// In more complicated situations, it may be beneficial (or necessary)
416/// to write some or all of the trait implementations directly.
417#[macro_export]
418macro_rules! defun {
419    {$id:ty { $($body:tt)* }} => {$crate::defun!{@self () $id { $($body)* }}};
420    {@$self: tt ($($id_gen:tt)*) $id:ty { $(
421        ($($generics:tt)*) $(calc where ($($calcbound:tt)*))? { $($args:tt)* } $({$($preamble:stmt;)*})? => { $($out:tt)* };
422    )* }} => {
423        $crate::defun_nocalc!{($($id_gen)*) $id {$(
424            ( $($generics)* ) { $($args)* } => { $($out)* };
425        )*}}
426        $(
427            impl<$($generics)*> $crate::engine::FunCalc< $crate::sexpr_pat_ty!{$($args)*} > for $id
428            where
429                //$id: $crate::engine::FunCall< $crate::sexpr_pat_ty!{$($args)*} >,
430                $crate::sexpr!{$($out)*}: $crate::engine::Calc< $crate::sexpr_quoted_types!{$($out)*} >,
431                $($($calcbound)*)?
432            {
433                type Result = <$crate::sexpr!{ $($out)* } as $crate::engine::Calc< $crate::sexpr_quoted_types!{$($out)*} >>::Result;
434                #[inline(always)]
435                fn calc($self, args: $crate::sexpr_pat_ty!{$($args)*})
436                -> Self::Result {
437                    #[allow(redundant_semicolons)]
438                    let syntax: $crate::sexpr_quoted_types!{$($out)*} = {
439                        let $crate::sexpr_pat!{$($args)*}: $crate::sexpr_pat_ty!{$($args)*} = args;
440                        $($($preamble;)*)?
441                        $crate::sexpr_quoted_vals!{$($out)*}
442                    };
443                    <$crate::sexpr!{$($out)*} as $crate::engine::Calc< $crate::sexpr_quoted_types!{$($out)*} >>::calc(syntax)
444                }
445            }
446        )*
447    }
448}
449
450/// A trait that all values implement.
451///
452/// Used as a base case in macro expansions that take optional trait bound arguments.
453pub trait Everything {}
454impl<T:?Sized> Everything for T {} 
455
456#[macro_export]
457macro_rules! defun_rust {
458    ( ($($header:tt)+) -> {$($expr:tt)*} $((as $($res_bound:tt)+))? $(where $($bound:tt)+)? ) => {
459        $($header)+ -> <$crate::sexpr!{ $($expr)* } as $crate::engine::Calc< $crate::sexpr_quoted_types!{$($expr)*} >>::Result
460        where $crate::sexpr!{$($expr)*}: $crate::engine::Calc< $crate::sexpr_quoted_types!{$($expr)*} >,
461            <$crate::sexpr!{ $($expr)* } as $crate::engine::Calc< $crate::sexpr_quoted_types!{$($expr)*} >>::Result: $crate::macros::Everything + $($($res_bound)+,)?
462            $( $($bound)+)?
463        {
464            let syntax: $crate::sexpr_quoted_types!{$($expr)*} = $crate::sexpr_quoted_vals!{$($expr)*};
465            <$crate::sexpr!{$($expr)*} as $crate::engine::Calc< $crate::sexpr_quoted_types!{$($expr)*} >>::calc(syntax)
466        }
467    }
468}
469
470#[macro_export]
471macro_rules! defun_nocalc {
472    {($($id_gen:tt)*) $id:ty { $(
473        ($($generics:tt)*) { $($args:tt)* } $(where ($($bound:tt)+))? => { $($out:tt)* };
474    )* }} => {
475        $crate::literal!{{$($id_gen)*} $id}
476        impl<$($id_gen)*> $crate::engine::Call for $id { type Conv=$crate::engine::cc::Func; }
477        $(
478            impl<$($generics)*> $crate::engine::FunCall< $crate::sexpr_pat_ty!{$($args)*} > for $id
479            where $crate::sexpr!{$($out)*}: $crate::engine::Eval $(,$($bound)+)? {
480                type Result = $crate::eval!{ $($out)* };
481            }
482        )*
483    }
484}
485
486/// Setup a new macro function, and some match arms for implementation
487///
488/// The only difference between this and `defun_nocalc!` is that the arguments
489/// are provided un-evaluated, which means you're matching on syntax
490/// instead of results.  It therefore provides the opportunity to
491/// short-circuit evaluation of some arguments.
492#[macro_export]
493macro_rules! defmacro {
494    {$id:ty { $(
495        ($($generics:tt)*) { $($args:tt)* } => { $($out:tt)* };
496    )* }} => {
497        $crate::literal!{$id}
498        impl $crate::engine::Call for $id { type Conv=$crate::engine::cc::Syntax; }
499        $(
500            impl<$($generics)*> $crate::engine::SynCall< sexpr!{$($args)*} > for $id
501            where $crate::sexpr!{$($out)*}: $crate::engine::Eval {
502                type Result = $crate::eval!{ $($out)* };
503            }
504        )*
505    }
506}
507
508#[cfg(test)]
509macro_rules! assert_type_eq {
510    ($a:ty, $b:ty) => { let _: ::core::marker::PhantomData<$a>=
511        <::core::marker::PhantomData<$b> as ::core::default::Default>::default();
512    }
513}
514