diff --git a/complexpr-bin/Cargo.toml b/complexpr-bin/Cargo.toml index 8458b38..b1d9734 100644 --- a/complexpr-bin/Cargo.toml +++ b/complexpr-bin/Cargo.toml @@ -7,10 +7,14 @@ edition = "2021" [dependencies] complexpr = { path = "../complexpr" } -rustyline = "10.0.0" backtrace = "0.3.66" -rustyline-derive = "0.7.0" +rustyline = { version = "10.0.0", optional = true } +rustyline-derive = { version = "0.7.0", optional = true } [[bin]] name = "complexpr" path = "src/main.rs" + +[features] +default = ["repl"] +repl = ["dep:rustyline", "dep:rustyline-derive"] diff --git a/complexpr-bin/src/helper.rs b/complexpr-bin/src/helper.rs index 9667606..2fa9414 100644 --- a/complexpr-bin/src/helper.rs +++ b/complexpr-bin/src/helper.rs @@ -1,3 +1,4 @@ +#![cfg(feature = "repl")] use std::borrow::Cow; use complexpr::env::EnvRef; @@ -100,6 +101,10 @@ impl Highlighter for CxprHelper { fn highlight_char(&self, line: &str, _: usize) -> bool { !line.is_empty() } + + fn highlight_hint<'h>(&self, hint: &'h str) -> Cow<'h, str> { + Cow::Owned(format!("\x1b[90m{}\x1b[0m", hint)) + } } fn validate_brackets(input: &str) -> ValidationResult { diff --git a/complexpr-bin/src/main.rs b/complexpr-bin/src/main.rs index afa404a..34b3f55 100644 --- a/complexpr-bin/src/main.rs +++ b/complexpr-bin/src/main.rs @@ -1,16 +1,10 @@ -use std::{fs, panic::{self, PanicInfo}}; +use std::{fs, panic::{self, PanicInfo}, process::ExitCode}; use backtrace::Backtrace; -use complexpr::{env::Environment, interpreter::interpret, value::Value, stdlib}; -use rustyline::{self, error::ReadlineError, Config, CompletionType, EditMode, hint::HistoryHinter, validate::MatchingBracketValidator, Editor}; +use complexpr::{interpreter::interpret}; mod helper; -use helper::CxprHelper; - -const C_RESET: &str = "\x1b[0m"; -const C_BLUE: &str = "\x1b[94m"; -const C_RED: &str = "\x1b[91m"; -const PROMPT: &str = "\x1b[94m>> \x1b[0m"; +mod repl; fn panic_hook(info: &PanicInfo) { eprintln!("{:?}", Backtrace::new()); @@ -28,60 +22,36 @@ fn panic_hook(info: &PanicInfo) { } } -fn main() -> Result<(), Box> { +fn main() -> ExitCode { panic::set_hook(Box::new(panic_hook)); let args: Vec = std::env::args().collect(); if args.len() == 2 { let fname = &args[1]; - let src = fs::read_to_string(fname)?; + let src = match fs::read_to_string(fname) { + Ok(src) => src, + Err(e) => { + eprintln!("Error reading file: {}", e); + return ExitCode::from(2); + } + }; let res = interpret(&src, Some(fname.into()), None, false); if let Err(e) = res { eprintln!("Error: {}", e); + return ExitCode::from(1); } } else { - repl()?; - } - Ok(()) -} - -fn repl() -> Result<(), Box> { - let config = Config::builder() - .history_ignore_space(true) - .completion_type(CompletionType::List) - .edit_mode(EditMode::Emacs) - .build(); - let env = Environment::new().wrap(); - let h = CxprHelper { - hinter: HistoryHinter {}, - colored_prompt: PROMPT.to_owned(), - validator: MatchingBracketValidator::new(), - env: env.clone(), - }; - let mut rl = Editor::with_config(config)?; - rl.set_helper(Some(h)); - println!("Press {}Ctrl+D{} to exit.", C_BLUE, C_RESET); - stdlib::load(&mut env.borrow_mut()); - stdlib::io::load(&mut env.borrow_mut()); - stdlib::iter::load(&mut env.borrow_mut()); - stdlib::math::load(&mut env.borrow_mut()); - loop { - let readline = rl.readline(">> "); - match readline { - Ok(line) => { - let result = interpret(&line, None, Some(env.clone()), true); - match result { - Ok(value) => { - if value != Value::Nil { - println!("{}", value.repr()); - } - env.borrow_mut().declare("_".into(), value); - } - Err(e) => eprintln!("{}Error: {}{}", C_RED, C_RESET, e) - } + #[cfg(feature = "repl")] + { + if let Err(e) = repl::repl() { + eprintln!("Error: {}", e); + return ExitCode::from(4) } - Err(ReadlineError::Eof) => break, - Err(_) => (), + } + #[cfg(not(feature = "repl"))] + { + eprintln!("Expected a file to execute. To use complexpr in repl mode, enable the 'repl' feature (enabled by default)."); + return ExitCode::from(3) } } - Ok(()) + ExitCode::SUCCESS } diff --git a/complexpr-bin/src/repl.rs b/complexpr-bin/src/repl.rs new file mode 100644 index 0000000..feee4d9 --- /dev/null +++ b/complexpr-bin/src/repl.rs @@ -0,0 +1,69 @@ +#![cfg(feature = "repl")] + +use rustyline::{self, error::ReadlineError, Config, CompletionType, EditMode, hint::HistoryHinter, validate::MatchingBracketValidator, Editor}; +use complexpr::{env::Environment, interpreter::interpret, value::Value, stdlib}; + +use crate::helper::CxprHelper; + +const C_RESET: &str = "\x1b[0m"; +const C_BLUE: &str = "\x1b[94m"; +const C_RED: &str = "\x1b[91m"; +const PROMPT: &str = "\x1b[94m>> \x1b[0m"; + +pub fn repl() -> Result<(), Box> { + let config = Config::builder() + .history_ignore_space(true) + .completion_type(CompletionType::List) + .edit_mode(EditMode::Emacs) + .build(); + + let env = Environment::new().wrap(); + + let h = CxprHelper { + hinter: HistoryHinter {}, + colored_prompt: PROMPT.to_owned(), + validator: MatchingBracketValidator::new(), + env: env.clone(), + }; + + let histfile = std::env::var("COMPLEXPR_HISTORY").ok(); + + let mut rl = Editor::with_config(config)?; + rl.set_helper(Some(h)); + if let Some(hf) = &histfile { + rl.load_history(hf)?; + } + + println!("Press {}Ctrl+D{} to exit.", C_BLUE, C_RESET); + + stdlib::load(&mut env.borrow_mut()); + stdlib::io::load(&mut env.borrow_mut()); + stdlib::iter::load(&mut env.borrow_mut()); + stdlib::math::load(&mut env.borrow_mut()); + + loop { + let readline = rl.readline(">> "); + match readline { + Ok(line) => { + rl.add_history_entry(&line); + let result = interpret(&line, None, Some(env.clone()), true); + match result { + Ok(value) => { + if value != Value::Nil { + println!("{}", value.repr()); + } + env.borrow_mut().declare("_".into(), value); + } + Err(e) => eprintln!("{}Error: {}{}", C_RED, C_RESET, e) + } + } + Err(ReadlineError::Eof) => break, + Err(_) => (), + } + } + + if let Some(hf) = &histfile { + rl.save_history(hf)? + } + Ok(()) +}