Skip to main content

cl_repl/
menu.rs

1use std::error::Error;
2
3use crate::{ansi, args::Mode, ctx};
4use cl_ast::{At, Expr};
5use cl_interpret::convalue::ConValue;
6use cl_lexer::Lexer;
7use cl_parser::Parser;
8use repline::{error::ReplResult, prebaked::*};
9
10pub fn clear() {
11    print!("{}", ansi::CLEAR_ALL);
12    banner()
13}
14
15pub fn banner() {
16    println!("--- conlang v{} 💪🦈 ---", env!("CARGO_PKG_VERSION"))
17}
18
19type ReplCallback = fn(&mut ctx::Context, &str) -> Result<Response, Box<dyn Error>>;
20type ReplMode = (&'static str, &'static str, &'static str, ReplCallback);
21
22#[rustfmt::skip]
23const MODES: &[ReplMode] = &[
24    (ansi::CYAN,            " .> ", "  > ", mode_run),
25    (ansi::BRIGHT_BLUE,     " .> ", "  > ", mode_lex),
26    (ansi::BRIGHT_MAGENTA,  " .> ", "  > ", mode_fmt),
27];
28
29const fn get_mode(mode: Mode) -> ReplMode {
30    match mode {
31        Mode::Lex => MODES[1],
32        Mode::Fmt => MODES[2],
33        Mode::Run => MODES[0],
34    }
35}
36
37/// Presents a selection interface to the user
38pub fn main_menu(mode: Mode, ctx: &mut ctx::Context) -> ReplResult<()> {
39    banner();
40
41    const HELP: &str = "Valid commands
42    help  : Print this list
43    clear : Clear the screen
44    exit  : Exit the program
45    lex   : Lex the input
46    fmt   : Format the input
47    run   : Evaluate some expressions";
48    ctx.env.bind("help", HELP);
49
50    let mut mode = get_mode(mode);
51    read_and_mut(mode.0, mode.1, mode.2, |rl, line| {
52        match line.trim() {
53            "" => return Ok(Response::Continue),
54            "help" => println!("{HELP}"),
55            "clear" => clear(),
56            "exit" => return Ok(Response::Break),
57            "lex" => mode = get_mode(Mode::Lex),
58            "fmt" => mode = get_mode(Mode::Fmt),
59            "run" => mode = get_mode(Mode::Run),
60            _ => return mode.3(ctx, line),
61        }
62        rl.set_prompt(mode.0, mode.1, mode.2);
63        Ok(Response::Accept)
64    })
65}
66
67pub fn mode_run(ctx: &mut ctx::Context, line: &str) -> Result<Response, Box<dyn Error>> {
68    use cl_ast::fold::Fold;
69    use cl_parser::inliner::ModuleInliner;
70
71    if line.trim().is_empty() {
72        return Ok(Response::Deny);
73    }
74    let code = Parser::new(Lexer::new("".into(), line)).parse::<At<Expr>>(0)?;
75    let Ok(code) = ModuleInliner::new(".").fold_at_expr(code);
76
77    print!("{}", ansi::OUTPUT);
78    match ctx.run(&code) {
79        Ok(ConValue::Empty) => print!("{}", ansi::RESET),
80        Ok(v) => println!("{}{v}", ansi::RESET),
81        Err(e) => println!("{}! > {e}{}", ansi::RED, ansi::RESET),
82    }
83    Ok(Response::Accept)
84}
85
86pub fn mode_lex(_ctx: &mut ctx::Context, line: &str) -> Result<Response, Box<dyn Error>> {
87    let mut lexer = Lexer::new("".into(), line);
88    while let Ok(token) = lexer.scan() {
89        crate::tools::print_token(&token);
90    }
91
92    Ok(Response::Accept)
93}
94
95pub fn mode_fmt(_ctx: &mut ctx::Context, line: &str) -> Result<Response, Box<dyn Error>> {
96    let mut p = Parser::new(Lexer::new("".into(), line));
97
98    match p.parse::<At<Expr>>(0) {
99        Ok(code) => println!("{}{code}{}", ansi::OUTPUT, ansi::RESET),
100        Err(e) => Err(e)?,
101    }
102
103    Ok(Response::Accept)
104}