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
107
108
109
110
111
112
113
114
115
#[warn(missing_docs)]
use doc_comment::doc_comment;
doc_comment!(include_str!("../README.md"));
use colored::Colorize as Colourise;
use rustyline::{error::ReadlineError, Cmd, Editor, KeyEvent};
use std::collections::HashMap;
use std::error::Error;
pub mod error;
pub mod lex;
pub mod prelude;
mod scan;
mod tests;
pub type Command = fn(lex::Arguments) -> Result<(), error::Error>;
pub struct Console {
pub command_table: HashMap<String, Command>,
pub prompt: String,
}
impl Console {
pub fn new(command_table: HashMap<String, Command>, prompt: &str) -> Console {
Console {
command_table,
prompt: prompt.to_owned(),
}
}
pub fn run_repl(&self) {
loop {
let mut rl = Editor::<()>::new();
rl.bind_sequence(KeyEvent::ctrl('A'), Cmd::HistorySearchForward);
rl.bind_sequence(KeyEvent::ctrl('B'), Cmd::HistorySearchBackward);
if rl.load_history(".rusterm_history").is_err() {
eprintln!("Could not load history file.");
}
let input = match rl.readline(&self.prompt) {
Ok(x) if x.is_empty() => continue,
Ok(x) if x == *"exit" => break,
Ok(x) => {
rl.add_history_entry(x.clone());
x
}
Err(ReadlineError::Interrupted) => {
eprintln!("^C");
break;
}
Err(ReadlineError::Eof) => {
eprintln!("^D");
break;
}
Err(x) => {
eprintln!(
"{}{} {}",
"Error while reading input".red().bold(),
":".bold(),
x
);
break;
}
};
if let Err(x) = self.parse(input) {
eprintln!("{}", x);
}
if rl.append_history(".rusterm_history").is_err() {
eprintln!("Could not append to history file.");
}
}
}
fn parse(&self, input: String) -> Result<(), Box<dyn Error>> {
let mut scanned_input = scan::scan(input);
let function_name = scanned_input.get(0).unwrap_or(&"".to_string()).clone();
if !function_name.is_empty() {
scanned_input.remove(0);
}
let lexed_input = lex::lex(scanned_input);
let function = match function_name {
x if x == *"help" => {
self.help();
return Ok(());
}
_ => self
.command_table
.get(&function_name)
.ok_or(error::Error::NoSuchCommand)?,
};
if let Err(x) = function(lexed_input) {
println!("{}", x)
}
Ok(())
}
fn help(&self) {
println!("{}", "List of commands:".bold());
for name in self.command_table.keys() {
println!("{}", name);
}
println!("help");
println!("exit")
}
}