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
37pub 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}