diff --git a/include/fintamath/expressions/Expression.hpp b/include/fintamath/expressions/Expression.hpp index a3738530f..17a5f734a 100644 --- a/include/fintamath/expressions/Expression.hpp +++ b/include/fintamath/expressions/Expression.hpp @@ -12,19 +12,19 @@ namespace fintamath { struct Term { Token name; - ArgumentPtr value; + std::unique_ptr value; public: Term() = default; - Term(std::string inName, ArgumentPtr inValue) + Term(std::string inName, std::unique_ptr inValue) : name(std::move(inName)), value(std::move(inValue)) { } }; -using TermVector = std::vector>; -using OperandStack = std::stack; +using TermVector = std::vector>; +using OperandStack = std::stack>; class Expression : public IExpressionCRTP { public: @@ -85,9 +85,9 @@ class Expression : public IExpressionCRTP { static TermVector tokensToTerms(const TokenVector &tokens); - static OperandStack termsToOperands(const TermVector &terms); + static OperandStack termsToOperands(TermVector &terms); - static ArgumentPtr operandsToExpr(OperandStack &operands); + static std::unique_ptr operandsToObject(OperandStack &operands); static ArgumentPtrVector unwrapComma(const ArgumentPtr &child); @@ -101,13 +101,13 @@ class Expression : public IExpressionCRTP { static bool canPrevTermBeBinaryOperator(const Term &term); - static bool isBinaryOperator(const ArgumentPtr &val); + static bool isBinaryOperator(const IMathObject *val); - static bool isPrefixOperator(const ArgumentPtr &val); + static bool isPrefixOperator(const IMathObject *val); - static bool isPostfixOperator(const ArgumentPtr &val); + static bool isPostfixOperator(const IMathObject *val); - static bool isNonOperatorFunction(const ArgumentPtr &val); + static bool isNonOperatorFunction(const IMathObject *val); static void validateFunctionArgs(const IFunction &func, const ArgumentPtrVector &args); @@ -121,7 +121,7 @@ class Expression : public IExpressionCRTP { friend std::unique_ptr makeExpr(const IFunction &func, const ArgumentPtrVector &args); - friend ArgumentPtr parseExpr(const std::string &str); + friend std::unique_ptr parseFintamath(const std::string &str); static Parser::Vector, const Token &> &getTermMakers(); @@ -137,8 +137,6 @@ class Expression : public IExpressionCRTP { mutable bool isSimplified = false; }; -ArgumentPtr parseExpr(const std::string &str); - Expression operator+(const Variable &lhs, const Variable &rhs); Expression operator+(const Expression &lhs, const Variable &rhs); diff --git a/include/fintamath/expressions/ExpressionParser.hpp b/include/fintamath/expressions/ExpressionParser.hpp new file mode 100644 index 000000000..e03bd354e --- /dev/null +++ b/include/fintamath/expressions/ExpressionParser.hpp @@ -0,0 +1,9 @@ +#pragma once + +#include "fintamath/core/IMathObject.hpp" + +namespace fintamath { + +std::unique_ptr parseFintamath(const std::string &str); + +} diff --git a/include/fintamath/expressions/IExpression.hpp b/include/fintamath/expressions/IExpression.hpp index ce05f53a3..58e404448 100644 --- a/include/fintamath/expressions/IExpression.hpp +++ b/include/fintamath/expressions/IExpression.hpp @@ -55,8 +55,6 @@ class IExpression : public IArithmetic { static void preciseSimplifyChild(ArgumentPtr &child); - static void constSimplifyChild(ArgumentPtr &child); - static ArgumentPtr callFunction(const IFunction &func, const ArgumentPtrVector &argPtrs); private: diff --git a/src/fintamath/config/ExpressionConfig.cpp b/src/fintamath/config/ExpressionConfig.cpp index 5a4bf4f06..b9074c829 100644 --- a/src/fintamath/config/ExpressionConfig.cpp +++ b/src/fintamath/config/ExpressionConfig.cpp @@ -98,28 +98,28 @@ struct ExpressionConfig { static void registerTermsMakers() { Expression::registerTermsMaker([](const Token &token) { - if (ArgumentPtr arg = IFunction::parse(token, IFunction::Type::Binary)) { - return std::make_unique(token, arg); + if (auto arg = IFunction::parse(token, IFunction::Type::Binary)) { + return std::make_unique(token, std::move(arg)); } - if (ArgumentPtr arg = IFunction::parse(token)) { - return std::make_unique(token, arg); + if (auto arg = IFunction::parse(token)) { + return std::make_unique(token, std::move(arg)); } return std::unique_ptr(); }); Expression::registerTermsMaker([](const Token &token) { - if (ArgumentPtr arg = ILiteral::parse(token)) { - return std::make_unique(token, arg); + if (auto arg = ILiteral::parse(token)) { + return std::make_unique(token, std::move(arg)); } return std::unique_ptr(); }); Expression::registerTermsMaker([](const Token &token) { - if (ArgumentPtr arg = INumber::parse(token)) { - return std::make_unique(token, arg->toMinimalObject()); + if (auto arg = INumber::parse(token)) { + return std::make_unique(token, std::move(arg)); } return std::unique_ptr(); diff --git a/src/fintamath/expressions/Expression.cpp b/src/fintamath/expressions/Expression.cpp index fcf23f645..a8bd3eb98 100644 --- a/src/fintamath/expressions/Expression.cpp +++ b/src/fintamath/expressions/Expression.cpp @@ -1,5 +1,6 @@ #include "fintamath/expressions/Expression.hpp" +#include "fintamath/expressions/ExpressionParser.hpp" #include "fintamath/expressions/ExpressionUtils.hpp" #include "fintamath/expressions/FunctionExpression.hpp" #include "fintamath/functions/arithmetic/Add.hpp" @@ -17,14 +18,14 @@ namespace fintamath { struct TermWithPriority { - std::shared_ptr term; + std::unique_ptr term; IOperator::Priority priority = IOperator::Priority::Lowest; public: TermWithPriority() = default; - TermWithPriority(std::shared_ptr inTerm, IOperator::Priority inPriority) + TermWithPriority(std::unique_ptr inTerm, IOperator::Priority inPriority) : term(std::move(inTerm)), priority(inPriority) { } @@ -33,7 +34,7 @@ struct TermWithPriority { Expression::Expression() : child(Integer(0).clone()) { } -Expression::Expression(const std::string &str) : child(parseExpr(str)) { +Expression::Expression(const std::string &str) : child(parseFintamath(str)) { } Expression::Expression(const ArgumentPtr &obj) : child(compress(obj)) { @@ -140,12 +141,12 @@ void Expression::updateStringMutable() const { stringCached = child->toString(); } -ArgumentPtr parseExpr(const std::string &str) { +std::unique_ptr parseFintamath(const std::string &str) { try { auto tokens = Tokenizer::tokenize(str); auto terms = Expression::tokensToTerms(tokens); auto stack = Expression::termsToOperands(terms); - auto expr = Expression::operandsToExpr(stack); + auto expr = Expression::operandsToObject(stack); return expr; } catch (const InvalidInputException &) { @@ -165,7 +166,7 @@ TermVector Expression::tokensToTerms(const TokenVector &tokens) { terms[i] = std::move(term); } else { - terms[i] = std::make_unique(tokens[i], ArgumentPtr()); + terms[i] = std::make_unique(tokens[i], std::unique_ptr()); } } @@ -178,20 +179,20 @@ TermVector Expression::tokensToTerms(const TokenVector &tokens) { // Use the shunting yard algorithm // https://en.m.wikipedia.org/wiki/Shunting_yard_algorithm -OperandStack Expression::termsToOperands(const TermVector &terms) { - std::stack outStack; +OperandStack Expression::termsToOperands(TermVector &terms) { + OperandStack outStack; std::stack operStack; - for (const auto &term : terms) { + for (auto &&term : terms) { if (!term->value) { if (term->name == "(") { - operStack.emplace(term, IOperator::Priority::Lowest); + operStack.emplace(std::move(term), IOperator::Priority::Lowest); } else if (term->name == ")") { while (!operStack.empty() && operStack.top().term->name != "(") { - outStack.emplace(operStack.top().term->value); + outStack.emplace(std::move(operStack.top().term->value)); operStack.pop(); } @@ -207,24 +208,24 @@ OperandStack Expression::termsToOperands(const TermVector &terms) { } else { if (is(term->value)) { - if (auto oper = cast(term->value)) { + if (auto *oper = cast(term->value.get())) { while (!operStack.empty() && operStack.top().term->name != "(" && operStack.top().priority <= oper->getOperatorPriority() && !isPrefixOperator(oper)) { - outStack.emplace(operStack.top().term->value); + outStack.emplace(std::move(operStack.top().term->value)); operStack.pop(); } - operStack.emplace(term, oper->getOperatorPriority()); + operStack.emplace(std::move(term), oper->getOperatorPriority()); } else { - operStack.emplace(term, IOperator::Priority::Highest); + operStack.emplace(std::move(term), IOperator::Priority::Highest); } } else { - outStack.emplace(term->value); + outStack.emplace(std::move(term->value)); } } } @@ -234,26 +235,27 @@ OperandStack Expression::termsToOperands(const TermVector &terms) { throw InvalidInputException(""); } - outStack.emplace(operStack.top().term->value); + outStack.emplace(std::move(operStack.top().term->value)); operStack.pop(); } return outStack; } -ArgumentPtr Expression::operandsToExpr(OperandStack &operands) { +std::unique_ptr Expression::operandsToObject(OperandStack &operands) { if (operands.empty()) { throw InvalidInputException(""); } - ArgumentPtr arg = operands.top(); + std::unique_ptr arg = std::move(operands.top()); operands.pop(); - if (auto func = cast(arg)) { - ArgumentPtr rhsChild = operandsToExpr(operands); + if (is(arg)) { + auto func = cast(std::move(arg)); + ArgumentPtr rhsChild = operandsToObject(operands); - if (isBinaryOperator(func)) { - ArgumentPtr lhsChild = operandsToExpr(operands); + if (isBinaryOperator(func.get())) { + ArgumentPtr lhsChild = operandsToObject(operands); return makeExpr(*func, {lhsChild, rhsChild}); } @@ -298,8 +300,8 @@ void Expression::insertMultiplications(TermVector &terms) { if (canNextTermBeBinaryOperator(*terms[i - 1]) && canPrevTermBeBinaryOperator(*terms[i])) { - auto term = std::make_shared(mul->toString(), mul); - terms.insert(terms.begin() + ptrdiff_t(i), term); + auto term = std::make_unique(mul->toString(), mul->clone()); + terms.insert(terms.begin() + ptrdiff_t(i), std::move(term)); i++; } } @@ -310,7 +312,7 @@ void Expression::fixOperatorTypes(TermVector &terms) { if (const auto &term = terms.front(); is(term->value) && - !isPrefixOperator(term->value)) { + !isPrefixOperator(term->value.get())) { term->value = IOperator::parse(term->name, IOperator::Priority::PrefixUnary); isFixed = isFixed && term->value; @@ -318,7 +320,7 @@ void Expression::fixOperatorTypes(TermVector &terms) { if (const auto &term = terms.back(); is(term->value) && - !isPostfixOperator(term->value)) { + !isPostfixOperator(term->value.get())) { term->value = IOperator::parse(term->name, IOperator::Priority::PostfixUnary); isFixed = isFixed && term->value; @@ -337,7 +339,7 @@ void Expression::fixOperatorTypes(TermVector &terms) { const auto &termPrev = terms[i - 1]; if (is(term->value) && - !isPrefixOperator(term->value) && + !isPrefixOperator(term->value.get()) && !canNextTermBeBinaryOperator(*termPrev)) { term->value = IOperator::parse(term->name, IOperator::Priority::PrefixUnary); @@ -351,7 +353,7 @@ void Expression::fixOperatorTypes(TermVector &terms) { const auto &termNext = terms[i + 1]; if (is(term->value) && - !isPostfixOperator(term->value) && + !isPostfixOperator(term->value.get()) && !canPrevTermBeBinaryOperator(*termNext)) { term->value = IOperator::parse(term->name, IOperator::Priority::PostfixUnary); @@ -369,12 +371,9 @@ void Expression::collapseFactorials(TermVector &terms) { const auto &term = terms[i]; const auto &termNext = terms[i + 1]; - if (auto factorial = cast(term->value); - factorial && - is(termNext->value)) { - - Factorial newFactorial; - newFactorial.setOrder(factorial->getOrder() + 1); + if (is(term->value) && is(termNext->value)) { + auto &oldFactorial = cast(*term->value); + Factorial newFactorial(oldFactorial.getOrder() + 1); term->value = newFactorial.clone(); terms.erase(terms.begin() + ptrdiff_t(i) + 1); @@ -384,36 +383,36 @@ void Expression::collapseFactorials(TermVector &terms) { } bool Expression::canNextTermBeBinaryOperator(const Term &term) { - return !(isPrefixOperator(term.value) || - isBinaryOperator(term.value) || - isNonOperatorFunction(term.value) || + return !(isPrefixOperator(term.value.get()) || + isBinaryOperator(term.value.get()) || + isNonOperatorFunction(term.value.get()) || term.name == "(" || term.name == ","); } bool Expression::canPrevTermBeBinaryOperator(const Term &term) { - return !(isPostfixOperator(term.value) || - isBinaryOperator(term.value) || + return !(isPostfixOperator(term.value.get()) || + isBinaryOperator(term.value.get()) || term.name == ")" || term.name == ","); } -bool Expression::isBinaryOperator(const ArgumentPtr &val) { - auto oper = cast(val); +bool Expression::isBinaryOperator(const IMathObject *val) { + const auto *oper = cast(val); return oper && oper->getFunctionType() == IFunction::Type::Binary; } -bool Expression::isPrefixOperator(const ArgumentPtr &val) { - auto oper = cast(val); +bool Expression::isPrefixOperator(const IMathObject *val) { + const auto *oper = cast(val); return oper && oper->getOperatorPriority() == IOperator::Priority::PrefixUnary; } -bool Expression::isPostfixOperator(const ArgumentPtr &val) { - auto oper = cast(val); +bool Expression::isPostfixOperator(const IMathObject *val) { + const auto *oper = cast(val); return oper && oper->getOperatorPriority() == IOperator::Priority::PostfixUnary; } -bool Expression::isNonOperatorFunction(const ArgumentPtr &val) { +bool Expression::isNonOperatorFunction(const IMathObject *val) { return is(val) && !is(val); } diff --git a/src/fintamath/expressions/IExpression.cpp b/src/fintamath/expressions/IExpression.cpp index 1e2c93d11..95a38abbb 100644 --- a/src/fintamath/expressions/IExpression.cpp +++ b/src/fintamath/expressions/IExpression.cpp @@ -92,8 +92,20 @@ void IExpression::preSimplifyChild(ArgumentPtr &child) { child = simplObj; } } + else { + if (const auto constChild = cast(child)) { + ArgumentPtr constVal = (*constChild)(); - constSimplifyChild(child); + if (const auto num = cast(constVal); num && !num->isPrecise()) { + child = constChild; + } + else { + child = constVal; + } + } + + child = child->toMinimalObject(); + } } void IExpression::postSimplifyChild(ArgumentPtr &child) { @@ -191,16 +203,6 @@ ArgumentPtr IExpression::preciseSimplify() const { } void IExpression::constSimplifyChild(ArgumentPtr &child) { - if (const auto constChild = cast(child)) { - ArgumentPtr constVal = (*constChild)(); - - if (const auto num = cast(constVal); num && !num->isPrecise()) { - child = constChild; - } - else { - child = constVal; - } - } } } diff --git a/src/fintamath/expressions/binary/DivExpression.cpp b/src/fintamath/expressions/binary/DivExpression.cpp index 27eb256d2..c9ad59304 100644 --- a/src/fintamath/expressions/binary/DivExpression.cpp +++ b/src/fintamath/expressions/binary/DivExpression.cpp @@ -78,6 +78,10 @@ ArgumentPtr DivExpression::constSimplify(const IFunction & /*func*/, const Argum return Integer(0).clone(); } + if (*rhs == Integer(1)) { + return lhs; + } + return {}; } diff --git a/tests/src/expressions/ExpressionParserTests.cpp b/tests/src/expressions/ExpressionParserTests.cpp new file mode 100644 index 000000000..57f981058 --- /dev/null +++ b/tests/src/expressions/ExpressionParserTests.cpp @@ -0,0 +1,43 @@ +#include + +#include "fintamath/expressions/ExpressionParser.hpp" + +#include + +using namespace fintamath; + +TEST(ExpressionParserTests, parseFintamathTest) { + EXPECT_EQ(parseFintamath("36/(8-6)3")->toString(), "36/(8 - 6) 3"); + EXPECT_EQ(parseFintamath("2%")->toString(), "2/100"); + EXPECT_EQ(parseFintamath("2.35%")->toString(), "(47/20)/100"); + EXPECT_EQ(parseFintamath("1100*4.76%")->toString(), "1100 (119/25)/100"); + EXPECT_EQ(parseFintamath("2.35%%%%")->toString(), "((((47/20)/100)/100)/100)/100"); + EXPECT_EQ(parseFintamath("1100*4.76%1100*4.76%")->toString(), "1100 (119/25)/100*1100 (119/25)/100"); + EXPECT_EQ(parseFintamath("((((((5)/(8)))/(1)))/(((((((9)/(4)))/(0)))/(5))))")->toString(), "((5/8)/1)/(((9/4)/0)/5)"); + EXPECT_EQ(parseFintamath("2%a")->toString(), "2/100 a"); + EXPECT_EQ(parseFintamath("2!!!!a!!!")->toString(), "2!!!! a!!!"); + EXPECT_EQ(parseFintamath("sin a")->toString(), "sin(a)"); + EXPECT_EQ(parseFintamath("s i n a")->toString(), "s i n a"); + EXPECT_EQ(parseFintamath("a(2)")->toString(), "a 2"); + EXPECT_EQ(parseFintamath("(2)a")->toString(), "2 a"); + EXPECT_EQ(parseFintamath("Ea")->toString(), "E a"); + EXPECT_EQ(parseFintamath("aE")->toString(), "a E"); + EXPECT_EQ(parseFintamath("aEE")->toString(), "a E E"); + EXPECT_EQ(parseFintamath("EEa")->toString(), "E E a"); + EXPECT_EQ(parseFintamath("x123")->toString(), "x 123"); + EXPECT_EQ(parseFintamath("x^y!")->toString(), "(x^y)!"); + EXPECT_EQ(parseFintamath("lnE")->toString(), "ln(E)"); + EXPECT_EQ(parseFintamath("lncossinE")->toString(), "ln(cos(sin(E)))"); + EXPECT_EQ(parseFintamath("ln cos sin a")->toString(), "ln(cos(sin(a)))"); + EXPECT_EQ(parseFintamath("log((Pi),(E)^((Pi)))")->toString(), "log(Pi, E^Pi)"); + EXPECT_EQ(parseFintamath("lb100")->toString(), "log(2, 100)"); + EXPECT_EQ(parseFintamath("log(4/9, 2/3)")->toString(), "log(4/9, 2/3)"); + EXPECT_EQ(parseFintamath("60deg")->toString(), "60 Pi/180"); + EXPECT_EQ(parseFintamath("adeg")->toString(), "a Pi/180"); + + EXPECT_THROW(parseFintamath("(1 = 1) / 2"), InvalidInputException); + EXPECT_THROW(parseFintamath("1+"), InvalidInputException); + EXPECT_THROW(parseFintamath("((((()))))"), InvalidInputException); + EXPECT_THROW(parseFintamath("(,) + (,)"), InvalidInputException); + EXPECT_THROW(parseFintamath("--"), InvalidInputException); +} diff --git a/tests/src/expressions/ExpressionTests.cpp b/tests/src/expressions/ExpressionTests.cpp index 2c857d175..4c700cb7e 100644 --- a/tests/src/expressions/ExpressionTests.cpp +++ b/tests/src/expressions/ExpressionTests.cpp @@ -1767,19 +1767,6 @@ TEST(ExpressionTests, getTypeTest) { EXPECT_EQ(Expression().getType(), MathObjectType::Expression); } -TEST(ExpressionTests, parseExprTest) { - EXPECT_EQ(parseExpr("36/(8-6)3")->toString(), "36/(8 - 6) 3"); - EXPECT_EQ(parseExpr("2%")->toString(), "2/100"); - EXPECT_EQ(parseExpr("2.35%")->toString(), "(47/20)/100"); - EXPECT_EQ(parseExpr("1100*4.76%")->toString(), "1100 (119/25)/100"); - EXPECT_EQ(parseExpr("2.35%%%%")->toString(), "((((47/20)/100)/100)/100)/100"); - EXPECT_EQ(parseExpr("1100*4.76%1100*4.76%")->toString(), "1100 (119/25)/100*1100 (119/25)/100"); - EXPECT_EQ(parseExpr("((((((5)/(8)))/(1)))/(((((((9)/(4)))/(0)))/(5))))")->toString(), "((5/8)/1)/(((9/4)/0)/5)"); - - EXPECT_THROW(parseExpr("(1 = 1) / 2"), InvalidInputException); - EXPECT_THROW(parseExpr("1+"), InvalidInputException); -} - TEST(ExpressionTests, variableVariablePlusOperatorTest) { EXPECT_EQ(Variable("a") + Variable("a"), Expression("2a")); EXPECT_EQ(Variable("a") + Variable("b"), Expression("a+b"));