DISABLE FDs (REMOVE ME).
[jackhill/mal.git] / rust / step2_eval.rs
1 use std::rc::Rc;
2 //use std::collections::HashMap;
3 use fnv::FnvHashMap;
4
5 #[macro_use]
6 extern crate lazy_static;
7 extern crate fnv;
8 extern crate itertools;
9 extern crate regex;
10
11 extern crate rustyline;
12 use rustyline::error::ReadlineError;
13 use rustyline::Editor;
14
15 #[macro_use]
16 #[allow(dead_code)]
17 mod types;
18 use crate::types::MalErr::ErrString;
19 use crate::types::MalVal::{Hash, Int, List, Nil, Sym, Vector};
20 use crate::types::{error, format_error, func, MalArgs, MalErr, MalRet, MalVal};
21 mod printer;
22 mod reader;
23 // TODO: figure out a way to avoid including env
24 #[allow(dead_code)]
25 mod env;
26
27 pub type Env = FnvHashMap<String, MalVal>;
28
29 // read
30 fn read(str: &str) -> MalRet {
31 reader::read_str(str.to_string())
32 }
33
34 // eval
35 fn eval_ast(ast: &MalVal, env: &Env) -> MalRet {
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 }
64 }
65
66 fn eval(ast: MalVal, env: Env) -> MalRet {
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 }
79 }
80 _ => eval_ast(&ast, &env),
81 }
82 }
83
84 // print
85 fn print(ast: &MalVal) -> String {
86 ast.pr_str(true)
87 }
88
89 fn rep(str: &str, env: &Env) -> Result<String, MalErr> {
90 let ast = read(str)?;
91 let exp = eval(ast, env.clone())?;
92 Ok(print(&exp))
93 }
94
95 fn 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 }
100 }
101
102 fn main() {
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 }
108
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)));
114
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 }
134 }
135 }
136 }