123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606 |
- /*
- * Copyright (c) 2020, the SerenityOS developers.
- *
- * SPDX-License-Identifier: BSD-2-Clause
- */
- #include <AK/Assertions.h>
- #include <AK/GenericLexer.h>
- #include <AK/NonnullOwnPtr.h>
- #include <AK/OwnPtr.h>
- #include <AK/Queue.h>
- #include <AK/String.h>
- #include <AK/StringView.h>
- #include <LibRegex/Regex.h>
- #include <stdio.h>
- #include <unistd.h>
- static void print_help_and_exit()
- {
- outln(R"(
- Usage: expr EXPRESSION
- expr [--help]
- Print the value of EXPRESSION to standard output.)");
- exit(0);
- }
- template<typename Fmt, typename... Args>
- [[noreturn]] void fail(Fmt&& fmt, Args&&... args)
- {
- warn("ERROR: \e[31m");
- warnln(StringView { fmt }, args...);
- warn("\e[0m");
- exit(2);
- }
- class Expression {
- public:
- enum Precedence {
- Or,
- And,
- Comp,
- ArithS,
- ArithM,
- StringO,
- Paren,
- };
- static NonnullOwnPtr<Expression> parse(Queue<StringView>& args, Precedence prec = Or);
- enum class Type {
- Integer,
- String,
- };
- virtual bool truth() const = 0;
- virtual int integer() const = 0;
- virtual String string() const = 0;
- virtual Type type() const = 0;
- virtual ~Expression() { }
- };
- class ValueExpression : public Expression {
- public:
- ValueExpression(int v)
- : as_integer(v)
- , m_type(Type::Integer)
- {
- }
- ValueExpression(String&& v)
- : as_string(move(v))
- , m_type(Type::String)
- {
- }
- virtual ~ValueExpression() { }
- private:
- virtual bool truth() const override
- {
- if (m_type == Type::String)
- return !as_string.is_empty();
- return integer() != 0;
- }
- virtual int integer() const override
- {
- switch (m_type) {
- case Type::Integer:
- return as_integer;
- case Type::String:
- if (auto converted = as_string.to_int(); converted.has_value())
- return converted.value();
- fail("Not an integer: '{}'", as_string);
- }
- VERIFY_NOT_REACHED();
- }
- virtual String string() const override
- {
- switch (m_type) {
- case Type::Integer:
- return String::formatted("{}", as_integer);
- case Type::String:
- return as_string;
- }
- VERIFY_NOT_REACHED();
- }
- virtual Type type() const override { return m_type; }
- union {
- int as_integer;
- String as_string;
- };
- Type m_type { Type::String };
- };
- class BooleanExpression : public Expression {
- public:
- enum class BooleanOperator {
- And,
- Or,
- };
- static BooleanOperator op_from(const StringView& sv)
- {
- if (sv == "&")
- return BooleanOperator::And;
- return BooleanOperator::Or;
- }
- BooleanExpression(BooleanOperator op, NonnullOwnPtr<Expression>&& left, NonnullOwnPtr<Expression>&& right)
- : m_op(op)
- , m_left(move(left))
- , m_right(move(right))
- {
- if (m_op == BooleanOperator::Or)
- m_left_truth = m_left->truth();
- else
- m_right_truth = m_right->truth();
- }
- private:
- virtual bool truth() const override
- {
- if (m_op == BooleanOperator::Or)
- return m_left_truth ? true : m_right->truth();
- return m_right_truth ? m_left->truth() : false;
- }
- virtual int integer() const override
- {
- switch (m_op) {
- case BooleanOperator::And:
- if (m_right_truth)
- return m_left->integer();
- return 0;
- case BooleanOperator::Or:
- if (m_left_truth)
- return m_left->integer();
- return m_right->integer();
- }
- VERIFY_NOT_REACHED();
- }
- virtual String string() const override
- {
- switch (m_op) {
- case BooleanOperator::And:
- if (m_right_truth)
- return m_left->string();
- return "0";
- case BooleanOperator::Or:
- if (m_left_truth)
- return m_left->string();
- return m_right->string();
- }
- VERIFY_NOT_REACHED();
- }
- virtual Type type() const override
- {
- switch (m_op) {
- case BooleanOperator::And:
- if (m_right_truth)
- return m_left->type();
- return m_right->type();
- case BooleanOperator::Or:
- if (m_left_truth)
- return m_left->type();
- return m_right->type();
- }
- VERIFY_NOT_REACHED();
- }
- BooleanOperator m_op { BooleanOperator::And };
- NonnullOwnPtr<Expression> m_left, m_right;
- bool m_left_truth { false }, m_right_truth { false };
- };
- class ComparisonExpression : public Expression {
- public:
- enum class ComparisonOperation {
- Less,
- LessEq,
- Eq,
- Neq,
- GreaterEq,
- Greater,
- };
- static ComparisonOperation op_from(const StringView& sv)
- {
- if (sv == "<")
- return ComparisonOperation::Less;
- if (sv == "<=")
- return ComparisonOperation::LessEq;
- if (sv == "=")
- return ComparisonOperation::Eq;
- if (sv == "!=")
- return ComparisonOperation::Neq;
- if (sv == ">=")
- return ComparisonOperation::GreaterEq;
- return ComparisonOperation::Greater;
- }
- ComparisonExpression(ComparisonOperation op, NonnullOwnPtr<Expression>&& left, NonnullOwnPtr<Expression>&& right)
- : m_op(op)
- , m_left(move(left))
- , m_right(move(right))
- {
- }
- private:
- template<typename T>
- bool compare(const T& left, const T& right) const
- {
- switch (m_op) {
- case ComparisonOperation::Less:
- return left < right;
- case ComparisonOperation::LessEq:
- return left == right || left < right;
- case ComparisonOperation::Eq:
- return left == right;
- case ComparisonOperation::Neq:
- return left != right;
- case ComparisonOperation::GreaterEq:
- return !(left < right);
- case ComparisonOperation::Greater:
- return left != right && !(left < right);
- }
- VERIFY_NOT_REACHED();
- }
- virtual bool truth() const override
- {
- switch (m_left->type()) {
- case Type::Integer:
- return compare(m_left->integer(), m_right->integer());
- case Type::String:
- return compare(m_left->string(), m_right->string());
- }
- VERIFY_NOT_REACHED();
- }
- virtual int integer() const override { return truth(); }
- virtual String string() const override { return truth() ? "1" : "0"; }
- virtual Type type() const override { return Type::Integer; }
- ComparisonOperation m_op { ComparisonOperation::Less };
- NonnullOwnPtr<Expression> m_left, m_right;
- };
- class ArithmeticExpression : public Expression {
- public:
- enum class ArithmeticOperation {
- Sum,
- Difference,
- Product,
- Quotient,
- Remainder,
- };
- static ArithmeticOperation op_from(const StringView& sv)
- {
- if (sv == "+")
- return ArithmeticOperation::Sum;
- if (sv == "-")
- return ArithmeticOperation::Difference;
- if (sv == "*")
- return ArithmeticOperation::Product;
- if (sv == "/")
- return ArithmeticOperation::Quotient;
- return ArithmeticOperation::Remainder;
- }
- ArithmeticExpression(ArithmeticOperation op, NonnullOwnPtr<Expression>&& left, NonnullOwnPtr<Expression>&& right)
- : m_op(op)
- , m_left(move(left))
- , m_right(move(right))
- {
- }
- private:
- virtual bool truth() const override
- {
- switch (m_op) {
- case ArithmeticOperation::Sum:
- return m_left->truth() || m_right->truth();
- default:
- return integer() != 0;
- }
- }
- virtual int integer() const override
- {
- auto right = m_right->integer();
- if (right == 0) {
- if (m_op == ArithmeticOperation::Product)
- return 0;
- if (m_op == ArithmeticOperation::Quotient || m_op == ArithmeticOperation::Remainder)
- fail("Division by zero");
- }
- auto left = m_left->integer();
- switch (m_op) {
- case ArithmeticOperation::Product:
- return right * left;
- case ArithmeticOperation::Sum:
- return right + left;
- case ArithmeticOperation::Difference:
- return left - right;
- case ArithmeticOperation::Quotient:
- return left / right;
- case ArithmeticOperation::Remainder:
- return left % right;
- }
- VERIFY_NOT_REACHED();
- }
- virtual String string() const override
- {
- return String::formatted("{}", integer());
- }
- virtual Type type() const override
- {
- return Type::Integer;
- }
- ArithmeticOperation m_op { ArithmeticOperation::Sum };
- NonnullOwnPtr<Expression> m_left, m_right;
- };
- class StringExpression : public Expression {
- public:
- enum class StringOperation {
- Substring,
- Index,
- Length,
- Match,
- };
- StringExpression(StringOperation op, NonnullOwnPtr<Expression> string, OwnPtr<Expression> pos_or_chars = {}, OwnPtr<Expression> length = {})
- : m_op(op)
- , m_str(move(string))
- , m_pos_or_chars(move(pos_or_chars))
- , m_length(move(length))
- {
- }
- private:
- virtual bool truth() const override
- {
- if (type() == Expression::Type::String)
- return !string().is_empty();
- return integer() != 0;
- }
- virtual int integer() const override
- {
- if (m_op == StringOperation::Substring || m_op == StringOperation::Match) {
- auto substr = string();
- if (auto integer = substr.to_int(); integer.has_value())
- return integer.value();
- else
- fail("Not an integer: '{}'", substr);
- }
- if (m_op == StringOperation::Index) {
- if (auto idx = m_str->string().find(m_pos_or_chars->string()); idx.has_value())
- return idx.value() + 1;
- return 0;
- }
- if (m_op == StringOperation::Length)
- return m_str->string().length();
- VERIFY_NOT_REACHED();
- }
- static auto safe_substring(const String& str, int start, int length)
- {
- if (start < 1 || (size_t)start > str.length())
- fail("Index out of range");
- --start;
- if (str.length() - start < (size_t)length)
- fail("Index out of range");
- return str.substring(start, length);
- }
- virtual String string() const override
- {
- if (m_op == StringOperation::Substring)
- return safe_substring(m_str->string(), m_pos_or_chars->integer(), m_length->integer());
- if (m_op == StringOperation::Match) {
- auto match = m_compiled_regex->match(m_str->string(), PosixFlags::Global);
- if (m_compiled_regex->parser_result.capture_groups_count == 0) {
- if (!match.success)
- return "0";
- size_t count = 0;
- for (auto& m : match.matches)
- count += m.view.length();
- return String::number(count);
- } else {
- if (!match.success)
- return "";
- StringBuilder result;
- for (auto& e : match.capture_group_matches[0])
- result.append(e.view.u8view());
- return result.build();
- }
- }
- return String::number(integer());
- }
- virtual Type type() const override
- {
- if (m_op == StringOperation::Substring)
- return Type::String;
- if (m_op == StringOperation::Match) {
- if (!m_pos_or_chars)
- fail("'match' expects a string pattern");
- ensure_regex();
- if (m_compiled_regex->parser_result.capture_groups_count == 0)
- return Type::Integer;
- return Type::String;
- }
- return Type::Integer;
- }
- void ensure_regex() const
- {
- if (!m_compiled_regex) {
- m_compiled_regex = make<regex::Regex<PosixBasic>>(m_pos_or_chars->string());
- if (m_compiled_regex->parser_result.error != regex::Error::NoError)
- fail("Regex error: {}", regex::get_error_string(m_compiled_regex->parser_result.error));
- }
- }
- StringOperation m_op { StringOperation::Substring };
- NonnullOwnPtr<Expression> m_str;
- OwnPtr<Expression> m_pos_or_chars, m_length;
- mutable OwnPtr<regex::Regex<PosixBasic>> m_compiled_regex;
- };
- NonnullOwnPtr<Expression> Expression::parse(Queue<StringView>& args, Precedence prec)
- {
- switch (prec) {
- case Or: {
- auto left = parse(args, And);
- while (!args.is_empty() && args.head() == "|") {
- args.dequeue();
- auto right = parse(args, And);
- left = make<BooleanExpression>(BooleanExpression::BooleanOperator::Or, move(left), move(right));
- }
- return left;
- }
- case And: {
- auto left = parse(args, Comp);
- while (!args.is_empty() && args.head() == "&") {
- args.dequeue();
- auto right = parse(args, Comp);
- left = make<BooleanExpression>(BooleanExpression::BooleanOperator::And, move(left), move(right));
- }
- return left;
- }
- case Comp: {
- auto left = parse(args, ArithS);
- while (!args.is_empty() && args.head().is_one_of("<", "<=", "=", "!=", "=>", ">")) {
- auto op = args.dequeue();
- auto right = parse(args, ArithM);
- left = make<ComparisonExpression>(ComparisonExpression::op_from(op), move(left), move(right));
- }
- return left;
- }
- case ArithS: {
- auto left = parse(args, ArithM);
- while (!args.is_empty() && args.head().is_one_of("+", "-")) {
- auto op = args.dequeue();
- auto right = parse(args, ArithM);
- left = make<ArithmeticExpression>(ArithmeticExpression::op_from(op), move(left), move(right));
- }
- return left;
- }
- case ArithM: {
- auto left = parse(args, StringO);
- while (!args.is_empty() && args.head().is_one_of("*", "/", "%")) {
- auto op = args.dequeue();
- auto right = parse(args, StringO);
- left = make<ArithmeticExpression>(ArithmeticExpression::op_from(op), move(left), move(right));
- }
- return left;
- }
- case StringO: {
- if (args.is_empty())
- fail("Expected a term");
- OwnPtr<Expression> left;
- while (!args.is_empty()) {
- auto& op = args.head();
- if (op == "+") {
- args.dequeue();
- left = make<ValueExpression>(args.dequeue());
- } else if (op == "substr") {
- args.dequeue();
- auto str = parse(args, Paren);
- auto pos = parse(args, Paren);
- auto len = parse(args, Paren);
- left = make<StringExpression>(StringExpression::StringOperation::Substring, move(str), move(pos), move(len));
- } else if (op == "index") {
- args.dequeue();
- auto str = parse(args, Paren);
- auto chars = parse(args, Paren);
- left = make<StringExpression>(StringExpression::StringOperation::Index, move(str), move(chars));
- } else if (op == "match") {
- args.dequeue();
- auto str = parse(args, Paren);
- auto pattern = parse(args, Paren);
- left = make<StringExpression>(StringExpression::StringOperation::Match, move(str), move(pattern));
- } else if (op == "length") {
- args.dequeue();
- auto str = parse(args, Paren);
- left = make<StringExpression>(StringExpression::StringOperation::Length, move(str));
- } else if (!left) {
- left = parse(args, Paren);
- }
- if (!args.is_empty() && args.head() == ":") {
- args.dequeue();
- auto right = parse(args, Paren);
- left = make<StringExpression>(StringExpression::StringOperation::Match, left.release_nonnull(), move(right));
- } else {
- return left.release_nonnull();
- }
- }
- return left.release_nonnull();
- }
- case Paren: {
- if (args.is_empty())
- fail("Expected a term");
- if (args.head() == "(") {
- args.dequeue();
- auto expr = parse(args);
- if (args.head() != ")")
- fail("Expected a close paren");
- args.dequeue();
- return expr;
- }
- return make<ValueExpression>(args.dequeue());
- }
- }
- fail("Invalid expression");
- }
- int main(int argc, char** argv)
- {
- if (pledge("stdio", nullptr) < 0) {
- perror("pledge");
- return 3;
- }
- if (unveil(nullptr, nullptr) < 0) {
- perror("unveil");
- return 3;
- }
- if ((argc == 2 && "--help"sv == argv[1]) || argc == 1)
- print_help_and_exit();
- Queue<StringView> args;
- for (int i = 1; i < argc; ++i)
- args.enqueue(argv[i]);
- auto expression = Expression::parse(args);
- if (!args.is_empty())
- fail("Extra tokens at the end of the expression");
- switch (expression->type()) {
- case Expression::Type::Integer:
- outln("{}", expression->integer());
- break;
- case Expression::Type::String:
- outln("{}", expression->string());
- break;
- }
- return expression->truth() ? 0 : 1;
- }
|