1use std::error::Error;
2
3use crate::{ansi, args::Mode, ctx};
4use cl_ast::Stmt;
5use cl_interpret::convalue::ConValue;
6use cl_lexer::Lexer;
7use cl_parser::Parser;
8use repline::{Error as RlError, 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, "l>", " >", mode_lex),
26 (ansi::BRIGHT_MAGENTA, "f>", " >", 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 let mut mode = get_mode(mode);
40 banner();
41
42 let mut rl = repline::Repline::new(mode.0, mode.1, mode.2);
43 loop {
44 rl.set_prompt(mode.0, mode.1, mode.2);
45
46 let line = match rl.read() {
47 Err(RlError::CtrlC(_)) => return Ok(()),
48 Err(RlError::CtrlD(line)) => {
49 rl.deny();
50 line
51 }
52 Ok(line) => line,
53 Err(e) => Err(e)?,
54 };
55 print!("\x1b[G\x1b[J");
56 match line.trim() {
57 "" => continue,
58 "clear" => clear(),
59 "mode run" => mode = get_mode(Mode::Run),
60 "mode lex" => mode = get_mode(Mode::Lex),
61 "mode fmt" => mode = get_mode(Mode::Fmt),
62 "quit" => return Ok(()),
63 "help" => println!(
64 "Valid commands
65 help : Print this list
66 clear : Clear the screen
67 quit : Exit the program
68 mode lex : Lex the input
69 mode fmt : Format the input
70 mode run : Evaluate some expressions"
71 ),
72 _ => match mode.3(ctx, &line) {
73 Ok(Response::Accept) => {
74 rl.accept();
75 continue;
76 }
77 Ok(Response::Deny) => {}
78 Ok(Response::Break) => return Ok(()),
79 Ok(Response::Continue) => continue,
80 Err(e) => rl.print_inline(format_args!("\t\x1b[91m{e}\x1b[0m"))?,
81 },
82 }
83 rl.deny();
84 }
85}
86
87pub fn mode_run(ctx: &mut ctx::Context, line: &str) -> Result<Response, Box<dyn Error>> {
88 use cl_ast::ast_visitor::Fold;
89 use cl_parser::inliner::ModuleInliner;
90
91 if line.trim().is_empty() {
92 return Ok(Response::Deny);
93 }
94 let code = Parser::new("", Lexer::new(line)).parse::<Stmt>()?;
95 let code = ModuleInliner::new(".").fold_stmt(code);
96
97 print!("{}", ansi::OUTPUT);
98 match ctx.run(&code) {
99 Ok(ConValue::Empty) => print!("{}", ansi::RESET),
100 Ok(v) => println!("{}{v}", ansi::RESET),
101 Err(e) => println!("{}! > {e}{}", ansi::RED, ansi::RESET),
102 }
103 Ok(Response::Accept)
104}
105
106pub fn mode_lex(_ctx: &mut ctx::Context, line: &str) -> Result<Response, Box<dyn Error>> {
107 for token in Lexer::new(line) {
108 match token {
109 Ok(token) => crate::tools::print_token(&token),
110 Err(e) => eprintln!("! > {}{e}{}", ansi::RED, ansi::RESET),
111 }
112 }
113
114 Ok(Response::Accept)
115}
116
117pub fn mode_fmt(_ctx: &mut ctx::Context, line: &str) -> Result<Response, Box<dyn Error>> {
118 let mut p = Parser::new("", Lexer::new(line));
119
120 match p.parse::<Stmt>() {
121 Ok(code) => println!("{}{code}{}", ansi::OUTPUT, ansi::RESET),
122 Err(e) => Err(e)?,
123 }
124
125 Ok(Response::Accept)
126}