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