From 57c449936fee4071d1c6cc518460b1cc47bb5f9a Mon Sep 17 00:00:00 2001 From: WerWolv Date: Fri, 4 Mar 2022 19:20:21 +0100 Subject: [PATCH] sys: Improved math evaluator --- plugins/builtin/include/math_evaluator.hpp | 18 +-- plugins/builtin/source/math_evaluator.cpp | 123 ++++++++++----------- 2 files changed, 71 insertions(+), 70 deletions(-) diff --git a/plugins/builtin/include/math_evaluator.hpp b/plugins/builtin/include/math_evaluator.hpp index c8ff83353..0a508a581 100644 --- a/plugins/builtin/include/math_evaluator.hpp +++ b/plugins/builtin/include/math_evaluator.hpp @@ -11,7 +11,8 @@ namespace hex { - enum class TokenType { + enum class TokenType + { Number, Variable, Function, @@ -19,7 +20,8 @@ namespace hex { Bracket }; - enum class Operator : u16 { + enum class Operator : u16 + { Invalid = 0x000, Assign = 0x010, Or = 0x020, @@ -47,7 +49,8 @@ namespace hex { Not = 0x0F1 }; - enum class BracketType : std::uint8_t { + enum class BracketType : std::uint8_t + { Left, Right }; @@ -66,19 +69,18 @@ namespace hex { public: MathEvaluator() = default; - std::optional evaluate(std::string input); + std::optional evaluate(const std::string &input); void registerStandardVariables(); void registerStandardFunctions(); - void setVariable(std::string name, long double value); - void setFunction(std::string name, std::function(std::vector)> function, size_t minNumArgs, size_t maxNumArgs); + void setVariable(const std::string &name, long double value); + void setFunction(const std::string &name, const std::function(std::vector)> &function, size_t minNumArgs, size_t maxNumArgs); std::unordered_map &getVariables() { return this->m_variables; } private: - std::queue parseInput(const char *input); - std::queue toPostfix(std::queue inputQueue); + std::queue parseInput(std::string input); std::optional evaluate(std::queue postfixTokens); std::unordered_map m_variables; diff --git a/plugins/builtin/source/math_evaluator.cpp b/plugins/builtin/source/math_evaluator.cpp index 47a7487d1..feea48184 100644 --- a/plugins/builtin/source/math_evaluator.cpp +++ b/plugins/builtin/source/math_evaluator.cpp @@ -7,19 +7,18 @@ #include #include #include -#include namespace hex { i16 comparePrecedence(const Operator &a, const Operator &b) { - return (static_cast(a) & 0x0F0) - (static_cast(b) & 0x0F0); + return static_cast((static_cast(a) & 0x0F0) - (static_cast(b) & 0x0F0)); } bool isLeftAssociative(const Operator op) { return (static_cast(op) & 0xF00) == 0; } - std::pair toOperator(std::string input) { + std::pair toOperator(const std::string &input) { if (input.starts_with("##")) return { Operator::Combine, 2 }; if (input.starts_with("==")) return { Operator::Equals, 2 }; if (input.starts_with("!=")) return { Operator::NotEquals, 2 }; @@ -48,10 +47,59 @@ namespace hex { return { Operator::Invalid, 0 }; } - std::queue MathEvaluator::parseInput(const char *input) { + static std::queue toPostfix(std::queue inputQueue) { + std::queue outputQueue; + std::stack operatorStack; + + while (!inputQueue.empty()) { + Token currToken = inputQueue.front(); + inputQueue.pop(); + + if (currToken.type == TokenType::Number || currToken.type == TokenType::Variable || currToken.type == TokenType::Function) + outputQueue.push(currToken); + else if (currToken.type == TokenType::Operator) { + while ((!operatorStack.empty()) && (operatorStack.top().type == TokenType::Operator && currToken.type == TokenType::Operator && (comparePrecedence(operatorStack.top().op, currToken.op) > 0) || (comparePrecedence(operatorStack.top().op, currToken.op) == 0 && isLeftAssociative(currToken.op))) && operatorStack.top().type != TokenType::Bracket) { + outputQueue.push(operatorStack.top()); + operatorStack.pop(); + } + operatorStack.push(currToken); + } else if (currToken.type == TokenType::Bracket) { + if (currToken.bracketType == BracketType::Left) + operatorStack.push(currToken); + else { + if (operatorStack.empty()) + throw std::invalid_argument("Mismatching parenthesis!"); + + while (operatorStack.top().type != TokenType::Bracket || (operatorStack.top().type == TokenType::Bracket && operatorStack.top().bracketType != BracketType::Left)) { + if (operatorStack.empty()) + throw std::invalid_argument("Mismatching parenthesis!"); + + outputQueue.push(operatorStack.top()); + operatorStack.pop(); + } + + operatorStack.pop(); + } + } + } + + while (!operatorStack.empty()) { + auto top = operatorStack.top(); + + if (top.type == TokenType::Bracket) + throw std::invalid_argument("Mismatching parenthesis!"); + + outputQueue.push(top); + operatorStack.pop(); + } + + return outputQueue; + } + + std::queue MathEvaluator::parseInput(std::string input) { std::queue inputQueue; - char *prevPos = const_cast(input); + char *prevPos = input.data(); for (char *pos = prevPos; *pos != 0x00;) { if (std::isdigit(*pos) || *pos == '.') { auto number = std::strtold(pos, &pos); @@ -111,13 +159,13 @@ namespace hex { pos++; for (const auto &expression : expressions) { - if (expression == "" && expressions.size() > 1) + if (expression.empty() && expressions.size() > 1) throw std::invalid_argument("Invalid function call syntax!"); - else if (expression == "") + else if (expression.empty()) break; - auto inputQueue = parseInput(expression.c_str()); - auto postfixTokens = toPostfix(inputQueue); + auto newInputQueue = parseInput(expression.c_str()); + auto postfixTokens = toPostfix(newInputQueue); auto result = evaluate(postfixTokens); if (!result.has_value()) @@ -145,55 +193,6 @@ namespace hex { return inputQueue; } - std::queue MathEvaluator::toPostfix(std::queue inputQueue) { - std::queue outputQueue; - std::stack operatorStack; - - while (!inputQueue.empty()) { - Token currToken = inputQueue.front(); - inputQueue.pop(); - - if (currToken.type == TokenType::Number || currToken.type == TokenType::Variable || currToken.type == TokenType::Function) - outputQueue.push(currToken); - else if (currToken.type == TokenType::Operator) { - while ((!operatorStack.empty()) && (operatorStack.top().type == TokenType::Operator && currToken.type == TokenType::Operator && (comparePrecedence(operatorStack.top().op, currToken.op) > 0) || (comparePrecedence(operatorStack.top().op, currToken.op) == 0 && isLeftAssociative(currToken.op))) && operatorStack.top().type != TokenType::Bracket) { - outputQueue.push(operatorStack.top()); - operatorStack.pop(); - } - operatorStack.push(currToken); - } else if (currToken.type == TokenType::Bracket) { - if (currToken.bracketType == BracketType::Left) - operatorStack.push(currToken); - else { - if (operatorStack.empty()) - throw std::invalid_argument("Mismatching parenthesis!"); - - while (operatorStack.top().type != TokenType::Bracket || (operatorStack.top().type == TokenType::Bracket && operatorStack.top().bracketType != BracketType::Left)) { - if (operatorStack.empty()) - throw std::invalid_argument("Mismatching parenthesis!"); - - outputQueue.push(operatorStack.top()); - operatorStack.pop(); - } - - operatorStack.pop(); - } - } - } - - while (!operatorStack.empty()) { - auto top = operatorStack.top(); - - if (top.type == TokenType::Bracket) - throw std::invalid_argument("Mismatching parenthesis!"); - - outputQueue.push(top); - operatorStack.pop(); - } - - return outputQueue; - } - std::optional MathEvaluator::evaluate(std::queue postfixTokens) { std::stack evaluationStack; @@ -227,7 +226,7 @@ namespace hex { result = static_cast(leftOperand) && static_cast(rightOperand); break; case Operator::Or: - result = static_cast(leftOperand) && static_cast(rightOperand); + result = static_cast(leftOperand) || static_cast(rightOperand); break; case Operator::Xor: result = (static_cast(leftOperand) ^ static_cast(rightOperand)) > 0; @@ -321,7 +320,7 @@ namespace hex { } - std::optional MathEvaluator::evaluate(std::string input) { + std::optional MathEvaluator::evaluate(const std::string &input) { auto inputQueue = parseInput(input.c_str()); std::string resultVariable = "ans"; @@ -351,11 +350,11 @@ namespace hex { return result; } - void MathEvaluator::setVariable(std::string name, long double value) { + void MathEvaluator::setVariable(const std::string &name, long double value) { this->m_variables[name] = value; } - void MathEvaluator::setFunction(std::string name, std::function(std::vector)> function, size_t minNumArgs, size_t maxNumArgs) { + void MathEvaluator::setFunction(const std::string &name, const std::function(std::vector)> &function, size_t minNumArgs, size_t maxNumArgs) { this->m_functions[name] = [minNumArgs, maxNumArgs, function](auto args) { if (args.size() < minNumArgs || args.size() > maxNumArgs) throw std::invalid_argument("Invalid number of function arguments!");