Commit | Line | Data |
---|---|---|
4ef4b17c JM |
1 | use std::rc::Rc; |
2 | //use std::collections::HashMap; | |
3 | use fnv::FnvHashMap; | |
4 | ||
5 | #[macro_use] | |
6 | extern crate lazy_static; | |
4ef4b17c | 7 | extern crate fnv; |
4a649b37 RF |
8 | extern crate itertools; |
9 | extern crate regex; | |
4ef4b17c JM |
10 | |
11 | extern crate rustyline; | |
12 | use rustyline::error::ReadlineError; | |
13 | use rustyline::Editor; | |
14 | ||
15 | #[macro_use] | |
16 | #[allow(dead_code)] | |
17 | mod types; | |
c9b18677 GL |
18 | use crate::types::MalErr::ErrString; |
19 | use crate::types::MalVal::{Hash, Int, List, Nil, Sym, Vector}; | |
20 | use crate::types::{error, format_error, func, MalArgs, MalErr, MalRet, MalVal}; | |
4ef4b17c | 21 | mod printer; |
4a649b37 | 22 | mod reader; |
4ef4b17c JM |
23 | // TODO: figure out a way to avoid including env |
24 | #[allow(dead_code)] | |
25 | mod env; | |
26 | ||
4a649b37 | 27 | pub type Env = FnvHashMap<String, MalVal>; |
4ef4b17c JM |
28 | |
29 | // read | |
30 | fn read(str: &str) -> MalRet { | |
4a649b37 | 31 | reader::read_str(str.to_string()) |
4ef4b17c JM |
32 | } |
33 | ||
34 | // eval | |
35 | fn eval_ast(ast: &MalVal, env: &Env) -> MalRet { | |
4a649b37 RF |
36 | match ast { |
37 | Sym(sym) => Ok(env | |
38 | .get(sym) | |
39 | .ok_or(ErrString(format!("'{}' not found", sym)))? | |
40 | .clone()), | |
41 | List(v, _) => { | |
42 | let mut lst: MalArgs = vec![]; | |
43 | for a in v.iter() { | |
44 | lst.push(eval(a.clone(), env.clone())?) | |
45 | } | |
46 | Ok(list!(lst)) | |
47 | } | |
48 | Vector(v, _) => { | |
49 | let mut lst: MalArgs = vec![]; | |
50 | for a in v.iter() { | |
51 | lst.push(eval(a.clone(), env.clone())?) | |
52 | } | |
53 | Ok(vector!(lst)) | |
54 | } | |
55 | Hash(hm, _) => { | |
56 | let mut new_hm: FnvHashMap<String, MalVal> = FnvHashMap::default(); | |
57 | for (k, v) in hm.iter() { | |
58 | new_hm.insert(k.to_string(), eval(v.clone(), env.clone())?); | |
59 | } | |
60 | Ok(Hash(Rc::new(new_hm), Rc::new(Nil))) | |
61 | } | |
62 | _ => Ok(ast.clone()), | |
63 | } | |
4ef4b17c JM |
64 | } |
65 | ||
66 | fn eval(ast: MalVal, env: Env) -> MalRet { | |
4a649b37 RF |
67 | match ast.clone() { |
68 | List(l, _) => { | |
69 | if l.len() == 0 { | |
70 | return Ok(ast); | |
71 | } | |
72 | match eval_ast(&ast, &env)? { | |
73 | List(ref el, _) => { | |
74 | let ref f = el[0].clone(); | |
75 | f.apply(el[1..].to_vec()) | |
76 | } | |
77 | _ => error("expected a list"), | |
78 | } | |
4ef4b17c | 79 | } |
4a649b37 RF |
80 | _ => eval_ast(&ast, &env), |
81 | } | |
4ef4b17c JM |
82 | } |
83 | ||
84 | ||
85 | fn print(ast: &MalVal) -> String { | |
4a649b37 | 86 | ast.pr_str(true) |
4ef4b17c JM |
87 | } |
88 | ||
4a649b37 RF |
89 | fn rep(str: &str, env: &Env) -> Result<String, MalErr> { |
90 | let ast = read(str)?; | |
91 | let exp = eval(ast, env.clone())?; | |
92 | Ok(print(&exp)) | |
4ef4b17c JM |
93 | } |
94 | ||
4a649b37 RF |
95 | fn int_op(op: fn(i64, i64) -> i64, a: MalArgs) -> MalRet { |
96 | match (a[0].clone(), a[1].clone()) { | |
97 | (Int(a0), Int(a1)) => Ok(Int(op(a0, a1))), | |
98 | _ => error("invalid int_op args"), | |
99 | } | |
4ef4b17c JM |
100 | } |
101 | ||
102 | fn main() { | |
4a649b37 RF |
103 | // `()` can be used when no completer is required |
104 | let mut rl = Editor::<()>::new(); | |
105 | if rl.load_history(".mal-history").is_err() { | |
106 | eprintln!("No previous history."); | |
107 | } | |
4ef4b17c | 108 | |
4a649b37 RF |
109 | let mut repl_env = Env::default(); |
110 | repl_env.insert("+".to_string(), func(|a: MalArgs| int_op(|i, j| i + j, a))); | |
111 | repl_env.insert("-".to_string(), func(|a: MalArgs| int_op(|i, j| i - j, a))); | |
112 | repl_env.insert("*".to_string(), func(|a: MalArgs| int_op(|i, j| i * j, a))); | |
113 | repl_env.insert("/".to_string(), func(|a: MalArgs| int_op(|i, j| i / j, a))); | |
4ef4b17c | 114 | |
4a649b37 RF |
115 | loop { |
116 | let readline = rl.readline("user> "); | |
117 | match readline { | |
118 | Ok(line) => { | |
119 | rl.add_history_entry(&line); | |
120 | rl.save_history(".mal-history").unwrap(); | |
121 | if line.len() > 0 { | |
122 | match rep(&line, &repl_env) { | |
123 | Ok(out) => println!("{}", out), | |
124 | Err(e) => println!("Error: {}", format_error(e)), | |
125 | } | |
126 | } | |
127 | } | |
128 | Err(ReadlineError::Interrupted) => continue, | |
129 | Err(ReadlineError::Eof) => break, | |
130 | Err(err) => { | |
131 | println!("Error: {:?}", err); | |
132 | break; | |
133 | } | |
4ef4b17c | 134 | } |
4ef4b17c | 135 | } |
4ef4b17c | 136 | } |