1use clap::{Arg, ArgAction, Command};
7use std::io::{Write, stdout};
8use syntax_tree::{AstNode, is_truthy};
9use thiserror::Error;
10use uucore::os_string_to_vec;
11use uucore::translate;
12use uucore::{
13 display::Quotable,
14 error::{UError, UResult},
15 format_usage,
16};
17
18mod locale_aware;
19mod syntax_tree;
20
21mod options {
22 pub const VERSION: &str = "version";
23 pub const HELP: &str = "help";
24 pub const EXPRESSION: &str = "expression";
25}
26
27pub type ExprResult<T> = Result<T, ExprError>;
28
29#[derive(Error, Clone, Debug, PartialEq, Eq)]
30pub enum ExprError {
31 #[error("{}", translate!("expr-error-unexpected-argument", "arg" => _0.quote()))]
32 UnexpectedArgument(String),
33 #[error("{}", translate!("expr-error-missing-argument", "arg" => _0.quote()))]
34 MissingArgument(String),
35 #[error("{}", translate!("expr-error-non-integer-argument"))]
36 NonIntegerArgument,
37 #[error("{}", translate!("expr-error-missing-operand"))]
38 MissingOperand,
39 #[error("{}", translate!("expr-error-division-by-zero"))]
40 DivisionByZero,
41 #[error("{}", translate!("expr-error-invalid-regex-expression"))]
42 InvalidRegexExpression,
43 #[error("{}", translate!("expr-error-expected-closing-brace-after", "arg" => _0.quote()))]
44 ExpectedClosingBraceAfter(String),
45 #[error("{}", translate!("expr-error-expected-closing-brace-instead-of", "arg" => _0.quote()))]
46 ExpectedClosingBraceInsteadOf(String),
47 #[error("{}", translate!("expr-error-unmatched-opening-parenthesis"))]
48 UnmatchedOpeningParenthesis,
49 #[error("{}", translate!("expr-error-unmatched-closing-parenthesis"))]
50 UnmatchedClosingParenthesis,
51 #[error("{}", translate!("expr-error-unmatched-opening-brace"))]
52 UnmatchedOpeningBrace,
53 #[error("{}", translate!("expr-error-invalid-bracket-content"))]
54 InvalidBracketContent,
55 #[error("{}", translate!("expr-error-trailing-backslash"))]
56 TrailingBackslash,
57 #[error("{}", translate!("expr-error-too-big-range-quantifier-index"))]
58 TooBigRangeQuantifierIndex,
59 #[error("{}", translate!("expr-error-match-utf8", "arg" => _0.quote()))]
60 UnsupportedNonUtf8Match(String),
61}
62
63impl UError for ExprError {
64 fn code(&self) -> i32 {
65 2
66 }
67
68 fn usage(&self) -> bool {
69 *self == Self::MissingOperand
70 }
71}
72
73pub fn uu_app() -> Command {
74 Command::new(uucore::util_name())
75 .version(uucore::crate_version!())
76 .help_template(uucore::localized_help_template(uucore::util_name()))
77 .about(translate!("expr-about"))
78 .override_usage(format_usage(&translate!("expr-usage")))
79 .after_help(translate!("expr-after-help"))
80 .infer_long_args(true)
81 .disable_help_flag(true)
82 .disable_version_flag(true)
83 .arg(
84 Arg::new(options::VERSION)
85 .long(options::VERSION)
86 .help(translate!("expr-help-version"))
87 .action(ArgAction::Version),
88 )
89 .arg(
90 Arg::new(options::HELP)
91 .long(options::HELP)
92 .help(translate!("expr-help-help"))
93 .action(ArgAction::Help),
94 )
95 .arg(
96 Arg::new(options::EXPRESSION)
97 .action(ArgAction::Append)
98 .allow_hyphen_values(true),
99 )
100}
101
102#[uucore::main(no_signals)]
103pub fn uumain(args: impl uucore::Args) -> UResult<()> {
104 let args = args
107 .skip(1) .map(os_string_to_vec)
109 .collect::<Result<Vec<_>, _>>()?;
110
111 if args.len() == 1 && args[0] == b"--help" {
112 uu_app().print_help()?;
113 } else if args.len() == 1 && args[0] == b"--version" {
114 writeln!(
115 stdout(),
116 "{} {}",
117 uucore::util_name(),
118 uucore::crate_version!()
119 )?;
120 } else {
121 let args = if !args.is_empty() && args[0] == b"--" {
123 &args[1..]
124 } else {
125 &args
126 };
127
128 let res = AstNode::parse(args)?.eval()?.eval_as_string();
129 let _ = stdout().write_all(&res);
130 let _ = stdout().write_all(b"\n");
131
132 if !is_truthy(&res.into()) {
133 return Err(1.into());
134 }
135 }
136
137 Ok(())
138}