From 216015502d626b29d788cc31b78905cd43d41db8 Mon Sep 17 00:00:00 2001 From: uncenter <47499684+uncenter@users.noreply.github.com> Date: Wed, 22 May 2024 15:35:11 -0400 Subject: [PATCH] refactor: use clap for argument parsing --- Cargo.lock | 254 +++++++++++++++++++++++++++++++++++++-- Cargo.toml | 3 +- src/cli/cli_interface.rs | 161 ------------------------- src/cli/flag_registry.rs | 91 -------------- src/cli/mod.rs | 2 - src/main.rs | 112 ++++++++++++++++- 6 files changed, 349 insertions(+), 274 deletions(-) delete mode 100644 src/cli/cli_interface.rs delete mode 100644 src/cli/flag_registry.rs delete mode 100644 src/cli/mod.rs diff --git a/Cargo.lock b/Cargo.lock index c4a53771..6c248fb9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4,14 +4,64 @@ version = 3 [[package]] name = "amber" -version = "1.0.0" +version = "0.3.1-alpha" dependencies = [ + "clap", "colored", "heraclitus-compiler", "itertools", "similar-string", ] +[[package]] +name = "anstream" +version = "0.6.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "418c75fa768af9c03be99d17643f93f79bbba589895012a80e3452a19ddda15b" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "is_terminal_polyfill", + "utf8parse", +] + +[[package]] +name = "anstyle" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "038dfcf04a5feb68e9c60b21c9625a54c2c0616e79b72b0fd87075a056ae1d1b" + +[[package]] +name = "anstyle-parse" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c03a11a9034d92058ceb6ee011ce58af4a9bf61491aa7e1e59ecd24bd40d22d4" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-query" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a64c907d4e79225ac72e2a354c9ce84d50ebb4586dee56c82b3ee73004f537f5" +dependencies = [ + "windows-sys 0.52.0", +] + +[[package]] +name = "anstyle-wincon" +version = "3.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61a38449feb7068f52bb06c12759005cf459ee52bb4adc1d5a7c4322d716fb19" +dependencies = [ + "anstyle", + "windows-sys 0.52.0", +] + [[package]] name = "bitflags" version = "2.4.0" @@ -33,6 +83,52 @@ dependencies = [ "libc", ] +[[package]] +name = "clap" +version = "4.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90bc066a67923782aa8515dbaea16946c5bcc5addbd668bb80af688e53e548a0" +dependencies = [ + "clap_builder", + "clap_derive", +] + +[[package]] +name = "clap_builder" +version = "4.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae129e2e766ae0ec03484e609954119f123cc1fe650337e155d03b022f24f7b4" +dependencies = [ + "anstream", + "anstyle", + "clap_lex", + "strsim", +] + +[[package]] +name = "clap_derive" +version = "4.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "528131438037fd55894f62d6e9f068b8f45ac57ffa77517819645d10aed04f64" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "clap_lex" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "98cc8fbded0c607b7ba9dd60cd98df59af97e84d24e49c8557331cfc26d301ce" + +[[package]] +name = "colorchoice" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b6a852b24ab71dffc585bcb46eaf7959d175cb865a7152e35b348d1b2960422" + [[package]] name = "colored" version = "2.0.4" @@ -41,7 +137,7 @@ checksum = "2674ec482fbc38012cf31e6c42ba0177b431a0cb6f15fe40efa5aab1bda516f6" dependencies = [ "is-terminal", "lazy_static", - "windows-sys", + "windows-sys 0.48.0", ] [[package]] @@ -58,7 +154,7 @@ checksum = "136526188508e25c6fef639d7927dfb3e0e3084488bf202267829cf7fc23dbdd" dependencies = [ "errno-dragonfly", "libc", - "windows-sys", + "windows-sys 0.48.0", ] [[package]] @@ -71,6 +167,12 @@ dependencies = [ "libc", ] +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + [[package]] name = "heraclitus-compiler" version = "1.5.8" @@ -96,9 +198,15 @@ checksum = "cb0889898416213fab133e1d33a0e5858a48177452750691bde3666d0fdbaf8b" dependencies = [ "hermit-abi", "rustix", - "windows-sys", + "windows-sys 0.48.0", ] +[[package]] +name = "is_terminal_polyfill" +version = "1.70.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8478577c03552c21db0e2724ffb8986a5ce7af88107e6be5d2ee6e158c12800" + [[package]] name = "itertools" version = "0.11.0" @@ -135,6 +243,24 @@ dependencies = [ "unicode-width", ] +[[package]] +name = "proc-macro2" +version = "1.0.83" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b33eb56c327dec362a9e55b3ad14f9d2f0904fb5a5b03b513ab5465399e9f43" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" +dependencies = [ + "proc-macro2", +] + [[package]] name = "rustix" version = "0.38.10" @@ -145,7 +271,7 @@ dependencies = [ "errno", "libc", "linux-raw-sys", - "windows-sys", + "windows-sys 0.48.0", ] [[package]] @@ -154,19 +280,57 @@ version = "1.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d3ac42455f28f7f9fc2ca816746b7143356f51ae195abb35d5bb4ac3808c7fa3" +[[package]] +name = "strsim" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" + +[[package]] +name = "syn" +version = "2.0.65" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2863d96a84c6439701d7a38f9de935ec562c8832cc55d1dde0f513b52fad106" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "unicode-ident" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" + [[package]] name = "unicode-width" version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b" +[[package]] +name = "utf8parse" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" + [[package]] name = "windows-sys" version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" dependencies = [ - "windows-targets", + "windows-targets 0.48.5", +] + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets 0.52.5", ] [[package]] @@ -175,13 +339,29 @@ version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" dependencies = [ - "windows_aarch64_gnullvm", - "windows_aarch64_msvc", - "windows_i686_gnu", - "windows_i686_msvc", - "windows_x86_64_gnu", - "windows_x86_64_gnullvm", - "windows_x86_64_msvc", + "windows_aarch64_gnullvm 0.48.5", + "windows_aarch64_msvc 0.48.5", + "windows_i686_gnu 0.48.5", + "windows_i686_msvc 0.48.5", + "windows_x86_64_gnu 0.48.5", + "windows_x86_64_gnullvm 0.48.5", + "windows_x86_64_msvc 0.48.5", +] + +[[package]] +name = "windows-targets" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f0713a46559409d202e70e28227288446bf7841d3211583a4b53e3f6d96e7eb" +dependencies = [ + "windows_aarch64_gnullvm 0.52.5", + "windows_aarch64_msvc 0.52.5", + "windows_i686_gnu 0.52.5", + "windows_i686_gnullvm", + "windows_i686_msvc 0.52.5", + "windows_x86_64_gnu 0.52.5", + "windows_x86_64_gnullvm 0.52.5", + "windows_x86_64_msvc 0.52.5", ] [[package]] @@ -190,38 +370,86 @@ version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7088eed71e8b8dda258ecc8bac5fb1153c5cffaf2578fc8ff5d61e23578d3263" + [[package]] name = "windows_aarch64_msvc" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9985fd1504e250c615ca5f281c3f7a6da76213ebd5ccc9561496568a2752afb6" + [[package]] name = "windows_i686_gnu" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" +[[package]] +name = "windows_i686_gnu" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88ba073cf16d5372720ec942a8ccbf61626074c6d4dd2e745299726ce8b89670" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87f4261229030a858f36b459e748ae97545d6f1ec60e5e0d6a3d32e0dc232ee9" + [[package]] name = "windows_i686_msvc" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" +[[package]] +name = "windows_i686_msvc" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db3c2bf3d13d5b658be73463284eaf12830ac9a26a90c717b7f771dfe97487bf" + [[package]] name = "windows_x86_64_gnu" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e4246f76bdeff09eb48875a0fd3e2af6aada79d409d33011886d3e1581517d9" + [[package]] name = "windows_x86_64_gnullvm" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "852298e482cd67c356ddd9570386e2862b5673c85bd5f88df9ab6802b334c596" + [[package]] name = "windows_x86_64_msvc" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0" diff --git a/Cargo.toml b/Cargo.toml index ebb7ef95..55ed841d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "amber" -version = "1.0.0" +version = "0.3.1-alpha" edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html @@ -10,6 +10,7 @@ heraclitus-compiler = "1.5.8" similar-string = "1.4.2" colored = "2.0.0" itertools = "0.11.0" +clap = { version = "4.5.4", features = ["derive"] } [profile.release] debug = true diff --git a/src/cli/cli_interface.rs b/src/cli/cli_interface.rs deleted file mode 100644 index 3727feaa..00000000 --- a/src/cli/cli_interface.rs +++ /dev/null @@ -1,161 +0,0 @@ -use heraclitus_compiler::prelude::*; -use colored::Colorize; -use crate::compiler::AmberCompiler; -use super::flag_registry::FlagRegistry; -use std::env; -use std::process::Command; -use std::{io, io::prelude::*}; -use std::fs; - -pub struct CLI { - args: Vec, - flags: FlagRegistry, - name: String, - exe_name: String, - version: String, - ext: String -} - -impl Default for CLI { - fn default() -> Self { - Self::new() - } -} - -impl CLI { - pub fn new() -> Self { - CLI { - args: vec![], - flags: FlagRegistry::new(), - name: "Amber".to_string(), - exe_name: "amber".to_string(), - version: env!("CARGO_PKG_VERSION").to_string(), - ext: ".ab".to_string() - } - } - - pub fn run(&mut self) { - self.flags.register("-e", true); - self.flags.register("-h", false); - self.flags.register("--help", false); - self.args = self.flags.parse(env::args().collect()); - // Check all flags - if self.flags.flag_triggered("-e") { - match self.flags.get_flag("-e").unwrap().value.clone() { - Some(code) => { - let code = format!("import * from \"std\"\n{code}"); - match AmberCompiler::new(code, None).compile() { - Ok((messages, code)) => { - messages.iter().for_each(|m| m.show()); - (!messages.is_empty()).then(|| self.render_dash()); - AmberCompiler::execute(code, self.flags.get_args()) - }, - Err(err) => { - err.show(); - std::process::exit(1); - } - } - }, - None => { - Message::new_err_msg("No value passed after -e flag") - .comment("Write code to be evaluated after the -e flag") - .show(); - std::process::exit(1); - } - } - } - // Parse input file - else if self.args.len() >= 2 { - let input = self.args[1].clone(); - match self.read_file(input.clone()) { - Ok(code) => { - match AmberCompiler::new(code, Some(input)).compile() { - Ok((messages, code)) => { - messages.iter().for_each(|m| m.show()); - // Save to the output file - if self.args.len() >= 3 { - Self::save_to_file(self.args[2].clone(), code) - } - // Execute the code - else { - (!messages.is_empty()).then(|| self.render_dash()); - AmberCompiler::execute(code, self.flags.get_args()); - } - }, - Err(err) => { - err.show(); - std::process::exit(1); - } - } - } - Err(err) => { - Message::new_err_msg(err.to_string()).show(); - std::process::exit(1); - } - } - } - else { - println!("{}'s compiler", self.name); - println!("Version {}\n", self.version); - println!("USAGE:\t\t\t\tEXAMPLE:"); - println!("{}", "For evaluation:".dimmed()); - { - let example = format!("{} foo{}", self.exe_name, self.ext).dimmed(); - println!("\t{} [INPUT]\t\t{}", self.exe_name, example); - } - { - let example = format!("{} -e \"\\$echo Hello World\\$\"", self.exe_name).dimmed(); - println!("\t{} -e [EXPR]\t\t{}", self.exe_name, example); - } - println!("{}", "For compiling:".dimmed()); - { - let example = format!("{} foo{} bar.sh", self.exe_name, self.ext).dimmed(); - println!("\t{} [INPUT] [OUTPUT]\t{}", self.exe_name, example); - } - } - } - - #[cfg(target_os = "windows")] - fn set_file_permission(_file: &fs::File, _output: String) { - // We don't need to set permission on Windows - } - - #[cfg(not(target_os = "windows"))] - fn set_file_permission(file: &std::fs::File, path: String) { - use std::os::unix::prelude::PermissionsExt; - let mut perm = fs::metadata(path).unwrap().permissions(); - perm.set_mode(0o755); - file.set_permissions(perm).unwrap(); - } - - #[inline] - fn save_to_file(output_path: String, code: String) { - match fs::File::create(output_path.clone()) { - Ok(mut file) => { - write!(file, "{}", code).unwrap(); - Self::set_file_permission(&file, output_path); - - }, - Err(err) => { - Message::new_err_msg(err.to_string()).show(); - std::process::exit(1); - } - } - } - - #[inline] - #[allow(unused_must_use)] - fn render_dash(&self) { - let str = "%.s─".dimmed(); - Command::new("bash") - .arg("-c") - .arg(format!("printf {str} $(seq 1 $(tput cols))")) - .spawn().unwrap().wait(); - println!(); - } - - #[inline] - fn read_file(&self, path: impl AsRef) -> io::Result { - fs::read_to_string(path.as_ref()) - } -} diff --git a/src/cli/flag_registry.rs b/src/cli/flag_registry.rs deleted file mode 100644 index f4abc5eb..00000000 --- a/src/cli/flag_registry.rs +++ /dev/null @@ -1,91 +0,0 @@ -use std::collections::HashMap; - -pub struct Flag { - pub takes_value: bool, - pub triggered: bool, - pub value: Option -} - -impl Flag { - pub fn new(takes_value: bool) -> Self { - Flag { - takes_value, - triggered: false, - value: None - } - } -} - -pub struct FlagRegistry { - flags: HashMap, - args: Vec -} - -impl Default for FlagRegistry { - fn default() -> Self { - Self::new() - } -} - -impl FlagRegistry { - #[inline] - pub fn new() -> Self { - FlagRegistry { - flags: HashMap::new(), - args: vec![] - } - } - - #[inline] - pub fn get_flag(&self, name: impl AsRef) -> Option<&Flag> { - self.flags.get(name.as_ref()) - } - - #[inline] - pub fn get_args(&self) -> &Vec { - &self.args - } - - #[inline] - pub fn flag_triggered(&self, name: impl AsRef) -> bool { - match self.flags.get(name.as_ref()) { - Some(flag) => flag.triggered, - None => false - } - } - - #[inline] - pub fn register(&mut self, name: impl AsRef, takes_value: bool) { - self.flags.insert(name.as_ref().to_string(), Flag::new(takes_value)); - } - - pub fn parse(&mut self, args: Vec) -> Vec { - let mut result = vec![]; - let mut is_value_flag = None; - let mut is_args = false; - for arg in args.iter() { - if is_args { - self.args.push(arg.clone()); - continue; - } - if let Some(name) = &is_value_flag { - let flag = self.flags.get_mut(name).unwrap(); - flag.value = Some(arg.clone()); - continue - } - if let Some(flag) = self.flags.get_mut(arg) { - flag.triggered = true; - if flag.takes_value { - is_value_flag = Some(arg.clone()); - } - continue - } - if arg == "--" { - is_args = true; - continue - } - result.push(arg.to_string()) - } - result - } -} \ No newline at end of file diff --git a/src/cli/mod.rs b/src/cli/mod.rs deleted file mode 100644 index 87d100d9..00000000 --- a/src/cli/mod.rs +++ /dev/null @@ -1,2 +0,0 @@ -pub mod flag_registry; -pub mod cli_interface; diff --git a/src/main.rs b/src/main.rs index f1aaca1c..7207ccb0 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,15 +1,115 @@ +mod compiler; mod modules; mod rules; -mod utils; mod translate; -mod compiler; -pub mod cli; -use cli::cli_interface::CLI; +mod utils; #[cfg(test)] pub mod tests; +use crate::compiler::AmberCompiler; +use clap::Parser; +use colored::Colorize; +use heraclitus_compiler::prelude::*; +use std::fs; +use std::io::prelude::*; +use std::path::PathBuf; +use std::process::Command; + +#[derive(Parser)] +#[command(version, arg_required_else_help(true))] +struct Cli { + input: Option, + output: Option, + + /// Code to evaluate + #[arg(short, long)] + eval: Option, +} + fn main() { - let mut cli = CLI::new(); - cli.run(); + let cli = Cli::parse(); + + if let Some(code) = cli.eval { + let code = format!("import * from \"std\"\n{code}"); + match AmberCompiler::new(code, None).compile() { + Ok((messages, code)) => { + messages.iter().for_each(|m| m.show()); + (!messages.is_empty()).then(|| render_dash()); + AmberCompiler::execute(code, &vec![]) + } + Err(err) => { + err.show(); + std::process::exit(1); + } + } + } else if let Some(input) = cli.input { + let input = String::from(input.to_string_lossy()); + + match fs::read_to_string(&input) { + Ok(code) => { + match AmberCompiler::new(code, Some(input)).compile() { + Ok((messages, code)) => { + messages.iter().for_each(|m| m.show()); + // Save to the output file + if let Some(output) = cli.output { + match fs::File::create(&output) { + Ok(mut file) => { + write!(file, "{}", code).unwrap(); + set_file_permission( + &file, + String::from(output.to_string_lossy()), + ); + } + Err(err) => { + Message::new_err_msg(err.to_string()).show(); + std::process::exit(1); + } + } + } + // Execute the code + else { + (!messages.is_empty()).then(|| render_dash()); + AmberCompiler::execute(code, &vec![]); + } + } + Err(err) => { + err.show(); + std::process::exit(1); + } + } + } + Err(err) => { + Message::new_err_msg(err.to_string()).show(); + std::process::exit(1); + } + } + } else { + } +} + +#[cfg(target_os = "windows")] +fn set_file_permission(_file: &fs::File, _output: String) { + // We don't need to set permission on Windows +} + +#[cfg(not(target_os = "windows"))] +fn set_file_permission(file: &std::fs::File, path: String) { + use std::os::unix::prelude::PermissionsExt; + let mut perm = fs::metadata(path).unwrap().permissions(); + perm.set_mode(0o755); + file.set_permissions(perm).unwrap(); +} + +#[inline] +#[allow(unused_must_use)] +fn render_dash() { + let str = "%.s─".dimmed(); + Command::new("bash") + .arg("-c") + .arg(format!("printf {str} $(seq 1 $(tput cols))")) + .spawn() + .unwrap() + .wait(); + println!(); }