DISABLE FDs (REMOVE ME).
[jackhill/mal.git] / rust / step2_eval.rs
CommitLineData
4ef4b17c
JM
1use std::rc::Rc;
2//use std::collections::HashMap;
3use fnv::FnvHashMap;
4
5#[macro_use]
6extern crate lazy_static;
4ef4b17c 7extern crate fnv;
4a649b37
RF
8extern crate itertools;
9extern crate regex;
4ef4b17c
JM
10
11extern crate rustyline;
12use rustyline::error::ReadlineError;
13use rustyline::Editor;
14
15#[macro_use]
16#[allow(dead_code)]
17mod types;
c9b18677
GL
18use crate::types::MalErr::ErrString;
19use crate::types::MalVal::{Hash, Int, List, Nil, Sym, Vector};
20use crate::types::{error, format_error, func, MalArgs, MalErr, MalRet, MalVal};
4ef4b17c 21mod printer;
4a649b37 22mod reader;
4ef4b17c
JM
23// TODO: figure out a way to avoid including env
24#[allow(dead_code)]
25mod env;
26
4a649b37 27pub type Env = FnvHashMap<String, MalVal>;
4ef4b17c
JM
28
29// read
30fn read(str: &str) -> MalRet {
4a649b37 31 reader::read_str(str.to_string())
4ef4b17c
JM
32}
33
34// eval
35fn eval_ast(ast: &MalVal, env: &Env) -> MalRet {
4a649b37
RF
36 match ast {
37 Sym(sym) => Ok(env
38 .get(sym)
39 .ok_or(ErrString(format!("'{}' not found", sym)))?
40 .clone()),
41 List(v, _) => {
42 let mut lst: MalArgs = vec![];
43 for a in v.iter() {
44 lst.push(eval(a.clone(), env.clone())?)
45 }
46 Ok(list!(lst))
47 }
48 Vector(v, _) => {
49 let mut lst: MalArgs = vec![];
50 for a in v.iter() {
51 lst.push(eval(a.clone(), env.clone())?)
52 }
53 Ok(vector!(lst))
54 }
55 Hash(hm, _) => {
56 let mut new_hm: FnvHashMap<String, MalVal> = FnvHashMap::default();
57 for (k, v) in hm.iter() {
58 new_hm.insert(k.to_string(), eval(v.clone(), env.clone())?);
59 }
60 Ok(Hash(Rc::new(new_hm), Rc::new(Nil)))
61 }
62 _ => Ok(ast.clone()),
63 }
4ef4b17c
JM
64}
65
66fn eval(ast: MalVal, env: Env) -> MalRet {
4a649b37
RF
67 match ast.clone() {
68 List(l, _) => {
69 if l.len() == 0 {
70 return Ok(ast);
71 }
72 match eval_ast(&ast, &env)? {
73 List(ref el, _) => {
74 let ref f = el[0].clone();
75 f.apply(el[1..].to_vec())
76 }
77 _ => error("expected a list"),
78 }
4ef4b17c 79 }
4a649b37
RF
80 _ => eval_ast(&ast, &env),
81 }
4ef4b17c
JM
82}
83
84// print
85fn print(ast: &MalVal) -> String {
4a649b37 86 ast.pr_str(true)
4ef4b17c
JM
87}
88
4a649b37
RF
89fn rep(str: &str, env: &Env) -> Result<String, MalErr> {
90 let ast = read(str)?;
91 let exp = eval(ast, env.clone())?;
92 Ok(print(&exp))
4ef4b17c
JM
93}
94
4a649b37
RF
95fn int_op(op: fn(i64, i64) -> i64, a: MalArgs) -> MalRet {
96 match (a[0].clone(), a[1].clone()) {
97 (Int(a0), Int(a1)) => Ok(Int(op(a0, a1))),
98 _ => error("invalid int_op args"),
99 }
4ef4b17c
JM
100}
101
102fn main() {
4a649b37
RF
103 // `()` can be used when no completer is required
104 let mut rl = Editor::<()>::new();
105 if rl.load_history(".mal-history").is_err() {
106 eprintln!("No previous history.");
107 }
4ef4b17c 108
4a649b37
RF
109 let mut repl_env = Env::default();
110 repl_env.insert("+".to_string(), func(|a: MalArgs| int_op(|i, j| i + j, a)));
111 repl_env.insert("-".to_string(), func(|a: MalArgs| int_op(|i, j| i - j, a)));
112 repl_env.insert("*".to_string(), func(|a: MalArgs| int_op(|i, j| i * j, a)));
113 repl_env.insert("/".to_string(), func(|a: MalArgs| int_op(|i, j| i / j, a)));
4ef4b17c 114
4a649b37
RF
115 loop {
116 let readline = rl.readline("user> ");
117 match readline {
118 Ok(line) => {
119 rl.add_history_entry(&line);
120 rl.save_history(".mal-history").unwrap();
121 if line.len() > 0 {
122 match rep(&line, &repl_env) {
123 Ok(out) => println!("{}", out),
124 Err(e) => println!("Error: {}", format_error(e)),
125 }
126 }
127 }
128 Err(ReadlineError::Interrupted) => continue,
129 Err(ReadlineError::Eof) => break,
130 Err(err) => {
131 println!("Error: {:?}", err);
132 break;
133 }
4ef4b17c 134 }
4ef4b17c 135 }
4ef4b17c 136}