1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
|
#[cfg(test)]
pub mod tests;
pub mod lib;
use rustyline::error::ReadlineError;
use rustyline::Editor;
use lib::environment::Environment;
const EXIT_STRINGS: &[&str; 3] = &[",q", "exit", "quit"];
fn read<T: rustyline::Helper>(rl: &mut Editor<T>) -> Result<String, String> {
let mut readline = rl.readline(">> ");
let mut full_line = String::new();
fn count_parens(s: &str) -> Result<bool, ()> {
let mut lcount = 0;
let mut rcount = 0;
for c in s.chars() {
match c {
'(' => lcount += 1,
')' => rcount += 1,
_ => {}
}
if rcount > lcount {
return Err(())
}
}
return Ok(lcount == rcount)
}
let mut loop_count = 0;
loop {
match readline {
Ok(line) => {
full_line.push_str(&line);
match count_parens(&full_line) {
Ok(true) => return Ok(full_line),
Ok(false) => readline = rl.readline("... "),
Err(_) => return Err("unexpected \")\"".to_string())
}
},
Err(ReadlineError::Interrupted) => {
return Err("user interrupt".to_string())
},
Err(ReadlineError::Eof) => {
if loop_count > 0 {
return Err("end of file".to_string());
} else {
return Ok(EXIT_STRINGS[0].to_string());
}
},
Err(err) => {
return Err(format!("{:?}", err));
}
}
loop_count += 1
}
}
fn means_exit(input: &str) -> bool {
EXIT_STRINGS.iter().any(|&s| s == input)
}
fn eval(env: &mut Environment, input: &str) -> String {
let sexp = match lib::tokenize::tokenize(input) {
Ok(x) => x,
Err(f) => return f
};
let res = lib::eval::eval(&sexp, env);
match res {
Ok(x) => format!("{}", x),
Err(f) => format!("Error: {}", f)
}
}
fn main() {
let mut env = Environment::new();
let hist_file = "history.txt";
// `()` can be used when no completer is required
let mut rl = Editor::<()>::new();
if rl.load_history(hist_file).is_err() {
println!("No previous history.");
}
loop {
let input_line = read(&mut rl);
match input_line {
Err(e) => {
println!("Error: {}", e);
continue;
}
Ok(expr) => {
if means_exit(&expr) {
break;
}
rl.add_history_entry(expr.as_str());
println!("{}", eval(&mut env, &expr));
}
};
}
rl.save_history(hist_file).unwrap();
}
|