1use crate::prelude::*;
2use crate::to_latex::to_latex_from_source_after_builtins;
3use std::env;
4use std::fs;
5use std::path::{Path, PathBuf};
6use std::process;
7
8pub const MAIN_DOT_LIT: &str = "main.lit";
9
10pub const VERSION: &str = env!("CARGO_PKG_VERSION");
11
12pub fn run_cli() {
13 let args: Vec<String> = env::args().skip(1).collect();
14 let mut index: usize = 0;
15
16 if !args.is_empty() {
17 let head = args[index].as_str();
18
19 match head {
20 "-help" => {
21 print_help_message();
22 println!();
23 println!("If no options are provided, starts interactive REPL mode.");
24 return;
25 }
26 "-version" => {
27 println!("Litex Kernel: litex {}", VERSION);
28 return;
29 }
30 "-e" => {
31 index += 1;
32 let code = match read_non_flag_value_after_flag(&args, &mut index, "-e") {
33 Ok(value) => value,
34 Err(message) => {
35 eprintln!("{}", message);
36 print_help_message();
37 process::exit(2);
38 }
39 };
40 let mut runtime = Runtime::new_with_builtin_code();
41 runtime.new_file_path_new_env_new_name_scope("-e");
42
43 let (stmt_results, runtime_error) = run_source_code(code.as_str(), &mut runtime);
44 let output =
45 render_run_source_code_output(&runtime, &stmt_results, &runtime_error, true);
46 println!("{}", output.1.trim());
47 println!("{}", repl_footer_placeholder());
48 return;
49 }
50 "-f" => {
51 index += 1;
52 let file_path = match read_non_flag_value_after_flag(&args, &mut index, "-f") {
53 Ok(value) => value,
54 Err(message) => {
55 eprintln!("{}", message);
56 print_help_message();
57 process::exit(2);
58 }
59 };
60 main_flag_file(file_path.as_str());
61 return;
62 }
63 "-r" => {
64 index += 1;
65 let repo_path = match read_non_flag_value_after_flag(&args, &mut index, "-r") {
66 Ok(value) => value,
67 Err(message) => {
68 eprintln!("{}", message);
69 print_help_message();
70 process::exit(2);
71 }
72 };
73 let joined = Path::new(repo_path.as_str()).join(MAIN_DOT_LIT);
74 let joined_string = match joined.to_str() {
75 Some(path_string) => path_string.to_string(),
76 None => {
77 eprintln!("Error: repo path is not valid UTF-8");
78 process::exit(1);
79 }
80 };
81 main_flag_file(joined_string.as_str());
82 return;
83 }
84 "-latex" => {
85 index += 1;
86 if index >= args.len() {
87 println!("{}", run_latex_interactive());
88 return;
89 }
90 let latex_target_flag = match read_any_value_after_flag(&args, &mut index, "-latex")
91 {
92 Ok(value) => value,
93 Err(message) => {
94 eprintln!("{}", message);
95 print_help_message();
96 process::exit(2);
97 }
98 };
99 let latex_output_result = match latex_target_flag.as_str() {
100 "-f" => {
101 let file_path =
102 match read_non_flag_value_after_flag(&args, &mut index, "-f") {
103 Ok(value) => value,
104 Err(message) => {
105 eprintln!("{}", message);
106 print_help_message();
107 process::exit(2);
108 }
109 };
110 compile_file_to_latex(file_path.as_str())
111 }
112 "-e" => {
113 let code = match read_non_flag_value_after_flag(&args, &mut index, "-e") {
114 Ok(value) => value,
115 Err(message) => {
116 eprintln!("{}", message);
117 print_help_message();
118 process::exit(2);
119 }
120 };
121 compile_code_to_latex(code.as_str())
122 }
123 "-r" => {
124 let repo_path =
125 match read_non_flag_value_after_flag(&args, &mut index, "-r") {
126 Ok(value) => value,
127 Err(message) => {
128 eprintln!("{}", message);
129 print_help_message();
130 process::exit(2);
131 }
132 };
133 let joined = Path::new(repo_path.as_str()).join(MAIN_DOT_LIT);
134 let joined_string = match joined.to_str() {
135 Some(path_string) => path_string.to_string(),
136 None => {
137 eprintln!("Error: repo path is not valid UTF-8");
138 process::exit(1);
139 }
140 };
141 compile_file_to_latex(joined_string.as_str())
142 }
143 _ => {
144 eprintln!(
145 "-latex must be followed by one of: -f <file>, -e <code>, -r <repo>"
146 );
147 print_help_message();
148 process::exit(2);
149 }
150 };
151 println!("{}", latex_output_result);
152 return;
153 }
154 "-fmt" => {
155 index += 1;
156 let code = match read_non_flag_value_after_flag(&args, &mut index, "-fmt") {
157 Ok(value) => value,
158 Err(message) => {
159 eprintln!("{}", message);
160 print_help_message();
161 process::exit(2);
162 }
163 };
164 println!("{}", format_code(code.as_str()));
165 return;
166 }
167 "-install" => {
168 index += 1;
169 let module_name =
170 match read_non_flag_value_after_flag(&args, &mut index, "-install") {
171 Ok(value) => value,
172 Err(message) => {
173 eprintln!("{}", message);
174 print_help_message();
175 process::exit(2);
176 }
177 };
178 install_module(module_name.as_str());
179 return;
180 }
181 "-uninstall" => {
182 index += 1;
183 let module_name =
184 match read_non_flag_value_after_flag(&args, &mut index, "-uninstall") {
185 Ok(value) => value,
186 Err(message) => {
187 eprintln!("{}", message);
188 print_help_message();
189 process::exit(2);
190 }
191 };
192 uninstall_module(module_name.as_str());
193 return;
194 }
195 "-list" => {
196 list_installed_modules();
197 return;
198 }
199 "-update" => {
200 index += 1;
201 let module_name = match read_non_flag_value_after_flag(&args, &mut index, "-update")
202 {
203 Ok(value) => value,
204 Err(message) => {
205 eprintln!("{}", message);
206 print_help_message();
207 process::exit(2);
208 }
209 };
210 update_module(module_name.as_str());
211 return;
212 }
213 "-tutorial" => {
214 run_tutorial();
215 return;
216 }
217 other => {
218 eprintln!("unknown argument: {}", other);
219 print_help_message();
220 process::exit(2);
221 }
222 }
223 }
224
225 run_repl(VERSION);
226}
227
228fn read_non_flag_value_after_flag(
230 args: &[String],
231 index: &mut usize,
232 flag_name: &str,
233) -> Result<String, String> {
234 let value = match args.get(*index) {
235 Some(candidate) if !candidate.starts_with('-') => candidate.clone(),
236 _ => {
237 return Err(format!("{} requires a value", flag_name));
238 }
239 };
240 *index += 1;
241 Ok(value)
242}
243
244fn read_any_value_after_flag(
246 args: &[String],
247 index: &mut usize,
248 flag_name: &str,
249) -> Result<String, String> {
250 let value = match args.get(*index) {
251 Some(candidate) => candidate.clone(),
252 None => return Err(format!("{} requires a value", flag_name)),
253 };
254 *index += 1;
255 Ok(value)
256}
257
258fn print_help_message() {
259 println!("{}", help_message());
260}
261
262fn remove_windows_carriage_return(path_or_code: &str) -> String {
263 path_or_code.replace('\r', "")
264}
265
266fn main_flag_file(file_flag: &str) {
267 let path = remove_windows_carriage_return(file_flag);
268
269 let abs_file_path: PathBuf = if Path::new(path.as_str()).is_absolute() {
270 PathBuf::from(path.as_str())
271 } else {
272 let working_directory_result = env::current_dir();
273 let working_directory = match working_directory_result {
274 Ok(path) => path,
275 Err(error) => {
276 eprintln!("Error: failed to get current working directory: {}", error);
277 return;
278 }
279 };
280 working_directory.join(path.as_str())
281 };
282
283 if abs_file_path.parent().is_none() {
284 eprintln!("Error: could not get parent directory of file path");
285 return;
286 }
287
288 let path_string = match abs_file_path.to_str() {
289 Some(path_string) => path_string.to_string(),
290 None => {
291 eprintln!("Error: file path is not valid UTF-8");
292 return;
293 }
294 };
295
296 let output = run_source_code_in_file(path_string.as_str());
297 println!("{}", string_with_trimmed_outer_newlines(output.as_str()));
298 println!("{}", repl_footer_placeholder());
299}
300
301fn string_with_trimmed_outer_newlines(text: &str) -> String {
302 text.trim().to_string()
303}
304
305fn repl_footer_placeholder() -> String {
306 "(REPL / ret-type footer: not implemented in Rust kernel yet)".to_string()
307}
308
309fn compile_code_to_latex(code: &str) -> String {
310 let code = remove_windows_carriage_return(code);
311 match to_latex_from_source_after_builtins(code.as_str(), "-latex -e") {
312 Ok(s) => s,
313 Err(e) => {
314 let runtime = Runtime::new();
315 display_runtime_error_json(&runtime, &e, true)
316 }
317 }
318}
319
320fn compile_file_to_latex(file_path: &str) -> String {
321 let source = match fs::read_to_string(file_path) {
322 Ok(content) => remove_windows_carriage_return(&content),
323 Err(e) => return format!("Could not read file {:?}: {}", file_path, e),
324 };
325 match to_latex_from_source_after_builtins(source.as_str(), file_path) {
326 Ok(s) => s,
327 Err(e) => {
328 let runtime = Runtime::new();
329 display_runtime_error_json(&runtime, &e, true)
330 }
331 }
332}
333
334fn format_code(_code: &str) -> String {
335 return "-fmt: format code is not implemented in the Rust kernel yet".to_string();
336}
337
338fn install_module(module_name: &str) -> String {
339 return format!(
340 "-install: module manager is not implemented in the Rust kernel yet (module: {})",
341 module_name
342 );
343}
344
345fn uninstall_module(module_name: &str) -> String {
346 return format!(
347 "-uninstall: module manager is not implemented in the Rust kernel yet (module: {})",
348 module_name
349 );
350}
351
352fn list_installed_modules() -> String {
353 return "-list: module manager is not implemented in the Rust kernel yet".to_string();
354}
355
356fn update_module(module_name: &str) -> String {
357 return format!(
358 "-update: module manager is not implemented in the Rust kernel yet (module: {})",
359 module_name
360 );
361}
362
363fn run_tutorial() -> String {
364 return "-tutorial: not implemented in the Rust kernel yet".to_string();
365}
366
367fn run_latex_interactive() -> String {
368 return "-latex: interactive LaTeX mode is not implemented in the Rust kernel yet".to_string();
369}
370
371fn help_message() -> String {
372 let result = r#"litex : run Litex interactively in your terminal
373litex -f <file> : run the given file
374litex -r <repo> : run the given repository
375litex -e <code> : execute the given code
376litex -latex : compile the given file or code to LaTeX interactively in your terminal
377litex -latex -f <file> : compile the given file to LaTeX
378litex -latex -e <code> : compile the given code to LaTeX
379litex -latex -r <repo> : compile the given repository to LaTeX
380litex -help : show the help message
381litex -version : show the version
382litex -fmt : format the given code
383litex -install <module> : install the given module
384litex -uninstall <module> : uninstall the given module
385litex -list : list all installed modules
386litex -update <module> : update the given module
387litex -tutorial : run the tutorial
388"#;
389 result.to_string()
390}