Commit | Line | Data |
---|---|---|
4ef4b17c JM |
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; | |
4ef4b17c | 8 | extern crate fnv; |
4a649b37 RF |
9 | extern crate itertools; |
10 | extern crate regex; | |
4ef4b17c JM |
11 | |
12 | extern crate rustyline; | |
13 | use rustyline::error::ReadlineError; | |
14 | use rustyline::Editor; | |
15 | ||
16 | #[macro_use] | |
17 | mod types; | |
c9b18677 GL |
18 | use crate::types::MalVal::{Bool, Func, Hash, List, MalFunc, Nil, Sym, Vector}; |
19 | use crate::types::{error, format_error, MalArgs, MalErr, MalRet, MalVal}; | |
4ef4b17c | 20 | mod env; |
4a649b37 RF |
21 | mod printer; |
22 | mod reader; | |
c9b18677 | 23 | use crate::env::{env_bind, env_get, env_new, env_set, env_sets, Env}; |
4ef4b17c JM |
24 | #[macro_use] |
25 | mod core; | |
26 | ||
27 | // read | |
28 | fn read(str: &str) -> MalRet { | |
4a649b37 | 29 | reader::read_str(str.to_string()) |
4ef4b17c JM |
30 | } |
31 | ||
32 | // eval | |
33 | fn eval_ast(ast: &MalVal, env: &Env) -> MalRet { | |
4a649b37 RF |
34 | match ast { |
35 | Sym(_) => Ok(env_get(&env, &ast)?), | |
36 | List(v, _) => { | |
37 | let mut lst: MalArgs = vec![]; | |
38 | for a in v.iter() { | |
39 | lst.push(eval(a.clone(), env.clone())?) | |
40 | } | |
41 | Ok(list!(lst)) | |
42 | } | |
43 | Vector(v, _) => { | |
44 | let mut lst: MalArgs = vec![]; | |
45 | for a in v.iter() { | |
46 | lst.push(eval(a.clone(), env.clone())?) | |
47 | } | |
48 | Ok(vector!(lst)) | |
49 | } | |
50 | Hash(hm, _) => { | |
51 | let mut new_hm: FnvHashMap<String, MalVal> = FnvHashMap::default(); | |
52 | for (k, v) in hm.iter() { | |
53 | new_hm.insert(k.to_string(), eval(v.clone(), env.clone())?); | |
54 | } | |
55 | Ok(Hash(Rc::new(new_hm), Rc::new(Nil))) | |
56 | } | |
57 | _ => Ok(ast.clone()), | |
58 | } | |
4ef4b17c JM |
59 | } |
60 | ||
61 | fn eval(mut ast: MalVal, mut env: Env) -> MalRet { | |
4a649b37 | 62 | let ret: MalRet; |
4ef4b17c | 63 | |
4a649b37 RF |
64 | 'tco: loop { |
65 | ret = match ast.clone() { | |
66 | List(l, _) => { | |
67 | if l.len() == 0 { | |
68 | return Ok(ast); | |
69 | } | |
70 | let a0 = &l[0]; | |
71 | match a0 { | |
72 | Sym(ref a0sym) if a0sym == "def!" => { | |
73 | env_set(&env, l[1].clone(), eval(l[2].clone(), env.clone())?) | |
74 | } | |
75 | Sym(ref a0sym) if a0sym == "let*" => { | |
76 | env = env_new(Some(env.clone())); | |
77 | let (a1, a2) = (l[1].clone(), l[2].clone()); | |
78 | match a1 { | |
79 | List(ref binds, _) | Vector(ref binds, _) => { | |
80 | for (b, e) in binds.iter().tuples() { | |
81 | match b { | |
82 | Sym(_) => { | |
83 | let _ = env_set( | |
84 | &env, | |
85 | b.clone(), | |
86 | eval(e.clone(), env.clone())?, | |
87 | ); | |
88 | } | |
89 | _ => { | |
90 | return error("let* with non-Sym binding"); | |
91 | } | |
92 | } | |
93 | } | |
94 | } | |
95 | _ => { | |
96 | return error("let* with non-List bindings"); | |
97 | } | |
98 | }; | |
99 | ast = a2; | |
100 | continue 'tco; | |
101 | } | |
102 | Sym(ref a0sym) if a0sym == "do" => { | |
103 | match eval_ast(&list!(l[1..l.len() - 1].to_vec()), &env)? { | |
104 | List(_, _) => { | |
105 | ast = l.last().unwrap_or(&Nil).clone(); | |
106 | continue 'tco; | |
107 | } | |
108 | _ => error("invalid do form"), | |
109 | } | |
110 | } | |
111 | Sym(ref a0sym) if a0sym == "if" => { | |
112 | let cond = eval(l[1].clone(), env.clone())?; | |
113 | match cond { | |
114 | Bool(false) | Nil if l.len() >= 4 => { | |
115 | ast = l[3].clone(); | |
116 | continue 'tco; | |
117 | } | |
118 | Bool(false) | Nil => Ok(Nil), | |
119 | _ if l.len() >= 3 => { | |
120 | ast = l[2].clone(); | |
121 | continue 'tco; | |
122 | } | |
123 | _ => Ok(Nil), | |
124 | } | |
125 | } | |
126 | Sym(ref a0sym) if a0sym == "fn*" => { | |
127 | let (a1, a2) = (l[1].clone(), l[2].clone()); | |
128 | Ok(MalFunc { | |
129 | eval: eval, | |
130 | ast: Rc::new(a2), | |
131 | env: env, | |
132 | params: Rc::new(a1), | |
133 | is_macro: false, | |
134 | meta: Rc::new(Nil), | |
135 | }) | |
136 | } | |
137 | _ => match eval_ast(&ast, &env)? { | |
138 | List(ref el, _) => { | |
139 | let ref f = el[0].clone(); | |
140 | let args = el[1..].to_vec(); | |
141 | match f { | |
142 | Func(_, _) => f.apply(args), | |
143 | MalFunc { | |
144 | ast: mast, | |
145 | env: menv, | |
146 | params, | |
147 | .. | |
148 | } => { | |
149 | let a = &**mast; | |
150 | let p = &**params; | |
151 | env = env_bind(Some(menv.clone()), p.clone(), args)?; | |
152 | ast = a.clone(); | |
153 | continue 'tco; | |
154 | } | |
155 | _ => error("attempt to call non-function"), | |
156 | } | |
157 | } | |
158 | _ => error("expected a list"), | |
159 | }, | |
4ef4b17c | 160 | } |
4ef4b17c | 161 | } |
4a649b37 RF |
162 | _ => eval_ast(&ast, &env), |
163 | }; | |
4ef4b17c | 164 | |
4a649b37 RF |
165 | break; |
166 | } // end 'tco loop | |
4ef4b17c | 167 | |
4a649b37 | 168 | ret |
4ef4b17c JM |
169 | } |
170 | ||
171 | ||
172 | fn print(ast: &MalVal) -> String { | |
4a649b37 | 173 | ast.pr_str(true) |
4ef4b17c JM |
174 | } |
175 | ||
4a649b37 RF |
176 | fn rep(str: &str, env: &Env) -> Result<String, MalErr> { |
177 | let ast = read(str)?; | |
178 | let exp = eval(ast, env.clone())?; | |
179 | Ok(print(&exp)) | |
4ef4b17c JM |
180 | } |
181 | ||
182 | fn main() { | |
4a649b37 RF |
183 | // `()` can be used when no completer is required |
184 | let mut rl = Editor::<()>::new(); | |
185 | if rl.load_history(".mal-history").is_err() { | |
186 | eprintln!("No previous history."); | |
187 | } | |
4ef4b17c | 188 | |
4a649b37 RF |
189 | // core.rs: defined using rust |
190 | let repl_env = env_new(None); | |
191 | for (k, v) in core::ns() { | |
192 | env_sets(&repl_env, k, v); | |
193 | } | |
4ef4b17c | 194 | |
4a649b37 RF |
195 | // core.mal: defined using the language itself |
196 | let _ = rep("(def! not (fn* (a) (if a false true)))", &repl_env); | |
197 | ||
198 | // main repl loop | |
199 | loop { | |
200 | let readline = rl.readline("user> "); | |
201 | match readline { | |
202 | Ok(line) => { | |
203 | rl.add_history_entry(&line); | |
204 | rl.save_history(".mal-history").unwrap(); | |
205 | if line.len() > 0 { | |
206 | match rep(&line, &repl_env) { | |
207 | Ok(out) => println!("{}", out), | |
208 | Err(e) => println!("Error: {}", format_error(e)), | |
209 | } | |
210 | } | |
211 | } | |
212 | Err(ReadlineError::Interrupted) => continue, | |
213 | Err(ReadlineError::Eof) => break, | |
214 | Err(err) => { | |
215 | println!("Error: {:?}", err); | |
216 | break; | |
217 | } | |
4ef4b17c | 218 | } |
4ef4b17c | 219 | } |
4ef4b17c | 220 | } |