From cfaa35d8c0df86164c1cb7e30be448c0b7f93c7a Mon Sep 17 00:00:00 2001 From: Nick Fitzgerald Date: Tue, 28 Sep 2021 15:14:42 -0700 Subject: [PATCH] Use `structopt` to derive CLI flags Instead of using `clap` directly --- cranelift/isle/Cargo.lock | 77 ++++++++++++++++++++++++++++++- cranelift/isle/isle/src/error.rs | 20 ++++++-- cranelift/isle/isle/src/lexer.rs | 38 ++++++++-------- cranelift/isle/islec/Cargo.toml | 2 +- cranelift/isle/islec/src/main.rs | 78 +++++++++++++++++--------------- 5 files changed, 153 insertions(+), 62 deletions(-) diff --git a/cranelift/isle/Cargo.lock b/cranelift/isle/Cargo.lock index f4bf43116d..08c529adf6 100644 --- a/cranelift/isle/Cargo.lock +++ b/cranelift/isle/Cargo.lock @@ -115,6 +115,15 @@ version = "0.25.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f0a01e0497841a3b2db4f8afa483cce65f7e96a3498bd6c541734792aeac8fe7" +[[package]] +name = "heck" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d621efb26863f0e9924c6ac577e8275e5e6b77455db64ffa6c65c904e9e132c" +dependencies = [ + "unicode-segmentation", +] + [[package]] name = "hermit-abi" version = "0.1.19" @@ -143,13 +152,19 @@ dependencies = [ name = "islec" version = "0.1.0" dependencies = [ - "clap", "env_logger", "isle", "log", "miette", + "structopt", ] +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + [[package]] name = "libc" version = "0.2.101" @@ -232,6 +247,30 @@ version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3a61765925aec40abdb23812a3a1a01fafc6ffb9da22768b2ce665a9e84e527c" +[[package]] +name = "proc-macro-error" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" +dependencies = [ + "proc-macro-error-attr", + "proc-macro2", + "quote", + "syn", + "version_check", +] + +[[package]] +name = "proc-macro-error-attr" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" +dependencies = [ + "proc-macro2", + "quote", + "version_check", +] + [[package]] name = "proc-macro2" version = "1.0.29" @@ -285,6 +324,30 @@ version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" +[[package]] +name = "structopt" +version = "0.3.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf9d950ef167e25e0bdb073cf1d68e9ad2795ac826f2f3f59647817cf23c0bfa" +dependencies = [ + "clap", + "lazy_static", + "structopt-derive", +] + +[[package]] +name = "structopt-derive" +version = "0.4.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "134d838a2c9943ac3125cf6df165eda53493451b719f3255b2a26b85f772d0ba" +dependencies = [ + "heck", + "proc-macro-error", + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "supports-color" version = "1.1.1" @@ -383,6 +446,12 @@ dependencies = [ "regex", ] +[[package]] +name = "unicode-segmentation" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8895849a949e7845e06bd6dc1aa51731a103c42707010a5b591c0038fb73385b" + [[package]] name = "unicode-width" version = "0.1.8" @@ -401,6 +470,12 @@ version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191" +[[package]] +name = "version_check" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5fecdca9a5291cc2b8dcf7dc02453fee791a280f3743cb0905f8822ae463b3fe" + [[package]] name = "winapi" version = "0.3.9" diff --git a/cranelift/isle/isle/src/error.rs b/cranelift/isle/isle/src/error.rs index f9278a3365..4aee4c0e2e 100644 --- a/cranelift/isle/isle/src/error.rs +++ b/cranelift/isle/isle/src/error.rs @@ -10,8 +10,14 @@ pub type Result = std::result::Result; #[derive(thiserror::Error, Diagnostic, Clone, Debug)] pub enum Error { /// An I/O error. - #[error(transparent)] - IoError(Arc), + #[error("{context}")] + IoError { + /// The underlying I/O error. + #[source] + error: Arc, + /// The context explaining what caused the I/O error. + context: String, + }, /// The input ISLE source has a parse error. #[error("parse error: {msg}")] @@ -53,9 +59,13 @@ pub enum Error { Errors(#[related] Vec), } -impl From for Error { - fn from(e: std::io::Error) -> Self { - Error::IoError(Arc::new(e)) +impl Error { + /// Create a `isle::Error` from the given I/O error and context. + pub fn from_io(error: std::io::Error, context: impl Into) -> Self { + Error::IoError { + error: Arc::new(error), + context: context.into(), + } } } diff --git a/cranelift/isle/isle/src/lexer.rs b/cranelift/isle/isle/src/lexer.rs index f71bd053ff..561a962fb3 100644 --- a/cranelift/isle/isle/src/lexer.rs +++ b/cranelift/isle/isle/src/lexer.rs @@ -1,7 +1,8 @@ //! Lexer for the ISLE language. -use crate::error::Result; +use crate::error::{Error, Result}; use std::borrow::Cow; +use std::path::Path; use std::sync::Arc; /// The lexer. @@ -90,35 +91,36 @@ impl<'a> Lexer<'a> { } /// Create a new lexer from the given files. - pub fn from_files(filenames: impl IntoIterator) -> Result> + pub fn from_files

(file_paths: impl IntoIterator) -> Result> where - S: AsRef, + P: AsRef, { - let filenames: Vec> = filenames.into_iter().map(|f| f.as_ref().into()).collect(); - assert!(!filenames.is_empty()); + let mut filenames = Vec::>::new(); + let mut file_texts = Vec::>::new(); - let file_contents: Vec> = filenames - .iter() - .map(|f| { - use std::io::Read; - let mut f = std::fs::File::open(&**f)?; - let mut s = String::new(); - f.read_to_string(&mut s)?; - Ok(s.into()) - }) - .collect::>()?; + for f in file_paths { + let f = f.as_ref(); + + filenames.push(f.display().to_string().into()); + + let s = std::fs::read_to_string(f) + .map_err(|e| Error::from_io(e, format!("failed to read file: {}", f.display())))?; + file_texts.push(s.into()); + } + + assert!(!filenames.is_empty()); let mut file_starts = vec![]; let mut buf = String::new(); - for file in &file_contents { + for text in &file_texts { file_starts.push(buf.len()); - buf += &file; + buf += &text; buf += "\n"; } let mut l = Lexer { filenames, - file_texts: file_contents, + file_texts, buf: Cow::Owned(buf.into_bytes()), file_starts, pos: Pos { diff --git a/cranelift/isle/islec/Cargo.toml b/cranelift/isle/islec/Cargo.toml index 61230c0e98..cc80b44fc0 100644 --- a/cranelift/isle/islec/Cargo.toml +++ b/cranelift/isle/islec/Cargo.toml @@ -9,5 +9,5 @@ license = "Apache-2.0 WITH LLVM-exception" log = "0.4" isle = { version = "*", path = "../isle/" } env_logger = { version = "0.8", default-features = false } -clap = "2.33" miette = { version = "3.0.0", features = ["fancy"] } +structopt = "0.3.23" diff --git a/cranelift/isle/islec/src/main.rs b/cranelift/isle/islec/src/main.rs index dd41a0c391..031c1769b8 100644 --- a/cranelift/isle/islec/src/main.rs +++ b/cranelift/isle/islec/src/main.rs @@ -1,6 +1,23 @@ -use clap::{App, Arg}; use isle::{compile, lexer, parser}; -use miette::{IntoDiagnostic, Result}; +use miette::{Context, IntoDiagnostic, Result}; +use std::{ + fs, + io::{self, Write}, + path::PathBuf, +}; +use structopt::StructOpt; + +#[derive(StructOpt)] +struct Opts { + /// The output file to write the generated Rust code to. `stdout` is used if + /// this is not given. + #[structopt(short, long, parse(from_os_str))] + output: Option, + + /// The input ISLE DSL source files. + #[structopt(parse(from_os_str))] + inputs: Vec, +} fn main() -> Result<()> { let _ = env_logger::try_init(); @@ -15,46 +32,33 @@ fn main() -> Result<()> { ) })); - let matches = App::new("isle") - .version(env!("CARGO_PKG_VERSION")) - .author("Chris Fallin ") - .about("Instruction selection logic engine (ISLE) code generator") - .arg( - Arg::with_name("input") - .short("i") - .long("input") - .value_name("FILE.isle") - .takes_value(true) - .multiple(true) - .required(true), - ) - .arg( - Arg::with_name("output") - .short("o") - .long("output") - .value_name("FILE.rs") - .takes_value(true) - .required(true), - ) - .get_matches(); + let opts = Opts::from_args(); - let input_files = matches - .values_of("input") - .unwrap() - .map(|s| s.to_string()) - .collect::>(); - let output_file = matches.value_of("output").unwrap(); - - let lexer = lexer::Lexer::from_files(input_files)?; + let lexer = lexer::Lexer::from_files(opts.inputs)?; let mut parser = parser::Parser::new(lexer); let defs = parser.parse_defs()?; let code = compile::compile(&defs)?; - { - use std::io::Write; - let mut f = std::fs::File::create(output_file).into_diagnostic()?; - writeln!(&mut f, "{}", code).into_diagnostic()?; - } + let stdout = io::stdout(); + let (mut output, output_name): (Box, _) = match &opts.output { + Some(f) => { + let output = Box::new( + fs::File::create(f) + .into_diagnostic() + .with_context(|| format!("failed to create '{}'", f.display()))?, + ); + (output, f.display().to_string()) + } + None => { + let output = Box::new(stdout.lock()); + (output, "".to_string()) + } + }; + + output + .write_all(code.as_bytes()) + .into_diagnostic() + .with_context(|| format!("failed to write to '{}'", output_name))?; Ok(()) }