1
1
mirror of https://github.com/kanaka/mal.git synced 2024-09-21 02:27:10 +03:00
mal/rust/step2_eval.rs
Joel Martin 4ef4b17cd0 rust: Update rust and update/refactor implementation
This rewrites the rust implementation to use many new features of the
current version of rust.

The refactor is much more concise (only 2/3rds the size) and switches
to using a lot of the more functional features (iterators, closures,
etc) that have been added or improved in rust.

Unfortunately, the implementation is a fair bit slower (about 30% on
perf3). It's not clear why this is the case but concision and being
more idiomatic wins over performance.
2018-07-13 17:21:00 -05:00

136 lines
3.3 KiB
Rust

use std::rc::Rc;
//use std::collections::HashMap;
use fnv::FnvHashMap;
#[macro_use]
extern crate lazy_static;
extern crate regex;
extern crate itertools;
extern crate fnv;
extern crate rustyline;
use rustyline::error::ReadlineError;
use rustyline::Editor;
#[macro_use]
#[allow(dead_code)]
mod types;
use types::{MalVal,MalArgs,MalRet,MalErr,error,format_error,func};
use types::MalVal::{Nil,Int,Sym,List,Vector,Hash};
use types::MalErr::{ErrString};
mod reader;
mod printer;
// TODO: figure out a way to avoid including env
#[allow(dead_code)]
mod env;
pub type Env = FnvHashMap<String,MalVal>;
// read
fn read(str: &str) -> MalRet {
reader::read_str(str.to_string())
}
// eval
fn eval_ast(ast: &MalVal, env: &Env) -> MalRet {
match ast {
Sym(sym) => {
Ok(env.get(sym)
.ok_or(ErrString(format!("'{}' not found", sym)))?
.clone())
},
List(v,_) => {
let mut lst: MalArgs = vec![];
for a in v.iter() { lst.push(eval(a.clone(), env.clone())?) }
Ok(list!(lst))
},
Vector(v,_) => {
let mut lst: MalArgs = vec![];
for a in v.iter() { lst.push(eval(a.clone(), env.clone())?) }
Ok(vector!(lst))
},
Hash(hm,_) => {
let mut new_hm: FnvHashMap<String,MalVal> = FnvHashMap::default();
for (k,v) in hm.iter() {
new_hm.insert(k.to_string(), eval(v.clone(), env.clone())?);
}
Ok(Hash(Rc::new(new_hm),Rc::new(Nil)))
},
_ => Ok(ast.clone()),
}
}
fn eval(ast: MalVal, env: Env) -> MalRet {
match ast.clone() {
List(l,_) => {
if l.len() == 0 { return Ok(ast); }
match eval_ast(&ast, &env)? {
List(ref el,_) => {
let ref f = el[0].clone();
f.apply(el[1..].to_vec())
},
_ => {
error("expected a list")
}
}
},
_ => eval_ast(&ast, &env),
}
}
// print
fn print(ast: &MalVal) -> String {
ast.pr_str(true)
}
fn rep(str: &str, env: &Env) -> Result<String,MalErr> {
let ast = read(str)?;
let exp = eval(ast, env.clone())?;
Ok(print(&exp))
}
fn int_op(op: fn(i64, i64) -> i64, a:MalArgs) -> MalRet {
match (a[0].clone(), a[1].clone()) {
(Int(a0), Int(a1)) => Ok(Int(op(a0,a1))),
_ => error("invalid int_op args"),
}
}
fn main() {
// `()` can be used when no completer is required
let mut rl = Editor::<()>::new();
if rl.load_history(".mal-history").is_err() {
println!("No previous history.");
}
let mut repl_env = Env::default();
repl_env.insert("+".to_string(), func(|a:MalArgs|{int_op(|i,j|{i+j},a)}));
repl_env.insert("-".to_string(), func(|a:MalArgs|{int_op(|i,j|{i-j},a)}));
repl_env.insert("*".to_string(), func(|a:MalArgs|{int_op(|i,j|{i*j},a)}));
repl_env.insert("/".to_string(), func(|a:MalArgs|{int_op(|i,j|{i/j},a)}));
loop {
let readline = rl.readline("user> ");
match readline {
Ok(line) => {
rl.add_history_entry(&line);
rl.save_history(".mal-history").unwrap();
if line.len() > 0 {
match rep(&line, &repl_env) {
Ok(out) => println!("{}", out),
Err(e) => println!("Error: {}", format_error(e)),
}
}
},
Err(ReadlineError::Interrupted) => continue,
Err(ReadlineError::Eof) => break,
Err(err) => {
println!("Error: {:?}", err);
break
}
}
}
}
// vim: ts=2:sw=2:expandtab