1use crate::{
3 args::{Args, Mode},
4 ctx::Context,
5 menu,
6 tools::print_token,
7};
8use cl_ast::File;
9use cl_interpret::{builtin::builtins, convalue::ConValue, env::Environment, interpret::Interpret};
10use cl_lexer::Lexer;
11use cl_parser::Parser;
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 = env.get_id(*v).cloned().unwrap_or_default();
29 return eval(env, &[string])
30 }
31 _ => Err(Error::TypeError())?
32 };
33 match Parser::new("eval", Lexer::new(string)).parse::<cl_ast::Stmt>() {
34 Err(e) => Ok(ConValue::Str(format!("{e}").into())),
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())
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())?,
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::Str(e.to_string().into())),
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 inliner = cl_parser::inliner::ModuleInliner::new(path.with_extension(""));
112 let file = std::fs::read_to_string(path)?;
113 let code = Parser::new(path.display().to_string(), Lexer::new(&file)).parse()?;
114 let code = match inliner.inline(code) {
115 Ok(a) => a,
116 Err((code, io_errs, parse_errs)) => {
117 for (file, err) in io_errs {
118 eprintln!("{}:{err}", file.display());
119 }
120 for (file, err) in parse_errs {
121 eprintln!("{}:{err}", file.display());
122 }
123 code
124 }
125 };
126 use cl_ast::WeightOf;
127 eprintln!("File {} weighs {} units", code.name, code.weight_of());
128
129 match env.eval(&code) {
130 Ok(v) => Ok(v),
131 Err(e) => {
132 eprintln!("{e}");
133 Ok(ConValue::Empty)
134 }
135 }
136}
137
138fn lex_code(path: &str, code: &str) -> Result<(), Box<dyn Error>> {
139 for token in Lexer::new(code) {
140 if !path.is_empty() {
141 print!("{}:", path);
142 }
143 match token {
144 Ok(token) => print_token(&token),
145 Err(e) => println!("{e}"),
146 }
147 }
148 Ok(())
149}
150
151fn fmt_code(path: &str, code: &str) -> Result<(), Box<dyn Error>> {
152 let code = Parser::new(path, Lexer::new(code)).parse::<File>()?;
153 println!("{code}");
154 Ok(())
155}
156
157fn run_code(path: &str, code: &str, env: &mut Environment) -> Result<(), Box<dyn Error>> {
158 let code = Parser::new(path, Lexer::new(code)).parse::<File>()?;
159 match code.interpret(env)? {
160 ConValue::Empty => {}
161 ret => println!("{ret}"),
162 }
163 if env.get("main".into()).is_ok() {
164 match env.call("main".into(), &[]) {
165 Ok(ConValue::Empty) => {}
166 Ok(ret) => println!("{ret}"),
167 Err(e) => println!("Error: {e}"),
168 }
169 }
170 Ok(())
171}