use std::borrow::Cow;
use std::borrow::Cow::*;
use crate::{
elementary_functions::{car, cdr, cons},
list_functions::assoc_v,
types::{Atom, NullableList, SExpression, Symbol},
};
use super::special_form_glue::*;
pub static mut TRACKLIST: Vec<Atom> = Vec::new();
pub fn eval(e: SExpression, a: &NullableList) -> Option<(SExpression, Cow<'_, NullableList>)> {
match e {
SExpression::Atom(e_atom) => match a.clone() {
NullableList::List(a_list) => assoc_v(e_atom.clone(), a_list).map_or_else(
|| Some((e_atom.into(), Borrowed(a))),
|e| Some((e, Borrowed(a))),
),
NullableList::NIL => Some((e_atom.into(), Borrowed(a))),
},
SExpression::List(e_list) => match car(e_list.clone()) {
SExpression::Atom(function) => {
unsafe {
if TRACKLIST.contains(&function) {
log::info!("ENTERING {e_list}");
}
}
match function.clone() {
Atom::Symbol(Symbol::SpecialForm(s)) => s.eval(e_list.clone(), a),
Atom::Symbol(Symbol::ElementaryFunction(f)) => {
f.eval(e_list.clone(), a).map(|(e, a)| (e, Borrowed(a)))
}
Atom::Symbol(Symbol::BuiltinFunc(f)) => {
f.eval(e_list.clone(), a).map(|(e, a)| (e, Borrowed(a)))
}
Atom::Symbol(Symbol::Other(s)) => {
handle_other_symbol(s, e_list.clone(), a).map(|e| (e, Borrowed(a)))
}
_ => {
log::error!("Tried to use {} like a function", e_list);
None
}
}
.map(|v| {
unsafe {
if TRACKLIST.contains(&function) {
log::info!("END OF {e_list}, VALUE IS\n{}", v.0);
}
}
v
})
}
SExpression::List(compound_func) => match car(compound_func.clone()) {
SExpression::Atom(Atom::Symbol(Symbol::SpecialForm(sf))) => sf.eval(e_list, a),
SExpression::Atom(Atom::Number(_)) => {
log::error!("Tried to use a number like a special form: {}", e_list);
None
}
_ => {
let f = car(compound_func.clone());
let nested_func = eval(f.into(), a)?.0;
let nested_func_args = cdr(compound_func);
let (func, new_a) = eval(cons(nested_func, nested_func_args).into(), a)?;
let val = eval(cons(func, cdr(e_list)).into(), &new_a)?.0;
Some((val, Borrowed(a)))
}
},
},
}
}
#[cfg(test)]
mod elementary_functions_tests {
use crate::elementary_functions::cons;
use crate::list_macros::list;
use crate::types::{F, NIL, T};
use super::*;
#[test]
fn test_eval_car() {
assert_eq!(
eval(
list![
ElementaryFunction::CAR,
list![SpecialForm::QUOTE, list!["A", "B"]]
]
.into(),
&NIL.into()
),
Some(("A".into(), Borrowed(&NIL.into())))
)
}
#[test]
fn test_eval_cdr() {
assert_eq!(
eval(
list![
ElementaryFunction::CDR,
list![SpecialForm::QUOTE, list!["A", "B"]]
]
.into(),
&NIL.into()
),
Some((list!["B"].into(), Borrowed(&NIL.into())))
)
}
#[test]
fn test_eval_cons() {
assert_eq!(
eval(
list![
ElementaryFunction::CONS,
list![SpecialForm::QUOTE, "A"],
list![SpecialForm::QUOTE, "B"]
]
.into(),
&NIL.into()
),
Some((cons("A", "B").into(), Borrowed(&NIL.into())))
);
assert_eq!(
eval(
list![
ElementaryFunction::CONS,
list![SpecialForm::QUOTE, list!["A", "B"]],
list![SpecialForm::QUOTE, "C"]
]
.into(),
&NIL.into()
),
Some((cons(list!["A", "B"], "C").into(), Borrowed(&NIL.into())))
);
}
#[test]
fn test_eval_eq() {
assert_eq!(
eval(
list![
ElementaryFunction::EQ,
list![SpecialForm::QUOTE, "x"],
list![SpecialForm::QUOTE, "x"]
]
.into(),
&NIL.into()
),
Some((T.into(), Borrowed(&NIL.into())))
);
assert_eq!(
eval(
list![
ElementaryFunction::EQ,
list![SpecialForm::QUOTE, "x"],
list![SpecialForm::QUOTE, "y"]
]
.into(),
&NIL.into()
),
Some((F.into(), Borrowed(&NIL.into())))
);
let a: NullableList = list![cons("x", 1), cons("y", 1)].into();
assert_eq!(
eval(list![ElementaryFunction::EQ, "x", "y"].into(), &a),
Some((T.into(), Borrowed(&a)))
);
}
#[test]
fn test_eval_atom() {
assert_eq!(
eval(list![ElementaryFunction::ATOM, "x"].into(), &NIL.into()),
Some((T.into(), Borrowed(&NIL.into())))
);
assert_eq!(
eval(
list![
ElementaryFunction::ATOM,
list![SpecialForm::QUOTE, list![1, 2, 3]]
]
.into(),
&NIL.into()
),
Some((F.into(), Borrowed(&NIL.into())))
);
let a: NullableList = list![cons("x", list![1, 2, 3])].into();
assert_eq!(
eval(list![ElementaryFunction::ATOM, "x"].into(), &a),
Some((F.into(), Borrowed(&a)))
);
}
#[test]
fn test_eval_car_of_cons() {
assert_eq!(
eval(
list![
ElementaryFunction::CAR,
list![ElementaryFunction::CONS, 1, 2]
]
.into(),
&NIL.into()
),
Some((1.into(), Borrowed(&NIL.into())))
);
assert_eq!(
eval(
list![
ElementaryFunction::CAR,
list![
ElementaryFunction::CONS,
list![SpecialForm::QUOTE, "A"],
list![SpecialForm::QUOTE, "B"]
]
]
.into(),
&NIL.into()
),
Some(("A".into(), Borrowed(&NIL.into())))
);
assert_eq!(
eval(
list![
ElementaryFunction::CAR,
list![
ElementaryFunction::CONS,
list![
ElementaryFunction::CONS,
list![SpecialForm::QUOTE, "A"],
list![SpecialForm::QUOTE, "B"]
],
list![SpecialForm::QUOTE, "C"]
]
]
.into(),
&NIL.into()
),
Some((cons("A", "B").into(), Borrowed(&NIL.into())))
);
}
}
#[cfg(test)]
mod invalid_sexps_tests {
use super::*;
use crate::list;
use crate::types::NIL;
#[test]
fn test_atom_as_func() {
assert_eq!(eval(list![list![1]].into(), &NIL.into()), None);
assert_eq!(eval(list![list![T]].into(), &NIL.into()), None);
assert_eq!(eval(list![list![NIL]].into(), &NIL.into()), None);
assert_eq!(eval(list![list![1, 2]].into(), &NIL.into()), None);
}
}