Skip to main content

monkey_rs/
repl.rs

1/*!
2# REPL
3
4Defines a Read-Eval-Print-Loop (REPL) for the Monkey programming language.
5*/
6use rustyline::error::ReadlineError;
7use rustyline::{DefaultEditor, Result};
8use std::cell::RefCell;
9use std::fs;
10use std::rc::Rc;
11
12use crate::eval;
13use crate::eval::environment::Env;
14use crate::parser;
15
16/// Runs a simple Read-Eval-Print-Loop (REPL) for the user to run Monkey code.
17pub fn start() -> Result<()> {
18    let mut rl = DefaultEditor::new()?;
19    let env: Env = Rc::new(RefCell::new(Default::default()));
20    let history_path = "/tmp/.monkey-history.txt";
21
22    match rl.load_history(history_path) {
23        Ok(_) => {}
24        Err(ReadlineError::Io(_)) => {
25            fs::File::create(history_path)?;
26        }
27        Err(err) => {
28            eprintln!("monkey-rs: Error loading history: {}", err);
29        }
30    };
31
32    println!(
33        r"
34       __  ___          __
35      /  |/  /__  ___  / /_____ __ __
36     / /|_/ / _ \/ _ \/  '_/ -_) // /
37    /_/  /_/\___/_//_/_/\_\\__/\_, /
38                              /___/
39        "
40    );
41    println!("Welcome to the Monkey programming language!");
42    println!("Feel free to type in commands\n");
43
44    loop {
45        let readline = rl.readline(">> ");
46        let mut input = String::new();
47
48        match readline {
49            Ok(mut line) => {
50                while line.ends_with(' ') {
51                    line.pop();
52                }
53                if line.is_empty() {
54                    continue;
55                }
56
57                loop {
58                    if line.as_bytes().ends_with(b"\\") {
59                        // Strip final backslash and add to current input
60                        line.pop();
61                        input += &line;
62
63                        // Re-prompt for additional lines
64                        match rl.readline(".. ") {
65                            Ok(next) => {
66                                line = next;
67                                while line.ends_with(' ') {
68                                    line.pop();
69                                }
70                            }
71                            Err(ReadlineError::Eof | ReadlineError::Interrupted) => {
72                                println!("Exiting...");
73                                rl.save_history(history_path)?;
74                                return Ok(());
75                            }
76                            Err(err) => {
77                                println!("Error: {:?}", err);
78                                return Err(err);
79                            }
80                        }
81                    } else {
82                        // Final line
83                        while line.ends_with(' ') {
84                            line.pop();
85                        }
86                        input += &line;
87                        break;
88                    }
89                }
90
91                rl.add_history_entry(&input)?;
92
93                match parser::parse(&input) {
94                    Ok(program) => match eval::eval(program, &Rc::clone(&env)) {
95                        Ok(evaluated) => println!("{}", evaluated),
96                        Err(e) => eprintln!("{}", e),
97                    },
98                    Err(e) => eprintln!("{}", e),
99                }
100            }
101            Err(ReadlineError::Eof | ReadlineError::Interrupted) => {
102                println!("Exiting...");
103                rl.save_history(history_path)?;
104                break;
105            }
106            Err(err) => {
107                println!("Error: {:?}", err);
108                break;
109            }
110        }
111    }
112
113    Ok(())
114}