libunseemly/runtime/
eval.rs

1#![macro_use]
2
3use crate::{
4    ast::Ast,
5    ast_walk::{walk, LazyWalkReses, WalkRule},
6    form::Form,
7    name::*,
8    util::assoc::Assoc,
9    walk_mode::{NegativeWalkMode, WalkMode},
10};
11use num::bigint::BigInt;
12use std::{self, rc::Rc};
13
14/// Values in Unseemly.
15
16#[derive(Debug, Clone, PartialEq)]
17pub enum Value {
18    Int(BigInt),
19    Sequence(Vec<Rc<Value>>), // TODO: switch to a different core sequence type
20    Function(Rc<Closure>),    // TODO: unsure if this Rc is needed
21    BuiltInFunction(BIF),
22    AbstractSyntax(Ast),
23    Struct(Assoc<Name, Value>),
24    Enum(Name, Vec<Value>),
25    // Hypothesis: all strings are either
26    //  in a formal language (and should be stored as ASTs instead)
27    //  or in a human language (and should be tokens for localization).
28    // But for now, here's a plain string.
29    Text(String),
30    Cell(Rc<std::cell::RefCell<Value>>),
31    // Reifying `Form`s causes loss of identity, so have an explicit (opaque) representation.
32    // Perhaps drop reification entirely, and just use an opaque type based on `std::any::Any`?
33    ParseContext(Box<crate::earley::ParseContext>),
34}
35
36pub use self::Value::*;
37
38#[derive(Debug, Clone, PartialEq)]
39pub struct Closure {
40    pub body: Ast,
41    pub params: Vec<Name>,
42    pub env: Assoc<Name, Value>,
43}
44
45impl Value {
46    /// Turns this `Value` into a "magic" `Ast` that evaluates to it.
47    /// The `Ast` will have the universal type
48    pub fn prefab(self) -> Ast {
49        raw_ast!(Node(
50            typed_form!(
51                "prefab_internal",
52                (impossible), // no syntax
53                cust_rc_box!(move |_| Ok(ast!(
54                        // Cheat: has the universal type, but we know it's safe because <mumble>.
55                        {"Type" "forall_type" :
56                            "param" => ["T"],
57                            "body" => (import [* [forall "param"]] (vr "T"))}))),
58                cust_rc_box!(move |_| Ok(self.clone()))
59            ),
60            crate::util::mbe::EnvMBE::new(),
61            crate::beta::ExportBeta::Nothing
62        ))
63    }
64}
65
66/// Creates an (ill-typed!) lambda expression that behaves like the closure.
67/// Free names in `self.body` remain free.
68impl Closure {
69    pub fn prefab(&self) -> Ast {
70        ast!({"Expr" "lambda" :
71            "param" => (@"p" ,seq self.params.iter().map(|n| ast!(*n))),
72            "p_t" => (@"p" ,seq self.params.iter().map(|_| ast!((trivial)))),
73            "body" => (import [* ["param" : "p_t"]] (,
74                crate::alpha::substitute(&self.body,
75                    &self.env.map(|v| v.clone().prefab())
76                )
77            ))
78        })
79    }
80}
81
82// Built-in function
83pub struct BIF(pub Rc<(dyn Fn(Vec<Value>) -> Value)>);
84
85pub fn apply__function_value(f: &Value, args: Vec<Value>) -> Value {
86    match *f {
87        BuiltInFunction(BIF(ref f)) => f(args.into_iter().collect()),
88        Function(ref cl) => {
89            let mut clo_env = cl.env.clone();
90            if cl.params.len() != args.len() {
91                panic!(
92                    "[type error] Attempted to apply {} arguments to function requiring {} \
93                     parameters",
94                    args.len(),
95                    cl.params.len()
96                );
97            }
98            for (p, a) in cl.params.iter().zip(args.into_iter()) {
99                clo_env = clo_env.set(*p, a)
100            }
101            eval(&cl.body, clo_env).unwrap()
102        }
103        _ => panic!("[type error] {:#?} is not a function", f),
104    }
105}
106
107impl PartialEq for BIF {
108    fn eq(&self, other: &BIF) -> bool { self as *const BIF == other as *const BIF }
109}
110
111impl Clone for BIF {
112    fn clone(&self) -> BIF { BIF(self.0.clone()) }
113}
114
115impl std::fmt::Display for Value {
116    fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> {
117        match *self {
118            Int(ref bi) => write!(f, "{}", bi),
119            Sequence(ref seq) => {
120                for elt in seq {
121                    write!(f, "{}", &*elt)?;
122                }
123                Ok(())
124            }
125            Function(_) => write!(f, "[closure]"),
126            BuiltInFunction(_) => write!(f, "[built-in function]"),
127            AbstractSyntax(ref ast) => write!(f, "'[{}]'", ast),
128            Struct(ref parts) => {
129                write!(f, "*[")?;
130                for (k, v) in parts.iter_pairs() {
131                    write!(f, "{}: {} ", k, v)?;
132                }
133                write!(f, "]*")
134            }
135            Enum(n, ref parts) => {
136                write!(f, "+[{}", n)?;
137                for p in parts.iter() {
138                    write!(f, " {}", p)?;
139                }
140                write!(f, "]+")
141            }
142            Text(ref st) => write!(f, "{}", st),
143            Cell(ref cell) => write!(f, "{}", cell.borrow()),
144            ParseContext(_) => write!(f, "[a language]"),
145        }
146    }
147}
148
149impl std::fmt::Debug for BIF {
150    fn fmt(&self, formatter: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> {
151        formatter.write_str("[built-in function]")
152    }
153}
154
155impl crate::walk_mode::WalkElt for Value {
156    fn from_ast(a: &Ast) -> Value { AbstractSyntax(a.clone()) }
157    fn to_ast(&self) -> Ast {
158        match *self {
159            AbstractSyntax(ref a) => a.clone(),
160            _ => icp!("[type error] {} is not syntax", self),
161        }
162    }
163}
164
165custom_derive! {
166    #[derive(Copy, Clone, Debug, Reifiable)]
167    pub struct Eval {}
168}
169custom_derive! {
170    #[derive(Copy, Clone, Debug, Reifiable)]
171    pub struct Destructure {}
172}
173
174impl WalkMode for Eval {
175    fn name() -> &'static str { "Evalu" }
176
177    type Elt = Value;
178    type Negated = Destructure;
179    type AsPositive = Eval;
180    type AsNegative = Destructure;
181    type Err = ();
182    type D = crate::walk_mode::Positive<Eval>;
183    type ExtraInfo = ();
184
185    fn get_walk_rule(f: &Form) -> WalkRule<Eval> {
186        // Macro invocations use `eval`, to avoid having a whole extra field in `Form`:
187        if f.name == n("macro_invocation") {
188            icp!("unexpanded macro!")
189        }
190        f.eval.pos().clone()
191    }
192    fn automatically_extend_env() -> bool { true }
193
194    fn walk_var(n: Name, cnc: &LazyWalkReses<Eval>) -> Result<Value, ()> {
195        match cnc.env.find(&n) {
196            Some(v) => Ok(v.clone()),
197            None => panic!("Undefined var `{}` in {}", n, cnc.env),
198        }
199    }
200
201    // TODO: maybe keep this from being called?
202    fn underspecified(_: Name) -> Value { val!(enum "why is this here?", ) }
203}
204
205impl WalkMode for Destructure {
206    fn name() -> &'static str { "Destr" }
207
208    type Elt = Value;
209    type Negated = Eval;
210    type AsPositive = Eval;
211    type AsNegative = Destructure;
212    type Err = ();
213    type D = crate::walk_mode::Negative<Destructure>;
214    type ExtraInfo = ();
215
216    /// The whole point of program evaluation is that the enviornment
217    ///  isn't generateable from the source tree.
218    /// Does that make sense? I suspect it does not.
219    fn get_walk_rule(f: &Form) -> WalkRule<Destructure> { f.eval.neg().clone() }
220    fn automatically_extend_env() -> bool { true } // TODO: think about this
221}
222
223impl NegativeWalkMode for Destructure {
224    fn needs_pre_match() -> bool { false } // Values don't have binding (in this mode!)
225}
226
227impl crate::walk_mode::WalkElt for Ast {
228    fn from_ast(a: &Ast) -> Ast { a.clone() }
229    fn to_ast(&self) -> Ast { self.clone() }
230}
231
232pub fn eval_top(expr: &Ast) -> Result<Value, ()> { eval(expr, Assoc::new()) }
233
234pub fn eval(expr: &Ast, env: Assoc<Name, Value>) -> Result<Value, ()> {
235    walk::<Eval>(expr, &LazyWalkReses::new_wrapper(env))
236}
237
238pub fn neg_eval(pat: &Ast, env: Assoc<Name, Value>) -> Result<Assoc<Name, Value>, ()> {
239    walk::<Destructure>(pat, &LazyWalkReses::new_wrapper(env))
240}
241
242custom_derive! {
243    #[derive(Copy, Clone, Debug, Reifiable)]
244    pub struct QQuote {}
245}
246custom_derive! {
247    #[derive(Copy, Clone, Debug, Reifiable)]
248    pub struct QQuoteDestr {}
249}
250
251impl WalkMode for QQuote {
252    fn name() -> &'static str { "QQuote" }
253
254    // Why not `Ast`? Because QQuote and Eval need to share environments.
255    type Elt = Value;
256    type Negated = QQuoteDestr;
257    type AsPositive = QQuote;
258    type AsNegative = QQuoteDestr;
259    type Err = ();
260    type D = crate::walk_mode::Positive<QQuote>;
261    type ExtraInfo = ();
262
263    fn walk_var(n: Name, _: &LazyWalkReses<Self>) -> Result<Value, ()> { Ok(val!(ast (vr n))) }
264    fn walk_atom(n: Name, _: &LazyWalkReses<Self>) -> Result<Value, ()> { Ok(val!(ast (at n))) }
265    // TODO #26: Just special-case "unquote" and "dotdotdot"
266    fn get_walk_rule(f: &Form) -> WalkRule<QQuote> { f.quasiquote.pos().clone() }
267    fn automatically_extend_env() -> bool { false }
268}
269
270impl WalkMode for QQuoteDestr {
271    fn name() -> &'static str { "QQDes" }
272
273    type Elt = Value;
274    type Negated = QQuote;
275    type AsPositive = QQuote;
276    type AsNegative = QQuoteDestr;
277    type Err = ();
278    type D = crate::walk_mode::Negative<QQuoteDestr>;
279    type ExtraInfo = ();
280
281    fn walk_var(n: Name, cnc: &LazyWalkReses<Self>) -> Result<Assoc<Name, Value>, ()> {
282        let val = val!(ast (vr n));
283        if cnc.context_elt() == &val {
284            Ok(Assoc::<Name, Value>::new())
285        } else {
286            Err(Self::qlit_mismatch_error(val, cnc.context_elt().clone()))
287        }
288    }
289    fn walk_atom(n: Name, cnc: &LazyWalkReses<Self>) -> Result<Assoc<Name, Value>, ()> {
290        let val = val!(ast (at n));
291        if cnc.context_elt() == &val {
292            Ok(Assoc::<Name, Value>::new())
293        } else {
294            Err(Self::qlit_mismatch_error(val, cnc.context_elt().clone()))
295        }
296    }
297    // TODO #26: Just special-case "unquote"
298    fn get_walk_rule(f: &Form) -> WalkRule<QQuoteDestr> { f.quasiquote.neg().clone() }
299    fn automatically_extend_env() -> bool { false }
300}
301
302impl NegativeWalkMode for QQuoteDestr {
303    fn needs_pre_match() -> bool { true } // Quoted syntax does have binding!
304}
305
306// `env` is a trap! We want a shifted `LazyWalkReses`!
307// pub fn qquote(expr: &Ast, env: Assoc<Name, Value>) -> Result<Value, ()> {
308//     walk::<QQuote>(expr, &LazyWalkReses::new_wrapper(env))
309// }
310//
311// pub fn qquote_destr(pat: &Ast, env: Assoc<Name, Value>)
312//         -> Result<Assoc<Name, Value>,()> {
313//     walk::<QQuoteDestr>(pat, &LazyWalkReses::new_wrapper(env))
314// }