use std::collections::HashMap;
use std::rc::Rc;
use std::result::Result;
use super::obj::{self, Name, Obj, Pair};
use super::parse::parse_stmt;
pub struct Ref {
name: &'static str,
}
impl ToString for Ref {
fn to_string(&self) -> String {
let mut s = String::with_capacity(self.name.len() + 11);
s.push_str("<function ");
s.push_str(self.name);
s.push('>');
return s;
}
}
pub trait Fun {
fn invoke(&self, list: &Obj) -> Result<Obj, String>;
}
pub trait FunMut {
fn invoke(&mut self, list: &Obj) -> Result<Obj, String>;
}
pub(crate) enum FnImpl<'f> {
Eval(fn(&mut Frame, &Obj) -> Result<Obj, String>),
Fn(fn(&Obj) -> Result<Obj, String>),
Fun(&'f Fun),
FunMut(&'f mut FunMut),
}
pub(crate) struct FnEntry<'f> {
pub imp: FnImpl<'f>,
obj: Obj,
}
pub struct Domain<'f> {
entries: HashMap<&'static str, FnEntry<'f>>,
}
impl<'f> Domain<'f> {
pub fn new() -> Self {
Domain {
entries: HashMap::new(),
}
}
pub(crate) fn register_eval(
&mut self,
name: &'static str,
f: fn(&mut Frame, &Obj) -> Result<Obj, String>,
) {
self.entries.insert(
name,
FnEntry {
imp: FnImpl::Eval(f),
obj: Rc::new(Ref { name: name }),
},
);
}
pub fn register(&mut self, name: &'static str, f: fn(&Obj) -> Result<Obj, String>) {
self.entries.insert(
name,
FnEntry {
imp: FnImpl::Fn(f),
obj: Rc::new(Ref { name: name }),
},
);
}
pub fn register_fun(&mut self, name: &'static str, f: &'f Fun) {
self.entries.insert(
name,
FnEntry {
imp: FnImpl::Fun(f),
obj: Rc::new(Ref { name: name }),
},
);
}
pub fn register_fun_mut(&mut self, name: &'static str, f: &'f mut FunMut) {
self.entries.insert(
name,
FnEntry {
imp: FnImpl::FunMut(f),
obj: Rc::new(Ref { name: name }),
},
);
}
}
#[derive(Clone)]
pub struct Binding {
pub name: String,
pub value: Obj,
}
#[derive(Clone)]
pub struct State {
pub env: Obj,
pub result: Binding,
}
impl State {
pub fn new() -> Self {
let nil = obj::nil();
State {
env: obj::pair(
obj::pair(obj::name("_".to_string()), nil.clone()),
nil.clone(),
),
result: Binding {
name: "".to_string(),
value: nil,
},
}
}
}
pub(crate) struct Frame<'m, 'f> {
pub domain: &'m mut Domain<'f>,
pub env: Obj,
}
impl<'m, 'f> Frame<'m, 'f> {
pub fn lookup_ref(&mut self, fref: &Ref) -> Option<&mut FnEntry<'f>> {
self.domain.entries.get_mut(fref.name)
}
}
pub fn eval_stmt<'m>(domain: &'m mut Domain, state: State, s: &str) -> Result<State, String> {
if s.trim_start().len() == 0 {
return Ok(State {
env: state.env,
result: Binding {
name: "".to_string(),
value: obj::nil(),
},
});
}
let (stmt, paren) = parse_stmt(s)?;
let mut var = "_".to_string();
let mut frame = Frame {
domain: domain,
env: state.env.clone(),
};
let value = if paren {
eval_expr(&mut frame, &stmt)
} else {
let head = stmt.downcast_ref::<Pair>().unwrap();
if let Some(name) = head.0.downcast_ref::<Name>() {
if name.0.starts_with("!") {
if name.0.len() == 1 {
var = choose_name(&frame);
} else {
var = name.0[1..].to_string();
}
if let Some(tail) = head.1.downcast_ref::<Pair>() {
if tail.0.is::<Pair>() && tail.1.is::<()>() {
eval_expr(&mut frame, &tail.0)
} else {
eval_toplevel_expr(&mut frame, &head.1)
}
} else {
eval_name(&mut frame, "_")
}
} else {
eval_toplevel_expr(&mut frame, &stmt)
}
} else {
eval_toplevel_expr(&mut frame, &stmt)
}
}?;
let mut new = obj::pair(
obj::pair(obj::name(var.to_string()), value.clone()),
cdr(&state.env).clone(),
);
if var != "_" {
new = obj::pair(car(&state.env).clone(), new);
}
Ok(State {
env: new,
result: Binding {
name: var.to_string(),
value: value,
},
})
}
fn eval_toplevel_expr(frame: &mut Frame, list_obj: &Obj) -> Result<Obj, String> {
if let Some(result) = eval_call(frame, list_obj.downcast_ref::<Pair>().unwrap()) {
result
} else {
let list_obj = eval_args(frame, list_obj)?;
let list = list_obj.downcast_ref::<Pair>().unwrap();
if list.1.is::<()>() {
Ok(list.0.clone())
} else {
Ok(list_obj)
}
}
}
pub(crate) fn eval_expr(frame: &mut Frame, expr: &Obj) -> Result<Obj, String> {
if let Some(pair) = expr.downcast_ref::<Pair>() {
match eval_call(frame, pair) {
Some(result) => result,
None => expected_function(),
}
} else if let Some(name) = expr.downcast_ref::<Name>() {
eval_name(frame, &name.0)
} else {
Ok(expr.clone())
}
}
fn eval_call(frame: &mut Frame, list: &Pair) -> Option<Result<Obj, String>> {
match eval_expr(frame, &list.0) {
Ok(x) => {
let fref = x.downcast_ref::<Ref>()?;
Some(if let Some(entry) = frame.domain.entries.get(fref.name) {
if let FnImpl::Eval(f) = entry.imp {
f(frame, &list.1)
} else {
match eval_args(frame, &list.1) {
Ok(args) => match frame.lookup_ref(fref).unwrap().imp {
FnImpl::Fn(f) => f(&args),
FnImpl::Fun(ref f) => f.invoke(&args),
FnImpl::FunMut(ref mut f) => f.invoke(&args),
_ => panic!(),
},
Err(msg) => Err(msg),
}
}
} else {
missing_function()
})
}
Err(msg) => Some(Err(msg)),
}
}
fn eval_args(frame: &mut Frame, list: &Obj) -> Result<Obj, String> {
Ok(if let Some(pair) = list.downcast_ref::<Pair>() {
let car = eval_expr(frame, &pair.0)?;
let cdr = eval_args(frame, &pair.1)?;
obj::pair(car, cdr)
} else {
list.clone()
})
}
fn eval_name(frame: &Frame, s: &str) -> Result<Obj, String> {
let mut level = &frame.env.clone();
loop {
let binding = car(level).clone();
if car(&binding).downcast_ref::<Name>().unwrap().0 == s {
return Ok(cdr(&binding).clone());
}
level = cdr(&level);
if level.is::<()>() {
break;
}
}
if let Some(x) = frame.domain.entries.get(s) {
return Ok(x.obj.clone());
}
Err(s.to_string())
}
fn choose_name<'m, 'f>(frame: &Frame<'m, 'f>) -> String {
for i in 1.. {
let mut s = String::new();
s.push('$');
s.push_str(&i.to_string());
if !eval_name(frame, &s).is_ok() {
return s;
}
}
panic!();
}
pub(crate) fn expected_function() -> Result<Obj, String> {
Err("not a function".to_string())
}
pub(crate) fn missing_function() -> Result<Obj, String> {
Err("function implementation is missing".to_string())
}
fn car(pair: &Obj) -> &Obj {
&pair.downcast_ref::<Pair>().unwrap().0
}
fn cdr(pair: &Obj) -> &Obj {
&pair.downcast_ref::<Pair>().unwrap().1
}
#[cfg(test)]
mod tests {
use super::*;
fn eval<'m>(s: &str, d: &'m mut Domain) -> Obj {
eval_stmt(d, State::new(), s).unwrap().result.value.clone()
}
struct Id;
impl Fun for Id {
fn invoke(&self, args: &Obj) -> Result<Obj, String> {
if let Some(pair) = args.downcast_ref::<Pair>() {
if pair.1.is::<()>() {
return Ok(pair.0.clone());
}
}
Err("id: wrong number of arguments".to_string())
}
}
struct Greet {
greetings: i32,
}
impl FunMut for Greet {
fn invoke(&mut self, args: &Obj) -> Result<Obj, String> {
if let Some(pair) = args.downcast_ref::<Pair>() {
if let Some(b) = pair.0.downcast_ref::<bool>() {
if *b {
self.greetings += 1;
return Ok(obj::string("hello, world".to_string()));
}
}
}
Ok(obj::nil())
}
}
#[test]
fn test_eval_stmt() {
let id = Id {};
let mut greet1 = Greet { greetings: 0 };
let mut greet2 = Greet { greetings: 0 };
let mut d = Domain::new();
d.register_fun("id", &id);
d.register_fun_mut("greet-1", &mut greet1);
d.register_fun_mut("greet-2", &mut greet2);
assert_eq!(*eval("id 123", &mut d).downcast_ref::<i64>().unwrap(), 123);
assert_eq!(
*eval("(greet-1 true)", &mut d)
.downcast_ref::<String>()
.unwrap(),
"hello, world"
);
assert_eq!(
*eval("id (id -123)", &mut d).downcast_ref::<i64>().unwrap(),
-123
);
eval("(greet-2 true)", &mut d);
eval("(greet-2 true)", &mut d);
eval("(greet-2 false)", &mut d);
assert_eq!(greet1.greetings, 1);
assert_eq!(greet2.greetings, 2);
}
#[test]
fn test_eval_list() {
let r = eval("1", &mut Domain::new());
assert_eq!(*r.downcast_ref::<i64>().unwrap(), 1);
let r = eval("2 3", &mut Domain::new());
let head = r.downcast_ref::<Pair>().unwrap();
assert_eq!(*head.0.downcast_ref::<i64>().unwrap(), 2);
let tail = head.1.downcast_ref::<Pair>().unwrap();
assert_eq!(*tail.0.downcast_ref::<i64>().unwrap(), 3);
tail.1.downcast_ref::<()>().unwrap();
let r = eval("4 5 6", &mut Domain::new());
let head = r.downcast_ref::<Pair>().unwrap();
assert_eq!(*head.0.downcast_ref::<i64>().unwrap(), 4);
let mid = head.1.downcast_ref::<Pair>().unwrap();
assert_eq!(*mid.0.downcast_ref::<i64>().unwrap(), 5);
let tail = mid.1.downcast_ref::<Pair>().unwrap();
assert_eq!(*tail.0.downcast_ref::<i64>().unwrap(), 6);
tail.1.downcast_ref::<()>().unwrap();
}
#[test]
fn test_state() {
let id = Id {};
let mut d = Domain::new();
d.register_fun("id", &id);
let s = State::new();
let s = eval_stmt(&mut d, s, "!x id true").unwrap();
let s = eval_stmt(&mut d, s, "id x").unwrap();
assert_eq!(s.result.value.downcast_ref::<bool>().unwrap().clone(), true);
let s = eval_stmt(&mut d, s, "x").unwrap();
assert_eq!(s.result.value.downcast_ref::<bool>().unwrap().clone(), true);
let s = eval_stmt(&mut d, s, "id _").unwrap();
assert_eq!(s.result.value.downcast_ref::<bool>().unwrap().clone(), true);
let s = eval_stmt(&mut d, s, "id 123").unwrap();
let s = eval_stmt(&mut d, s, "id _").unwrap();
assert_eq!(s.result.value.downcast_ref::<i64>().unwrap().clone(), 123);
let s = eval_stmt(&mut d, s, "!y").unwrap();
let s = eval_stmt(&mut d, s, "id y").unwrap();
assert_eq!(s.result.value.downcast_ref::<i64>().unwrap().clone(), 123);
let s = eval_stmt(&mut d, s, r#"id "abc""#).unwrap();
let s = eval_stmt(&mut d, s, "!").unwrap();
let s = eval_stmt(&mut d, s, "id $1").unwrap();
assert_eq!(
s.result.value.downcast_ref::<String>().unwrap().clone(),
"abc"
);
let s = eval_stmt(&mut d, s, "id x").unwrap();
assert_eq!(s.result.value.downcast_ref::<bool>().unwrap().clone(), true);
let s = eval_stmt(&mut d, s, "id y").unwrap();
assert_eq!(s.result.value.downcast_ref::<i64>().unwrap().clone(), 123);
let s = eval_stmt(&mut d, s, "id $1").unwrap();
assert_eq!(
s.result.value.downcast_ref::<String>().unwrap().clone(),
"abc"
);
assert!(eval_stmt(&mut d, s.clone(), "(!z id false)").is_err());
let s = eval_stmt(&mut d, s, "!z id false").unwrap();
let s = eval_stmt(&mut d, s, "(id z)").unwrap();
assert_eq!(
s.result.value.downcast_ref::<bool>().unwrap().clone(),
false
);
let s = eval_stmt(&mut d, s, "(id z)").unwrap();
let s = eval_stmt(&mut d, s, "(id _)").unwrap();
assert_eq!(
s.result.value.downcast_ref::<bool>().unwrap().clone(),
false
);
let s = eval_stmt(&mut d, s, "!-- id (id 555)").unwrap();
let s = eval_stmt(&mut d, s, "id --").unwrap();
assert_eq!(s.result.value.downcast_ref::<i64>().unwrap().clone(), 555);
let s = eval_stmt(&mut d, s, "!$3 id 3").unwrap();
let s = eval_stmt(&mut d, s, "id $3").unwrap();
assert_eq!(s.result.value.downcast_ref::<i64>().unwrap().clone(), 3);
let s = eval_stmt(&mut d, s, "! id 2").unwrap();
let s = eval_stmt(&mut d, s, "id $2").unwrap();
assert_eq!(s.result.value.downcast_ref::<i64>().unwrap().clone(), 2);
let s = eval_stmt(&mut d, s, "! id 4").unwrap();
let s = eval_stmt(&mut d, s, "id $4").unwrap();
assert_eq!(s.result.value.downcast_ref::<i64>().unwrap().clone(), 4);
}
}