Skip to main content

run/
repl.rs

1//! Interactive REPL (Read-Eval-Print Loop) for the run scripting language.
2
3use crate::{config, interpreter, parser};
4use std::env;
5use std::io::{self, Write};
6
7const PKG_VERSION: &str = env!("CARGO_PKG_VERSION");
8
9/// Start an interactive shell (REPL) for the run scripting language.
10pub fn run_repl() {
11    let run_shell = env::var("RUN_SHELL").unwrap_or_else(|_| {
12        if cfg!(target_os = "windows") {
13            // Try to find pwsh (PowerShell 7+) first, then fallback to powershell (Windows PowerShell)
14            if which::which("pwsh").is_ok() {
15                "pwsh".to_string()
16            } else {
17                "powershell".to_string()
18            }
19        } else {
20            "sh".to_string()
21        }
22    });
23    println!("Run Shell {PKG_VERSION} ({run_shell})");
24    println!("Type 'exit' or press Ctrl+D to quit\n");
25
26    let mut interpreter = interpreter::Interpreter::new();
27
28    // Load Runfile functions into the REPL
29    if let Some(config_content) = config::load_config() {
30        match parser::parse_script(&config_content) {
31            Ok(program) => {
32                if let Err(e) = interpreter.execute(program) {
33                    eprintln!("Warning: Error loading Runfile functions: {e}");
34                }
35            }
36            Err(e) => {
37                eprintln!("Warning: Error parsing Runfile: {e}");
38            }
39        }
40    }
41
42    let stdin = io::stdin();
43    let mut stdout = io::stdout();
44
45    loop {
46        // Print prompt
47        print!("> ");
48        if let Err(e) = stdout.flush() {
49            eprintln!("Error flushing stdout: {e}");
50            break;
51        }
52
53        // Read line
54        let mut input = String::new();
55        match stdin.read_line(&mut input) {
56            Ok(0) => {
57                // EOF (Ctrl+D)
58                println!("\nGoodbye!");
59                break;
60            }
61            Ok(_) => {
62                let input = input.trim();
63
64                // Check for exit command
65                if input == "exit" || input == "quit" {
66                    println!("Goodbye!");
67                    break;
68                }
69
70                // Skip empty lines
71                if input.is_empty() {
72                    continue;
73                }
74
75                // Try to parse and execute the input
76                match parser::parse_script(input) {
77                    Ok(program) => {
78                        if let Err(e) = interpreter.execute(program) {
79                            eprintln!("Error: {e}");
80                        }
81                    }
82                    Err(e) => {
83                        eprintln!("{}", parser::ParseError::from_pest(&e, input, None));
84                    }
85                }
86            }
87            Err(e) => {
88                eprintln!("Error reading input: {e}");
89                break;
90            }
91        }
92    }
93}