cl-repl: Flatten menu structure so new friendo won't have to ^C^C
Plus it was more annoying than this is.
This commit is contained in:
parent
06ed0eae54
commit
8d641d0060
@ -11,4 +11,4 @@ pub const RESET: &str = "\x1b[0m";
|
||||
pub const OUTPUT: &str = "\x1b[38;5;117m";
|
||||
|
||||
pub const CLEAR_LINES: &str = "\x1b[G\x1b[J";
|
||||
pub const CLEAR_ALL: &str = "\x1b[H\x1b[2J";
|
||||
pub const CLEAR_ALL: &str = "\x1b[H\x1b[2J\x1b[3J";
|
||||
|
@ -49,10 +49,9 @@ pub fn is_terminal() -> bool {
|
||||
/// The CLI's operating mode
|
||||
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord)]
|
||||
pub enum Mode {
|
||||
#[default]
|
||||
Menu,
|
||||
Lex,
|
||||
Fmt,
|
||||
#[default]
|
||||
Run,
|
||||
}
|
||||
|
||||
|
@ -18,12 +18,7 @@ pub fn run(args: Args) -> Result<(), Box<dyn Error>> {
|
||||
let mut env = Environment::new();
|
||||
|
||||
env.add_builtins(&builtins! {
|
||||
/// Clears the screen
|
||||
fn clear() {
|
||||
menu::clear();
|
||||
Ok(ConValue::Empty)
|
||||
}
|
||||
|
||||
/// Lexes, parses, and evaluates an expression in the current env
|
||||
fn eval(string) @env {
|
||||
use cl_interpret::error::Error;
|
||||
let string = match string {
|
||||
@ -84,12 +79,7 @@ pub fn run(args: Args) -> Result<(), Box<dyn Error>> {
|
||||
eprintln!("{e}")
|
||||
}
|
||||
let mut ctx = Context::with_env(env);
|
||||
match mode {
|
||||
Mode::Menu => menu::main_menu(&mut ctx)?,
|
||||
Mode::Lex => menu::lex(&mut ctx)?,
|
||||
Mode::Fmt => menu::fmt(&mut ctx)?,
|
||||
Mode::Run => menu::run(&mut ctx)?,
|
||||
}
|
||||
menu::main_menu(mode, &mut ctx)?;
|
||||
} else {
|
||||
let path = format_path_for_display(file.as_deref());
|
||||
let code = match &file {
|
||||
@ -100,7 +90,7 @@ pub fn run(args: Args) -> Result<(), Box<dyn Error>> {
|
||||
match mode {
|
||||
Mode::Lex => lex_code(&path, &code),
|
||||
Mode::Fmt => fmt_code(&path, &code),
|
||||
Mode::Run | Mode::Menu => run_code(&path, &code, &mut env),
|
||||
Mode::Run => run_code(&path, &code, &mut env),
|
||||
}?;
|
||||
}
|
||||
Ok(())
|
||||
|
@ -1,9 +1,11 @@
|
||||
use crate::{ansi, ctx};
|
||||
use std::error::Error;
|
||||
|
||||
use crate::{ansi, args::Mode, ctx};
|
||||
use cl_ast::Stmt;
|
||||
use cl_interpret::convalue::ConValue;
|
||||
use cl_lexer::Lexer;
|
||||
use cl_parser::Parser;
|
||||
use repline::{error::ReplResult, prebaked::*};
|
||||
use repline::{Error as RlError, error::ReplResult, prebaked::*};
|
||||
|
||||
pub fn clear() {
|
||||
print!("{}", ansi::CLEAR_ALL);
|
||||
@ -14,36 +16,78 @@ pub fn banner() {
|
||||
println!("--- conlang v{} 💪🦈 ---", env!("CARGO_PKG_VERSION"))
|
||||
}
|
||||
|
||||
/// Presents a selection interface to the user
|
||||
pub fn main_menu(ctx: &mut ctx::Context) -> ReplResult<()> {
|
||||
banner();
|
||||
run(ctx)?;
|
||||
read_and(ansi::GREEN, "mu>", " ?>", |line| {
|
||||
match line.trim() {
|
||||
"clear" => clear(),
|
||||
"l" | "lex" => lex(ctx)?,
|
||||
"f" | "fmt" => fmt(ctx)?,
|
||||
"r" | "run" => run(ctx)?,
|
||||
"q" | "quit" => return Ok(Response::Break),
|
||||
"h" | "help" => println!(
|
||||
"Valid commands
|
||||
lex (l): Spin up a lexer, and lex some lines
|
||||
fmt (f): Format the input
|
||||
run (r): Enter the REPL, and evaluate some statements
|
||||
help (h): Print this list
|
||||
quit (q): Exit the program"
|
||||
),
|
||||
_ => Err("Unknown command. Type \"help\" for help")?,
|
||||
type ReplCallback = fn(&mut ctx::Context, &str) -> Result<Response, Box<dyn Error>>;
|
||||
type ReplMode = (&'static str, &'static str, &'static str, ReplCallback);
|
||||
|
||||
#[rustfmt::skip]
|
||||
const MODES: &[ReplMode] = &[
|
||||
(ansi::CYAN, ".>", " >", mode_run),
|
||||
(ansi::BRIGHT_BLUE, "l>", " >", mode_lex),
|
||||
(ansi::BRIGHT_MAGENTA, "f>", " >", mode_fmt),
|
||||
];
|
||||
|
||||
const fn get_mode(mode: Mode) -> ReplMode {
|
||||
match mode {
|
||||
Mode::Lex => MODES[1],
|
||||
Mode::Fmt => MODES[2],
|
||||
Mode::Run => MODES[0],
|
||||
}
|
||||
Ok(Response::Accept)
|
||||
})
|
||||
}
|
||||
|
||||
pub fn run(ctx: &mut ctx::Context) -> ReplResult<()> {
|
||||
/// Presents a selection interface to the user
|
||||
pub fn main_menu(mode: Mode, ctx: &mut ctx::Context) -> ReplResult<()> {
|
||||
let mut mode = get_mode(mode);
|
||||
banner();
|
||||
|
||||
let mut rl = repline::Repline::new(mode.0, mode.1, mode.2);
|
||||
loop {
|
||||
rl.set_prompt(mode.0, mode.1, mode.2);
|
||||
|
||||
let line = match rl.read() {
|
||||
Err(RlError::CtrlC(_)) => return Ok(()),
|
||||
Err(RlError::CtrlD(line)) => {
|
||||
rl.deny();
|
||||
line
|
||||
}
|
||||
Ok(line) => line,
|
||||
Err(e) => Err(e)?,
|
||||
};
|
||||
print!("\x1b[G\x1b[J");
|
||||
match line.trim() {
|
||||
"" => continue,
|
||||
"clear" => clear(),
|
||||
"mode run" => mode = get_mode(Mode::Run),
|
||||
"mode lex" => mode = get_mode(Mode::Lex),
|
||||
"mode fmt" => mode = get_mode(Mode::Fmt),
|
||||
"quit" => return Ok(()),
|
||||
"help" => println!(
|
||||
"Valid commands
|
||||
help : Print this list
|
||||
clear : Clear the screen
|
||||
quit : Exit the program
|
||||
mode lex : Lex the input
|
||||
mode fmt : Format the input
|
||||
mode run : Evaluate some expressions"
|
||||
),
|
||||
_ => match mode.3(ctx, &line) {
|
||||
Ok(Response::Accept) => {
|
||||
rl.accept();
|
||||
continue;
|
||||
}
|
||||
Ok(Response::Deny) => {}
|
||||
Ok(Response::Break) => return Ok(()),
|
||||
Ok(Response::Continue) => continue,
|
||||
Err(e) => rl.print_inline(format_args!("\t\x1b[91m{e}\x1b[0m"))?,
|
||||
},
|
||||
}
|
||||
rl.deny();
|
||||
}
|
||||
}
|
||||
|
||||
pub fn mode_run(ctx: &mut ctx::Context, line: &str) -> Result<Response, Box<dyn Error>> {
|
||||
use cl_ast::ast_visitor::Fold;
|
||||
use cl_parser::inliner::ModuleInliner;
|
||||
|
||||
read_and(ansi::CYAN, "cl>", " ?>", |line| {
|
||||
if line.trim().is_empty() {
|
||||
return Ok(Response::Deny);
|
||||
}
|
||||
@ -57,11 +101,9 @@ pub fn run(ctx: &mut ctx::Context) -> ReplResult<()> {
|
||||
Err(e) => println!("{}! > {e}{}", ansi::RED, ansi::RESET),
|
||||
}
|
||||
Ok(Response::Accept)
|
||||
})
|
||||
}
|
||||
|
||||
pub fn lex(_ctx: &mut ctx::Context) -> ReplResult<()> {
|
||||
read_and(ansi::BRIGHT_BLUE, "lx>", " ?>", |line| {
|
||||
pub fn mode_lex(_ctx: &mut ctx::Context, line: &str) -> Result<Response, Box<dyn Error>> {
|
||||
for token in Lexer::new(line) {
|
||||
match token {
|
||||
Ok(token) => crate::tools::print_token(&token),
|
||||
@ -70,11 +112,9 @@ pub fn lex(_ctx: &mut ctx::Context) -> ReplResult<()> {
|
||||
}
|
||||
|
||||
Ok(Response::Accept)
|
||||
})
|
||||
}
|
||||
|
||||
pub fn fmt(_ctx: &mut ctx::Context) -> ReplResult<()> {
|
||||
read_and(ansi::BRIGHT_MAGENTA, "cl>", " ?>", |line| {
|
||||
pub fn mode_fmt(_ctx: &mut ctx::Context, line: &str) -> Result<Response, Box<dyn Error>> {
|
||||
let mut p = Parser::new("", Lexer::new(line));
|
||||
|
||||
match p.parse::<Stmt>() {
|
||||
@ -83,5 +123,4 @@ pub fn fmt(_ctx: &mut ctx::Context) -> ReplResult<()> {
|
||||
}
|
||||
|
||||
Ok(Response::Accept)
|
||||
})
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user