hyperon/metta/
interpreter.rs

1//! MeTTa assembly language implementation. See
2//! [minimal MeTTa documentation](https://github.com/trueagi-io/hyperon-experimental/blob/main/docs/minimal-metta.md) for details.
3
4use hyperon_atom::*;
5use hyperon_atom::matcher::*;
6use hyperon_space::*;
7use crate::metta::*;
8use crate::metta::types::*;
9use crate::metta::runner::stdlib::core::IfEqualOp;
10use hyperon_common::collections::CowArray;
11
12use std::fmt::{Debug, Display, Formatter};
13use std::convert::TryFrom;
14use std::rc::Rc;
15use std::fmt::Write;
16use std::cell::RefCell;
17use itertools::Itertools;
18use hyperon_atom::gnd::number::Number;
19
20macro_rules! match_atom {
21    ($atom:tt ~ $pattern:tt => $succ:tt , _ => $error:tt) => {
22        match_atom!{ $atom ~ $pattern if true => $succ , _ => $error }
23    };
24    ($atom:tt ~ $pattern:tt if $cond:expr => $succ:tt , _ => $error:tt) => {
25        match atom_as_slice(&$atom) {
26            #[allow(unused_variables)]
27            Some($pattern) if $cond => {
28                match atom_into_array($atom) {
29                    Some($pattern) => $succ,
30                    _ => panic!("Unexpected state"),
31                }
32            }
33            _ => $error,
34        }
35    };
36}
37
38macro_rules! call_native {
39    ($func:ident, $atom:expr) => {
40        call_native_atom($func, stringify!($func), $atom)
41    }
42}
43
44/// Operation return handler, it is triggered when nested operation is finished
45/// and returns its results. First argument gets the reference to the stack
46/// which on the top has the frame of the wrapping operation. Last two
47/// arguments are the result of the nested operation. Handler returns
48/// None when it is not ready to provide new stack (it can happen in case of
49/// collapse-bind operation) or new stack with variable bindings to continue
50/// execution of the program.
51type ReturnHandler = fn(Rc<RefCell<Stack>>, Atom, Bindings) -> Option<(Stack, Bindings)>;
52
53#[derive(Debug, Clone)]
54#[cfg_attr(test, derive(PartialEq))]
55struct Stack {
56    // Internal mutability is required to implement collapse-bind. All alternatives
57    // reference the same collapse-bind Stack instance. When some alternative
58    // finishes it modifies the collapse-bind state adding the result to the
59    // collapse-bind list of results.
60    // TODO: Try representing Option via Stack::Bottom
61    prev: Option<Rc<RefCell<Self>>>,
62    atom: Atom,
63    ret: ReturnHandler,
64    // TODO: Could it be replaced by calling a return handler when setting the flag?
65    finished: bool,
66    vars: Variables,
67    depth: usize,
68}
69
70fn no_handler(_stack: Rc<RefCell<Stack>>, _atom: Atom, _bindings: Bindings) -> Option<(Stack, Bindings)> {
71    panic!("Unexpected state");
72}
73
74impl Stack {
75    fn from_prev_with_vars(prev: Option<Rc<RefCell<Self>>>, atom: Atom, vars: Variables, ret: ReturnHandler) -> Self {
76        let depth = prev.as_ref().map_or(1, |prev| prev.borrow().depth + 1);
77        Self{ prev, atom, ret, finished: false, vars, depth }
78    }
79
80    fn from_prev_keep_vars(prev: Option<Rc<RefCell<Self>>>, atom: Atom, ret: ReturnHandler) -> Self {
81        let vars = Self::vars_copy(&prev);
82        let depth = prev.as_ref().map_or(1, |prev| prev.borrow().depth + 1);
83        Self{ prev, atom, ret, finished: false, vars, depth }
84    }
85
86    fn finished(prev: Option<Rc<RefCell<Self>>>, atom: Atom) -> Self {
87        let depth = prev.as_ref().map_or(1, |prev| prev.borrow().depth + 1);
88        Self{ prev, atom, ret: no_handler, finished: true, vars: Variables::new(), depth }
89    }
90
91    fn depth(&self) -> usize {
92        self.depth
93    }
94
95    // TODO: should it be replaced by Iterator implementation?
96    fn fold<T, F: FnMut(T, &Stack) -> T>(&self, mut val: T, mut app: F) -> T {
97        val = app(val, self);
98        match &self.prev {
99            None => val,
100            Some(prev) => prev.borrow().fold(val, app),
101        }
102    }
103
104    fn vars_copy(prev: &Option<Rc<RefCell<Self>>>) -> Variables {
105        match prev {
106            Some(prev) => prev.borrow().vars.clone(),
107            None => Variables::new(),
108        }
109    }
110
111    fn add_vars_it<'a, I: 'a + Iterator<Item=&'a VariableAtom>>(prev: &Option<Rc<RefCell<Self>>>, vars: I) -> Variables {
112        match prev {
113            Some(prev) => {
114                let mut next = prev.borrow().vars.clone();
115                next.insert_all(vars);
116                next
117            },
118            None => vars.cloned().collect(),
119        }
120    }
121}
122
123impl Display for Stack {
124    fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
125        fn print_level(buffer: &mut String, level: usize, last: bool, stack: &Stack) -> std::fmt::Result {
126            let prefix = if last { "=> " } else { "   " };
127            let ret = if stack.finished { "return " } else { "" };
128            write!(buffer, "{}{:05} {}{} {}\n", prefix, level, ret, stack.atom, stack.vars)
129        }
130
131        let buffer = &mut String::new();
132        let last_level = self.depth();
133        let res = print_level(buffer, last_level, true, self);
134        self.prev.as_ref().map_or(res, |prev| {
135            prev.borrow().fold((res, last_level - 1), |(res, level), top| {
136                (res.and_then(|_| print_level(buffer, level, false, top)), level - 1)
137            }).0
138        })
139        .and_then(|_| write!(f, "{}", buffer))
140    }
141}
142
143#[derive(Debug)]
144#[cfg_attr(test, derive(PartialEq))]
145struct InterpretedAtom(Stack, Bindings);
146
147impl Display for InterpretedAtom {
148    fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
149        if self.1.is_empty() {
150            write!(f, "{}", self.0)
151        } else {
152            write!(f, "{}\n{}", self.1, self.0)
153        }
154    }
155}
156
157#[derive(Debug)]
158struct InterpreterContext {
159    space: DynSpace,
160}
161
162impl InterpreterContext {
163    fn new(space: DynSpace) -> Self {
164        Self{ space }
165    }
166}
167
168/// This wrapper is to keep interpreter interface compatible with previous
169/// implementation and will be removed in future.
170
171/// State of the interpreter which passed between `interpret_step` calls.
172#[derive(Debug)]
173pub struct InterpreterState {
174    /// List of the alternatives to evaluate further.
175    plan: Vec<InterpretedAtom>,
176    /// List of the completely evaluated results to be returned.
177    finished: Vec<Atom>,
178    /// Evaluation context.
179    context: InterpreterContext,
180    /// Maximum stack depth
181    max_stack_depth: usize,
182}
183
184fn atom_as_slice(atom: &Atom) -> Option<&[Atom]> {
185    <&[Atom]>::try_from(atom).ok()
186}
187
188fn atom_as_slice_mut(atom: &mut Atom) -> Option<&mut [Atom]> {
189    <&mut [Atom]>::try_from(atom).ok()
190}
191
192fn atom_into_array<const N: usize>(atom: Atom) -> Option<[Atom; N]> {
193    <[Atom; N]>::try_from(atom).ok()
194}
195
196impl InterpreterState {
197
198    /// INTERNAL USE ONLY. Create an InterpreterState that is ready to yield results
199    pub(crate) fn new_finished(space: DynSpace, results: Vec<Atom>) -> Self {
200        Self {
201            plan: vec![],
202            finished: results,
203            context: InterpreterContext::new(space),
204            max_stack_depth: 0,
205        }
206    }
207
208    /// Returns true if there are alternatives which can be evaluated further.
209    pub fn has_next(&self) -> bool {
210        !self.plan.is_empty()
211    }
212
213    /// Returns vector of fully evaluated results or error if there are still
214    /// alternatives to be evaluated.
215    pub fn into_result(self) -> Result<Vec<Atom>, String> {
216        if self.has_next() {
217            Err("Evaluation is not finished".into())
218        } else {
219            Ok(self.finished)
220        }
221    }
222
223    fn pop(&mut self) -> Option<InterpretedAtom> {
224        self.plan.pop()
225    }
226
227    fn push(&mut self, atom: InterpretedAtom) {
228        if atom.0.prev.is_none() && atom.0.finished {
229            let InterpretedAtom(stack, bindings) = atom;
230            let atom = apply_bindings_to_atom_move(stack.atom, &bindings);
231            self.finished.push(atom);
232        } else {
233            self.plan.push(atom);
234        }
235    }
236
237    pub fn set_max_stack_depth(&mut self, depth: usize) {
238        self.max_stack_depth = depth;
239    }
240}
241
242impl std::fmt::Display for InterpreterState {
243    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
244        write!(f, "{:?}\n", self.plan)
245    }
246}
247
248/// Initialize interpreter and returns the starting interpreter state.
249/// See [crate::metta::interpreter] for algorithm explanation.
250///
251/// # Arguments
252/// * `space` - atomspace to query for interpretation
253/// * `expr` - atom to interpret
254pub fn interpret_init(space: DynSpace, expr: &Atom) -> InterpreterState {
255    let context = InterpreterContext::new(space);
256    InterpreterState {
257        plan: vec![InterpretedAtom(atom_to_stack(expr.clone(), None), Bindings::new())],
258        finished: vec![],
259        context,
260        max_stack_depth: 0,
261    }
262}
263
264/// Perform next step of the interpretation return the resulting interpreter
265/// state. See [crate::metta::interpreter] for algorithm explanation.
266///
267/// # Arguments
268/// * `state` - interpreter state from the previous step.
269pub fn interpret_step(mut state: InterpreterState) -> InterpreterState {
270    let interpreted_atom = state.pop().unwrap();
271    log::debug!("interpret_step:\n{}", interpreted_atom);
272    let InterpretedAtom(stack, bindings) = interpreted_atom;
273    for result in interpret_stack(&state.context, stack, bindings, state.max_stack_depth) {
274        state.push(result);
275    }
276    state
277}
278
279/// Interpret passed atom and return a new plan, result or error. This function
280/// blocks until result is calculated. For step by step interpretation one
281/// should use [interpret_init] and [interpret_step] functions.
282/// # Arguments
283/// * `space` - atomspace to query for interpretation
284/// * `expr` - atom to interpret
285pub fn interpret(space: DynSpace, expr: &Atom) -> Result<Vec<Atom>, String> {
286    let mut state = interpret_init(space, expr);
287    while state.has_next() {
288        state = interpret_step(state);
289    }
290    state.into_result()
291}
292
293fn is_embedded_op(atom: &Atom) -> bool {
294    let expr = atom_as_slice(&atom);
295    match expr {
296        Some([op, ..]) => *op == EVAL_SYMBOL
297            || *op == EVALC_SYMBOL
298            || *op == CHAIN_SYMBOL
299            || *op == UNIFY_SYMBOL
300            || *op == CONS_ATOM_SYMBOL
301            || *op == DECONS_ATOM_SYMBOL
302            || *op == FUNCTION_SYMBOL
303            || *op == COLLAPSE_BIND_SYMBOL
304            || *op == SUPERPOSE_BIND_SYMBOL
305            || *op == METTA_SYMBOL
306            || *op == CONTEXT_SPACE_SYMBOL
307            || *op == CALL_NATIVE_SYMBOL,
308        _ => false,
309    }
310}
311
312fn is_op(atom: &Atom, op: &Atom) -> bool {
313    let expr = atom_as_slice(&atom);
314    match expr {
315        Some([opp, ..]) => opp == op,
316        _ => false,
317    }
318}
319
320fn is_function_op(atom: &Atom) -> bool {
321    is_op(atom, &FUNCTION_SYMBOL)
322}
323
324#[derive(Debug, Clone)]
325#[cfg_attr(test, derive(PartialEq))]
326struct Variables(im::HashSet<VariableAtom>);
327
328impl Variables {
329    fn new() -> Self {
330        Self(im::HashSet::new())
331    }
332    fn insert(&mut self, var: VariableAtom) -> Option<VariableAtom> {
333        self.0.insert(var)
334    }
335    fn insert_all<'a, I: 'a + Iterator<Item=&'a VariableAtom>>(&mut self, it: I) {
336        it.for_each(|var| { self.insert(var.clone()); });
337    }
338    fn iter(&self) -> impl Iterator<Item=&'_ VariableAtom> {
339        self.0.iter()
340    }
341}
342fn vars_from_atom(atom: &Atom) -> impl Iterator<Item=&VariableAtom> {
343    atom.iter().filter_type::<&VariableAtom>()
344}
345
346impl FromIterator<VariableAtom> for Variables {
347    fn from_iter<I: IntoIterator<Item=VariableAtom>>(iter: I) -> Self {
348        Self(im::HashSet::from_iter(iter))
349    }
350}
351
352impl VariableSet for Variables {
353    type Iter<'a> = im::hashset::Iter<'a, hyperon_atom::VariableAtom> where Self: 'a;
354
355    fn contains(&self, var: &VariableAtom) -> bool {
356        self.0.contains(var)
357    }
358    fn iter(&self) -> Self::Iter<'_> {
359        self.0.iter()
360    }
361}
362
363impl Display for Variables {
364    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
365        write!(f, "[")
366            .and_then(|_| self.iter().take(1).fold(Ok(()),
367                |res, atom| res.and_then(|_| write!(f, "{}", atom))))
368            .and_then(|_| self.iter().skip(1).fold(Ok(()),
369                |res, atom| res.and_then(|_| write!(f, " {}", atom))))
370            .and_then(|_| write!(f, "]"))
371    }
372}
373
374fn interpret_stack(context: &InterpreterContext, stack: Stack, mut bindings: Bindings, max_stack_depth: usize) -> Vec<InterpretedAtom> {
375    if stack.finished {
376        // first executed minimal operation returned error
377        if stack.prev.is_none() {
378            return vec![InterpretedAtom(stack, bindings)];
379        }
380        let Stack{ prev, mut atom, .. } = stack;
381        let prev = match prev {
382            Some(prev) => prev,
383            None => panic!("Unexpected state"),
384        };
385        {
386            let outer_vars = &prev.borrow().vars;
387            bindings.apply_and_retain(&mut atom, |v| outer_vars.contains(v));
388        }
389        let ret = prev.borrow().ret;
390        ret(prev, atom, bindings)
391            .map_or(vec![], |(stack, bindings)| vec![InterpretedAtom(stack, bindings)])
392    } else if max_stack_depth > 0 && stack.depth >= max_stack_depth 
393        && atom_as_slice(&stack.atom).map_or(false, |expr| expr[0] == METTA_SYMBOL)
394    {
395        // Return error if stack depth limit is reached.
396        // Current implementation has the following pecularities:
397        //
398        // 1. The stack depth is calculated in terms of the minimal MeTTa
399        // instructions not MeTTa instructions thus the actual stack depth can
400        // be bigger than expected as minimal MeTTa instructions are more
401        // detailed.
402        //
403        // 2. Execution is interrupted and returns error only on the next MeTTa
404        // function call. This is required to correctly return error from the
405        // minimal MeTTa stack when MeTTa program is executed.
406        //
407        // 3. Case/capture/superpose/collapse/assert operations call interpreter
408        // from the interpreter stack and it leads to the creation of the new
409        // stack. Thus if case (or other similar operation) is used the limit
410        // counter is started from the beginning for the nested expression.
411        let Stack{ prev, atom, .. } = stack;
412        let stack = Stack::finished(prev, error_atom(atom, STACK_OVERFLOW_SYMBOL));
413        vec![InterpretedAtom(stack, bindings)]
414    } else {
415        let expr = atom_as_slice(&stack.atom);
416        let result = match expr {
417            Some([op, ..]) if *op == EVAL_SYMBOL => {
418                eval(context, stack, bindings)
419            },
420            Some([op, ..]) if *op == EVALC_SYMBOL => {
421                evalc(context, stack, bindings)
422            },
423            Some([op, ..]) if *op == CHAIN_SYMBOL => {
424                chain(stack, bindings)
425            },
426            Some([op, ..]) if *op == FUNCTION_SYMBOL => {
427                panic!("Unexpected state")
428            },
429            Some([op, ..]) if *op == COLLAPSE_BIND_SYMBOL => {
430                collapse_bind(stack, bindings)
431            },
432            Some([op, ..]) if *op == UNIFY_SYMBOL => {
433                unify(stack, bindings)
434            },
435            Some([op, ..]) if *op == DECONS_ATOM_SYMBOL => {
436                decons_atom(stack, bindings)
437            },
438            Some([op, ..]) if *op == CONS_ATOM_SYMBOL => {
439                cons_atom(stack, bindings)
440            },
441            Some([op, ..]) if *op == SUPERPOSE_BIND_SYMBOL => {
442                superpose_bind(stack, bindings)
443            },
444            Some([op, ..]) if *op == METTA_SYMBOL => {
445                metta_sym(stack, bindings)
446            },
447            Some([op, ..]) if *op == CONTEXT_SPACE_SYMBOL => {
448                context_space(context, stack, bindings)
449            },
450            Some([op, ..]) if *op == CALL_NATIVE_SYMBOL => {
451                call_native_symbol(stack, bindings)
452            },
453            _ => {
454                let stack = Stack::finished(stack.prev, stack.atom);
455                vec![InterpretedAtom(stack, bindings)]
456            },
457        };
458        result
459    }
460}
461
462fn return_not_reducible() -> Atom {
463    NOT_REDUCIBLE_SYMBOL
464}
465
466fn error_msg(atom: Atom, err: String) -> Atom {
467    error_atom(atom, Atom::sym(err))
468}
469
470fn error_atom(atom: Atom, err: Atom) -> Atom {
471    Atom::expr([ERROR_SYMBOL, atom, err])
472}
473
474fn finished_result(atom: Atom, bindings: Bindings, prev: Option<Rc<RefCell<Stack>>>) -> Vec<InterpretedAtom> {
475    vec![InterpretedAtom(Stack::finished(prev, atom), bindings)]
476}
477
478fn evalc(_context: &InterpreterContext, stack: Stack, bindings: Bindings) -> Vec<InterpretedAtom> {
479    let Stack{ prev, atom: eval, vars, .. } = stack;
480    let (to_eval, space) = match_atom!{
481        eval ~ [_op, to_eval, space]
482            if space.as_gnd::<DynSpace>().is_some() => (to_eval, space),
483        _ => {
484            let error = format!("expected: ({} <atom> <space>), found: {}", EVALC_SYMBOL, eval);
485            return finished_result(error_msg(eval, error), bindings, prev);
486        }
487    };
488    let space = space.as_gnd::<DynSpace>().unwrap();
489    eval_impl(to_eval, &space, bindings, prev, vars)
490}
491
492fn eval(context: &InterpreterContext, stack: Stack, bindings: Bindings) -> Vec<InterpretedAtom> {
493    let Stack{ prev, atom: eval, vars, .. } = stack;
494    let to_eval = match_atom!{
495        eval ~ [_op, to_eval] => to_eval,
496        _ => {
497            let error = format!("expected: ({} <atom>), found: {}", EVAL_SYMBOL, eval);
498            return finished_result(error_msg(eval, error), bindings, prev);
499        }
500    };
501    eval_impl(to_eval, &context.space, bindings, prev, vars)
502}
503
504fn eval_impl(to_eval: Atom, space: &DynSpace, bindings: Bindings, prev: Option<Rc<RefCell<Stack>>>, vars: Variables) -> Vec<InterpretedAtom> {
505    let to_eval = apply_bindings_to_atom_move(to_eval, &bindings);
506    log::debug!("eval: to_eval: {}", to_eval);
507    match atom_as_slice(&to_eval) {
508        Some([Atom::Grounded(op), args @ ..]) => {
509            match op.as_grounded().as_execute() {
510                None => query(space, prev, to_eval, bindings, vars),
511                Some(executable) => {
512                    let exec_res = executable.execute_bindings(args);
513                    match exec_res {
514                        Ok(results) => {
515                            let call_stack = call_to_stack(to_eval, vars, prev.clone());
516                            let results: Vec<InterpretedAtom> = results.into_iter()
517                                .flat_map(|(atom, b)| {
518                                    let c = move |b| (apply_bindings_to_atom_move(atom.clone(), &b), b);
519                                    match b {
520                                        None => BindingsSet::from(bindings.clone()).into_iter().map(c),
521                                        Some(b) => b.merge(&bindings).into_iter().map(c),
522                                    }
523                                })
524                                .map(|(res, b)| eval_result(prev.clone(), res, &call_stack, b))
525                                .collect();
526                            log::debug!("eval: execution results: {:?}", results);
527                            if results.is_empty() {
528                                // There is no valid reason to return empty result from
529                                // the grounded function. If alternative should be removed
530                                // from the plan then EMPTY_SYMBOL is a proper result.
531                                // If grounded atom returns no value then UNIT_ATOM()
532                                // should be returned. NotReducible or Exec::NoReduce
533                                // can be returned to let a caller know that function
534                                // is not defined on a passed input data. Thus we can
535                                // interpreter empty result by any way we like.
536                                finished_result(EMPTY_SYMBOL, bindings, prev)
537                            } else {
538                                results
539                            }
540                        },
541                        Err(ExecError::Runtime(err)) =>
542                            finished_result(error_msg(to_eval, err), bindings, prev),
543                        Err(ExecError::NoReduce) =>
544                            // TODO: we could remove ExecError::NoReduce and explicitly
545                            // return NOT_REDUCIBLE_SYMBOL from the grounded function instead.
546                            finished_result(return_not_reducible(), bindings, prev),
547                        Err(ExecError::IncorrectArgument) =>
548                            finished_result(return_not_reducible(), bindings, prev),
549                    }
550                },
551            }
552        },
553        _ if is_embedded_op(&to_eval) =>
554            vec![InterpretedAtom(atom_to_stack(to_eval, prev), bindings)],
555        _ => query(space, prev, to_eval, bindings, vars),
556    }
557}
558
559fn eval_result(prev: Option<Rc<RefCell<Stack>>>, res: Atom, call_stack: &Rc<RefCell<Stack>>, mut bindings: Bindings) -> InterpretedAtom {
560    let stack = if is_function_op(&res) {
561        let mut stack = function_to_stack(res, Some(call_stack.clone()));
562        let call_stack = call_stack.borrow();
563        // Apply arguments bindings is required to replace formal argument
564        // variables by matched actual argument variables. Otherwise the
565        // equality of formal and actual argument variables will be cleaned up
566        // on any return from the nested function call.
567        // TODO: we could instead add formal argument variables of the called
568        // functions into stack.vars collection. One way of doing it is getting
569        // list of formal var parameters from the function definition atom
570        // but we don't have it returned from the query. Another way is to
571        // find all variables equalities in bindings with variables
572        // from call_stack.vars.
573        bindings.apply_and_retain(&mut stack.atom, |v| call_stack.vars.contains(v));
574        stack
575    } else {
576        Stack::finished(prev, res)
577    };
578    InterpretedAtom(stack, bindings)
579}
580
581fn call_to_stack(call: Atom, mut vars: Variables, prev: Option<Rc<RefCell<Stack>>>) -> Rc<RefCell<Stack>> {
582    vars.insert_all(vars_from_atom(&call));
583    let stack = Stack::from_prev_with_vars(prev, call, vars, call_ret);
584    Rc::new(RefCell::new(stack))
585}
586
587#[cfg(not(feature = "variable_operation"))]
588fn is_variable_op(atom: &Atom) -> bool {
589    match atom {
590        Atom::Expression(expr) => is_variable_op_expr(expr),
591        _ => false,
592    }
593}
594
595#[cfg(not(feature = "variable_operation"))]
596fn is_variable_op_expr(expr: &ExpressionAtom) -> bool {
597    match expr.children().get(0) {
598        Some(Atom::Variable(_)) => true,
599        Some(Atom::Expression(expr)) => is_variable_op_expr(expr),
600        _ => false,
601    }
602}
603
604fn query(space: &DynSpace, prev: Option<Rc<RefCell<Stack>>>, to_eval: Atom, bindings: Bindings, vars: Variables) -> Vec<InterpretedAtom> {
605    #[cfg(not(feature = "variable_operation"))]
606    if is_variable_op(&to_eval) {
607        // TODO: This is a hotfix. Better way of doing this is adding
608        // a function which modifies minimal MeTTa interpreter code
609        // in order to skip such evaluations in metta-call function.
610        return finished_result(return_not_reducible(), bindings, prev)
611    }
612    let var_x = &VariableAtom::new("X").make_unique();
613    let query = Atom::expr([EQUAL_SYMBOL, to_eval.clone(), Atom::Variable(var_x.clone())]);
614    let results = space.borrow().query(&query);
615    log::debug!("interpreter::query: query: {}", query);
616    log::debug!("interpreter::query: results.len(): {}, bindings.len(): {}, results: {} bindings: {}",
617        results.len(), bindings.len(), results, bindings);
618    let call_stack = call_to_stack(to_eval, vars, prev.clone());
619    let result = |res, bindings| eval_result(prev.clone(), res, &call_stack, bindings);
620    let results: Vec<InterpretedAtom> = results.into_iter().flat_map(|b| {
621        log::debug!("interpreter::query: b: {}", b);
622        b.merge(&bindings).into_iter()
623    }).filter_map(move |b| {
624        b.resolve(&var_x).map_or(None, |res| {
625            if b.has_loops() {
626                None
627            } else {
628                Some(result(res, b))
629            }
630        })
631    })
632    .collect();
633    if results.is_empty() {
634        finished_result(return_not_reducible(), bindings, prev)
635    } else {
636        results
637    }
638}
639
640fn atom_to_stack(atom: Atom, prev: Option<Rc<RefCell<Stack>>>) -> Stack {
641    let expr = atom_as_slice(&atom);
642    let result = match expr {
643        Some([op, ..]) if *op == CHAIN_SYMBOL =>
644            chain_to_stack(atom, prev),
645        Some([op, ..]) if *op == FUNCTION_SYMBOL =>
646            function_to_stack(atom, prev),
647        Some([op, ..]) if *op == EVAL_SYMBOL =>
648            Stack::from_prev_keep_vars(prev, atom, no_handler),
649        Some([op, ..]) if *op == UNIFY_SYMBOL =>
650            unify_to_stack(atom, prev),
651        _ =>
652            Stack::from_prev_keep_vars(prev, atom, no_handler),
653    };
654    result
655}
656
657fn chain_to_stack(mut atom: Atom, prev: Option<Rc<RefCell<Stack>>>) -> Stack {
658    let mut nested = Atom::sym("%Nested%");
659    let (nested_arg, templ_arg) = match atom_as_slice_mut(&mut atom) {
660        Some([_op, nested, Atom::Variable(_var), templ]) => (nested, templ),
661        _ => {
662            let error: String = format!("expected: ({} <nested> (: <var> Variable) <templ>), found: {}", CHAIN_SYMBOL, atom);
663            return Stack::finished(prev, error_msg(atom, error));
664        },
665    };
666    std::mem::swap(nested_arg, &mut nested);
667    let nested_vars: im::HashSet<&VariableAtom> = vars_from_atom(&nested).collect();
668    let templ_vars: im::HashSet<&VariableAtom> = vars_from_atom(templ_arg).collect();
669    let both_vars = nested_vars.intersection(templ_vars).into_iter();
670    let vars = Stack::add_vars_it(&prev, both_vars);
671    let cur = Stack::from_prev_with_vars(prev, atom, vars, chain_ret);
672    atom_to_stack(nested, Some(Rc::new(RefCell::new(cur))))
673}
674
675fn chain_ret(stack: Rc<RefCell<Stack>>, atom: Atom, bindings: Bindings) -> Option<(Stack, Bindings)> {
676    let mut stack = (*stack.borrow()).clone();
677    let nested = atom;
678    let Stack{ atom: chain, .. } = &mut stack;
679    let arg = match atom_as_slice_mut(chain) {
680        Some([_op, nested, Atom::Variable(_var), _templ]) => nested,
681        _ => panic!("Unexpected state"),
682    };
683    *arg = nested;
684    Some((stack, bindings))
685}
686
687fn chain(stack: Stack, bindings: Bindings) -> Vec<InterpretedAtom> {
688    let Stack{ prev, atom: chain, vars, .. } = stack;
689    let (nested, var, templ) = match_atom!{
690        chain ~ [_op, nested, Atom::Variable(var), templ] => (nested, var, templ),
691        _ => {
692            panic!("Unexpected state")
693        }
694    };
695    let b = Bindings::new().add_var_binding(var, nested).unwrap();
696    let templ = apply_bindings_to_atom_move(templ, &b);
697    let stack = atom_to_stack(templ, prev);
698    if let Some(prev) = stack.prev.as_ref() {
699        prev.borrow_mut().vars.insert_all(vars.iter());
700    }
701    vec![InterpretedAtom(stack, bindings)]
702}
703
704fn function_to_stack(mut atom: Atom, prev: Option<Rc<RefCell<Stack>>>) -> Stack {
705    let mut nested = Atom::sym("%Nested%");
706    let nested_arg = match atom_as_slice_mut(&mut atom) {
707        Some([_op, nested @ Atom::Expression(_)]) => nested,
708        _ => {
709            let error: String = format!("expected: ({} (: <body> Expression)), found: {}", FUNCTION_SYMBOL, atom);
710            return Stack::finished(prev, error_msg(atom, error));
711        },
712    };
713    std::mem::swap(nested_arg, &mut nested);
714    let cur = Stack::from_prev_keep_vars(prev, atom, function_ret);
715    atom_to_stack(nested, Some(Rc::new(RefCell::new(cur))))
716}
717
718fn call_ret(stack: Rc<RefCell<Stack>>, atom: Atom, bindings: Bindings) -> Option<(Stack, Bindings)> {
719    let stack = Stack::finished(stack.borrow().prev.clone(), atom);
720    Some((stack, bindings))
721}
722
723fn function_ret(stack: Rc<RefCell<Stack>>, atom: Atom, bindings: Bindings) -> Option<(Stack, Bindings)> {
724    match_atom!{
725        atom ~ [op, result] if *op == RETURN_SYMBOL => {
726            let stack = Stack::finished(stack.borrow().prev.clone(), result);
727            Some((stack, bindings))
728        },
729        _ => {
730            if is_embedded_op(&atom) {
731                Some((atom_to_stack(atom, Some(stack)), bindings))
732            } else {
733                let prev = stack.borrow().prev.clone();
734                let err = if let Some(ref prev) = prev {
735                    error_atom(prev.borrow().atom.clone(), NO_RETURN_SYMBOL)
736                } else {
737                    error_atom(atom, NO_RETURN_SYMBOL)
738                };
739                let stack = Stack::finished(prev, err);
740                Some((stack, bindings))
741            }
742        }
743    }
744}
745
746fn collapse_bind(stack: Stack, bindings: Bindings) -> Vec<InterpretedAtom> {
747    let Stack{ prev, atom: collapse, vars, .. } = stack;
748
749    let mut nested = Atom::Expression(ExpressionAtom::new(CowArray::Allocated(Vec::new())));
750    let collapse = match collapse {
751        Atom::Expression(mut expr) => {
752            let children = expr.children_mut();
753            std::mem::swap(&mut nested, &mut children[1]);
754            children.push(Atom::value(bindings.clone()));
755            Atom::Expression(expr)
756        },
757        _ => panic!("Unexpected state"),
758    };
759
760    let prev = Stack::from_prev_with_vars(prev, collapse, vars, collapse_bind_ret);
761    let prev = Rc::new(RefCell::new(prev));
762    let cur = atom_to_stack(nested, Some(prev.clone()));
763    let dummy = Stack::finished(Some(prev), EMPTY_SYMBOL);
764    vec![InterpretedAtom(dummy, bindings.clone()), InterpretedAtom(cur, bindings)]
765}
766
767fn collapse_bind_ret(stack: Rc<RefCell<Stack>>, atom: Atom, bindings: Bindings) -> Option<(Stack, Bindings)> {
768    let nested = atom;
769    if nested != EMPTY_SYMBOL {
770        let stack_ref = &mut *stack.borrow_mut();
771        let Stack{ atom: collapse, .. } = stack_ref;
772        match atom_as_slice_mut(collapse) {
773            Some([_op, Atom::Expression(ref mut finished), _bindings]) => {
774                finished.children_mut().push(atom_bindings_into_atom(nested, bindings));
775            },
776            _ => panic!("Unexpected state"),
777        };
778    }
779
780    // all alternatives are evaluated
781    match Rc::into_inner(stack).map(RefCell::into_inner) {
782        Some(stack) => {
783            let Stack{ prev, atom: collapse, .. } = stack;
784            let (result, bindings) = match atom_into_array(collapse) {
785                Some([_op, result, bindings]) => (result, atom_into_bindings(bindings)),
786                None => panic!("Unexpected state"),
787            };
788            Some((Stack::finished(prev, result), bindings))
789        },
790        None => None,
791    }
792}
793
794fn atom_bindings_into_atom(atom: Atom, bindings: Bindings) -> Atom {
795    Atom::expr([atom, Atom::value(bindings)])
796}
797
798fn unify_to_stack(mut atom: Atom, prev: Option<Rc<RefCell<Stack>>>) -> Stack {
799    let () = match atom_as_slice_mut(&mut atom) {
800        Some([_op, _a, _b, _then, _else]) => (),
801        _ => {
802            let error: String = format!("expected: ({} <atom> <pattern> <then> <else>), found: {}", UNIFY_SYMBOL, atom);
803            return Stack::finished(prev, error_msg(atom, error));
804        },
805    };
806    Stack::from_prev_with_vars(prev, atom, Variables::new(), no_handler)
807}
808
809fn unify(stack: Stack, bindings: Bindings) -> Vec<InterpretedAtom> {
810    let Stack{ prev, atom: unify, .. } = stack;
811    let (atom, pattern, then, else_) = match_atom!{
812        unify ~ [_op, atom, pattern, then, else_] => (atom, pattern, then, else_),
813        _ => {
814            let error: String = format!("expected: ({} <atom> <pattern> <then> <else>), found: {}", UNIFY_SYMBOL, unify);
815            return finished_result(error_msg(unify, error), bindings, prev);
816        }
817    };
818
819    let matches: Vec<Bindings> = match_atoms(&atom, &pattern).collect();
820    let result = |bindings| {
821        let then = apply_bindings_to_atom_move(then.clone(), &bindings);
822        let stack = Stack::finished(prev.clone(), then);
823        InterpretedAtom(stack, bindings)
824    };
825    let bindings_ref = &bindings;
826    let matches: Vec<InterpretedAtom> = matches.into_iter().flat_map(move |b| {
827        b.merge(bindings_ref).into_iter().filter_map(move |b| {
828            if b.has_loops() {
829                None
830            } else {
831                Some(result(b))
832            }
833        })
834    })
835    .collect();
836    if matches.is_empty() {
837        finished_result(else_, bindings, prev)
838    } else {
839        matches
840    }
841}
842
843fn decons_atom(stack: Stack, bindings: Bindings) -> Vec<InterpretedAtom> {
844    let Stack{ prev, atom: decons, .. } = stack;
845    let expr = match_atom!{
846        decons ~ [_op, Atom::Expression(expr)] if expr.children().len() > 0 => expr,
847        _ => {
848            let error: String = format!("expected: ({} (: <expr> Expression)), found: {}", DECONS_ATOM_SYMBOL, decons);
849            return finished_result(error_msg(decons, error), bindings, prev);
850        }
851    };
852    let mut children = expr.into_children();
853    let head = children.remove(0);
854    let tail = children;
855    finished_result(Atom::expr([head, Atom::expr(tail)]), bindings, prev)
856}
857
858fn cons_atom(stack: Stack, bindings: Bindings) -> Vec<InterpretedAtom> {
859    let Stack{ prev, atom: cons, .. } = stack;
860    let (head, tail) = match_atom!{
861        cons ~ [_op, head, Atom::Expression(tail)] => (head, tail),
862        _ => {
863            let error: String = format!("expected: ({} <head> (: <tail> Expression)), found: {}", CONS_ATOM_SYMBOL, cons);
864            return finished_result(error_msg(cons, error), bindings, prev);
865        }
866    };
867    let mut children = vec![head];
868    children.extend(tail.into_children());
869    finished_result(Atom::expr(children), bindings, prev)
870}
871
872fn atom_into_atom_bindings(pair: Atom) -> (Atom, Bindings) {
873    match_atom!{
874        pair ~ [atom, bindings] => (atom, atom_into_bindings(bindings)),
875        _ => {
876            panic!("(Atom Bindings) pair is expected, {} was received", pair)
877        }
878    }
879}
880
881fn atom_into_bindings(bindings: Atom) -> Bindings {
882    match bindings.as_gnd::<Bindings>() {
883        Some(bindings) => {
884            // TODO: cloning is ineffective, but it is not possible
885            // to convert grounded atom into internal value at the
886            // moment
887            bindings.clone()
888        },
889        _ => panic!("Unexpected state: second item cannot be converted to Bindings"),
890    }
891}
892
893fn superpose_bind(stack: Stack, bindings: Bindings) -> Vec<InterpretedAtom> {
894    let Stack{ prev, atom: superpose, .. } = stack;
895    let collapsed = match_atom!{
896        superpose ~ [_op, Atom::Expression(collapsed)] => collapsed,
897        _ => {
898            let error: String = format!("expected: ({} (: <collapsed> Expression)), found: {}", SUPERPOSE_BIND_SYMBOL, superpose);
899            return finished_result(error_msg(superpose, error), bindings, prev);
900        }
901    };
902    collapsed.into_children().into_iter()
903        .map(atom_into_atom_bindings)
904        .flat_map(|(atom, b)| {
905            let result = |atom, bindings| {
906                let stack = Stack::finished(prev.clone(), atom);
907                InterpretedAtom(stack, bindings)
908            };
909            b.merge(&bindings).into_iter().filter_map(move |b| {
910                if b.has_loops() {
911                    None
912                } else {
913                    Some(result(atom.clone(), b))
914                }
915            })
916        })
917        .collect()
918}
919
920type NativeFunc = fn(Atom, Bindings) -> MettaResult;
921
922fn call_native_symbol(stack: Stack, bindings: Bindings) -> Vec<InterpretedAtom> {
923    let Stack{ prev, atom: call, vars, .. } = stack;
924    let (name, func, args) = match_atom!{
925        call ~ [_op, name, func, args]
926            if func.as_gnd::<NativeFunc>().is_some() => (name, func, args),
927        _ => {
928            let error = format!("expected: ({} func args), found: {}", CALL_NATIVE_SYMBOL, call);
929            return finished_result(error_msg(call, error), bindings, prev);
930        }
931    };
932
933    let call_stack = Some(call_to_stack(Atom::expr([name, args.clone()]), vars, prev));
934    let func = func.as_gnd::<NativeFunc>().expect("Unexpected state");
935    func(args, bindings)
936        .map(|(atom, bindings)| InterpretedAtom(atom_to_stack(atom, call_stack.clone()), bindings))
937        .collect()
938}
939
940fn metta_sym(stack: Stack, bindings: Bindings) -> Vec<InterpretedAtom> {
941    let Stack{ prev, atom: metta, .. } = stack;
942    let (atom, typ, space) = match_atom!{
943        metta ~ [_op, atom, typ, space]
944            if space.as_gnd::<DynSpace>().is_some() => (atom, typ, space),
945        _ => {
946            let error = format!("expected: ({} atom type space), found: {}", METTA_SYMBOL, metta);
947            return finished_result(error_msg(metta, error), bindings, prev);
948        }
949    };
950
951    vec![InterpretedAtom(atom_to_stack(call_native!(metta_impl, Atom::expr([atom, typ, space])), prev), bindings)]
952}
953
954fn context_space(context: &InterpreterContext, stack: Stack, bindings: Bindings) -> Vec<InterpretedAtom> {
955    let space = context.space.clone();
956    let Stack{ prev, atom: ctx_space, .. } = stack;
957    let _ = match_atom!{
958        ctx_space ~ [_op] => (),
959        _ => {
960            let error = format!("expected: ({}), found: {}", CONTEXT_SPACE_SYMBOL, ctx_space);
961            return finished_result(error_msg(ctx_space, error), bindings, prev);
962        }
963    };
964    finished_result(Atom::gnd(space), bindings, prev)
965}
966
967type MettaResult = Box<dyn Iterator<Item=(Atom, Bindings)>>;
968
969#[inline]
970fn once<'a, T: 'a>(data: T) -> Box<dyn Iterator<Item=T> + 'a> {
971    Box::new(std::iter::once(data))
972}
973
974#[inline]
975fn empty<'a, T: 'a>() -> Box<dyn Iterator<Item=T> + 'a> {
976    Box::new(std::iter::empty())
977}
978
979#[inline]
980fn call_native_atom(func: NativeFunc, name: &str, args: Atom) -> Atom {
981    function_atom(Atom::expr([CALL_NATIVE_SYMBOL, Atom::sym(name), Atom::value(func), args]))
982}
983
984#[inline]
985fn return_atom(atom: Atom) -> Atom {
986    Atom::expr([RETURN_SYMBOL, atom])
987}
988
989#[inline]
990fn function_atom(atom: Atom) -> Atom {
991    Atom::expr([FUNCTION_SYMBOL, atom])
992}
993
994fn metta_impl(args: Atom, bindings: Bindings) -> MettaResult {
995    let (atom, typ, space) = match_atom!{
996        args ~ [atom, typ, space]
997            if space.as_gnd::<DynSpace>().is_some() => (atom, typ, space),
998        _ => {
999            let error = format!("expected args: (atom type space), found: {}", args);
1000            return once((return_atom(error_msg(call_native!(metta_impl, args), error)), bindings));
1001        }
1002    };
1003
1004    match &atom {
1005        _ if typ == ATOM_TYPE_ATOM => once((return_atom(atom), bindings)),
1006        Atom::Variable(_) => once((return_atom(atom), bindings)),
1007        Atom::Symbol(_) if typ == ATOM_TYPE_SYMBOL => once((return_atom(atom), bindings)),
1008        Atom::Symbol(_) => type_cast(space, atom, typ, bindings),
1009        Atom::Grounded(_) if typ == ATOM_TYPE_GROUNDED => once((return_atom(atom), bindings)),
1010        Atom::Grounded(_) =>  type_cast(space, atom, typ, bindings),
1011        Atom::Expression(_) if typ == ATOM_TYPE_EXPRESSION => once((return_atom(atom), bindings)),
1012        Atom::Expression(e) if e.is_evaluated() => once((return_atom(atom), bindings)),
1013        Atom::Expression(_) => {
1014            let var = Atom::Variable(VariableAtom::new("x").make_unique());
1015            let res = Atom::Variable(VariableAtom::new("res").make_unique());
1016            once((Atom::expr([CHAIN_SYMBOL, Atom::expr([COLLAPSE_BIND_SYMBOL, call_native!(interpret_expression, Atom::expr([atom.clone(), typ, space]))]), var.clone(),
1017                Atom::expr([CHAIN_SYMBOL, call_native!(check_alternatives, Atom::expr([atom, var])), res.clone(),
1018                    return_atom(res)
1019                ])
1020            ]), bindings))
1021        }
1022    }
1023}
1024
1025fn get_meta_type(atom: &Atom) -> Atom {
1026    match atom {
1027        Atom::Variable(_) => ATOM_TYPE_VARIABLE,
1028        Atom::Symbol(_) => ATOM_TYPE_SYMBOL,
1029        Atom::Expression(_) => ATOM_TYPE_EXPRESSION,
1030        Atom::Grounded(_) => ATOM_TYPE_GROUNDED,
1031    }
1032}
1033
1034fn type_cast(space: Atom, atom: Atom, expected_type: Atom, bindings: Bindings) -> MettaResult {
1035    let space = space.as_gnd::<DynSpace>().unwrap();
1036    let types = get_atom_types(space, &atom);
1037
1038    let mut errors = Vec::with_capacity(types.len());
1039    for actual_type in types.into_iter().filter(AtomType::is_valid).map(AtomType::into_atom) {
1040        let res = match_types(&expected_type, &actual_type, bindings.clone());
1041        match res {
1042            Ok(it) => return Box::new(it.map(move |b| (return_atom(atom.clone()), b))),
1043            Err(_) => errors.push(actual_type),
1044        }
1045    }
1046
1047    Box::new(errors.into_iter().map(move |actual_type| {
1048        let err = error_atom(atom.clone(), Atom::expr([BAD_TYPE_SYMBOL, expected_type.clone(), actual_type]));
1049        (return_atom(err), bindings.clone())
1050    }))
1051}
1052
1053fn match_types(type1: &Atom, type2: &Atom, bindings: Bindings) -> Result<MatchResultIter, MatchResultIter>  {
1054    if *type1 == ATOM_TYPE_UNDEFINED
1055        || *type2 == ATOM_TYPE_UNDEFINED
1056        || *type1 == ATOM_TYPE_ATOM
1057        || *type2 == ATOM_TYPE_ATOM {
1058        Ok(once(bindings))
1059    } else {
1060        let bindings_copy = bindings.clone();
1061        let mut result = match_atoms(type1, type2)
1062            .flat_map(move |b| b.merge(&bindings).into_iter())
1063            .peekable();
1064        if result.peek().is_none() {
1065            log::trace!("match_types: no match: {} !~ {}", type1, type2);
1066            Err(once(bindings_copy))
1067        } else {
1068            if log::log_enabled!(log::Level::Trace) {
1069                let result: Vec<Bindings> = result.collect();
1070                log::trace!("match_types: match: {} ~ {}, bindings {:?}", type1, type2, result);
1071                Ok(Box::new(result.into_iter()))
1072            } else {
1073                Ok(Box::new(result))
1074            }
1075        }
1076    }
1077}
1078
1079fn check_alternatives(args: Atom, bindings: Bindings) -> MettaResult {
1080    let (original, expr) = match_atom!{
1081        args ~ [original, Atom::Expression(expr)] => (original, expr),
1082        _ => {
1083            let error = format!("expected args: ((: expr Expression)), found: {}", args);
1084            return once((return_atom(error_msg(call_native!(check_alternatives, args), error)), bindings));
1085        }
1086    };
1087    let results = expr.into_children().into_iter()
1088        .map(atom_into_atom_bindings);
1089    let mut succ = results.clone()
1090        .filter(|(atom, _bindings)| !atom_is_error(&atom))
1091        .map(move |(mut atom, bindings)| {
1092            if original == atom {
1093                match &mut atom {
1094                    Atom::Expression(e) => e.set_evaluated(),
1095                    _ => {},
1096                };
1097            }
1098            (return_atom(atom), bindings)
1099        })
1100        .peekable();
1101    let err = results
1102        .filter(|(atom, _bindings)| atom_is_error(&atom))
1103        .map(|(atom, bindings)| (return_atom(atom), bindings));
1104    match succ.peek() {
1105        Some(_) => Box::new(succ),
1106        None => Box::new(err),
1107    }
1108}
1109
1110fn interpret_expression(args: Atom, bindings: Bindings) -> MettaResult {
1111    let (expr, expr_typ, space) = match_atom!{
1112        args ~ [expr, expr_typ, space]
1113            if space.as_gnd::<DynSpace>().is_some() => (expr, expr_typ, space),
1114        _ => {
1115            let error = format!("expected args: (atom type space), found: {}", args);
1116            return once((return_atom(error_msg(call_native!(interpret_expression, args), error)), bindings));
1117        }
1118    };
1119    match atom_as_slice(&expr) {
1120        Some([op, _args @ ..]) => {
1121            let space_ref = space.as_gnd::<DynSpace>().unwrap();
1122            let actual_types = get_atom_types(space_ref, op);
1123
1124            let only_error_types = actual_types.iter().all(AtomType::is_error);
1125            if only_error_types {
1126                log::debug!("interpret_expression: op type check: expr: {}, op types: [{}]", expr, actual_types.iter().format(", "));
1127                return Box::new(actual_types.into_iter().map(move |t| (return_atom(t.into_error_unchecked()), bindings.clone())))
1128            }
1129
1130            let mut func_types = actual_types.iter()
1131                .filter(|t| t.is_valid() && t.is_function())
1132                .map(AtomType::as_atom)
1133                .peekable();
1134            let func = if func_types.peek().is_some() {
1135                let ret_typ = expr_typ.clone();
1136                let type_check_results = func_types.flat_map(|typ| check_if_function_type_is_applicable(&expr, typ, &ret_typ, space_ref, bindings.clone()));
1137                let mut errors = Vec::new();
1138                for res in type_check_results {
1139                    log::debug!("interpret_expression: function type check: expr: {} type: {:?}", expr, res);
1140                    match res {
1141                        (Ok((op_type, mut ret_type)), bindings) => {
1142                            // TODO: it is a hack to prevent Expression working
1143                            // like Atom return type. On the one hand we could
1144                            // remove old code to prevent Atom results being
1145                            // interpreted after the fix of return type-check
1146                            // on the other hand may be it is logical to make
1147                            // Expression have the same semantics. It would be
1148                            // useful to provide the manner to execute funciton
1149                            // body before returning the Atom (or Expression)
1150                            // as is.
1151                            if ret_type == ATOM_TYPE_EXPRESSION {
1152                                ret_type = ATOM_TYPE_UNDEFINED;
1153                            }
1154                            let reduced = Atom::Variable(VariableAtom::new("reduced").make_unique());
1155                            let result = Atom::Variable(VariableAtom::new("result").make_unique());
1156                            return once((Atom::expr([CHAIN_SYMBOL, call_native!(interpret_function, Atom::expr([expr.clone(), op_type, expr_typ.clone(), space.clone()])), reduced.clone(),
1157                                Atom::expr([CHAIN_SYMBOL, call_native!(metta_call, Atom::expr([reduced, ret_type, space.clone()])), result.clone(),
1158                                    return_atom(result)
1159                                ])
1160                            ]), bindings));
1161                        },
1162                        (Err(err), bindings) => errors.push((err, bindings)),
1163                    }
1164                }
1165                Box::new(errors.into_iter()
1166                    .map(move |(err, bindings)| (return_atom(err), bindings)))
1167            } else {
1168                empty()
1169            };
1170
1171            let has_tuple_type = actual_types.iter().any(|t| (t.is_valid() && !t.is_function()) || t.as_atom() == &ATOM_TYPE_UNDEFINED);
1172            let tuple = if has_tuple_type {
1173                let reduced = Atom::Variable(VariableAtom::new("reduced").make_unique());
1174                let result = Atom::Variable(VariableAtom::new("result").make_unique());
1175                once((
1176                    Atom::expr([CHAIN_SYMBOL, call_native!(interpret_tuple, Atom::expr([expr.clone(), space.clone()])), reduced.clone(),
1177                        Atom::expr([CHAIN_SYMBOL, call_native!(metta_call, Atom::expr([reduced, expr_typ.clone(), space.clone()])), result.clone(),
1178                            return_atom(result)
1179                        ])
1180                    ]), bindings.clone()))
1181            } else {
1182                empty()
1183            };
1184
1185            Box::new(tuple.chain(func))
1186        },
1187        _ => type_cast(space, expr, expr_typ, bindings),
1188    }
1189}
1190
1191fn interpret_tuple(args: Atom, bindings: Bindings) -> MettaResult {
1192    let (expr, space) = match_atom!{
1193        args ~ [Atom::Expression(expr), space]
1194            if space.as_gnd::<DynSpace>().is_some() => (expr, space),
1195        _ => {
1196            let error = format!("expected args: ((: expr Expression) space), found: {}", args);
1197            return once((return_atom(error_msg(call_native!(interpret_tuple, args), error)), bindings));
1198        }
1199    };
1200    if expr.children().is_empty() {
1201        once((return_atom(Atom::Expression(expr)), bindings))
1202    } else {
1203        let mut tuple = expr.into_children();
1204        let head = tuple.remove(0);
1205        let tail = tuple;
1206        let rhead = Atom::Variable(VariableAtom::new("rhead").make_unique());
1207        let rtail = Atom::Variable(VariableAtom::new("rtail").make_unique());
1208        let result = Atom::Variable(VariableAtom::new("result").make_unique());
1209        once((
1210            Atom::expr([CHAIN_SYMBOL, Atom::expr([METTA_SYMBOL, head, ATOM_TYPE_UNDEFINED, space.clone()]), rhead.clone(),
1211                call_native!(return_on_error, Atom::expr([rhead.clone(),
1212                    Atom::expr([CHAIN_SYMBOL, call_native!(interpret_tuple, Atom::expr([Atom::expr(tail), space.clone()])), rtail.clone(),
1213                        call_native!(return_on_error, Atom::expr([rtail.clone(),
1214                            Atom::expr([CHAIN_SYMBOL, Atom::expr([CONS_ATOM_SYMBOL, rhead, rtail]), result.clone(),
1215                                return_atom(result)
1216                            ])
1217                        ]))
1218                    ])
1219                ]))
1220            ]), bindings))
1221    }
1222}
1223
1224fn interpret_function(args: Atom, bindings: Bindings) -> MettaResult {
1225    let (atom, op_type, ret_type, space) = match_atom!{
1226        args ~ [Atom::Expression(atom), Atom::Expression(op_type), ret_type, space]
1227            if space.as_gnd::<DynSpace>().is_some() &&
1228                op_type.children().get(0) == Some(&ARROW_SYMBOL) => (atom, op_type, ret_type, space),
1229        _ => {
1230            let error = format!("expected args: ((: atom Expression) (: op_type Expression) ret_type space), found: {}", args);
1231            return once((return_atom(error_msg(call_native!(interpret_function, args), error)), bindings));
1232        }
1233    };
1234    let mut call = atom.clone().into_children();
1235    let head = call.remove(0);
1236    let args = call;
1237    let mut arg_types: Vec<Atom> = op_type.children().into();
1238    arg_types.remove(0);
1239    let arg_types = Atom::expr(arg_types);
1240    let rop = Atom::Variable(VariableAtom::new("rop").make_unique());
1241    let rargs = Atom::Variable(VariableAtom::new("rargs").make_unique());
1242    let result = Atom::Variable(VariableAtom::new("result").make_unique());
1243    let unpacked_args = Atom::Variable(VariableAtom::new("unpacked_args").make_unique());
1244    let call_interpret_args = call_native!(interpret_args, Atom::expr([Atom::Expression(atom), Atom::expr(args), arg_types, ret_type, space.clone()]));
1245    once((
1246        Atom::expr([CHAIN_SYMBOL, Atom::expr([METTA_SYMBOL, head, Atom::Expression(op_type), space.clone()]), rop.clone(),
1247            call_native!(return_on_error, Atom::expr([rop.clone(), 
1248                Atom::expr([CHAIN_SYMBOL, call_interpret_args.clone(), rargs.clone(),
1249                    Atom::expr([UNIFY_SYMBOL, Atom::expr([Atom::sym("Ok"), unpacked_args.clone()]), rargs.clone(),
1250                        Atom::expr([CHAIN_SYMBOL, Atom::expr([CONS_ATOM_SYMBOL, rop, unpacked_args]), result.clone(),
1251                            return_atom(result)
1252                        ]),
1253                        return_atom(rargs)
1254                    ])
1255                ])
1256            ]))
1257        ]), bindings))
1258}
1259
1260fn check_if_function_type_is_applicable<'a>(expr: &'a Atom, op_type: &'a Atom, expected_type: &'a Atom, space: &'a DynSpace, bindings: Bindings) -> Box<dyn Iterator<Item=(Result<(Atom, Atom), Atom>, Bindings)> + 'a> {
1261    log::trace!("check_if_function_type_is_applicable: function type check: expr: {}, op_type: {}, expected_type: {}", expr, op_type, expected_type);
1262    let arg_types: &ExpressionAtom = (op_type).try_into().unwrap();
1263    let arg_types = arg_types.children();
1264    let actual_args: &ExpressionAtom = (expr).try_into().unwrap();
1265    let actual_args = actual_args.children();
1266    if (arg_types.len() - 2) != (actual_args.len() - 1) {
1267        return once((Err(error_atom(expr.clone(), INCORRECT_NUMBER_OF_ARGUMENTS_SYMBOL)), bindings));
1268    }
1269    let mut actual_args = actual_args.iter();
1270    actual_args.next();
1271    let mut arg_types = arg_types.iter();
1272    assert_eq!(arg_types.next().unwrap(), &ARROW_SYMBOL);
1273    check_if_function_type_is_applicable_(expr, op_type,
1274        actual_args, arg_types, expected_type, space, bindings)
1275}
1276
1277fn is_meta_type(atom: &Atom) -> bool {
1278    if *atom == ATOM_TYPE_ATOM
1279        || *atom == ATOM_TYPE_SYMBOL
1280        || *atom == ATOM_TYPE_VARIABLE
1281        || *atom == ATOM_TYPE_EXPRESSION
1282        || *atom == ATOM_TYPE_GROUNDED {
1283        true
1284    } else {
1285        false
1286    }
1287}
1288
1289fn match_meta_types(actual: &Atom, expected: &Atom) -> bool {
1290    if *expected == ATOM_TYPE_ATOM {
1291        true
1292    } else {
1293        actual == expected
1294    }
1295}
1296
1297fn check_if_function_type_is_applicable_<'a>(expr: &'a Atom, op_type: &'a Atom,
1298        mut actual_args: std::slice::Iter<'a, Atom>, mut arg_types: std::slice::Iter<'a, Atom>,
1299        expected_type: &'a Atom, space: &'a DynSpace, bindings: Bindings) -> Box<dyn Iterator<Item=(Result<(Atom, Atom), Atom>, Bindings)> + 'a> {
1300    match actual_args.next() {
1301        None => {
1302            let ret_type = arg_types.next().unwrap();
1303            log::trace!("check_if_function_type_is_applicable_: function type check: expr: {}, ret_type: {}, expected_type: {}", expr, ret_type, expected_type);
1304            // Here expected_type is always some specific type not meta-type. It is because
1305            // there are two places to assign expected_type. First place is passing result of
1306            // the function call to another function. In this case expected_type is an expected
1307            // type of the outer function argument. Second place is explicit call to
1308            // `metta`. In this case expected_type is explicitly set as an argument and handled
1309            // in metta_impl() Rust function which compares it with passed expression
1310            // meta-type. Thus if expected_type is meta-type it is always first compared to the
1311            // expression's meta-type and type check finishes.
1312            match match_types(ret_type, expected_type, bindings) {
1313                Ok(matches) => Box::new(matches.map(move |bindings| (Ok((op_type.clone(), ret_type.clone())), bindings))),
1314                Err(nomatch) => Box::new(nomatch.map(move |bindings| (Err(Atom::expr([ERROR_SYMBOL, expr.clone(), Atom::expr([BAD_TYPE_SYMBOL, expected_type.clone(), ret_type.clone()])])), bindings))),
1315            }
1316        },
1317        Some(actual_arg) => {
1318            let formal_arg_type = arg_types.next().unwrap();
1319            if is_meta_type(formal_arg_type) && match_meta_types(&get_meta_type(actual_arg), formal_arg_type) {
1320                check_if_function_type_is_applicable_(expr, op_type, actual_args, arg_types, expected_type, space, bindings)
1321            } else {
1322                let actual_arg_types = get_atom_types(space, actual_arg)
1323                    .into_iter()
1324                    .inspect(move |typ| log::trace!("check_if_function_type_is_applicable_: function type check: expr: {}, actual_arg: {}, actual_type: {}", expr, actual_arg, typ));
1325                let iter = actual_arg_types.flat_map(move |actual_arg_type| -> Box<dyn Iterator<Item=(Result<(Atom, Atom), Atom>, Bindings)> + '_> {
1326                    match actual_arg_type.into_atom_or_error() {
1327                        Ok(actual_arg_type) => {
1328                            match match_types(formal_arg_type, &actual_arg_type, bindings.clone()) {
1329                                Ok(matches) => {
1330                                    let actual_args = actual_args.clone();
1331                                    let arg_types = arg_types.clone();
1332                                    Box::new(matches.flat_map(move |bindings| check_if_function_type_is_applicable_(expr, op_type, actual_args.clone(), arg_types.clone(), expected_type, space, bindings)))
1333                                },
1334                                Err(nomatch) => {
1335                                    let arg_id = TryInto::<&ExpressionAtom>::try_into(expr).unwrap().children().len() - arg_types.len();
1336                                    Box::new(nomatch.map(move |bindings| {
1337                                        let formal_arg_type = apply_bindings_to_atom_move(formal_arg_type.clone(), &bindings);
1338                                        (Err(Atom::expr([ERROR_SYMBOL, expr.clone(), Atom::expr([BAD_ARG_TYPE_SYMBOL, Atom::gnd(Number::Integer(arg_id as i64)), formal_arg_type, actual_arg_type.clone()])])), bindings)
1339                                    }))
1340                                },
1341                            }
1342                        },
1343                        Err(error) => once((Err(error), bindings.clone())),
1344                    }
1345                });
1346                Box::new(iter)
1347            }
1348        },
1349    }
1350}
1351
1352fn interpret_args(args_: Atom, bindings: Bindings) -> MettaResult {
1353    let (atom, args, arg_types, ret_type, space) = match_atom!{
1354        args_ ~ [atom, Atom::Expression(args), Atom::Expression(arg_types), ret_type, space]
1355            if space.as_gnd::<DynSpace>().is_some() => (atom, args, arg_types, ret_type, space),
1356        _ => {
1357            let error = format!("expected args: (atom (: args Expression) (: arg_types Expression) ret_type space), found: {}", args_);
1358            return once((return_atom(error_msg(call_native!(interpret_args, args_), error)), bindings));
1359        }
1360    };
1361    let mut types = arg_types.into_children();
1362    if types.is_empty() {
1363        return once((return_atom(error_atom(atom, INCORRECT_NUMBER_OF_ARGUMENTS_SYMBOL)), bindings));
1364    }
1365    let types_head = types.remove(0);
1366    let types_tail = types;
1367    if args.children().is_empty() {
1368        once((return_atom(Atom::expr([Atom::sym("Ok"), Atom::Expression(args.clone())])), bindings))
1369    } else {
1370        let mut args = args.into_children();
1371        let args_head = args.remove(0);
1372        let args_tail = args;
1373        let rhead = Atom::Variable(VariableAtom::new("rhead").make_unique());
1374        let rtail = Atom::Variable(VariableAtom::new("rtail").make_unique());
1375        let result = Atom::Variable(VariableAtom::new("result").make_unique());
1376        let tail = Atom::Variable(VariableAtom::new("tail").make_unique());
1377        let call_self = call_native!(interpret_args, Atom::expr([atom, Atom::expr(args_tail), Atom::expr(types_tail), ret_type, space.clone()]));
1378        let recursion = Atom::expr([CHAIN_SYMBOL, call_self.clone(), rtail.clone(),
1379            Atom::expr([UNIFY_SYMBOL, Atom::expr([Atom::sym("Ok"), tail.clone()]), rtail.clone(),
1380                Atom::expr([CHAIN_SYMBOL, Atom::expr([CONS_ATOM_SYMBOL, rhead.clone(), tail]), result.clone(),
1381                    return_atom(Atom::expr([Atom::sym("Ok"), result]))
1382                ]),
1383                return_atom(rtail)
1384            ])
1385        ]);
1386        once((
1387            Atom::expr([CHAIN_SYMBOL, Atom::expr([METTA_SYMBOL, args_head.clone(), types_head, space.clone()]), rhead.clone(),
1388                Atom::expr([EVAL_SYMBOL, Atom::expr([Atom::gnd(IfEqualOp{}), rhead.clone(), args_head,
1389                    recursion.clone(),
1390                    call_native!(return_on_error, Atom::expr([rhead, 
1391                        recursion
1392                    ]))
1393                ])])
1394            ]), bindings))
1395    }
1396}
1397
1398fn return_on_error(args: Atom, bindings: Bindings) -> MettaResult {
1399    let (atom, then) = match_atom!{
1400        args ~ [atom, then] => (atom, then),
1401        _ => {
1402            let error = format!("expected args: (atom then), found: {}", args);
1403            return once((return_atom(error_msg(call_native!(return_on_error, args), error)), bindings));
1404        }
1405    };
1406    if EMPTY_SYMBOL == atom {
1407        once((return_atom(return_atom(EMPTY_SYMBOL)), bindings))
1408    } else if atom_is_error(&atom) {
1409        once((return_atom(return_atom(atom)), bindings))
1410    } else {
1411        once((return_atom(then), bindings))
1412    }
1413}
1414
1415fn metta_call(args: Atom, bindings: Bindings) -> MettaResult {
1416    let (atom, typ, space) = match_atom!{
1417        args ~ [atom, typ, space]
1418            if space.as_gnd::<DynSpace>().is_some() => (atom, typ, space),
1419        _ => {
1420            let error = format!("expected args: (atom type space), found: {}", args);
1421            return once((return_atom(error_msg(call_native!(metta_call, args), error)), bindings));
1422        }
1423    };
1424    if atom_is_error(&atom) {
1425        once((return_atom(atom), bindings))
1426    } else {
1427        let result = Atom::Variable(VariableAtom::new("result").make_unique());
1428        let ret = Atom::Variable(VariableAtom::new("ret").make_unique());
1429        once((
1430            // TODO: At the moment metta_call() is called we already know
1431            // should we call atom as a tuple or as a function.
1432            // But (eval (<op> <args>)) inside independently decides whether it
1433            // should call grounded operation <op>, or match (= (<op> <args>) <res>).
1434            // This can lead to the conflict if user defines a function using
1435            // grounded atom (for instance (+3 1 2 3)) and after type analysis
1436            // interpreter decides we need to match it then calling eval will
1437            // analyze the expression again and may call grounded op instead of
1438            // matching.
1439            Atom::expr([CHAIN_SYMBOL, Atom::expr([EVALC_SYMBOL, atom.clone(), space.clone()]), result.clone(),
1440                Atom::expr([CHAIN_SYMBOL, call_native!(metta_call_return, Atom::expr([atom, result, typ, space])), ret.clone(),
1441                    return_atom(ret)
1442                ])
1443            ]), bindings))
1444    }
1445}
1446
1447fn metta_call_return(args: Atom, bindings: Bindings) -> MettaResult {
1448    let (atom, result, typ, space) = match_atom!{
1449        args ~ [atom, result, typ, space]
1450            if space.as_gnd::<DynSpace>().is_some() => (atom, result, typ, space),
1451        _ => {
1452            let error = format!("expected args: (atom result type space), found: {}", args);
1453            return once((return_atom(error_msg(call_native!(metta_call_return, args), error)), bindings));
1454        }
1455    };
1456    if NOT_REDUCIBLE_SYMBOL == result {
1457        once((return_atom(atom), bindings))
1458    } else if EMPTY_SYMBOL == result {
1459        once((return_atom(EMPTY_SYMBOL), bindings))
1460    } else if atom_is_error(&result) {
1461        once((return_atom(result), bindings))
1462    } else {
1463        let ret = Atom::Variable(VariableAtom::new("ret").make_unique());
1464        once((
1465            Atom::expr([CHAIN_SYMBOL, Atom::expr([METTA_SYMBOL, result, typ, space]), ret.clone(),
1466                return_atom(ret)
1467            ]), bindings))
1468    }
1469}
1470
1471#[cfg(test)]
1472mod tests {
1473    use super::*;
1474    use crate::metta::text::metta_atom;
1475    use crate::space::grounding::metta_space;
1476    use hyperon_common::assert_eq_no_order;
1477    use hyperon_macros::metta;
1478
1479    #[test]
1480    fn interpret_atom_evaluate_incorrect_args() {
1481        assert_eq!(call_interpret(space(""), &metta_atom("(eval)")),
1482            vec![expr!("Error" ("eval") "expected: (eval <atom>), found: (eval)")]);
1483        assert_eq!(call_interpret(space(""), &metta_atom("(eval a b)")),
1484            vec![expr!("Error" ("eval" "a" "b") "expected: (eval <atom>), found: (eval a b)")]);
1485    }
1486
1487    #[test]
1488    fn interpret_atom_evaluate_atom() {
1489        let result = call_interpret(space("(= a b)"), &metta_atom("(eval a)"));
1490        assert_eq!(result, vec![metta_atom("b")]);
1491    }
1492
1493    #[test]
1494    fn interpret_atom_evaluate_atom_no_definition() {
1495        let result = call_interpret(space(""), &metta_atom("(eval a)"));
1496        assert_eq!(result, vec![metta_atom("NotReducible")]);
1497    }
1498
1499    #[test]
1500    fn interpret_atom_evaluate_empty_expression() {
1501        let result = call_interpret(space(""), &metta_atom("(eval ())"));
1502        assert_eq!(result, vec![metta_atom("NotReducible")]);
1503    }
1504
1505    #[test]
1506    fn interpret_atom_evaluate_grounded_value() {
1507        let result = call_interpret(space(""), &expr!("eval" {6}));
1508        assert_eq!(result, vec![metta_atom("NotReducible")]);
1509    }
1510
1511
1512    #[test]
1513    fn interpret_atom_evaluate_pure_expression() {
1514        let space = space("(= (foo $a B) $a)");
1515        let result = call_interpret(space, &metta_atom("(eval (foo A $b))"));
1516        assert_eq!(result, vec![metta_atom("A")]);
1517    }
1518
1519    #[test]
1520    fn interpret_atom_evaluate_pure_expression_non_determinism() {
1521        let space = space("
1522            (= color red)
1523            (= color green)
1524            (= color blue)
1525        ");
1526        let result = call_interpret(space, &metta_atom("(eval color)"));
1527        assert_eq_no_order!(result, vec![
1528            metta_atom("red"),
1529            metta_atom("green"),
1530            metta_atom("blue"),
1531        ]);
1532    }
1533
1534    #[test]
1535    fn interpret_atom_evaluate_pure_expression_no_definition() {
1536        let result = call_interpret(space(""), &metta_atom("(eval (foo A))"));
1537        assert_eq!(result, vec![metta_atom("NotReducible")]);
1538    }
1539
1540    #[test]
1541    fn interpret_atom_evaluate_pure_expression_variable_in_space() {
1542        let space = space("$t (= (foo $a B) $a)");
1543        let result = call_interpret(space, &metta_atom("(eval (foo A $b))"));
1544        assert_eq!(result, vec![metta_atom("A")]);
1545    }
1546
1547    #[test]
1548    fn interpret_atom_evaluate_pure_expression_variable_name_conflict() {
1549        let space = space("(= (foo ($W)) True)");
1550        let result = call_interpret(space, &metta_atom("(eval (foo $W))"));
1551        assert_eq!(result[0], sym!("True"));
1552    }
1553
1554    #[test]
1555    fn interpret_atom_evaluate_grounded_expression() {
1556        let result = call_interpret(space(""), &expr!("eval" ({MulXUndefinedType(7)} {6})));
1557        assert_eq!(result, vec![expr!({42})]);
1558    }
1559
1560    #[test]
1561    fn interpret_atom_evaluate_grounded_expression_empty() {
1562        let result = call_interpret(space(""), &expr!("eval" ({ReturnNothing()} {6})));
1563        assert_eq!(result, vec![EMPTY_SYMBOL]);
1564    }
1565
1566    #[test]
1567    fn interpret_atom_evaluate_grounded_expression_noreduce() {
1568        let result = call_interpret(space(""), &expr!("eval" ({NonReducible()} {6})));
1569        assert_eq!(result, vec![NOT_REDUCIBLE_SYMBOL]);
1570    }
1571
1572    #[test]
1573    fn interpret_atom_evaluate_grounded_expression_incorrect_argument() {
1574        let result = call_interpret(space(""), &expr!("eval" ({IncorrectArgument()} {6.5})));
1575        assert_eq!(result, vec![NOT_REDUCIBLE_SYMBOL]);
1576    }
1577
1578    #[test]
1579    fn interpret_atom_evaluate_grounded_expression_error() {
1580        let result = call_interpret(space(""), &expr!("eval" ({ThrowError()} {"Test error"})));
1581        assert_eq!(result, vec![expr!("Error" ({ThrowError()} {"Test error"}) "Test error")]);
1582    }
1583
1584    #[test]
1585    fn interpret_atom_evaluate_variable_operation() {
1586        let space = space("(= (foo $a B) $a)");
1587        let result = call_interpret(space, &metta_atom("(eval ($a A $b))"));
1588        #[cfg(feature = "variable_operation")]
1589        assert_eq!(result, vec![metta_atom("A")]);
1590        #[cfg(not(feature = "variable_operation"))]
1591        assert_eq!(result, vec![NOT_REDUCIBLE_SYMBOL]);
1592    }
1593
1594    #[test]
1595    fn interpret_atom_evaluate_variable_via_call_direct_equality() {
1596        let space = space("
1597            (= (bar) (function (return ())))
1598            (= (foo $b) (function
1599              (chain (eval (bar)) $_
1600              (unify $b value
1601                (return ())
1602                (return (Error () \"Unexpected error\")) ))))");
1603        let result = call_interpret(space,
1604            &metta_atom("(chain (eval (foo $a)) $_ $a)"));
1605        assert_eq!(result[0], sym!("value"));
1606    }
1607
1608    #[test]
1609    fn interpret_atom_evaluate_variable_via_call_struct_equality() {
1610        let formal_arg_struct = space("
1611            (= (bar) (function (return ())))
1612            (= (foo ($b)) (function
1613              (chain (eval (bar)) $_
1614              (unify $b value
1615                (return ())
1616                (return (Error () \"Unexpected error\")) ))))");
1617        let result = call_interpret(formal_arg_struct,
1618            &metta_atom("(chain (eval (foo $a)) $_ $a)"));
1619        assert_eq!(result[0], expr!(("value")));
1620
1621        let actual_arg_struct = space("
1622            (= (bar) (function (return ())))
1623            (= (foo $b) (function
1624              (chain (eval (bar)) $_
1625              (unify $b (value)
1626                (return ())
1627                (return (Error () \"Unexpected error\")) ))))");
1628        let result = call_interpret(actual_arg_struct,
1629            &metta_atom("(chain (eval (foo ($a))) $_ $a)"));
1630        assert_eq!(result[0], sym!("value"));
1631    }
1632
1633    #[test]
1634    fn interpret_atom_evaluate_variable_operation_nested() {
1635        let space = space("(= ((baz $a) $b) ($a $b))");
1636        let result = call_interpret(space, &metta_atom("(eval (($a A) B))"));
1637        #[cfg(feature = "variable_operation")]
1638        assert_eq!(result, vec![metta_atom("(A B)")]);
1639        #[cfg(not(feature = "variable_operation"))]
1640        assert_eq!(result, vec![NOT_REDUCIBLE_SYMBOL]);
1641    }
1642
1643    #[test]
1644    fn interpret_atom_evaluate_non_executable_grounded_atom_on_a_first_position() {
1645        let space = space("(= ($x > $y) (> $x $y))");
1646
1647        let result = call_interpret(space, &expr!("eval" ({1} ">" {2})));
1648        assert_eq!(result, vec![expr!(">" {1} {2})]);
1649    }
1650
1651
1652    #[test]
1653    fn interpret_atom_chain_incorrect_args() {
1654        assert_eq!(call_interpret(space(""), &metta_atom("(chain n $v t o)")),
1655            vec![expr!("Error" ("chain" "n" v "t" "o") "expected: (chain <nested> (: <var> Variable) <templ>), found: (chain n $v t o)")]);
1656        assert_eq!(call_interpret(space(""), &metta_atom("(chain n v t)")),
1657            vec![expr!("Error" ("chain" "n" "v" "t") "expected: (chain <nested> (: <var> Variable) <templ>), found: (chain n v t)")]);
1658        assert_eq!(call_interpret(space(""), &metta_atom("(chain n $v)")),
1659            vec![expr!("Error" ("chain" "n" v) "expected: (chain <nested> (: <var> Variable) <templ>), found: (chain n $v)")]);
1660    }
1661
1662    #[test]
1663    fn interpret_atom_chain_atom() {
1664        let result = call_interpret(space(""), &expr!("chain" ("A" () {6} y) x ("bar" x)));
1665        assert_eq!(result, vec![expr!("bar" ("A" () {6} y))]);
1666    }
1667
1668
1669    #[test]
1670    fn interpret_atom_chain_evaluation() {
1671        let space = space("(= (foo $a B) $a)");
1672        let result = call_interpret(space, &metta_atom("(chain (eval (foo A $b)) $x (bar $x))"));
1673        assert_eq!(result, vec![metta_atom("(bar A)")]);
1674    }
1675
1676    #[test]
1677    fn interpret_atom_chain_nested_evaluation() {
1678        let space = space("(= (foo $a B) $a)");
1679        let result = call_interpret(space, &metta_atom("(chain (chain (eval (foo A $b)) $x (bar $x)) $y (baz $y))"));
1680        assert_eq!(result, vec![metta_atom("(baz (bar A))")]);
1681    }
1682
1683    #[test]
1684    fn interpret_atom_chain_nested_value() {
1685        let result = call_interpret(space(""), &metta_atom("(chain (chain A $x (bar $x)) $y (baz $y))"));
1686        assert_eq!(result, vec![metta_atom("(baz (bar A))")]);
1687    }
1688
1689    #[test]
1690    fn interpret_atom_chain_expression_non_determinism() {
1691        let space = space("
1692            (= (color) red)
1693            (= (color) green)
1694            (= (color) blue)
1695        ");
1696        let result = call_interpret(space, &metta_atom("(chain (eval (color)) $x (bar $x))"));
1697        assert_eq_no_order!(result, vec![
1698            metta_atom("(bar red)"),
1699            metta_atom("(bar green)"),
1700            metta_atom("(bar blue))"),
1701        ]);
1702    }
1703
1704    #[test]
1705    fn interpret_atom_chain_return() {
1706        let result = call_interpret(space(""), &metta_atom("(chain Empty $x (bar $x))"));
1707        assert_eq!(result, vec![metta_atom("(bar Empty)")]);
1708    }
1709
1710    #[test]
1711    fn interpret_atom_chain_keep_var_from_evaluated_part() {
1712        let result = call_interpret(space("(= (even 4) True)"), &metta_atom("(chain (eval (even $x)) $res (= (is-even $x) $res))"));
1713        assert_eq!(result, vec![metta_atom("(= (is-even 4) True)")]);
1714    }
1715
1716
1717    #[test]
1718    fn interpret_atom_unify_incorrect_args() {
1719        assert_eq!(call_interpret(space(""), &metta_atom("(unify a p t e o)")),
1720            vec![expr!("Error" ("unify" "a" "p" "t" "e" "o") "expected: (unify <atom> <pattern> <then> <else>), found: (unify a p t e o)")]);
1721        assert_eq!(call_interpret(space(""), &metta_atom("(unify a p t)")),
1722            vec![expr!("Error" ("unify" "a" "p" "t") "expected: (unify <atom> <pattern> <then> <else>), found: (unify a p t)")]);
1723    }
1724
1725    #[test]
1726    fn interpret_atom_unify_then() {
1727        let result = call_interpret(space(""), &metta_atom("(unify (A $b) ($a B) ($a $b) Empty)"));
1728        assert_eq!(result, vec![metta_atom("(A B)")]);
1729    }
1730
1731    #[test]
1732    fn interpret_atom_unify_else() {
1733        let result = call_interpret(space(""), &metta_atom("(unify (A $b C) ($a B D) ($a $b) Empty)"));
1734        assert_eq!(result, vec![EMPTY_SYMBOL]);
1735    }
1736
1737
1738    #[test]
1739    fn interpret_atom_decons_atom_incorrect_args() {
1740        assert_eq!(call_interpret(space(""), &metta_atom("(decons-atom a)")),
1741            vec![expr!("Error" ("decons-atom" "a") "expected: (decons-atom (: <expr> Expression)), found: (decons-atom a)")]);
1742        assert_eq!(call_interpret(space(""), &metta_atom("(decons-atom (a) (b))")),
1743            vec![expr!("Error" ("decons-atom" ("a") ("b")) "expected: (decons-atom (: <expr> Expression)), found: (decons-atom (a) (b))")]);
1744        assert_eq!(call_interpret(space(""), &metta_atom("(decons-atom)")),
1745            vec![expr!("Error" ("decons-atom") "expected: (decons-atom (: <expr> Expression)), found: (decons-atom)")]);
1746    }
1747
1748    #[test]
1749    fn interpret_atom_decons_atom_empty() {
1750        let result = call_interpret(space(""), &metta_atom("(decons-atom ())"));
1751        assert_eq!(result, vec![expr!("Error" ("decons-atom" ()) "expected: (decons-atom (: <expr> Expression)), found: (decons-atom ())")]);
1752    }
1753
1754    #[test]
1755    fn interpret_atom_decons_atom_single() {
1756        let result = call_interpret(space(""), &metta_atom("(decons-atom (a))"));
1757        assert_eq!(result, vec![metta_atom("(a ())")]);
1758    }
1759
1760    #[test]
1761    fn interpret_atom_decons_atom_list() {
1762        let result = call_interpret(space(""), &metta_atom("(decons-atom (a b c))"));
1763        assert_eq!(result, vec![metta_atom("(a (b c))")]);
1764    }
1765
1766
1767    #[test]
1768    fn interpret_atom_cons_atom_incorrect_args() {
1769        assert_eq!(call_interpret(space(""), &metta_atom("(cons-atom a (e) o)")),
1770            vec![expr!("Error" ("cons-atom" "a" ("e") "o") "expected: (cons-atom <head> (: <tail> Expression)), found: (cons-atom a (e) o)")]);
1771        assert_eq!(call_interpret(space(""), &metta_atom("(cons-atom a e)")),
1772            vec![expr!("Error" ("cons-atom" "a" "e") "expected: (cons-atom <head> (: <tail> Expression)), found: (cons-atom a e)")]);
1773        assert_eq!(call_interpret(space(""), &metta_atom("(cons-atom a)")),
1774            vec![expr!("Error" ("cons-atom" "a") "expected: (cons-atom <head> (: <tail> Expression)), found: (cons-atom a)")]);
1775    }
1776
1777    #[test]
1778    fn interpret_atom_cons_atom_empty() {
1779        let result = call_interpret(space(""), &metta_atom("(cons-atom a ())"));
1780        assert_eq!(result, vec![metta_atom("(a)")]);
1781    }
1782
1783    #[test]
1784    fn interpret_atom_cons_atom_single() {
1785        let result = call_interpret(space(""), &metta_atom("(cons-atom a (b))"));
1786        assert_eq!(result, vec![metta_atom("(a b)")]);
1787    }
1788
1789    #[test]
1790    fn interpret_atom_cons_atom_list() {
1791        let result = call_interpret(space(""), &metta_atom("(cons-atom a (b c))"));
1792        assert_eq!(result, vec![metta_atom("(a b c)")]);
1793    }
1794
1795
1796    #[test]
1797    fn test_superpose_bind() {
1798        let vars: Variables = [ "a", "b", "c" ].into_iter().map(VariableAtom::new).collect();
1799        let atom = Atom::expr([Atom::sym("superpose-bind"),
1800            Atom::expr([atom_bindings_into_atom(expr!("foo" a b), bind!{ a: expr!("A"), c: expr!("C") })])]);
1801        let stack = Stack{ prev: None, atom, ret: no_handler, finished: false, vars: vars.clone(), depth: 1 };
1802
1803        let result = superpose_bind(stack, bind!{ b: expr!("B"), d: expr!("D") });
1804
1805        assert_eq!(result, vec![InterpretedAtom(
1806                Stack{ prev: None, atom: expr!("foo" a b), ret: no_handler, finished: true, vars: Variables::new(), depth: 1 },
1807                bind!{ a: expr!("A"), b: expr!("B"), c: expr!("C"), d: expr!("D") }
1808        )]);
1809    }
1810
1811    #[test]
1812    fn interpret_function_error_on_no_return() {
1813        let result = call_interpret(space(""), &metta_atom("(function (SomeValue))"));
1814        assert_eq!(result, vec![metta_atom("(Error (SomeValue) NoReturn)")]);
1815
1816        let result = call_interpret(space("
1817            (= (foo) (function (SomeValue)))
1818        "), &metta_atom("(eval (foo))"));
1819        assert_eq!(result, vec![metta_atom("(Error (foo) NoReturn)")]);
1820    }
1821
1822    #[test]
1823    fn metta_turing_machine() {
1824        let space = space("
1825            (= (tm $rule $state $tape)
1826              (function (eval (tm-body $rule $state $tape))) )
1827
1828            (= (tm-body $rule $state $tape)
1829              (unify $state HALT
1830                (return $tape)
1831                (chain (eval (read $tape)) $char
1832                  (chain (eval ($rule $state $char)) $res
1833                    (unify $res ($next-state $next-char $dir)
1834                      (chain (eval (move $tape $next-char $dir)) $next-tape
1835                        (eval (tm-body $rule $next-state $next-tape)) )
1836                      (return (Error (tm-body $rule $state $tape) \"Incorrect state\")) )))))
1837
1838            (= (read ($head $hole $tail)) $hole)
1839
1840            (= (move ($head $hole $tail) $char N) ($head $char $tail))
1841            (= (move ($head $hole $tail) $char L) (function
1842              (chain (cons-atom $char $head) $next-head
1843                (chain (decons-atom $tail) $list
1844                  (unify $list ($next-hole $next-tail)
1845                    (return ($next-head $next-hole $next-tail))
1846                    (return ($next-head 0 ())) )))))
1847            (= (move ($head $hole $tail) $char R) (function
1848              (chain (cons-atom $char $tail) $next-tail
1849                (chain (decons-atom $head) $list
1850                  (unify $list ($next-hole $next-head)
1851                    (return ($next-head $next-hole $next-tail))
1852                    (return (() 0 $next-tail)) )))))
1853
1854            (= (busy-beaver A 0) (B 1 R))
1855            (= (busy-beaver A 1) (C 1 L))
1856
1857            (= (busy-beaver B 0) (A 1 L))
1858            (= (busy-beaver B 1) (B 1 R))
1859
1860            (= (busy-beaver C 0) (B 1 L))
1861            (= (busy-beaver C 1) (HALT 1 N))
1862
1863        ");
1864        let result = interpret(space, &metta_atom("(eval (tm busy-beaver A (() 0 ())))"));
1865        assert_eq!(result, Ok(vec![metta_atom("((1 1) 1 (1 1 1))")]));
1866    }
1867
1868    #[test]
1869    fn interpret_minimal_metta_smoketest() {
1870        let space = space("
1871            (= (foo $a B) $a)
1872            (= (fu $x) (function (chain (eval (foo $x B)) $r (return $r))))
1873            (= (color) red)
1874            (= (color) green)
1875            (= (color) blue)
1876        ");
1877        let result = interpret(space.clone(), &metta_atom("(chain (chain A $x $x) $y $y)"));
1878        assert_eq!(result, Ok(vec![metta_atom("A")]));
1879        let result = interpret(space.clone(), &metta_atom("(chain (chain (eval (foo A $b)) $x (bar $x)) $y (baz $y))"));
1880        assert_eq!(result, Ok(vec![metta_atom("(baz (bar A))")]));
1881        let result = interpret(space.clone(), &metta_atom("(chain (chain (eval (fu A)) $x (bar $x)) $y (baz $y))"));
1882        assert_eq!(result, Ok(vec![metta_atom("(baz (bar A))")]));
1883        let result = interpret(space.clone(), &metta_atom("(unify (A $b) ($a B) ($a $b) Empty)"));
1884        assert_eq!(result, Ok(vec![metta_atom("(A B)")]));
1885        let result = interpret(space.clone(), &metta_atom("(decons-atom (a b c))"));
1886        assert_eq!(result, Ok(vec![metta_atom("(a (b c))")]));
1887        let result = interpret(space.clone(), &metta_atom("(cons-atom a (b c))"));
1888        assert_eq!(result, Ok(vec![metta_atom("(a b c)")]));
1889        let result = interpret(space.clone(), &metta_atom("(chain (collapse-bind (eval (color))) $collapsed (superpose-bind $collapsed))")).unwrap();
1890        assert_eq_no_order!(result, vec![metta_atom("red"), metta_atom("green"), metta_atom("blue")]);
1891        let result = interpret(space.clone(), &metta_atom("((P $a B) $a)"));
1892        assert_eq!(result, Ok(vec![metta_atom("((P $a B) $a)")]));
1893        let result = interpret(space.clone(), &metta_atom("(collapse-bind (eval (color)))")).unwrap();
1894        assert_eq!(result.len(), 1);
1895        assert_eq_no_order!(atom_as_slice(&result[0]).unwrap(), [
1896            atom_bindings_into_atom(expr!("red"), bind!{}),
1897            atom_bindings_into_atom(expr!("green"), bind!{}),
1898            atom_bindings_into_atom(expr!("blue"), bind!{})
1899        ]);
1900    }
1901
1902    fn space(text: &str) -> DynSpace {
1903        metta_space(text)
1904    }
1905
1906    fn call_interpret(space: DynSpace, atom: &Atom) -> Vec<Atom> {
1907        let _ = env_logger::builder().is_test(true).try_init();
1908        let result = interpret(space, atom);
1909        assert!(result.is_ok());
1910        result.unwrap()
1911    }
1912
1913    #[derive(PartialEq, Clone, Debug)]
1914    struct ThrowError();
1915
1916    impl Grounded for ThrowError {
1917        fn type_(&self) -> Atom {
1918            expr!("->" "&str" "Error")
1919        }
1920        fn as_execute(&self) -> Option<&dyn CustomExecute> {
1921            Some(self)
1922        }
1923    }
1924
1925    impl CustomExecute for ThrowError {
1926        fn execute(&self, args: &[Atom]) -> Result<Vec<Atom>, ExecError> {
1927            Err((*args[0].as_gnd::<&str>().unwrap()).into())
1928        }
1929    }
1930
1931    impl Display for ThrowError {
1932        fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
1933            write!(f, "throw-error")
1934        }
1935    }
1936
1937    #[derive(PartialEq, Clone, Debug)]
1938    struct NonReducible();
1939
1940    impl Grounded for NonReducible {
1941        fn type_(&self) -> Atom {
1942            expr!("->" "u32" "u32")
1943        }
1944        fn as_execute(&self) -> Option<&dyn CustomExecute> {
1945            Some(self)
1946        }
1947    }
1948
1949    impl CustomExecute for NonReducible {
1950        fn execute(&self, _args: &[Atom]) -> Result<Vec<Atom>, ExecError> {
1951            Err(ExecError::NoReduce)
1952        }
1953    }
1954
1955    impl Display for NonReducible {
1956        fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
1957            write!(f, "non-reducible")
1958        }
1959    }
1960
1961    #[derive(PartialEq, Clone, Debug)]
1962    struct IncorrectArgument();
1963
1964    impl Grounded for IncorrectArgument {
1965        fn type_(&self) -> Atom {
1966            expr!("->" "u32" "u32")
1967        }
1968        fn as_execute(&self) -> Option<&dyn CustomExecute> {
1969            Some(self)
1970        }
1971    }
1972
1973    impl CustomExecute for IncorrectArgument {
1974        fn execute(&self, _args: &[Atom]) -> Result<Vec<Atom>, ExecError> {
1975            Err(ExecError::IncorrectArgument)
1976        }
1977    }
1978
1979    impl Display for IncorrectArgument {
1980        fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
1981            write!(f, "incorrect-argument")
1982        }
1983    }
1984
1985    #[derive(PartialEq, Clone, Debug)]
1986    struct MulXUndefinedType(i32);
1987
1988    impl Grounded for MulXUndefinedType {
1989        fn type_(&self) -> Atom {
1990            ATOM_TYPE_UNDEFINED
1991        }
1992        fn as_execute(&self) -> Option<&dyn CustomExecute> {
1993            Some(self)
1994        }
1995    }
1996
1997    impl CustomExecute for MulXUndefinedType {
1998        fn execute(&self, args: &[Atom]) -> Result<Vec<Atom>, ExecError> {
1999            Ok(vec![Atom::value(self.0 * args.get(0).unwrap().as_gnd::<i32>().unwrap())])
2000        }
2001    }
2002
2003    impl Display for MulXUndefinedType {
2004        fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
2005            write!(f, "x{}", self.0)
2006        }
2007    }
2008
2009    #[derive(PartialEq, Clone, Debug)]
2010    struct ReturnNothing();
2011
2012    impl Grounded for ReturnNothing {
2013        fn type_(&self) -> Atom {
2014            ATOM_TYPE_UNDEFINED
2015        }
2016        fn as_execute(&self) -> Option<&dyn CustomExecute> {
2017            Some(self)
2018        }
2019    }
2020
2021    impl CustomExecute for ReturnNothing {
2022        fn execute(&self, _args: &[Atom]) -> Result<Vec<Atom>, ExecError> {
2023            Ok(vec![])
2024        }
2025    }
2026
2027    impl Display for ReturnNothing {
2028        fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
2029            write!(f, "return-nothing")
2030        }
2031    }
2032
2033    #[test]
2034    fn interpret_duplicated_types() {
2035        let space = space("
2036            (: foo (-> A A))
2037            (: foo (-> A A))
2038            (: foo (-> Atom A))
2039            (: a A)
2040            (= (foo $x) a)
2041        ");
2042        let result = interpret(space.clone(), &Atom::expr([METTA_SYMBOL, expr!("foo" "a"), ATOM_TYPE_UNDEFINED, Atom::gnd(space)]));
2043        assert_eq!(result, Ok(vec![metta_atom("a")]));
2044    }
2045
2046    #[test]
2047    fn run_metta_using_context() {
2048        let outer = space("");
2049        let nested = space("
2050            (= (foo $x) $x)
2051        ");
2052        let result = interpret(outer, &Atom::expr([METTA_SYMBOL, expr!("foo" "a"), ATOM_TYPE_UNDEFINED, Atom::gnd(nested)]));
2053        assert_eq!(result, Ok(vec![metta_atom("a")]));
2054    }
2055
2056    #[test]
2057    fn interpret_stop_evaluation() {
2058        let space = space("
2059            (= (bar) X)
2060            (= (foo) (bar))
2061
2062            (: q (-> Atom Atom))
2063            (= (q $a) $a)
2064            (= (e $a) $a)
2065        ");
2066        let result = interpret(space.clone(), &Atom::expr([METTA_SYMBOL, expr!("q" ("foo")), ATOM_TYPE_UNDEFINED, Atom::gnd(space.clone())]));
2067        assert_eq!(result, Ok(vec![Atom::expr([Atom::sym("foo")])]));
2068        let result = interpret(space.clone(), &Atom::expr([METTA_SYMBOL, expr!("e" ("q" ("foo"))), ATOM_TYPE_UNDEFINED, Atom::gnd(space)]));
2069        assert_eq!(result, Ok(vec![Atom::sym("X")]));
2070    }
2071
2072    #[test]
2073    fn interpret_context_space() {
2074        let space = space("");
2075
2076        let result = interpret(space.clone(), &Atom::expr([CONTEXT_SPACE_SYMBOL]));
2077        assert_eq!(result, Ok(vec![Atom::gnd(space)]));
2078    }
2079
2080    #[test]
2081    fn interpret_variable_substitution() {
2082        let space = space("
2083            (: unify (-> Atom Atom Atom Atom %Undefined%))
2084            (= (foo) (bar))
2085            (= (bar) a)
2086        ");
2087        let result = interpret(space.clone(), 
2088            &Atom::expr([METTA_SYMBOL, 
2089                Atom::expr([UNIFY_SYMBOL, Atom::gnd(space.clone()), Atom::expr([EQUAL_SYMBOL, Atom::expr([Atom::sym("foo")]), Atom::var("x")]), Atom::var("x"), EMPTY_SYMBOL]),
2090                ATOM_TYPE_UNDEFINED,
2091                Atom::gnd(space)]));
2092
2093        assert_eq!(result, Ok(vec![metta_atom("a")]));
2094    }
2095
2096    #[test]
2097    fn interpret_variable_substitution_after_superpose() {
2098        let space = space("
2099            (= (foo a) xa)
2100            (= (foo b) xb)
2101        ");
2102        let result = interpret(space.clone(), &metta_atom("
2103            (chain (collapse-bind (eval (foo $a))) $collapsed
2104              (chain (superpose-bind $collapsed) $x
2105                ($x $a)))
2106        ")).unwrap();
2107        assert_eq_no_order!(result, vec![metta_atom("(xa a)"), metta_atom("(xb b)")]);
2108    }
2109
2110    #[derive(PartialEq, Clone, Debug)]
2111    struct EvalCounter(Rc<RefCell<i64>>);
2112
2113    impl Grounded for EvalCounter {
2114        fn type_(&self) -> Atom {
2115            expr!("->" ("->"))
2116        }
2117        fn as_execute(&self) -> Option<&dyn CustomExecute> {
2118            Some(self)
2119        }
2120    }
2121
2122    impl CustomExecute for EvalCounter {
2123        fn execute(&self, _args: &[Atom]) -> Result<Vec<Atom>, ExecError> {
2124            *self.0.borrow_mut() += 1;
2125            Err(ExecError::NoReduce)
2126        }
2127    }
2128
2129    impl Display for EvalCounter {
2130        fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
2131            write!(f, "increment-self")
2132        }
2133    }
2134
2135    #[test]
2136    fn interpret_evaluate_once() {
2137        let counter = EvalCounter(Rc::new(RefCell::new(0)));
2138        let space = space("
2139            (= (one $x) (two $x))
2140            (= (two $x) ok)
2141        ");
2142        let result = interpret(space.clone(), &Atom::expr([METTA_SYMBOL, expr!("one" ({counter.clone()})), ATOM_TYPE_UNDEFINED, Atom::gnd(space.clone())]));
2143        assert_eq!(result, Ok(vec![Atom::sym("ok")]));
2144        assert_eq!(*counter.0.borrow(), 1);
2145    }
2146
2147    #[test]
2148    fn type_check_returned_value() {
2149        let space = space("
2150            (: foo (-> Number))
2151            (= (foo) 1)
2152            (: bar (-> Bool))
2153            (= (bar) (foo))
2154        ");
2155        let result = interpret(space.clone(), &metta!((metta (bar) %Undefined% {space.clone()})));
2156        assert_eq!(result, Ok(vec![metta!((Error (foo) (BadType Bool Number)))]));
2157    }
2158
2159    #[test]
2160    fn test_incorrect_number_of_arguments_issue_1037() {
2161        let space = space("
2162            (: a A)
2163            (: b B)
2164            (: foo (-> A B))
2165        ");
2166
2167        let result = interpret(space.clone(), &metta!((metta (foo b c) %Undefined% {space.clone()})));
2168        assert_eq!(result, Ok(vec![metta!((Error (foo b c) IncorrectNumberOfArguments))]));
2169    }
2170}