Merge pull request #445 from bjh21/bjh21-bbc-basic
[jackhill/mal.git] / rust / step7_quote.rs
CommitLineData
4ef4b17c
JM
1use std::rc::Rc;
2//use std::collections::HashMap;
3use fnv::FnvHashMap;
4use itertools::Itertools;
5
6#[macro_use]
7extern crate lazy_static;
8extern crate regex;
9extern crate itertools;
10extern crate fnv;
11
12extern crate rustyline;
13use rustyline::error::ReadlineError;
14use rustyline::Editor;
15
16#[macro_use]
17mod types;
18use types::{MalVal,MalArgs,MalRet,MalErr,error,format_error};
19use types::MalVal::{Nil,Bool,Str,Sym,List,Vector,Hash,Func,MalFunc};
20mod reader;
21mod printer;
22mod env;
23use env::{Env,env_new,env_bind,env_get,env_set,env_sets};
24#[macro_use]
25mod core;
26
27// read
28fn read(str: &str) -> MalRet {
29 reader::read_str(str.to_string())
30}
31
32// eval
33fn quasiquote(ast: &MalVal) -> MalVal {
34 match ast {
35 List(ref v,_) | Vector(ref v,_) if v.len() > 0 => {
36 let a0 = &v[0];
37 match a0 {
38 Sym(ref s) if s == "unquote" => v[1].clone(),
39 _ => {
40 match a0 {
41 List(ref v0,_) | Vector(ref v0,_) if v0.len() > 0 => {
42 match v0[0] {
43 Sym(ref s) if s == "splice-unquote" => {
44 list![Sym("concat".to_string()),
45 v0[1].clone(),
46 quasiquote(&list!(v[1..].to_vec()))]
47 },
48 _ => {
49 list![Sym("cons".to_string()),
50 quasiquote(a0),
51 quasiquote(&list!(v[1..].to_vec()))]
52 },
53 }
54 },
55 _ => {
56 list![Sym("cons".to_string()),
57 quasiquote(a0),
58 quasiquote(&list!(v[1..].to_vec()))]
59 }
60 }
61 }
62 }
63 },
64 _ => list![Sym("quote".to_string()), ast.clone()]
65 }
66}
67
68fn eval_ast(ast: &MalVal, env: &Env) -> MalRet {
69 match ast {
70 Sym(_) => Ok(env_get(&env, &ast)?),
71 List(v,_) => {
72 let mut lst: MalArgs = vec![];
73 for a in v.iter() { lst.push(eval(a.clone(), env.clone())?) }
74 Ok(list!(lst))
75 },
76 Vector(v,_) => {
77 let mut lst: MalArgs = vec![];
78 for a in v.iter() { lst.push(eval(a.clone(), env.clone())?) }
79 Ok(vector!(lst))
80 },
81 Hash(hm,_) => {
82 let mut new_hm: FnvHashMap<String,MalVal> = FnvHashMap::default();
83 for (k,v) in hm.iter() {
84 new_hm.insert(k.to_string(), eval(v.clone(), env.clone())?);
85 }
86 Ok(Hash(Rc::new(new_hm),Rc::new(Nil)))
87 },
88 _ => Ok(ast.clone()),
89 }
90}
91
92fn eval(mut ast: MalVal, mut env: Env) -> MalRet {
93 let ret: MalRet;
94
95 'tco: loop {
96
97 ret = match ast.clone() {
98 List(l,_) => {
99 if l.len() == 0 { return Ok(ast); }
100 let a0 = &l[0];
101 match a0 {
102 Sym(ref a0sym) if a0sym == "def!" => {
103 env_set(&env, l[1].clone(), eval(l[2].clone(), env.clone())?)
104 },
105 Sym(ref a0sym) if a0sym == "let*" => {
106 env = env_new(Some(env.clone()));
107 let (a1, a2) = (l[1].clone(), l[2].clone());
108 match a1 {
109 List(ref binds,_) | Vector(ref binds,_) => {
110 for (b, e) in binds.iter().tuples() {
111 match b {
112 Sym(_) => {
113 let _ = env_set(&env, b.clone(),
114 eval(e.clone(), env.clone())?);
115 },
116 _ => {
117 return error("let* with non-Sym binding");
118 }
119 }
120 }
121 },
122 _ => {
123 return error("let* with non-List bindings");
124 }
125 };
126 ast = a2;
127 continue 'tco;
128 },
129 Sym(ref a0sym) if a0sym == "quote" => {
130 Ok(l[1].clone())
131 },
132 Sym(ref a0sym) if a0sym == "quasiquote" => {
133 ast = quasiquote(&l[1]);
134 continue 'tco;
135 },
136 Sym(ref a0sym) if a0sym == "do" => {
137 match eval_ast(&list!(l[1..l.len()-1].to_vec()), &env)? {
138 List(_,_) => {
139 ast = l.last().unwrap_or(&Nil).clone();
140 continue 'tco;
141 },
142 _ => error("invalid do form"),
143 }
144 },
145 Sym(ref a0sym) if a0sym == "if" => {
146 let cond = eval(l[1].clone(), env.clone())?;
147 match cond {
148 Bool(false) | Nil if l.len() >= 4 => {
149 ast = l[3].clone();
150 continue 'tco;
151 },
152 Bool(false) | Nil => Ok(Nil),
153 _ if l.len() >= 3 => {
154 ast = l[2].clone();
155 continue 'tco;
156 },
157 _ => Ok(Nil)
158 }
159 },
160 Sym(ref a0sym) if a0sym == "fn*" => {
161 let (a1, a2) = (l[1].clone(), l[2].clone());
162 Ok(MalFunc{eval: eval, ast: Rc::new(a2), env: env,
163 params: Rc::new(a1), is_macro: false,
164 meta: Rc::new(Nil)})
165 },
166 Sym(ref a0sym) if a0sym == "eval" => {
167 ast = eval(l[1].clone(), env.clone())?;
168 while let Some(ref e) = env.clone().outer {
169 env = e.clone();
170 }
171 continue 'tco;
172 },
173 _ => {
174 match eval_ast(&ast, &env)? {
175 List(ref el,_) => {
176 let ref f = el[0].clone();
177 let args = el[1..].to_vec();
178 match f {
179 Func(_,_) => f.apply(args),
180 MalFunc{ast: mast, env: menv, params, ..} => {
181 let a = &**mast;
182 let p = &**params;
183 env = env_bind(Some(menv.clone()), p.clone(), args)?;
184 ast = a.clone();
185 continue 'tco;
186 },
187 _ => error("attempt to call non-function"),
188 }
189 },
190 _ => {
191 error("expected a list")
192 }
193 }
194 }
195 }
196 },
197 _ => eval_ast(&ast, &env),
198 };
199
200 break;
201
202 } // end 'tco loop
203
204 ret
205}
206
207// print
208fn print(ast: &MalVal) -> String {
209 ast.pr_str(true)
210}
211
212fn rep(str: &str, env: &Env) -> Result<String,MalErr> {
213 let ast = read(str)?;
214 let exp = eval(ast, env.clone())?;
215 Ok(print(&exp))
216}
217
218fn main() {
219 let mut args = std::env::args();
220 let arg1 = args.nth(1);
221
222 // `()` can be used when no completer is required
223 let mut rl = Editor::<()>::new();
224 if rl.load_history(".mal-history").is_err() {
6c4cc8ad 225 eprintln!("No previous history.");
4ef4b17c
JM
226 }
227
228 // core.rs: defined using rust
229 let repl_env = env_new(None);
230 for (k, v) in core::ns() {
231 env_sets(&repl_env, k, v);
232 }
233 env_sets(&repl_env, "*ARGV*", list!(args.map(Str).collect()));
234
235 // core.mal: defined using the language itself
236 let _ = rep("(def! not (fn* (a) (if a false true)))", &repl_env);
e6d41de4 237 let _ = rep("(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \"\nnil)\")))))", &repl_env);
4ef4b17c
JM
238
239 // Invoked with arguments
240 if let Some(f) = arg1 {
241 match rep(&format!("(load-file \"{}\")",f), &repl_env) {
242 Ok(_) => std::process::exit(0),
243 Err(e) => {
244 println!("Error: {}", format_error(e));
245 std::process::exit(1);
246 }
247 }
248 }
249
250 // main repl loop
251 loop {
252 let readline = rl.readline("user> ");
253 match readline {
254 Ok(line) => {
255 rl.add_history_entry(&line);
256 rl.save_history(".mal-history").unwrap();
257 if line.len() > 0 {
258 match rep(&line, &repl_env) {
259 Ok(out) => println!("{}", out),
260 Err(e) => println!("Error: {}", format_error(e)),
261 }
262 }
263 },
264 Err(ReadlineError::Interrupted) => continue,
265 Err(ReadlineError::Eof) => break,
266 Err(err) => {
267 println!("Error: {:?}", err);
268 break
269 }
270 }
271 }
272}
273
274// vim: ts=2:sw=2:expandtab