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