Merge pull request #383 from asarhaddon/ada2tco-do
[jackhill/mal.git] / rust / step3_env.rs
1 use std::rc::Rc;
2 //use std::collections::HashMap;
3 use fnv::FnvHashMap;
4 use itertools::Itertools;
5
6 #[macro_use]
7 extern crate lazy_static;
8 extern crate regex;
9 extern crate itertools;
10 extern crate fnv;
11
12 extern crate rustyline;
13 use rustyline::error::ReadlineError;
14 use rustyline::Editor;
15
16 #[macro_use]
17 #[allow(dead_code)]
18 mod types;
19 use types::{MalVal,MalArgs,MalRet,MalErr,error,format_error,func};
20 use types::MalVal::{Nil,Int,Sym,List,Vector,Hash};
21 mod reader;
22 mod printer;
23 mod env;
24 use env::{Env,env_new,env_get,env_set,env_sets};
25
26 // read
27 fn read(str: &str) -> MalRet {
28 reader::read_str(str.to_string())
29 }
30
31 // eval
32 fn eval_ast(ast: &MalVal, env: &Env) -> MalRet {
33 match ast {
34 Sym(_) => Ok(env_get(&env, &ast)?),
35 List(v,_) => {
36 let mut lst: MalArgs = vec![];
37 for a in v.iter() { lst.push(eval(a.clone(), env.clone())?) }
38 Ok(list!(lst))
39 },
40 Vector(v,_) => {
41 let mut lst: MalArgs = vec![];
42 for a in v.iter() { lst.push(eval(a.clone(), env.clone())?) }
43 Ok(vector!(lst))
44 },
45 Hash(hm,_) => {
46 let mut new_hm: FnvHashMap<String,MalVal> = FnvHashMap::default();
47 for (k,v) in hm.iter() {
48 new_hm.insert(k.to_string(), eval(v.clone(), env.clone())?);
49 }
50 Ok(Hash(Rc::new(new_hm),Rc::new(Nil)))
51 },
52 _ => Ok(ast.clone()),
53 }
54 }
55
56 fn eval(ast: MalVal, env: Env) -> MalRet {
57 match ast.clone() {
58 List(l,_) => {
59 if l.len() == 0 { return Ok(ast); }
60 let a0 = &l[0];
61 match a0 {
62 Sym(ref a0sym) if a0sym == "def!" => {
63 env_set(&env, l[1].clone(), eval(l[2].clone(), env.clone())?)
64 },
65 Sym(ref a0sym) if a0sym == "let*" => {
66 let let_env = env_new(Some(env.clone()));
67 let (a1, a2) = (l[1].clone(), l[2].clone());
68 match a1 {
69 List(ref binds,_) | Vector(ref binds,_) => {
70 for (b, e) in binds.iter().tuples() {
71 match b {
72 Sym(_) => {
73 let _ = env_set(&let_env, b.clone(),
74 eval(e.clone(), let_env.clone())?);
75 },
76 _ => {
77 return error("let* with non-Sym binding");
78 }
79 }
80 }
81 },
82 _ => {
83 return error("let* with non-List bindings");
84 }
85 };
86 eval(a2, let_env)
87 },
88 _ => {
89 match eval_ast(&ast, &env)? {
90 List(ref el,_) => {
91 let ref f = el[0].clone();
92 f.apply(el[1..].to_vec())
93 },
94 _ => {
95 error("expected a list")
96 }
97 }
98 }
99 }
100 },
101 _ => eval_ast(&ast, &env),
102 }
103 }
104
105 // print
106 fn print(ast: &MalVal) -> String {
107 ast.pr_str(true)
108 }
109
110 fn rep(str: &str, env: &Env) -> Result<String,MalErr> {
111 let ast = read(str)?;
112 let exp = eval(ast, env.clone())?;
113 Ok(print(&exp))
114 }
115
116 fn int_op(op: fn(i64, i64) -> i64, a:MalArgs) -> MalRet {
117 match (a[0].clone(), a[1].clone()) {
118 (Int(a0), Int(a1)) => Ok(Int(op(a0,a1))),
119 _ => error("invalid int_op args"),
120 }
121 }
122
123 fn main() {
124 // `()` can be used when no completer is required
125 let mut rl = Editor::<()>::new();
126 if rl.load_history(".mal-history").is_err() {
127 println!("No previous history.");
128 }
129
130 let repl_env = env_new(None);
131 env_sets(&repl_env, "+", func(|a:MalArgs|{int_op(|i,j|{i+j},a)}));
132 env_sets(&repl_env, "-", func(|a:MalArgs|{int_op(|i,j|{i-j},a)}));
133 env_sets(&repl_env, "*", func(|a:MalArgs|{int_op(|i,j|{i*j},a)}));
134 env_sets(&repl_env, "/", func(|a:MalArgs|{int_op(|i,j|{i/j},a)}));
135
136 loop {
137 let readline = rl.readline("user> ");
138 match readline {
139 Ok(line) => {
140 rl.add_history_entry(&line);
141 rl.save_history(".mal-history").unwrap();
142 if line.len() > 0 {
143 match rep(&line, &repl_env) {
144 Ok(out) => println!("{}", out),
145 Err(e) => println!("Error: {}", format_error(e)),
146 }
147 }
148 },
149 Err(ReadlineError::Interrupted) => continue,
150 Err(ReadlineError::Eof) => break,
151 Err(err) => {
152 println!("Error: {:?}", err);
153 break
154 }
155 }
156 }
157 }
158
159 // vim: ts=2:sw=2:expandtab