1use crate::{
3 args::{Args, Mode},
4 ctx::Context,
5 menu,
6 tools::print_token,
7};
8use cl_ast::{At, Expr, types::Symbol};
9use cl_interpret::{builtin::builtins, convalue::ConValue, env::Environment, interpret::Interpret};
10use cl_lexer::Lexer;
11use cl_parser::{Parser, inliner::ModuleInliner};
12use std::{borrow::Cow, error::Error, path::Path};
13
14pub fn run(args: Args) -> Result<(), Box<dyn Error>> {
16 let Args { file, include, mode, repl } = args;
17
18 let mut env = Environment::new();
19
20 env.add_builtins(&builtins! {
21 fn eval(string) @env {
23 use cl_interpret::error::Error;
24 let string = match string {
25 ConValue::Str(string) => string.to_ref(),
26 ConValue::String(string) => string.as_str(),
27 ConValue::Ref(v) => {
28 let string = v.get(env).cloned().unwrap_or_default();
29 return eval(env, &[string])
30 }
31 _ => Err(Error::TypeError("string", string.typename()))?
32 };
33 match Parser::new(Lexer::new("eval".into(), string)).parse::<Expr>(0) {
34 Err(e) => Ok(ConValue::String(format!("{e}"))),
35 Ok(v) => v.interpret(env),
36 }
37 }
38
39 fn import(path) @env {
41 use cl_interpret::error::Error;
42 match path {
43 ConValue::Str(path) => load_file(env, &**path).or(Ok(ConValue::Empty)),
44 ConValue::String(path) => load_file(env, &**path).or(Ok(ConValue::Empty)),
45 _ => Err(Error::TypeError("string", path.typename()))
46 }
47 }
48
49 fn putchar(ConValue::Char(c)) {
50 print!("{c}");
51 Ok(ConValue::Empty)
52 }
53
54 fn get_line(prompt) {
56 use cl_interpret::error::Error;
57 let prompt = match prompt {
58 ConValue::Str(prompt) => prompt.to_ref(),
59 ConValue::String(prompt) => prompt.as_str(),
60 _ => Err(Error::TypeError("string", prompt.typename()))?,
61 };
62 match repline::Repline::new("", prompt, "").read() {
63 Ok(line) => Ok(ConValue::String(line)),
64 Err(repline::Error::CtrlD(line)) => Ok(ConValue::String(line)),
65 Err(repline::Error::CtrlC(_)) => Err(cl_interpret::error::Error::Break(ConValue::Empty)),
66 Err(e) => Ok(ConValue::String(e.to_string())),
67 }
68 }
69 });
70
71 for path in include {
72 load_file(&mut env, path)?;
73 }
74
75 if repl {
76 if let Some(file) = file
77 && let Err(e) = load_file(&mut env, file)
78 {
79 eprintln!("{e}")
80 }
81 let mut ctx = Context::with_env(env);
82 menu::main_menu(mode, &mut ctx)?;
83 } else {
84 let path = format_path_for_display(file.as_deref());
85 let code = match &file {
86 Some(file) => std::fs::read_to_string(file)?,
87 None => std::io::read_to_string(std::io::stdin())?,
88 };
89
90 match mode {
91 Mode::Lex => lex_code(&path, &code),
92 Mode::Fmt => fmt_code(&path, &code),
93 Mode::Run => run_code(&path, &code, &mut env),
94 }?;
95 }
96 Ok(())
97}
98
99fn format_path_for_display(path: Option<&Path>) -> Cow<'_, str> {
100 match path {
101 Some(file) => file
102 .to_str()
103 .map(Cow::Borrowed)
104 .unwrap_or_else(|| Cow::Owned(file.display().to_string())),
105 None => Cow::Borrowed(""),
106 }
107}
108
109fn load_file(env: &mut Environment, path: impl AsRef<Path>) -> Result<ConValue, Box<dyn Error>> {
110 let path = path.as_ref();
111 let path_display: Symbol = path.display().to_string().as_str().into();
112 let inliner = ModuleInliner::new(path.with_extension(""));
113 let file = std::fs::read_to_string(path)?;
114 let code: Expr = Parser::new(Lexer::new(path_display, &file)).parse(0)?;
115 let code = match inliner.inline(code) {
116 Ok(a) => a,
117 Err((code, io_errs, parse_errs)) => {
118 for (file, err) in io_errs {
119 eprintln!("{}:{err}", file.display());
120 }
121 for (file, err) in parse_errs {
122 eprintln!("{}:{err}", file.display());
123 }
124 code
125 }
126 };
127 match env.eval(&code) {
131 Ok(v) => Ok(v),
132 Err(e) => {
133 eprintln!("{e}");
134 Ok(ConValue::Empty)
135 }
136 }
137}
138
139fn lex_code(path: &str, code: &str) -> Result<(), Box<dyn Error>> {
140 let mut lexer = Lexer::new(path.into(), code);
141 while let Ok(token) = lexer.scan() {
142 if !path.is_empty() {
143 print!("{path}:");
144 }
145 print_token(&token);
146 }
147 Ok(())
148}
149
150fn fmt_code(path: &str, code: &str) -> Result<(), Box<dyn Error>> {
151 let code = Parser::new(Lexer::new(path.into(), code)).parse::<At<Expr>>(0)?;
152 println!("{code}");
153 Ok(())
154}
155
156fn run_code(path: &str, code: &str, env: &mut Environment) -> Result<(), Box<dyn Error>> {
157 let code = Parser::new(Lexer::new(path.into(), code)).parse::<At<Expr>>(0)?;
158 code.interpret(env)?;
159 if env.get("main".into()).is_ok() {
160 match env.call("main".into(), &[]) {
161 Ok(ConValue::Empty) => {}
162 Ok(ret) => println!("{ret}"),
163 Err(e) => println!("Error: {e}"),
164 }
165 }
166 Ok(())
167}