1use rustyline::DefaultEditor;
2
3use crate::{parser::ast::Node, val::ProtectedVal, Vm};
4
5pub struct Repl {
22 vm: Vm,
23 editor: DefaultEditor,
24}
25
26impl Repl {
27 pub fn new(vm: Vm) -> rustyline::Result<Repl> {
29 let editor = DefaultEditor::new()?;
30 Ok(Repl { vm, editor })
31 }
32
33 pub fn into_vm(self) -> Vm {
35 self.vm
36 }
37
38 pub fn as_vm_mut(&mut self) -> &mut Vm {
40 &mut self.vm
41 }
42
43 pub fn as_vm(&self) -> &Vm {
45 &self.vm
46 }
47
48 pub fn eval_next_input(&mut self) -> rustyline::Result<ProtectedVal<'_>> {
51 let mut input = String::new();
52 fn input_is_ready(input: &str) -> rustyline::Result<bool> {
53 if input.is_empty() {
54 return Ok(false);
55 }
56 for node_or_err in Node::parse(&input) {
57 match node_or_err {
58 Ok(_) => {}
59 Err(crate::parser::ast::AstParseError::UnclosedParen) => {
60 return Ok(false);
61 }
62 Err(err) => {
63 return rustyline::Result::Err(rustyline::error::ReadlineError::Io(
64 std::io::Error::new(std::io::ErrorKind::InvalidInput, err),
65 ))
66 }
67 }
68 }
69 Ok(true)
70 }
71 while !input_is_ready(&input)? {
72 let prompt = if input.is_empty() { ">> " } else { ".. " };
73 match self.editor.readline(prompt) {
74 Ok(line) => input.push_str(line.as_str()),
75 Err(err) => return Err(err),
76 };
77 }
78 let res = self
79 .vm
80 .eval_str(&input)
81 .inspect(|v| println!("{v}"))
82 .map_err(|err| std::io::Error::new(std::io::ErrorKind::InvalidInput, err).into());
83 let _ = self.editor.add_history_entry(input);
84 res
85 }
86}