wander_repl/
lib.rs

1// This Source Code Form is subject to the terms of the Mozilla Public
2// License, v. 2.0. If a copy of the MPL was not distributed with this
3// file, You can obtain one at http://mozilla.org/MPL/2.0/.
4
5//! This module is the library module for the ligature-repl project.
6
7use 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<()> { //TODO this should accept REPLState not create it
21    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        //":remote" => todo!(),
67        //":local" => todo!(),
68        ":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}