159 lines
3.9 KiB
Rust
159 lines
3.9 KiB
Rust
use std::{cell::RefCell, io::IsTerminal, process::ExitCode, rc::Rc};
|
|
|
|
use clap::ColorChoice;
|
|
use rustyline::{error::ReadlineError, history::{FileHistory, History}, ColorMode, Config, Editor};
|
|
use talc_lang::{compiler::compile_repl, symbol::Symbol, value::{function::disasm_recursive, Value}, Vm};
|
|
|
|
use crate::{helper::TalcHelper, Args};
|
|
|
|
#[derive(Clone, Copy, Default)]
|
|
pub struct ReplColors {
|
|
pub reset: &'static str,
|
|
pub error: &'static str,
|
|
}
|
|
|
|
impl ReplColors {
|
|
fn new(colors: ColorChoice) -> Self {
|
|
let colors = match colors {
|
|
ColorChoice::Always => true,
|
|
ColorChoice::Never => false,
|
|
ColorChoice::Auto => std::io::stdout().is_terminal(),
|
|
};
|
|
if !colors {
|
|
return Self::default()
|
|
}
|
|
|
|
Self {
|
|
reset: "\x1b[0m",
|
|
error: "\x1b[91m",
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
fn get_colmode(args: &Args) -> ColorMode {
|
|
match args.color {
|
|
ColorChoice::Auto => ColorMode::Enabled,
|
|
ColorChoice::Always => ColorMode::Forced,
|
|
ColorChoice::Never => ColorMode::Disabled,
|
|
}
|
|
}
|
|
|
|
pub fn init_rustyline(args: &Args) -> Result<Editor<TalcHelper, FileHistory>, ExitCode> {
|
|
let config = Config::builder()
|
|
.auto_add_history(true)
|
|
.color_mode(get_colmode(args))
|
|
.check_cursor_position(true)
|
|
.completion_type(rustyline::CompletionType::List)
|
|
.max_history_size(4096).unwrap()
|
|
.build();
|
|
let mut hist = FileHistory::default();
|
|
if let Some(f) = &args.histfile {
|
|
if hist.load(f).is_err() {
|
|
eprintln!("Warn: failed to load history");
|
|
} else if hist.save(f).is_err() {
|
|
eprintln!("Warn: failed to save history");
|
|
}
|
|
}
|
|
match rustyline::Editor::with_history(config, hist) {
|
|
Ok(rl) => Ok(rl),
|
|
Err(ReadlineError::Io(e)) => {
|
|
eprintln!("Error creating repl: {e}");
|
|
Err(ExitCode::FAILURE)
|
|
},
|
|
Err(ReadlineError::Errno(e)) => {
|
|
eprintln!("Error creating repl: {e}");
|
|
Err(ExitCode::FAILURE)
|
|
},
|
|
Err(_) => Err(ExitCode::SUCCESS)
|
|
}
|
|
}
|
|
|
|
pub fn repl(args: &Args) -> ExitCode {
|
|
if args.disasm {
|
|
eprintln!("input disassembly enabled");
|
|
}
|
|
|
|
let parser = talc_lang::Parser::new();
|
|
let mut compiler_globals = Vec::new();
|
|
let mut vm = Vm::new(256);
|
|
talc_std::load_all(&mut vm);
|
|
|
|
let interrupt = vm.get_interrupt();
|
|
let ctrlc_res = ctrlc::set_handler(move || {
|
|
interrupt.fetch_or(true, std::sync::atomic::Ordering::Relaxed);
|
|
});
|
|
if let Err(e) = ctrlc_res {
|
|
eprintln!("Warn: couldn't set ctrl+c handler: {e}");
|
|
}
|
|
|
|
let prev1_sym = Symbol::get("_");
|
|
let prev2_sym = Symbol::get("__");
|
|
let prev3_sym = Symbol::get("___");
|
|
|
|
vm.set_global(prev1_sym, Value::Nil);
|
|
vm.set_global(prev2_sym, Value::Nil);
|
|
vm.set_global(prev3_sym, Value::Nil);
|
|
|
|
let c = ReplColors::new(args.color);
|
|
|
|
let mut rl = match init_rustyline(args) {
|
|
Ok(rl) => rl,
|
|
Err(e) => return e,
|
|
};
|
|
|
|
let vm = Rc::new(RefCell::new(vm));
|
|
|
|
rl.set_helper(Some(TalcHelper::new(vm.clone())));
|
|
|
|
loop {
|
|
if let Some(f) = &args.histfile {
|
|
let _ = rl.save_history(f);
|
|
}
|
|
let line = rl.readline(">> ");
|
|
let line = match line {
|
|
Ok(line) => line,
|
|
Err(ReadlineError::Eof) => return ExitCode::SUCCESS,
|
|
Err(ReadlineError::Interrupted) => continue,
|
|
Err(e) => {
|
|
eprintln!("{}Error:{} {e}", c.error, c.reset);
|
|
continue
|
|
},
|
|
};
|
|
|
|
let ex = match parser.parse(&line) {
|
|
Ok(ex) => ex,
|
|
Err(e) => {
|
|
eprintln!("{}Error:{} {e}", c.error, c.reset);
|
|
continue
|
|
},
|
|
};
|
|
|
|
let (f, g) = compile_repl(&ex, &compiler_globals);
|
|
compiler_globals = g;
|
|
let func = Rc::new(f);
|
|
|
|
if args.disasm {
|
|
if let Err(e) = disasm_recursive(&func, &mut std::io::stderr()) {
|
|
eprintln!("{}Error:{} {e}", c.error, c.reset);
|
|
}
|
|
}
|
|
|
|
let mut vm = vm.borrow_mut();
|
|
match vm.run_function(func.clone(), vec![func.into()]) {
|
|
Ok(v) => {
|
|
let prev2 = vm.get_global(prev2_sym).unwrap().clone();
|
|
vm.set_global(prev3_sym, prev2);
|
|
let prev1 = vm.get_global(prev1_sym).unwrap().clone();
|
|
vm.set_global(prev2_sym, prev1);
|
|
vm.set_global(prev1_sym, v.clone());
|
|
if v != Value::Nil {
|
|
println!("{v:#}");
|
|
}
|
|
}
|
|
Err(e) => eprintln!("{}Error:{} {e}", c.error, c.reset),
|
|
}
|
|
}
|
|
}
|
|
|