1use rustyline::error::ReadlineError;
8use rustyline::{DefaultEditor, Result};
9use tabled::{
10 settings::{object::Rows, Modify, Width},
11 Table, Tabled,
12};
13use wander::environment::Environment;
14use wander::{introspect, run, HostFunctionBinding, HostType};
15
16pub struct REPLState<T: HostType> {
17 pub bindings: Environment<T>,
18}
19
20pub fn start_repl<T: HostType>(state: &mut REPLState<T>) -> Result<()> { println!("Welcome to Wander's REPL!");
22 println!("Press Ctrl+C or Ctrl+D or enter `:q` to quit.");
23 println!("Enter :help or :h for help.");
24 println!("---");
25 let mut rl = DefaultEditor::new()?;
26 if rl.load_history("history.txt").is_err() {
27 println!("No previous history.");
28 }
29 loop {
30 let readline = rl.readline("> ");
31 match readline {
32 Ok(line) => {
33 rl.add_history_entry(line.as_str())?;
34 if line.trim().starts_with(':') {
35 if !handle_command(&line, state) {
36 break;
37 }
38 } else {
39 let result = run(line.as_str(), &mut state.bindings);
40 match result {
41 Ok(result) => println!("{result}"),
42 Err(err) => println!("Error: {err:?}"),
43 }
44 }
45 }
46 Err(ReadlineError::Interrupted) => {
47 println!("Good Bye!");
48 break;
49 }
50 Err(ReadlineError::Eof) => {
51 println!("Good Bye!");
52 break;
53 }
54 Err(err) => {
55 println!("Error: {:?}", err);
56 break;
57 }
58 }
59 }
60 rl.save_history("history.txt")
61}
62
63fn handle_command<T: HostType>(input: &str, instance: &mut REPLState<T>) -> bool {
64 let mut parts = input.split_whitespace();
65 match parts.next().unwrap() {
66 ":parse" | ":p" => parse(input, &instance.bindings),
69 ":status" | ":s" => status(),
70 ":quit" | ":q" => quit(),
71 ":bindings" | ":b" => bindings(&instance.bindings),
72 ":environment" | ":e" => environment(&mut instance.bindings),
73 ":help" | ":h" => help(),
74 ":broadcast" => broadcast(input),
75 s => {
76 println!("Unknown command - {s}");
77 true
78 }
79 }
80}
81
82fn parse<T: HostType>(input: &str, instance: &Environment<T>) -> bool {
83 let input = if input.starts_with(":parse") {
84 input.replacen(":parse", "", 1)
85 } else {
86 input.replacen(":p", "", 1)
87 };
88 let input = input.trim();
89 let introspection = introspect(&input, instance).unwrap();
90 println!("Tokens:\n{:?}\n", introspection.tokens_ws);
91 println!("Tokens Filtered:\n{:?}\n", introspection.tokens);
92 println!("Transformed:\n{:?}\n", introspection.tokens_transformed);
93 println!("Element:\n{:?}\n", introspection.element);
94 println!("Expression:\n{:?}\n", introspection.expression);
95 true
96}
97
98fn broadcast(_input: &str) -> bool {
99 true
100}
101
102fn bindings<T: HostType>(bindings: &Environment<T>) -> bool {
103 bindings
104 .bound_names()
105 .iter()
106 .for_each(|binding| println!("{binding}"));
107 true
108}
109
110fn environment<T: HostType>(bindings: &mut Environment<T>) -> bool {
111 let mut display: Vec<EnvironmentDisplay> = bindings
112 .environment()
113 .into_iter()
114 .map(EnvironmentDisplay::from)
115 .collect();
116 display.sort();
117 let mut table = Table::new(display);
118 table
119 .with(
120 Modify::new(Rows::new(1..))
121 .with(Width::wrap(30).keep_words())
122 .with(Width::increase(20)),
123 )
124 .with(Width::increase(150));
125 println!("{table}");
126 true
127}
128
129fn help() -> bool {
130 true
131}
132
133fn status() -> bool {
134 println!("...");
135 true
136}
137
138fn quit() -> bool {
139 false
140}
141
142#[derive(PartialEq, Eq, PartialOrd, Ord, Tabled)]
143pub struct EnvironmentDisplay {
144 pub name: String,
145 pub parameters: String,
146 pub result: String,
147 pub doc_string: String,
148}
149
150impl From<HostFunctionBinding> for EnvironmentDisplay {
151 fn from(value: HostFunctionBinding) -> Self {
152 EnvironmentDisplay {
153 name: value.name,
154 parameters: format!("{:?}", value.parameters),
155 result: format!("{:?}", value.result),
156 doc_string: value.doc_string,
157 }
158 }
159}