Improved bin/repl functionality
This commit is contained in:
parent
7ec8742e17
commit
1327dc9569
4 changed files with 103 additions and 55 deletions
|
@ -7,10 +7,14 @@ edition = "2021"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
complexpr = { path = "../complexpr" }
|
complexpr = { path = "../complexpr" }
|
||||||
rustyline = "10.0.0"
|
|
||||||
backtrace = "0.3.66"
|
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]]
|
[[bin]]
|
||||||
name = "complexpr"
|
name = "complexpr"
|
||||||
path = "src/main.rs"
|
path = "src/main.rs"
|
||||||
|
|
||||||
|
[features]
|
||||||
|
default = ["repl"]
|
||||||
|
repl = ["dep:rustyline", "dep:rustyline-derive"]
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
#![cfg(feature = "repl")]
|
||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
|
|
||||||
use complexpr::env::EnvRef;
|
use complexpr::env::EnvRef;
|
||||||
|
@ -100,6 +101,10 @@ impl Highlighter for CxprHelper {
|
||||||
fn highlight_char(&self, line: &str, _: usize) -> bool {
|
fn highlight_char(&self, line: &str, _: usize) -> bool {
|
||||||
!line.is_empty()
|
!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 {
|
fn validate_brackets(input: &str) -> ValidationResult {
|
||||||
|
|
|
@ -1,16 +1,10 @@
|
||||||
use std::{fs, panic::{self, PanicInfo}};
|
use std::{fs, panic::{self, PanicInfo}, process::ExitCode};
|
||||||
|
|
||||||
use backtrace::Backtrace;
|
use backtrace::Backtrace;
|
||||||
use complexpr::{env::Environment, interpreter::interpret, value::Value, stdlib};
|
use complexpr::{interpreter::interpret};
|
||||||
use rustyline::{self, error::ReadlineError, Config, CompletionType, EditMode, hint::HistoryHinter, validate::MatchingBracketValidator, Editor};
|
|
||||||
|
|
||||||
mod helper;
|
mod helper;
|
||||||
use helper::CxprHelper;
|
mod repl;
|
||||||
|
|
||||||
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";
|
|
||||||
|
|
||||||
fn panic_hook(info: &PanicInfo) {
|
fn panic_hook(info: &PanicInfo) {
|
||||||
eprintln!("{:?}", Backtrace::new());
|
eprintln!("{:?}", Backtrace::new());
|
||||||
|
@ -28,60 +22,36 @@ fn panic_hook(info: &PanicInfo) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
fn main() -> ExitCode {
|
||||||
panic::set_hook(Box::new(panic_hook));
|
panic::set_hook(Box::new(panic_hook));
|
||||||
let args: Vec<String> = std::env::args().collect();
|
let args: Vec<String> = std::env::args().collect();
|
||||||
if args.len() == 2 {
|
if args.len() == 2 {
|
||||||
let fname = &args[1];
|
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);
|
let res = interpret(&src, Some(fname.into()), None, false);
|
||||||
if let Err(e) = res {
|
if let Err(e) = res {
|
||||||
eprintln!("Error: {}", e);
|
eprintln!("Error: {}", e);
|
||||||
|
return ExitCode::from(1);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
repl()?;
|
#[cfg(feature = "repl")]
|
||||||
}
|
{
|
||||||
Ok(())
|
if let Err(e) = repl::repl() {
|
||||||
}
|
eprintln!("Error: {}", e);
|
||||||
|
return ExitCode::from(4)
|
||||||
fn repl() -> Result<(), Box<dyn std::error::Error>> {
|
|
||||||
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)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Err(ReadlineError::Eof) => break,
|
#[cfg(not(feature = "repl"))]
|
||||||
Err(_) => (),
|
{
|
||||||
|
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
|
||||||
}
|
}
|
||||||
|
|
69
complexpr-bin/src/repl.rs
Normal file
69
complexpr-bin/src/repl.rs
Normal file
|
@ -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<dyn std::error::Error>> {
|
||||||
|
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(())
|
||||||
|
}
|
Loading…
Reference in a new issue