bbc-basic: Slight tweak to heap size.
[jackhill/mal.git] / rust / step6_file.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 mod types;
18 use types::{MalVal,MalArgs,MalRet,MalErr,error,format_error};
19 use types::MalVal::{Nil,Bool,Str,Sym,List,Vector,Hash,Func,MalFunc};
20 mod reader;
21 mod printer;
22 mod env;
23 use env::{Env,env_new,env_bind,env_get,env_set,env_sets};
24 #[macro_use]
25 mod core;
26
27 // read
28 fn read(str: &str) -> MalRet {
29 reader::read_str(str.to_string())
30 }
31
32 // eval
33 fn 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
57 fn 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 Sym(ref a0sym) if a0sym == "eval" => {
125 ast = eval(l[1].clone(), env.clone())?;
126 while let Some(ref e) = env.clone().outer {
127 env = e.clone();
128 }
129 continue 'tco;
130 },
131 _ => {
132 match eval_ast(&ast, &env)? {
133 List(ref el,_) => {
134 let ref f = el[0].clone();
135 let args = el[1..].to_vec();
136 match f {
137 Func(_,_) => f.apply(args),
138 MalFunc{ast: mast, env: menv, params, ..} => {
139 let a = &**mast;
140 let p = &**params;
141 env = env_bind(Some(menv.clone()), p.clone(), args)?;
142 ast = a.clone();
143 continue 'tco;
144 },
145 _ => error("attempt to call non-function"),
146 }
147 },
148 _ => {
149 error("expected a list")
150 }
151 }
152 }
153 }
154 },
155 _ => eval_ast(&ast, &env),
156 };
157
158 break;
159
160 } // end 'tco loop
161
162 ret
163 }
164
165 // print
166 fn print(ast: &MalVal) -> String {
167 ast.pr_str(true)
168 }
169
170 fn rep(str: &str, env: &Env) -> Result<String,MalErr> {
171 let ast = read(str)?;
172 let exp = eval(ast, env.clone())?;
173 Ok(print(&exp))
174 }
175
176 fn main() {
177 let mut args = std::env::args();
178 let arg1 = args.nth(1);
179
180 // `()` can be used when no completer is required
181 let mut rl = Editor::<()>::new();
182 if rl.load_history(".mal-history").is_err() {
183 eprintln!("No previous history.");
184 }
185
186 // core.rs: defined using rust
187 let repl_env = env_new(None);
188 for (k, v) in core::ns() {
189 env_sets(&repl_env, k, v);
190 }
191 env_sets(&repl_env, "*ARGV*", list!(args.map(Str).collect()));
192
193 // core.mal: defined using the language itself
194 let _ = rep("(def! not (fn* (a) (if a false true)))", &repl_env);
195 let _ = rep("(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \")\")))))", &repl_env);
196
197 // Invoked with arguments
198 if let Some(f) = arg1 {
199 match rep(&format!("(load-file \"{}\")",f), &repl_env) {
200 Ok(_) => std::process::exit(0),
201 Err(e) => {
202 println!("Error: {}", format_error(e));
203 std::process::exit(1);
204 }
205 }
206 }
207
208 // main repl loop
209 loop {
210 let readline = rl.readline("user> ");
211 match readline {
212 Ok(line) => {
213 rl.add_history_entry(&line);
214 rl.save_history(".mal-history").unwrap();
215 if line.len() > 0 {
216 match rep(&line, &repl_env) {
217 Ok(out) => println!("{}", out),
218 Err(e) => println!("Error: {}", format_error(e)),
219 }
220 }
221 },
222 Err(ReadlineError::Interrupted) => continue,
223 Err(ReadlineError::Eof) => break,
224 Err(err) => {
225 println!("Error: {:?}", err);
226 break
227 }
228 }
229 }
230 }
231
232 // vim: ts=2:sw=2:expandtab