1use crate::calc::context::Context;
2use crate::calc::undo::Undo;
3use crate::calc::value::{Value, ValueRef};
4use crate::engine::Engine;
5use crate::error::MyResult;
6use crate::read::action::Action;
7use crate::read::count::Count;
8use crate::read::helper::{CommandEditor, CommandHelper};
9use itertools::Itertools;
10use rustyline::config::Configurer;
11use rustyline::CompletionType;
12use std::cell::RefCell;
13use std::collections::{BTreeMap, BTreeSet, HashMap};
14use std::io::Write;
15use std::rc::Rc;
16use std::time::SystemTime;
17
18pub enum Operation<W: Write> {
19 ValueNone(fn() -> MyResult<ValueRef>),
20 ValueOne(fn(ValueRef) -> MyResult<ValueRef>),
21 ValueTwo(fn(ValueRef, ValueRef) -> MyResult<ValueRef>),
22 ValueAll(fn(Vec<ValueRef>) -> MyResult<ValueRef>),
23 TimeNow(fn(&Option<SystemTime>) -> MyResult<ValueRef>),
24 TimeCast(fn(ValueRef) -> MyResult<()>),
25 ContextNone(fn(&mut Context)),
26 ContextOne(fn(&mut Context, ValueRef)),
27 EngineNone(fn(&mut Engine<W>, &mut W) -> MyResult<bool>),
28 EngineUndo(fn(&mut Engine<W>, &mut Undo, &mut Undo, Option<&str>) -> MyResult<bool>, Count, Count),
29}
30
31impl<W: Write> Operation<W> {
32 pub fn count_parameters(&self) -> (Count, Count) {
33 match self {
34 Operation::ValueNone(_) => (Count::Some(0), Count::Some(1)),
35 Operation::ValueOne(_) => (Count::Some(1), Count::Some(1)),
36 Operation::ValueTwo(_) => (Count::Some(2), Count::Some(1)),
37 Operation::ValueAll(_) => (Count::All, Count::Some(1)),
38 Operation::TimeNow(_) => (Count::Some(0), Count::Some(1)),
39 Operation::TimeCast(_) => (Count::Some(1), Count::Some(1)),
40 Operation::ContextNone(_) => (Count::Some(0), Count::Some(0)),
41 Operation::ContextOne(_) => (Count::Some(1), Count::Some(0)),
42 Operation::EngineNone(_) => (Count::Some(0), Count::Some(0)),
43 Operation::EngineUndo(_, input, output) => (*input, *output),
44 }
45 }
46}
47
48#[derive(Debug, PartialEq, Copy, Clone)]
49pub enum Completion {
50 Keyword,
51 Filename,
52}
53
54pub enum Directive<W: Write> {
55 EngineOne(fn(&mut Engine<W>, &mut W, String) -> MyResult<bool>, Completion),
56 EngineAll(fn(&mut Engine<W>, &mut W, Vec<String>) -> MyResult<bool>, Completion),
57}
58
59pub enum Description {
60 Separator(String),
61 Operation((String, Count, Count, String)),
62 Directive((String, String)),
63}
64
65pub type Operations<W> = HashMap<String, Rc<Operation<W>>>;
66pub type Directives<W> = HashMap<String, Rc<Directive<W>>>;
67pub type Definitions<W> = BTreeMap<String, (Vec<Action<W>>, Count, Count, String)>;
68pub type Descriptions = Vec<Description>;
69
70pub struct Interface<W: Write> {
71 operations: Operations<W>,
72 directives: Directives<W>,
73 definitions: Definitions<W>,
74 variables: HashMap<String, ValueRef>,
75 descriptions: Descriptions,
76}
77
78impl<W: Write> Interface<W> {
79 pub fn new() -> Self {
80 let operations = Operations::new();
81 let directives = Directives::new();
82 let definitions = Definitions::new();
83 let variables = HashMap::new();
84 let descriptions = Descriptions::new();
85 Self { operations, directives, definitions, variables, descriptions }
86 }
87
88 pub fn build() -> Self {
89 let interface = Self::new();
90 let interface = interface
91 .with_separator("Arithmetic operations")
92 .with_operation(vec!["add", "+"], Operation::ValueTwo(Value::calc_add), "Add two values")
93 .with_operation(vec!["sub", "-"], Operation::ValueTwo(Value::calc_sub), "Subtract two values")
94 .with_operation(vec!["mul", "*"], Operation::ValueTwo(Value::calc_mul), "Multiply two values")
95 .with_operation(vec!["div", "/"], Operation::ValueTwo(Value::calc_div), "Divide two values")
96 .with_operation(vec!["mod", "%"], Operation::ValueTwo(Value::calc_mod), "Modulo two values")
97 .with_operation(vec!["neg"], Operation::ValueOne(Value::calc_neg), "Find the negative")
98 .with_operation(vec!["inv"], Operation::ValueOne(Value::calc_inv), "Find the inverse")
99 .with_operation(vec!["pow"], Operation::ValueTwo(Value::calc_pow), "Raise to the power")
100 .with_operation(vec!["sqrt"], Operation::ValueOne(Value::calc_sqrt), "Find the square root")
101 .with_operation(vec!["sum"], Operation::ValueAll(Value::calc_sum), "Sum all values")
102 .with_operation(vec!["prod"], Operation::ValueAll(Value::calc_prod), "Multiply all values");
103 let interface = interface
104 .with_separator("Bitwise operations")
105 .with_operation(vec!["and"], Operation::ValueTwo(Value::calc_and), "Bitwise AND two values")
106 .with_operation(vec!["or"], Operation::ValueTwo(Value::calc_or), "Bitwise OR two values")
107 .with_operation(vec!["xor"], Operation::ValueTwo(Value::calc_xor), "Bitwise XOR two values")
108 .with_operation(vec!["shl"], Operation::ValueTwo(Value::calc_shl), "Shift left (multiply by power of 2)")
109 .with_operation(vec!["shr"], Operation::ValueTwo(Value::calc_shr), "Shift right (divide by power of 2)");
110 let interface = interface
111 .with_separator("Time operations")
112 .with_operation(vec!["now"], Operation::TimeNow(Value::calc_now), "Get the current time (in UTC)")
113 .with_operation(vec!["plain"], Operation::TimeCast(Value::cast_plain), "Format as a plain value")
114 .with_operation(vec!["delta"], Operation::TimeCast(Value::cast_delta), "Format as a delta value (duration)")
115 .with_operation(vec!["time"], Operation::TimeCast(Value::cast_time), "Format as a time value (in UTC)");
116 let interface = interface
117 .with_separator("Formatting commands")
118 .with_operation(vec!["dec"], Operation::ContextNone(Context::set_dec), "Format as decimal values")
119 .with_operation(vec!["hex"], Operation::ContextNone(Context::set_hex), "Format as hexadecimal values")
120 .with_operation(vec!["sep"], Operation::ContextNone(Context::set_sep), "Include comma separators")
121 .with_operation(vec!["nosep"], Operation::ContextNone(Context::no_sep), "Include no separators")
122 .with_operation(vec!["dp"], Operation::ContextOne(Context::set_dp), "Use fixed decimal places")
123 .with_operation(vec!["nodp"], Operation::ContextNone(Context::no_dp), "Use free decimal places");
124 let interface = interface
125 .with_separator("Stack commands")
126 .with_operation(vec!["clear", "c"], Operation::EngineUndo(Engine::clear_values, Count::All, Count::Some(0)), "Remove all values from the stack")
127 .with_operation(vec!["pop", "p"], Operation::EngineUndo(Engine::pop_value, Count::Some(1), Count::Some(0)), "Remove a value from the stack")
128 .with_operation(vec!["dup", "d"], Operation::EngineUndo(Engine::dup_value, Count::Some(1), Count::Some(2)), "Duplicate a value on the stack")
129 .with_operation(vec!["swap", "s"], Operation::EngineUndo(Engine::swap_values, Count::Some(2), Count::Some(2)), "Swap two values on the stack")
130 .with_operation(vec!["cut"], Operation::EngineUndo(Engine::cut_value, Count::Some(1), Count::Some(0)), "Cut a value to the internal clipboard")
131 .with_operation(vec!["copy"], Operation::EngineUndo(Engine::copy_value, Count::Some(1), Count::Some(1)), "Copy a value to the internal clipboard")
132 .with_operation(vec!["paste"], Operation::EngineUndo(Engine::paste_value, Count::Some(0), Count::Some(1)), "Paste a value from the internal clipboard");
133 let interface = interface
134 .with_separator("History commands")
135 .with_operation(vec!["undo", "u"], Operation::EngineUndo(Engine::undo_stack, Count::Some(0), Count::Some(0)), "Undo the last operation")
136 .with_operation(vec!["redo", "r"], Operation::EngineUndo(Engine::redo_stack, Count::Some(0), Count::Some(0)), "Redo the next operation")
137 .with_operation(vec!["hist", "h"], Operation::EngineNone(Engine::show_history), "Show all undo/redo history");
138 let interface = interface
139 .with_separator("General directives")
140 .with_directive(vec!["import"], Directive::EngineAll(Engine::import_file, Completion::Filename), "Import file e.g. \"import file.txt\"")
141 .with_directive(vec!["export"], Directive::EngineAll(Engine::export_file, Completion::Filename), "Export file e.g. \"export file.txt\"")
142 .with_directive(vec!["set", "="], Directive::EngineOne(Engine::set_variable, Completion::Keyword), "Set variable, e.g. \"set x\"")
143 .with_directive(vec!["define"], Directive::EngineAll(Engine::define_function, Completion::Keyword), "Define function e.g. \"define cube 3 pow\"");
144 let interface = interface
145 .with_separator("General commands")
146 .with_operation(vec!["show"], Operation::EngineNone(Engine::show_stack), "Show all values on the stack")
147 .with_operation(vec!["help"], Operation::EngineNone(Engine::show_help), "Show this help text");
148 interface
149 }
150
151 fn with_separator(mut self, description: &str) -> Self {
152 self.descriptions.push(Description::Separator(String::from(description)));
153 self
154 }
155
156 fn with_operation(
157 mut self,
158 keywords: Vec<&str>,
159 operation: Operation<W>,
160 description: &str,
161 ) -> Self {
162 let operation = Rc::new(operation);
163 for keyword in keywords.iter() {
164 self.operations.insert(keyword.to_string(), Rc::clone(&operation));
165 }
166 let keywords = Self::describe_keywords(keywords);
167 let (input, output) = operation.count_parameters();
168 self.descriptions.push(Description::Operation((
169 keywords,
170 input,
171 output,
172 String::from(description),
173 )));
174 self
175 }
176
177 fn with_directive(
178 mut self,
179 keywords: Vec<&str>,
180 directive: Directive<W>,
181 description: &str,
182 ) -> Self {
183 let directive = Rc::new(directive);
184 for keyword in keywords.iter() {
185 self.directives.insert(keyword.to_string(), Rc::clone(&directive));
186 }
187 let keyword = Self::describe_keywords(keywords);
188 self.descriptions.push(Description::Directive((
189 format!("{keyword}..."),
190 String::from(description),
191 )));
192 self
193 }
194
195 #[cfg(test)]
196 fn with_definition(
197 mut self,
198 keyword: &str,
199 actions: Vec<Action<W>>,
200 input: Count,
201 output: Count,
202 description: String,
203 ) -> Self {
204 self.insert_definition(keyword, actions, input, output, description);
205 self
206 }
207
208 pub fn insert_definition(
209 &mut self,
210 keyword: &str,
211 actions: Vec<Action<W>>,
212 input: Count,
213 output: Count,
214 description: String,
215 ) {
216 self.definitions.insert(keyword.to_string(), (actions, input, output, description));
217 }
218
219 pub fn remove_definition(&mut self, keyword: &str) {
220 self.definitions.remove(keyword);
221 }
222
223 pub fn insert_variable(&mut self, keyword: String, value: ValueRef) -> bool {
224 self.variables.insert(keyword, value).is_some()
225 }
226
227 pub fn remove_variable(&mut self, keyword: &str) {
228 self.variables.remove(keyword);
229 }
230
231 pub fn get_variable(&self, keyword: &str) -> Option<ValueRef> {
232 if let Some(value) = self.variables.get(keyword) {
233 let value = value.as_ref().borrow().clone();
234 return Some(Rc::new(RefCell::new(value)));
235 }
236 None
237 }
238
239 #[cfg(test)]
240 pub fn get_variables(&self) -> &HashMap<String, ValueRef> {
241 &self.variables
242 }
243
244 fn describe_keywords(keywords: Vec<&str>) -> String {
245 if keywords.len() == 2 {
246 let primary = keywords[0];
247 let secondary = keywords[1];
248 if primary.starts_with(secondary) {
249 let chop = secondary.len();
250 return format!("{}({})", secondary, &primary[chop..]);
251 }
252 }
253 keywords.iter().map(|x| x.to_string()).join(",")
254 }
255
256 pub fn create_editor(&self) -> MyResult<CommandEditor> {
257 let mut editor = CommandEditor::new()?;
258 editor.set_helper(Some(CommandHelper::new()));
259 editor.set_completion_type(CompletionType::List);
260 self.adjust_editor(&mut editor);
261 Ok(editor)
262 }
263
264 pub fn adjust_editor(&self, editor: &mut CommandEditor) {
265 if let Some(helper) = editor.helper_mut() {
266 helper.set_commands(self.get_commands());
267 helper.set_completions(self.get_completions());
268 }
269 }
270
271 fn get_commands(&self) -> Vec<String> {
272 let commands = self.operations.keys()
273 .chain(self.directives.keys())
274 .chain(self.definitions.keys())
275 .chain(self.variables.keys())
276 .map(String::clone)
277 .collect::<BTreeSet<String>>();
278 commands.into_iter().collect::<Vec<String>>()
279 }
280
281 fn get_completions(&self) -> HashMap<String, Completion> {
282 let mut directives = HashMap::new();
283 for (keyword, directive) in self.directives.iter() {
284 match directive.as_ref() {
285 Directive::EngineOne(_, completion) => {
286 directives.insert(String::from(keyword), *completion);
287 }
288 Directive::EngineAll(_, completion) => {
289 directives.insert(String::from(keyword), *completion);
290 }
291 }
292 }
293 directives
294 }
295
296 pub fn get_operation(&self, token: &str) -> Option<Rc<Operation<W>>> {
297 self.operations.get(token).map(Rc::clone)
298 }
299
300 pub fn get_directive(&self, token: &str) -> Option<Rc<Directive<W>>> {
301 self.directives.get(token).map(Rc::clone)
302 }
303
304 pub fn get_definition(&self, token: &str) -> Option<Vec<Action<W>>> {
305 self.definitions.get(token).map(|(x, _, _, _)| x).map(Vec::clone)
306 }
307
308 pub fn show_help(&self, writer: &mut W, interact: bool) -> MyResult<()> {
317 let indent = if interact { " " } else { "" };
318 for description in self.descriptions.iter() {
319 match description {
320 Description::Separator(description) => {
321 writeln!(writer, "{}{}:", indent, description)?;
322 }
323 Description::Operation((keyword, input, output, description)) => {
324 writeln!(writer, "{} {:>3} {:7} {:3} {}", indent, input, keyword, output, description)?;
325 }
326 Description::Directive((keyword, description)) => {
327 writeln!(writer, "{} {:>3} {:12} {}", indent, "", keyword, description)?;
328 }
329 }
330 }
331 self.show_definitions(writer, interact)?;
332 Ok(())
333 }
334
335 pub fn show_definitions(&self, writer: &mut W, interact: bool) -> MyResult<()> {
336 let indent = if interact { " " } else { "" };
337 if !self.definitions.is_empty() {
338 writeln!(writer, "{}Defined functions:", indent)?;
339 for (keyword, (_, input, output, description)) in self.definitions.iter() {
340 if let Count::Some(0) = output {
341 writeln!(writer, "{} {:>3} {:12} Function \"{}\"", indent, input, keyword, description)?;
342 } else {
343 writeln!(writer, "{} {:>3} {:7} {:3} Function \"{}\"", indent, input, keyword, output, description)?;
344 }
345 }
346 }
347 Ok(())
348 }
349}
350
351#[cfg(test)]
352pub mod tests {
353 use crate::calc::value::{Value, ValueRef};
354 use crate::engine::Engine;
355 use crate::error::MyResult;
356 use crate::read::count::Count;
357 use crate::read::interface::{Completion, Directive, Interface, Operation};
358 use crate::util::text::tests::BufferWriter;
359 use pretty_assertions::assert_eq;
360 use std::cell::RefCell;
361 use std::collections::HashMap;
362 use std::rc::Rc;
363
364 #[test]
365 fn test_completes_commands() {
366 let expected_commands = vec![
367 "eight",
368 "five",
369 "four",
370 "nine",
371 "one",
372 "seven",
373 "six",
374 "ten",
375 "three",
376 "two",
377 ];
378 let expected_completions = HashMap::from([
379 (String::from("five"), Completion::Filename),
380 (String::from("six"), Completion::Filename),
381 (String::from("seven"), Completion::Filename),
382 (String::from("eight"), Completion::Keyword),
383 ]);
384 let interface = Interface::<BufferWriter>::new()
385 .with_operation(vec!["one", "two"], Operation::ValueNone(dummy_nullary), "")
386 .with_operation(vec!["three"], Operation::ValueNone(dummy_nullary), "")
387 .with_operation(vec!["four"], Operation::ValueNone(dummy_nullary), "")
388 .with_directive(vec!["five", "six"], Directive::EngineAll(dummy_directive, Completion::Filename), "")
389 .with_directive(vec!["seven"], Directive::EngineAll(dummy_directive, Completion::Filename), "")
390 .with_directive(vec!["eight"], Directive::EngineAll(dummy_directive, Completion::Keyword), "")
391 .with_definition("nine", vec![], Count::Some(0), Count::Some(0), String::from(""))
392 .with_definition("ten", vec![], Count::Some(0), Count::Some(0), String::from(""));
393 assert_eq!(interface.get_commands(), expected_commands);
394 assert_eq!(interface.get_completions(), expected_completions);
395 }
396
397 pub fn dummy_nullary() -> MyResult<ValueRef> {
398 Ok(Rc::new(RefCell::new(Value::new(None))))
399 }
400
401 pub fn dummy_unary(_: ValueRef) -> MyResult<ValueRef> {
402 Ok(Rc::new(RefCell::new(Value::new(None))))
403 }
404
405 pub fn dummy_binary(_: ValueRef, _: ValueRef) -> MyResult<ValueRef> {
406 Ok(Rc::new(RefCell::new(Value::new(None))))
407 }
408
409 pub fn dummy_series(_: Vec<ValueRef>) -> MyResult<ValueRef> {
410 Ok(Rc::new(RefCell::new(Value::new(None))))
411 }
412
413 pub fn dummy_directive(
414 _engine: &mut Engine<BufferWriter>,
415 _writer: &mut BufferWriter,
416 _tokens: Vec<String>,
417 ) -> MyResult<bool> {
418 Ok(false)
419 }
420
421 #[test]
422 fn test_shows_help_text() {
423 let expected = "\
424Arithmetic operations:
425 N N add,+ N Add two values
426 N N sub,- N Subtract two values
427 N N mul,* N Multiply two values
428 N N div,/ N Divide two values
429 N N mod,% N Modulo two values
430 N neg N Find the negative
431 N inv N Find the inverse
432 N N pow N Raise to the power
433 N sqrt N Find the square root
434 * sum N Sum all values
435 * prod N Multiply all values
436Bitwise operations:
437 N N and N Bitwise AND two values
438 N N or N Bitwise OR two values
439 N N xor N Bitwise XOR two values
440 N N shl N Shift left (multiply by power of 2)
441 N N shr N Shift right (divide by power of 2)
442Time operations:
443 now N Get the current time (in UTC)
444 N plain N Format as a plain value
445 N delta N Format as a delta value (duration)
446 N time N Format as a time value (in UTC)
447Formatting commands:
448 dec Format as decimal values
449 hex Format as hexadecimal values
450 sep Include comma separators
451 nosep Include no separators
452 N dp Use fixed decimal places
453 nodp Use free decimal places
454Stack commands:
455 * c(lear) Remove all values from the stack
456 N p(op) Remove a value from the stack
457 N d(up) N N Duplicate a value on the stack
458 N N s(wap) N N Swap two values on the stack
459 N cut Cut a value to the internal clipboard
460 N copy N Copy a value to the internal clipboard
461 paste N Paste a value from the internal clipboard
462History commands:
463 u(ndo) Undo the last operation
464 r(edo) Redo the next operation
465 h(ist) Show all undo/redo history
466General directives:
467 import... Import file e.g. \"import file.txt\"
468 export... Export file e.g. \"export file.txt\"
469 set,=... Set variable, e.g. \"set x\"
470 define... Define function e.g. \"define cube 3 pow\"
471General commands:
472 show Show all values on the stack
473 help Show this help text
474";
475 let interface = Interface::build();
476 let mut writer = BufferWriter::new();
477 assert!(interface.show_help(&mut writer, false).is_ok());
478 assert_eq!(writer.buffer, expected);
479 }
480
481 #[test]
482 fn test_shows_definitions_with_null() {
483 let expected = "\
484Defined functions:
485 answer N Function \"6 7 mul\"
486 N cube N Function \"3 pow\"
487 N N very-long-a Function \"\"
488 N N very-long-bb Function \"\"
489 N N very-long-ccc Function \"\"
490 N N very-long-dddd Function \"\"
491 N N very-long-eeeee Function \"\"
492";
493 let mut interface = Interface::build();
494 let mut writer = BufferWriter::new();
495 interface.insert_definition("answer", vec![], Count::Some(0), Count::Some(1), String::from("6 7 mul"));
496 interface.insert_definition("cube", vec![], Count::Some(1), Count::Some(1), String::from("3 pow"));
497 interface.insert_definition("very-long-a", vec![], Count::Some(2), Count::Some(0), String::from(""));
498 interface.insert_definition("very-long-bb", vec![], Count::Some(2), Count::Some(0), String::from(""));
499 interface.insert_definition("very-long-ccc", vec![], Count::Some(2), Count::Some(0), String::from(""));
500 interface.insert_definition("very-long-dddd", vec![], Count::Some(2), Count::Some(0), String::from(""));
501 interface.insert_definition("very-long-eeeee", vec![], Count::Some(2), Count::Some(0), String::from(""));
502 assert!(interface.show_definitions(&mut writer, false).is_ok());
503 assert_eq!(writer.buffer, expected);
504 }
505
506 #[test]
507 fn test_shows_definitions_with_value() {
508 let expected = "\
509Defined functions:
510 answer N Function \"6 7 mul\"
511 N cube N Function \"3 pow\"
512 N N long-a N N Function \"\"
513 N N long-bb N N Function \"\"
514 N N long-ccc N N Function \"\"
515 N N long-dddd N N Function \"\"
516 N N long-eeeee N N Function \"\"
517";
518 let mut interface = Interface::build();
519 let mut writer = BufferWriter::new();
520 interface.insert_definition("answer", vec![], Count::Some(0), Count::Some(1), String::from("6 7 mul"));
521 interface.insert_definition("cube", vec![], Count::Some(1), Count::Some(1), String::from("3 pow"));
522 interface.insert_definition("long-a", vec![], Count::Some(2), Count::Some(2), String::from(""));
523 interface.insert_definition("long-bb", vec![], Count::Some(2), Count::Some(2), String::from(""));
524 interface.insert_definition("long-ccc", vec![], Count::Some(2), Count::Some(2), String::from(""));
525 interface.insert_definition("long-dddd", vec![], Count::Some(2), Count::Some(2), String::from(""));
526 interface.insert_definition("long-eeeee", vec![], Count::Some(2), Count::Some(2), String::from(""));
527 assert!(interface.show_definitions(&mut writer, false).is_ok());
528 assert_eq!(writer.buffer, expected);
529 }
530}