From 86d24176e4d71dd57462b966d21f60cdfc88ea2d Mon Sep 17 00:00:00 2001 From: Terts Diepraam Date: Thu, 29 Sep 2022 18:13:11 +0200 Subject: [PATCH] expr: update to clap 4 and remove custom argument parsing --- src/uu/expr/Cargo.toml | 2 +- src/uu/expr/expr.md | 59 +++++++++++++++ src/uu/expr/src/expr.rs | 140 +++++++++++------------------------- src/uu/expr/src/tokens.rs | 10 +-- src/uucore_procs/src/lib.rs | 4 +- 5 files changed, 107 insertions(+), 108 deletions(-) create mode 100644 src/uu/expr/expr.md diff --git a/src/uu/expr/Cargo.toml b/src/uu/expr/Cargo.toml index 52ddc10d1..e3f8b1d57 100644 --- a/src/uu/expr/Cargo.toml +++ b/src/uu/expr/Cargo.toml @@ -15,7 +15,7 @@ edition = "2021" path = "src/expr.rs" [dependencies] -clap = { version = "3.2", features = ["wrap_help", "cargo"] } +clap = { version = "4.0", features = ["wrap_help", "cargo"] } num-bigint = "0.4.0" num-traits = "0.2.15" onig = { version = "~6.4", default-features = false } diff --git a/src/uu/expr/expr.md b/src/uu/expr/expr.md new file mode 100644 index 000000000..1faa88781 --- /dev/null +++ b/src/uu/expr/expr.md @@ -0,0 +1,59 @@ +# expr + +## About + +Print the value of EXPRESSION to standard output + +## Usage +``` +expr [EXPRESSION] +expr [OPTIONS] +``` + +## After help + +Print the value of EXPRESSION to standard output. A blank line below +separates increasing precedence groups. EXPRESSION may be: + + ARG1 | ARG2 ARG1 if it is neither null nor 0, otherwise ARG2 + + ARG1 & ARG2 ARG1 if neither argument is null or 0, otherwise 0 + + ARG1 < ARG2 ARG1 is less than ARG2 + ARG1 <= ARG2 ARG1 is less than or equal to ARG2 + ARG1 = ARG2 ARG1 is equal to ARG2 + ARG1 != ARG2 ARG1 is unequal to ARG2 + ARG1 >= ARG2 ARG1 is greater than or equal to ARG2 + ARG1 > ARG2 ARG1 is greater than ARG2 + + ARG1 + ARG2 arithmetic sum of ARG1 and ARG2 + ARG1 - ARG2 arithmetic difference of ARG1 and ARG2 + + ARG1 * ARG2 arithmetic product of ARG1 and ARG2 + ARG1 / ARG2 arithmetic quotient of ARG1 divided by ARG2 + ARG1 % ARG2 arithmetic remainder of ARG1 divided by ARG2 + + STRING : REGEXP anchored pattern match of REGEXP in STRING + + match STRING REGEXP same as STRING : REGEXP + substr STRING POS LENGTH substring of STRING, POS counted from 1 + index STRING CHARS index in STRING where any CHARS is found, or 0 + length STRING length of STRING + + TOKEN interpret TOKEN as a string, even if it is a + keyword like 'match' or an operator like '/' + + ( EXPRESSION ) value of EXPRESSION + +Beware that many operators need to be escaped or quoted for shells. +Comparisons are arithmetic if both ARGs are numbers, else lexicographical. +Pattern matches return the string matched between \( and \) or null; if +\( and \) are not used, they return the number of characters matched or 0. + +Exit status is 0 if EXPRESSION is neither null nor 0, 1 if EXPRESSION is null +or 0, 2 if EXPRESSION is syntactically invalid, and 3 if an error occurred. + +Environment variables: + * EXPR_DEBUG_TOKENS=1 dump expression's tokens + * EXPR_DEBUG_RPN=1 dump expression represented in reverse polish notation + * EXPR_DEBUG_SYA_STEP=1 dump each parser step + * EXPR_DEBUG_AST=1 dump expression represented abstract syntax tree" \ No newline at end of file diff --git a/src/uu/expr/src/expr.rs b/src/uu/expr/src/expr.rs index 9b4ec0b19..508359e52 100644 --- a/src/uu/expr/src/expr.rs +++ b/src/uu/expr/src/expr.rs @@ -5,29 +5,47 @@ //* For the full copyright and license information, please view the LICENSE //* file that was distributed with this source code. -use clap::{crate_version, Arg, Command}; -use uucore::error::{UResult, USimpleError}; +use clap::{crate_version, Arg, ArgAction, Command}; +use uucore::{ + error::{UResult, USimpleError}, + format_usage, help_section, help_usage, +}; mod syntax_tree; mod tokens; -const VERSION: &str = "version"; -const HELP: &str = "help"; -static USAGE: &str = r#"Print the value of EXPRESSION to standard output - expr [EXPRESSION] - expr [OPTIONS]"#; +mod options { + pub const VERSION: &str = "version"; + pub const HELP: &str = "help"; + pub const EXPRESSION: &str = "expression"; +} -pub fn uu_app<'a>() -> Command<'a> { +pub fn uu_app() -> Command { Command::new(uucore::util_name()) .version(crate_version!()) - .override_usage(USAGE) + .about(help_section!("about", "expr.md")) + .override_usage(format_usage(help_usage!("expr.md"))) + .after_help(help_section!("after help", "expr.md")) .infer_long_args(true) + .disable_help_flag(true) + .disable_version_flag(true) .arg( - Arg::new(VERSION) - .long(VERSION) - .help("output version information and exit"), + Arg::new(options::VERSION) + .long(options::VERSION) + .help("output version information and exit") + .action(ArgAction::Version), + ) + .arg( + Arg::new(options::HELP) + .long(options::HELP) + .help("display this help and exit") + .action(ArgAction::Help), + ) + .arg( + Arg::new(options::EXPRESSION) + .action(ArgAction::Append) + .allow_hyphen_values(true), ) - .arg(Arg::new(HELP).long(HELP).help("display this help and exit")) } #[uucore::main] @@ -36,20 +54,19 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> { // For expr utility we do not want getopts. // The following usage should work without escaping hyphens: `expr -15 = 1 + 2 \* \( 3 - -4 \)` + let matches = uu_app().try_get_matches_from(args)?; + let token_strings = matches + .get_many::(options::EXPRESSION) + .map(|v| v.into_iter().map(|s| s.as_ref()).collect::>()) + .unwrap_or_default(); - if maybe_handle_help_or_version(&args) { - Ok(()) - } else { - let token_strings = args[1..].to_vec(); - - match process_expr(&token_strings) { - Ok(expr_result) => print_expr_ok(&expr_result), - Err(expr_error) => Err(USimpleError::new(2, &expr_error)), - } + match process_expr(&token_strings[..]) { + Ok(expr_result) => print_expr_ok(&expr_result), + Err(expr_error) => Err(USimpleError::new(2, &expr_error)), } } -fn process_expr(token_strings: &[String]) -> Result { +fn process_expr(token_strings: &[&str]) -> Result { let maybe_tokens = tokens::strings_to_tokens(token_strings); let maybe_ast = syntax_tree::tokens_to_ast(maybe_tokens); evaluate_ast(maybe_ast) @@ -67,80 +84,3 @@ fn print_expr_ok(expr_result: &str) -> UResult<()> { fn evaluate_ast(maybe_ast: Result, String>) -> Result { maybe_ast.and_then(|ast| ast.evaluate()) } - -fn maybe_handle_help_or_version(args: &[String]) -> bool { - if args.len() == 2 { - if args[1] == "--help" { - print_help(); - true - } else if args[1] == "--version" { - print_version(); - true - } else { - false - } - } else { - false - } -} - -fn print_help() { - //! The following is taken from GNU coreutils' "expr --help" output. - println!( - r#"Usage: expr EXPRESSION - or: expr OPTION - - --help display this help and exit - --version output version information and exit - -Print the value of EXPRESSION to standard output. A blank line below -separates increasing precedence groups. EXPRESSION may be: - - ARG1 | ARG2 ARG1 if it is neither null nor 0, otherwise ARG2 - - ARG1 & ARG2 ARG1 if neither argument is null or 0, otherwise 0 - - ARG1 < ARG2 ARG1 is less than ARG2 - ARG1 <= ARG2 ARG1 is less than or equal to ARG2 - ARG1 = ARG2 ARG1 is equal to ARG2 - ARG1 != ARG2 ARG1 is unequal to ARG2 - ARG1 >= ARG2 ARG1 is greater than or equal to ARG2 - ARG1 > ARG2 ARG1 is greater than ARG2 - - ARG1 + ARG2 arithmetic sum of ARG1 and ARG2 - ARG1 - ARG2 arithmetic difference of ARG1 and ARG2 - - ARG1 * ARG2 arithmetic product of ARG1 and ARG2 - ARG1 / ARG2 arithmetic quotient of ARG1 divided by ARG2 - ARG1 % ARG2 arithmetic remainder of ARG1 divided by ARG2 - - STRING : REGEXP anchored pattern match of REGEXP in STRING - - match STRING REGEXP same as STRING : REGEXP - substr STRING POS LENGTH substring of STRING, POS counted from 1 - index STRING CHARS index in STRING where any CHARS is found, or 0 - length STRING length of STRING - + TOKEN interpret TOKEN as a string, even if it is a - keyword like 'match' or an operator like '/' - - ( EXPRESSION ) value of EXPRESSION - -Beware that many operators need to be escaped or quoted for shells. -Comparisons are arithmetic if both ARGs are numbers, else lexicographical. -Pattern matches return the string matched between \( and \) or null; if -\( and \) are not used, they return the number of characters matched or 0. - -Exit status is 0 if EXPRESSION is neither null nor 0, 1 if EXPRESSION is null -or 0, 2 if EXPRESSION is syntactically invalid, and 3 if an error occurred. - -Environment variables: - * EXPR_DEBUG_TOKENS=1 dump expression's tokens - * EXPR_DEBUG_RPN=1 dump expression represented in reverse polish notation - * EXPR_DEBUG_SYA_STEP=1 dump each parser step - * EXPR_DEBUG_AST=1 dump expression represented abstract syntax tree"# - ); -} - -fn print_version() { - println!("{} {}", uucore::util_name(), crate_version!()); -} diff --git a/src/uu/expr/src/tokens.rs b/src/uu/expr/src/tokens.rs index a0365898d..6ff930f81 100644 --- a/src/uu/expr/src/tokens.rs +++ b/src/uu/expr/src/tokens.rs @@ -69,12 +69,12 @@ impl Token { } } -pub fn strings_to_tokens(strings: &[String]) -> Result, String> { +pub fn strings_to_tokens(strings: &[&str]) -> Result, String> { let mut tokens_acc = Vec::with_capacity(strings.len()); let mut tok_idx = 1; for s in strings { - let token_if_not_escaped = match s.as_ref() { + let token_if_not_escaped = match *s { "(" => Token::ParOpen, ")" => Token::ParClose, @@ -94,15 +94,15 @@ pub fn strings_to_tokens(strings: &[String]) -> Result, Stri "match" | "index" => Token::PrefixOp { arity: 2, - value: s.clone(), + value: s.to_string(), }, "substr" => Token::PrefixOp { arity: 3, - value: s.clone(), + value: s.to_string(), }, "length" => Token::PrefixOp { arity: 1, - value: s.clone(), + value: s.to_string(), }, _ => Token::new_value(s), diff --git a/src/uucore_procs/src/lib.rs b/src/uucore_procs/src/lib.rs index d8082e28a..32a247309 100644 --- a/src/uucore_procs/src/lib.rs +++ b/src/uucore_procs/src/lib.rs @@ -168,9 +168,9 @@ fn parse_usage(content: &str) -> String { // Replace the util name (assumed to be the first word) with "{}" // to be replaced with the runtime value later. if let Some((_util, args)) = l.split_once(' ') { - format!("{{}} {}", args) + format!("{{}} {}\n", args) } else { - "{}".to_string() + "{}\n".to_string() } }) .collect()