From 61ef02df2c128cd86efcddd3fa53870bc8d575c4 Mon Sep 17 00:00:00 2001 From: Miquel Sabaté Solà Date: Wed, 22 Jan 2025 15:57:49 +0100 Subject: Remove the dependency on clap MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit All three binaries (i.e. readrom, nasm and xa65) used it, and to be honest rolling our own argument parsing was actually pretty easy to achieve. This frees us from a big dependency and it also frees us from inner dependencies such as 'clap_derive' which prevented us from being able to build binaries purely statically. As a side effect, we can now display help/version messages which are more like I'm used to, and certain checks can be moved in the parsing directly instead of having to be done later. As a cherry on top, there is some type masturbation that gets removed along the way. Signed-off-by: Miquel Sabaté Solà --- crates/nasm/src/main.rs | 128 ++++++++++++++++++++++++++++++++++-------------- 1 file changed, 91 insertions(+), 37 deletions(-) (limited to 'crates/nasm/src/main.rs') diff --git a/crates/nasm/src/main.rs b/crates/nasm/src/main.rs index e757207..ef7c7d4 100644 --- a/crates/nasm/src/main.rs +++ b/crates/nasm/src/main.rs @@ -1,37 +1,104 @@ -use clap::Parser as ClapParser; use std::fs::File; use std::io::{self, Write}; use std::path::Path; use xixanta::assembler::assemble; use xixanta::SourceInfo; -/// Assembler for the 6502 microprocessor that targets the NES/Famicom. -#[derive(ClapParser, Debug)] -#[command(version, about, long_about = None)] +/// Version for this program. +const VERSION: &str = "0.1.0"; + +#[derive(Default)] struct Args { - /// Assemble the instructions given on this file. file: String, - - /// Linker configuration to be used. This configuration can be an identifier - /// for the configurations already baked in into this application, or it can - /// be a file path to a configuration of your choosing. See the - /// documentation for more information on this format. Defaults to 'nrom'. - #[arg(short = 'c', long)] config: Option, - - /// Place the output into the given file. Ignored if the `stdout` flag - /// is provided. Defaults to `out.nes`. - #[arg(short = 'o', long)] out: Option, + werror: bool, + stdout: bool, +} - /// Treat warnings as errors. - #[arg(short = 'W', value_name = "Error")] - w: Option, +// Print the help message and quit. +fn print_help() { + println!("Assembler for the 6502 microprocessor that targets the NES/Famicom.\n"); + println!("usage: nasm [OPTIONS] \n"); + println!("Options:"); + println!(" -c, --config \tLinker configuration to be used, whether an identifier or a file path."); + println!(" -o, --out \tFile path where the output should be located after execution."); + println!(" --stdout\t\tPrint the output binary to the standard output."); + println!(" -Werror\t\tWarnings should be treated as errors."); + std::process::exit(0); +} - /// Spit the output into the standard output instead. This ignores any given - /// `out` flag. Disabled by default. - #[arg(long, default_value_t = false)] - stdout: bool, +// Parse the arguments given to the program and returns an Args object with the +// given information. +fn parse_arguments() -> Args { + let mut args = std::env::args(); + let mut res = Args::default(); + + // Skip command name. + args.next(); + + while let Some(arg) = args.next() { + match arg.as_str() { + "-c" | "--config" => match res.config { + Some(_) => die("only specify the '-C/--config' flag once".to_string()), + None => match args.next() { + Some(v) => res.config = Some(v), + None => { + die("you need to provide a value for the '-C/--config' flag".to_string()) + } + }, + }, + "-h" | "--help" => print_help(), + "-o" | "--out" => match res.out { + Some(_) => die("only specify the '-o/--out' flag once".to_string()), + None => { + if res.stdout { + die("you cannot mix '-o/--out' and '--stdout'".to_string()); + } + match args.next() { + Some(v) => res.out = Some(v), + None => { + die("you need to provide a value for the '-o/--out' flag".to_string()) + } + } + } + }, + "--stdout" => match res.out { + Some(_) => die("you cannot mix '-o/--out' and '--stdout'".to_string()), + None => { + if res.stdout { + die("only specify the '--stdout' flag once".to_string()); + } + res.stdout = true; + } + }, + "-v" | "--version" => { + println!("nasm {}", VERSION); + std::process::exit(0); + } + "-Werror" => { + if res.werror { + die("only specify the '-Werror' flag once".to_string()); + } + res.werror = true; + } + _ => { + if arg.starts_with('-') { + die(format!("don't know how to handle the '{}' flag", arg)); + } + if !res.file.is_empty() { + die("cannot have multiple source files".to_string()); + } + res.file = arg; + } + } + } + + if res.file.is_empty() { + die("you need to specify a source file".to_string()); + } + + res } // Print the given `message` and exit(1). @@ -41,7 +108,7 @@ fn die(message: String) { } fn main() { - let args = Args::parse(); + let args = parse_arguments(); // Select the input stream and build the source object. let path = Path::new(&args.file); @@ -74,19 +141,6 @@ fn main() { } }; - // Check if warnings have to be treated as errors. - let warn_as_errors = match args.w { - Some(value) => { - if value.to_lowercase() != "error" { - die("the '-W' flag can only be used as '-Werror'".to_string()); - return; - } else { - true - } - } - None => false, - }; - // And assemble. let mut error_count = 0; let res = assemble( @@ -98,7 +152,7 @@ fn main() { // Print warnings and errors first, while also computing the amount of them // that exists. for warning in res.warnings { - if warn_as_errors { + if args.werror { eprintln!("error: {}", warning); error_count += 1; } else { -- cgit v1.2.3