lambdas/dsl.rs
1use crate::*;
2
3use std::collections::HashMap;
4use std::fmt::{Debug};
5use std::hash::Hash;
6use std::collections::hash_map::Values;
7
8pub type DSLFn<D> = fn(Env<D>, &Evaluator<D>) -> VResult<D>;
9
10#[derive(Clone)]
11pub struct DSLEntry<D: Domain> {
12 pub name: Symbol, // eg "map" or "0" or "[1,2,3]"
13 pub val: Val<D>,
14 pub tp: Type,
15 pub arity: usize,
16}
17
18#[derive(Clone)]
19pub struct DSL<D:Domain> {
20 pub entries: HashMap<Symbol,DSLEntry<D>>,
21}
22
23impl<D: Domain> DSLEntry<D> {
24 pub fn new(name: Symbol, val: Val<D>, tp: Type) -> Self {
25 let arity = tp.arity();
26 DSLEntry {
27 name,
28 val,
29 tp,
30 arity,
31 }
32 }
33}
34impl<D: Domain> DSL<D> {
35 pub fn new( entries: Vec<DSLEntry<D>> ) -> Self {
36 DSL {
37 entries: entries.into_iter().map(|entry| (entry.name, entry)).collect()
38 }
39 }
40}
41
42
43/// The key trait that defines a domain
44pub trait Domain: Clone + Debug + PartialEq + Eq + Hash + 'static {
45 /// Domain::Data is attached to the Evaluator so all DSL functions will have a
46 /// mut ref to it (through the handle argument). Feel free to make it the empty
47 /// tuple if you dont need it.
48 /// Motivation: For some complicated domains you could leave Ids as pointers and
49 /// have your domaindata be a system to lookup the actual value from the pointer
50 /// (and guarantee no value has multiple pointers so that comparison works by Ids).
51 /// Btw, I briefly implemented it so that the whole system worked by these pointers
52 /// and it was absolutely miserable, see my notes. But this is here if someone finds
53 /// a use for it. Ofc be careful not to break function purity with this but otherwise
54 /// be creative :)
55 type Data: Debug + Default;
56 // type Type: Debug + Clone + PartialEq + Eq + Hash;
57
58 /// given a primitive's symbol return a runtime Val object. For function primitives
59 /// this should return a PrimFun(CurriedFn) object.
60 fn val_of_prim(p: egg::Symbol) -> Option<Val<Self>> {
61 Self::dsl_entry(p).map(|entry| entry.val.clone()).or_else(||
62 Self::val_of_prim_fallback(p))
63 }
64
65 fn val_of_prim_fallback(p: egg::Symbol) -> Option<Val<Self>>;
66
67 /// given a function primitive's symbol return the function pointer
68 /// you can use to call the function.
69 /// Breakdown of the function type: it takes a slice of values as input (the args)
70 /// along with a mut ref to an Expr (I'll refer to as a "handle") which is necessary
71 /// for calling .apply(f,x). This setup with a handle guarantees we can always track
72 /// when applys happen and log them in our Expr.evals, and also it's necessary for
73 /// executing LamClosures in order to look up their body Id (and we wouldn't want
74 /// LamClosures to carry around full Exprs because that would break the Expr.evals tracking)
75 fn lookup_fn_ptr(p: Symbol) -> DSLFn<Self>;
76
77 fn dsl_entry(p: Symbol) -> Option<&'static DSLEntry<Self>>;
78
79 fn dsl_entries() -> Values<'static, Symbol, DSLEntry<Self>>;
80
81 fn type_of_dom_val(&self) -> Type;
82
83 fn type_of_prim(p: Symbol) -> Type {
84 Self::dsl_entry(p).map(|entry| entry.tp.clone()).unwrap_or_else(|| {
85 Self::type_of_dom_val(&Self::val_of_prim(p).unwrap().dom().unwrap())
86 })
87 }
88
89}
90