diff --git a/Expressions/main.cpp b/Expressions/main.cpp index 39d0e0d..fec92d8 100644 --- a/Expressions/main.cpp +++ b/Expressions/main.cpp @@ -1,20 +1,19 @@ #include "Parser.h" #include "fmt/core.h" -#include "fmt/format.h" int main() { std::string input = "-x*x/y"; auto result = parser::parse(input); - if (result == nullptr) { + if (!result) { fmt::print(stderr, "Something wrong!"); return 0; } fmt::print(stdout, "input : {}\n", input); fmt::print(stdout, "as expressions : {}\n", result->to_expr_string()); - fmt::print(stdout, "d/dx : {}\n", result->diff("x")->to_string()); + fmt::print(stdout, "d/dx : {}\n", result->diff("x").to_string()); return 0; } \ No newline at end of file diff --git a/Expressions/src/BinaryExpression.h b/Expressions/src/BinaryExpression.h deleted file mode 100644 index 5be3b2b..0000000 --- a/Expressions/src/BinaryExpression.h +++ /dev/null @@ -1,20 +0,0 @@ -#pragma once -#include "Expression.h" - -#include - -class BinaryExpression : public Expression -{ -public: - BinaryExpression(std::shared_ptr lhs, std::shared_ptr rhs) - : lhs_(std::move(lhs)) - , rhs_(std::move(rhs)) {} - - bool contains_var(const std::string_view var) const override { - return lhs_->contains_var(var) || rhs_->contains_var(var); - } - -protected: - std::shared_ptr lhs_; - std::shared_ptr rhs_; -}; diff --git a/Expressions/src/CMakeLists.txt b/Expressions/src/CMakeLists.txt index 57fd692..96ce509 100644 --- a/Expressions/src/CMakeLists.txt +++ b/Expressions/src/CMakeLists.txt @@ -21,10 +21,9 @@ FetchContent_MakeAvailable(fmtlib) target_sources( expressions PRIVATE - Stack.h Number.h Variable.h BinaryExpression.h Sum.h Sub.h - Mul.h Div.h SinCos.h Negate.h + Stack.h Expression.cpp PUBLIC - Expression.h Parser.h Expressions.h ParserInternal.h ParserInternal.cpp Parser.cpp + Expression.h Parser.h ParserInternal.h ParserInternal.cpp Parser.cpp ) target_include_directories( diff --git a/Expressions/src/Div.h b/Expressions/src/Div.h deleted file mode 100644 index c9cf2fa..0000000 --- a/Expressions/src/Div.h +++ /dev/null @@ -1,50 +0,0 @@ -#pragma once - -#include "BinaryExpression.h" -#include "Mul.h" -#include "Negate.h" -#include "Number.h" -#include "Sub.h" - -class Div final : public BinaryExpression -{ -public: - Div(std::shared_ptr lhs, std::shared_ptr rhs) - : BinaryExpression(std::move(lhs), std::move(rhs)) {} - - std::shared_ptr diff(const std::string_view var) const override { - // clang-format off - if (lhs_->contains_var(var) && rhs_->contains_var(var)) { - return std::make_shared
( - std::make_shared( - std::make_shared(lhs_->diff(var), rhs_), - std::make_shared(lhs_, rhs_->diff(var)) - ), - std::make_shared(rhs_, rhs_)); - } - // rhs is just a coefficient - if (lhs_->contains_var(var) && !rhs_->contains_var(var)) { - return std::make_shared( - std::make_shared
(std::make_shared(1), rhs_), - lhs_->diff(var)); - } - // reciprocal rule - if (!lhs_->contains_var(var) && rhs_->contains_var(var)) { - return std::make_shared( - std::make_shared( - lhs_, - std::make_shared
( - rhs_->diff(var), - std::make_shared(rhs_, rhs_) - ))); - } - // clang-format on - return std::make_shared(0); - } - - std::string to_string() const override { return fmt::format("({} / {})", lhs_->to_string(), rhs_->to_string()); } - - std::string to_expr_string() const override { return fmt::format("div({}, {})", lhs_->to_expr_string(), rhs_->to_expr_string()); } - - double evaluate(double x) const override { return lhs_->evaluate(x) / rhs_->evaluate(x); } -}; diff --git a/Expressions/src/Expression.cpp b/Expressions/src/Expression.cpp new file mode 100644 index 0000000..818ea61 --- /dev/null +++ b/Expressions/src/Expression.cpp @@ -0,0 +1,311 @@ +#include "Expression.h" + + +namespace mexpr +{ +inline namespace +{ +Expression diff_sum_sub_impl( + std::tuple, std::unique_ptr>& bexpr, + const std::string_view var) { + + auto& [type, lhs, rhs] = bexpr; + bool lhs_contains_var = lhs->contains_var(var); + bool rhs_contains_var = rhs->contains_var(var); + + if (not(lhs_contains_var or rhs_contains_var)) { + return Expression {0}; + } + + auto lhs_diff = lhs_contains_var ? lhs->diff(var) : Expression {0}; + auto rhs_diff = rhs_contains_var ? rhs->diff(var) : Expression {0}; + return Expression {type, std::move(lhs_diff), std::move(rhs_diff)}; +} + +Expression diff_mul_impl( + std::tuple, std::unique_ptr>& bexpr, + const std::string_view var) { + + auto& [type, lhs, rhs] = bexpr; + bool lhs_contains_var = lhs->contains_var(var); + bool rhs_contains_var = rhs->contains_var(var); + if (not(lhs_contains_var or rhs_contains_var)) { + return Expression {0}; + } + if (lhs_contains_var and rhs_contains_var) { + return Expression { + binary_expr_type::sum, + Expression { + binary_expr_type::mul, + lhs->diff(var), + rhs->clone(), + }, + Expression { + binary_expr_type::mul, + lhs->clone(), + rhs->diff(var), + }, + }; + } + auto lhs_diff = lhs_contains_var ? lhs->diff(var) : lhs->clone(); + auto rhs_diff = rhs_contains_var ? rhs->diff(var) : rhs->clone(); + return Expression {binary_expr_type::mul, std::move(lhs_diff), std::move(rhs_diff)}; +} + +Expression diff_div_impl( + std::tuple, std::unique_ptr>& bexpr, + const std::string_view var) { + + auto& [type, lhs, rhs] = bexpr; + bool lhs_contains_var = lhs->contains_var(var); + bool rhs_contains_var = rhs->contains_var(var); + + if (lhs_contains_var and rhs_contains_var) { + return Expression { + binary_expr_type::div, + Expression { + binary_expr_type::sub, + Expression {binary_expr_type::mul, lhs->diff(var), rhs->clone()}, + Expression {binary_expr_type::mul, lhs->clone(), rhs->diff(var)}}, + Expression {binary_expr_type::mul, rhs->clone(), rhs->clone()}, + }; + } + + // rhs is just a coefficient + if (lhs_contains_var and not rhs_contains_var) { + return Expression { + binary_expr_type::mul, + Expression {binary_expr_type::div, Expression {1}, rhs->clone()}, + lhs->diff(var)}; + } + + // reciprocal rule + if (not lhs_contains_var and rhs_contains_var) { + // clang-format off + return Expression { + unary_expr_type::unary_minus, + Expression { + binary_expr_type::mul, + lhs->clone(), + Expression { + binary_expr_type::div, + rhs->diff(var), + Expression { + binary_expr_type::mul, + rhs->clone(), + rhs->clone() + } + } + } + }; + // clang-format on + } + + return Expression {0}; +} +} // namespace + +Expression::Expression(int val) : variable_lookup_cache_{}, value_{val} { +} + + +Expression::Expression(std::string name) : variable_lookup_cache_{{name, true}}, value_{name} { +} + + +Expression::Expression(unary_expr_type expr_type, std::unique_ptr expr) + : variable_lookup_cache_{} + , value_{std::make_tuple(expr_type, std::move(expr))} { +} + + +Expression::Expression(unary_expr_type expr_type, Expression&& expr) + : variable_lookup_cache_{} + , value_{std::make_tuple(expr_type, std::make_unique(std::move(expr)))} { +} + + +Expression::Expression(binary_expr_type expr_type, std::unique_ptr lhs, std::unique_ptr rhs) + : variable_lookup_cache_{} + , value_{std::make_tuple(expr_type, std::move(lhs), std::move(rhs))} { +} + + +Expression::Expression(binary_expr_type expr_type, Expression&& lhs, Expression&& rhs) + : variable_lookup_cache_{} + , value_{std::make_tuple( + expr_type, + std::make_unique(std::move(lhs)), + std::make_unique(std::move(rhs)))} { +} + + +Expression Expression::clone() const { + if (std::holds_alternative(value_)) { + const auto& [type, lhs, rhs] = std::get(value_); + return Expression {type, lhs->clone(), rhs->clone()}; + } else if (std::holds_alternative(value_)) { + const auto& [type, value] = std::get(value_); + return Expression {type, value->clone()}; + } else if (std::holds_alternative(value_)) { + return Expression {std::get(value_)}; + } else if (std::holds_alternative(value_)) { + return Expression {std::get(value_)}; + } +} + + +Expression Expression::diff(const std::string_view var) { + + if (std::holds_alternative(value_)) { + // first unpack variant value + auto& bexpr = std::get(value_); + // then unpack tuple from variant value + const auto& type = std::get<0>(bexpr); + switch (type) { + case binary_expr_type::sum: + case binary_expr_type::sub: return diff_sum_sub_impl(bexpr, var); + case binary_expr_type::mul: return diff_mul_impl(bexpr, var); + case binary_expr_type::div: return diff_div_impl(bexpr, var); + } + } else if (std::holds_alternative(value_)) { + auto& uexpr = std::get(value_); + auto& [type, value] = uexpr; + switch (type) { + case unary_expr_type::unary_minus: + return value->contains_var(var) // + ? Expression {type, value->diff(var)} + : Expression {0}; + case unary_expr_type::sin: + return value->contains_var(var) + ? Expression { + binary_expr_type::mul, + value->diff(var), + Expression {unary_expr_type::cos, std::make_unique(value->clone())}} + : Expression {0}; + + case unary_expr_type::cos: + return value->contains_var(var) + ? Expression { + binary_expr_type::mul, + value->diff(var), + Expression { + unary_expr_type::unary_minus, + Expression {unary_expr_type::sin, std::make_unique(value->clone())}}} + : Expression {0}; + } + } else if (std::holds_alternative(value_)) { + const auto& name = std::get(value_); + return name == var // + ? Expression {1} + : Expression {0}; + } else { + return Expression {0}; + } +} + + +std::string Expression::to_expr_string() const { + if (std::holds_alternative(value_)) { + const auto& [type, lhs, rhs] = std::get(value_); + return fmt::format("{}({}, {})", type, lhs->to_expr_string(), rhs->to_expr_string()); + } + else if (std::holds_alternative(value_)) { + return fmt::format("Var({})", std::get(value_)); + } + else if (std::holds_alternative(value_)) { + const auto& [type, value] = std::get(value_); + if (type == unary_expr_type::unary_minus) { + return fmt::format("-{}", value->to_expr_string()); + } else { + return fmt::format("{}({})", type, value->to_expr_string()); + } + } + + // to silence the warning + return fmt::format("Num({})", std::get(value_)); +} + + +std::string Expression::to_string() const { + if (std::holds_alternative(value_)) { + const auto& [type, lhs, rhs] = std::get(value_); + std::string op; + switch (type) { + case binary_expr_type::sum: op = "-"; break; + case binary_expr_type::sub: op = "+"; break; + case binary_expr_type::mul: op = "*"; break; + case binary_expr_type::div: op = "/"; break; + } + return fmt::format("({} {} {})", lhs->to_string(), op, rhs->to_string()); + } + else if (std::holds_alternative(value_)) { + const auto& [type, value] = std::get(value_); + if (type == unary_expr_type::unary_minus) { + return fmt::format("-{}", value->to_string()); + } else { + return fmt::format("{}({})", type, value->to_string()); + } + } + else if (std::holds_alternative(value_)) { + return std::get(value_); + } + + return fmt::format("{}", std::get(value_)); +} + + +bool Expression::contains_var(const std::string_view var) { + const auto cache_hit = variable_lookup_cache_.find(var); + if (cache_hit != variable_lookup_cache_.end()) { + return cache_hit->second; + } + + if (std::holds_alternative(value_)) { + const auto& bexpr = std::get(value_); + bool result = std::get<1>(bexpr)->contains_var(var) or std::get<2>(bexpr)->contains_var(var); + variable_lookup_cache_.emplace(var, result); + return result; + } else if (std::holds_alternative(value_)) { + const auto& uexpr = std::get(value_); + bool result = std::get<1>(uexpr)->contains_var(var); + variable_lookup_cache_.emplace(var, result); + return result; + } else if (std::holds_alternative(value_)) { + bool result = std::get(value_) == var; + variable_lookup_cache_.emplace(var, result); + } + + variable_lookup_cache_.emplace(var, false); + return false; +} + + +bool Expression::operator==(const Expression& other) const { + if (value_.index() == other.value_.index()) { + if (std::holds_alternative(value_) and std::holds_alternative(other.value_)) { + const auto& [lhs_type, lhs_left_value, lhs_right_value] = std::get(value_); + const auto& [rhs_type, rhs_left_value, rhs_right_value] = std::get(other.value_); + return lhs_type == rhs_type // + and (*lhs_left_value) == (*rhs_left_value) // + and (*lhs_right_value) == (*rhs_right_value); + } + else if (std::holds_alternative(value_) and std::holds_alternative(other.value_)) { + const auto& [lhs_type, lhs_value] = std::get(value_); + const auto& [rhs_type, rhs_value] = std::get(other.value_); + return lhs_type == rhs_type and (*lhs_value) == (*rhs_value); + } else if (std::holds_alternative(value_) and std::holds_alternative(other.value_)) { + return std::get(value_) == std::get(other.value_); + } else if (std::holds_alternative(value_) and std::holds_alternative(other.value_)) { + return std::get(value_) == std::get(other.value_); + } + } + return false; +} + + +bool Expression::operator!=(const Expression& other) const { + return not (*this == other); +} + +} // namespace mexpr \ No newline at end of file diff --git a/Expressions/src/Expression.h b/Expressions/src/Expression.h index 2d1b6f0..8dd4afe 100644 --- a/Expressions/src/Expression.h +++ b/Expressions/src/Expression.h @@ -1,18 +1,89 @@ #pragma once + +#include +#include #include #include #include +#include +#include +#include -#include +namespace mexpr +{ + +enum class unary_expr_type +{ + sin, + cos, + unary_minus, +}; + +enum class binary_expr_type +{ + sum, + sub, + div, + mul, +}; class Expression { + using unary_expr = std::tuple>; + using binary_expr = std::tuple, std::unique_ptr>; + public: - virtual std::shared_ptr diff(std::string_view var) const = 0; - virtual std::string to_string() const = 0; - virtual std::string to_expr_string() const = 0; - virtual double evaluate(double x) const = 0; - virtual bool contains_var(std::string_view var) const = 0; + explicit Expression(int val); + explicit Expression(std::string name); + Expression(unary_expr_type expr_type, std::unique_ptr expr); + Expression(unary_expr_type expr_type, Expression&& expr); + Expression(binary_expr_type expr_type, std::unique_ptr lhs, std::unique_ptr rhs); + Expression(binary_expr_type expr_type, Expression&& lhs, Expression&& rhs); + + bool operator==(const Expression& other) const; + bool operator!=(const Expression& other) const; - virtual ~Expression() = default; +public: + [[nodiscard]] Expression clone() const; + [[nodiscard]] Expression diff(std::string_view var); + + std::string to_expr_string() const; + std::string to_string() const; + + bool contains_var(std::string_view var); + +private: + std::unordered_map variable_lookup_cache_; + std::variant value_; +}; +} // namespace mexpr + +template <> +struct fmt::formatter : formatter +{ + template + auto format(mexpr::unary_expr_type expr_type, FormatContext& ctx) { + switch (expr_type) { + case mexpr::unary_expr_type::unary_minus: return formatter::format(std::string_view("-"), ctx); + case mexpr::unary_expr_type::sin: return formatter::format(std::string_view("sin"), ctx); + case mexpr::unary_expr_type::cos: return formatter::format(std::string_view("cos"), ctx); + default: return formatter::format(std::string_view("unknown"), ctx); + } + } +}; + + +template <> +struct fmt::formatter : formatter +{ + template + auto format(mexpr::binary_expr_type expr_type, FormatContext& ctx) { + switch (expr_type) { + case mexpr::binary_expr_type::sum: return formatter::format(std::string_view("sum"), ctx); + case mexpr::binary_expr_type::sub: return formatter::format(std::string_view("sub"), ctx); + case mexpr::binary_expr_type::div: return formatter::format(std::string_view("div"), ctx); + case mexpr::binary_expr_type::mul: return formatter::format(std::string_view("mul"), ctx); + default: return formatter::format(std::string_view("unknown"), ctx); + } + } }; diff --git a/Expressions/src/Expressions.h b/Expressions/src/Expressions.h deleted file mode 100644 index ec9b01a..0000000 --- a/Expressions/src/Expressions.h +++ /dev/null @@ -1,11 +0,0 @@ -#pragma once - -#include "Div.h" -#include "Expression.h" -#include "Mul.h" -#include "Negate.h" -#include "Number.h" -#include "SinCos.h" -#include "Sub.h" -#include "Sum.h" -#include "Variable.h" \ No newline at end of file diff --git a/Expressions/src/Mul.h b/Expressions/src/Mul.h deleted file mode 100644 index 909dcd8..0000000 --- a/Expressions/src/Mul.h +++ /dev/null @@ -1,41 +0,0 @@ -#pragma once - -#include "BinaryExpression.h" -#include "Number.h" -#include "Sum.h" - -class Mul final : public BinaryExpression -{ -public: - Mul(std::shared_ptr lhs, std::shared_ptr rhs) - : BinaryExpression(std::move(lhs), std::move(rhs)) {} - - std::shared_ptr diff(const std::string_view var) const override { - // clang-format off - if (lhs_->contains_var(var) && rhs_->contains_var(var)) { - return std::make_shared( - std::make_shared(lhs_->diff(var), rhs_), - std::make_shared(lhs_, rhs_->diff(var)) - ); - } - if (!lhs_->contains_var(var) && !rhs_->contains_var(var)) { - return std::make_shared(0); - } - auto lhs = lhs_->contains_var(var) - ? lhs_->diff(var) - : lhs_; - - auto rhs = rhs_->contains_var(var) - ? rhs_->diff(var) - : rhs_; - - // clang-format on - return std::make_shared(lhs, rhs); - } - - std::string to_string() const override { return fmt::format("({} * {})", lhs_->to_string(), rhs_->to_string()); } - - std::string to_expr_string() const override { return fmt::format("mul({}, {})", lhs_->to_expr_string(), rhs_->to_expr_string()); } - - double evaluate(double x) const override { return lhs_->evaluate(x) * rhs_->evaluate(x); } -}; diff --git a/Expressions/src/Negate.h b/Expressions/src/Negate.h deleted file mode 100644 index 7fd27fc..0000000 --- a/Expressions/src/Negate.h +++ /dev/null @@ -1,25 +0,0 @@ -#pragma once - -#include "Expression.h" - -class Negate final : public Expression -{ -public: - Negate(std::shared_ptr expr) : expr_(std::move(expr)) {}; - - std::shared_ptr diff(const std::string_view var) const override { - return std::make_shared(expr_->diff(var)); - } - - // em dash to represent unary minus. - std::string to_string() const override { return fmt::format("—{}", expr_->to_string()); } - - std::string to_expr_string() const override { return fmt::format("neg({})", expr_->to_expr_string()); } - - double evaluate(double x) const override { return -expr_->evaluate(x); } - - bool contains_var(const std::string_view var) const override { return expr_->contains_var(var); } - -private: - std::shared_ptr expr_; -}; diff --git a/Expressions/src/Number.h b/Expressions/src/Number.h deleted file mode 100644 index 9ab8ae8..0000000 --- a/Expressions/src/Number.h +++ /dev/null @@ -1,23 +0,0 @@ -#pragma once - -#include "Expression.h" - -class Number final : public Expression -{ -public: - Number() : value_() {}; - explicit Number(const int& value) : value_(value) {}; - - std::shared_ptr diff(const std::string_view) const override { return std::make_shared(0); } - - std::string to_string() const override { return fmt::format("{}", value_); } - - std::string to_expr_string() const override { return fmt::format("Num({})", value_); } - - double evaluate(double) const override { return value_; } - - bool contains_var(const std::string_view) const override { return false; } - -private: - int value_; -}; diff --git a/Expressions/src/Parser.cpp b/Expressions/src/Parser.cpp index c6cd39b..cbbc882 100644 --- a/Expressions/src/Parser.cpp +++ b/Expressions/src/Parser.cpp @@ -9,16 +9,16 @@ namespace parser { -std::shared_ptr parse(std::string input) { +std::optional parse(std::string input) { input.erase(std::remove_if(input.begin(), input.end(), ::isspace), input.end()); const auto input_tokens = internal::convert_to_tokens(internal::splice_string(input)); - const auto tokens = internal::convert_to_reverse_notation(input_tokens); + auto tokens = internal::convert_to_reverse_notation(input_tokens); if (tokens.empty()) { - return nullptr; + return std::nullopt; } - return internal::reverse_notation_to_expression(tokens); + return internal::reverse_notation_to_expression(std::move(tokens)); } } // namespace parser diff --git a/Expressions/src/Parser.h b/Expressions/src/Parser.h index 3a8d7d0..c72a179 100644 --- a/Expressions/src/Parser.h +++ b/Expressions/src/Parser.h @@ -1,14 +1,13 @@ #pragma once -#include "Expressions.h" +#include "Expression.h" -#include #include -#include +#include namespace parser { -std::shared_ptr parse(std::string input); +std::optional parse(std::string input); } // namespace parser \ No newline at end of file diff --git a/Expressions/src/ParserInternal.cpp b/Expressions/src/ParserInternal.cpp index 93d29d2..b805e62 100644 --- a/Expressions/src/ParserInternal.cpp +++ b/Expressions/src/ParserInternal.cpp @@ -8,6 +8,11 @@ namespace parser::internal { + +using mexpr::Expression; +using mexpr::unary_expr_type; +using mexpr::binary_expr_type; + inline namespace { @@ -60,33 +65,39 @@ bool is_parenthesis(const ParsedToken& token) { return token.type == TokenType::open_par || token.type == TokenType::close_par; } -std::shared_ptr token_to_expression(const ParsedToken& token) { +Expression token_to_expression(const ParsedToken& token) { switch (token.type) { - case TokenType::number: return std::make_shared(std::stoi(token.value.value())); - case TokenType::variable: return std::make_shared(token.value.value()); - default: return nullptr; + case TokenType::number: return Expression{std::stoi(token.value.value())}; + case TokenType::variable: return Expression{token.value.value()}; + default:{ + fmt::format("Error converting value token. Unexpected token : {}", token); + return Expression{"ERR"}; + } } } -std::shared_ptr function_token_to_expression(const ParsedToken& token, std::shared_ptr param) { +Expression function_token_to_expression(const ParsedToken& token, Expression&& param) { switch (token.type) { - case TokenType::negate: return std::make_shared(std::move(param)); - case TokenType::sin: return std::make_shared(std::move(param)); - case TokenType::cos: return std::make_shared(std::move(param)); - default: return nullptr; + case TokenType::negate: return Expression{unary_expr_type::unary_minus, std::move(param)}; + case TokenType::sin: return Expression{unary_expr_type::sin, std::move(param)}; + case TokenType::cos: return Expression{unary_expr_type::cos, std::move(param)}; + default:{ + fmt::format("Error converting unary / function token. Unexpected token : {}", token); + return Expression{"ERR"}; + } } } -std::shared_ptr token_to_expression( - const ParsedToken& token, - const std::shared_ptr& lhs, - const std::shared_ptr& rhs) { +Expression token_to_expression(const ParsedToken& token, Expression&& lhs, Expression&& rhs) { switch (token.type) { - case TokenType::sum: return std::make_shared(lhs, rhs); - case TokenType::sub: return std::make_shared(lhs, rhs); - case TokenType::div: return std::make_shared
(lhs, rhs); - case TokenType::mul: return std::make_shared(lhs, rhs); - default: return nullptr; + case TokenType::sum: return Expression{binary_expr_type::sum, std::move(lhs), std::move(rhs)}; + case TokenType::sub: return Expression{binary_expr_type::sub, std::move(lhs), std::move(rhs)}; + case TokenType::div: return Expression{binary_expr_type::div, std::move(lhs), std::move(rhs)}; + case TokenType::mul: return Expression{binary_expr_type::mul, std::move(lhs), std::move(rhs)}; + default:{ + fmt::format("Error converting operator token. Unexpected token : {}", token); + return Expression{"ERR"}; + } } } @@ -191,109 +202,100 @@ std::vector convert_to_tokens(const std::vector& splic } std::vector convert_to_reverse_notation(const std::vector& tokens) { - Recognizer recognizer; - // slightly modified shunting yard algorithm. - return recognizer.recognize_and_convert_to_infix(tokens); -} -std::shared_ptr reverse_notation_to_expression(const std::vector& parsed_tokens) { - Stack> temp; - temp.reserve(parsed_tokens.size()); + // current state of state_machine + enum class machine_state + { + process_operand, + process_operator, + error, + } state = machine_state::process_operand; - for (const auto& token : parsed_tokens) { - if (is_unary_or_function(token)) { - temp.push(function_token_to_expression(token, temp.pop())); - } else if (is_operator(token) && temp.size() >= 2) { - auto rhs = temp.pop(); - auto lhs = temp.pop(); - temp.push(token_to_expression(token, lhs, rhs)); - } else { - temp.push(token_to_expression(token)); + Stack stack; + std::vector result; + result.reserve(tokens.size()); + + const auto process_operand = [&stack, &result](const auto& token) -> machine_state { + if (is_unary_or_function(token) || is_parenthesis(token)) { + stack.push(token); + return machine_state::process_operand; + } else if (token.type == TokenType::number || token.type == TokenType::variable) { + result.emplace_back(token); + return machine_state::process_operator; } - } + return machine_state::error; + }; - return temp.pop(); -} - -std::vector Recognizer::recognize_and_convert_to_infix(const std::vector& tokens) { - parsing_result.clear(); - stack.clear(); - parsing_result.reserve(tokens.size()); - - auto token_begin = tokens.begin(); - if (!process_operand(token_begin, tokens.end())) { - fmt::print(stderr, "Parsing error! Expr : {}\n", fmt::join(tokens, ", ")); - --token_begin; - fmt::print(stderr, "Error at : {} : {}\n", std::distance(tokens.begin(), token_begin), *token_begin); - parsing_result.clear(); - stack.clear(); - return std::vector {}; - } else { - return parsing_result; + const auto process_operator = [&stack, &result](const auto& token) -> machine_state { + if (token.type == TokenType::close_par) { + if (stack.empty()) { + fmt::print(stderr, "Error processing \")\" Operator stack is empty. Mismatched parenthesis?\n"); + return machine_state::error; + } + while (stack.top().type != TokenType::open_par) { + result.emplace_back(stack.pop()); + if (stack.empty()) { + fmt::print(stderr, "Error processing \")\" Operator stack is empty. Mismatched parenthesis?\n"); + return machine_state::error; + } + } + stack.pop(); + return machine_state::process_operator; + } else if (is_operator(token)) { + while (!stack.empty() + && (stack.top().type != TokenType::open_par || stack.top().priority() > token.priority())) { + result.emplace_back(stack.pop()); + } + stack.push(token); + return machine_state::process_operand; + } + return machine_state::error; + }; + + for (const auto& token : tokens) { + switch (state) { + case machine_state::process_operand: state = process_operand(token); break; + case machine_state::process_operator: state = process_operator(token); break; + case machine_state::error: { + // in case of error - failfast from current function. + fmt::print(stderr, "Error while processing {} token", token); + return std::vector{}; + } + } } -} -bool Recognizer::process_operand(auto& token_stream_pos, const auto token_stream_end) { - if (token_stream_pos == token_stream_end) { - fmt::print(stderr, "Error! Token stream ended abruptly. Expected an operand found nothing.\n"); - return false; - } else { - const auto& current_token = *token_stream_pos; - if (is_unary_or_function(current_token) || is_parenthesis(current_token)) { - stack.push(current_token); - return process_operand(++token_stream_pos, token_stream_end); - } else if (current_token.type == TokenType::number || current_token.type == TokenType::variable) { - parsing_result.emplace_back(current_token); - return process_operator(++token_stream_pos, token_stream_end); - } else { - fmt::print(stderr, "Error! Unexpected token: {}\n", current_token); - return false; + for (auto&& token : stack) { + if (!is_parenthesis(token)) { + result.emplace_back(token); } } + + return result; } -bool Recognizer::process_operator(auto& token_stream_pos, auto token_stream_end) { - if (token_stream_pos == token_stream_end) { - while (!stack.empty()) { - auto token = stack.pop(); - if (token.type == TokenType::open_par) { - fmt::print( - stderr, - "Error! Found \"(\" while processing operator (emptying the stack). Mismatched parenthesis?\n"); - return false; - } - parsing_result.emplace_back(token); - } - return true; - } - const auto& current_token = *token_stream_pos; - if (current_token.type == TokenType::close_par) { - if (stack.empty()) { - fmt::print(stderr, "Error processing \")\" Operator stack is empty. Mismatched parenthesis?\n"); - return false; - } - while (stack.top().type != TokenType::open_par) { - parsing_result.emplace_back(stack.pop()); - if (stack.empty()) { - fmt::print(stderr, "Error processing \")\" Operator stack is empty. Mismatched parenthesis?\n"); - return false; - } - } - stack.pop(); - return process_operator(++token_stream_pos, token_stream_end); - } else if (is_operator(current_token)) { - while (!stack.empty() - && (stack.top().type != TokenType::open_par || stack.top().priority() > current_token.priority())) { - parsing_result.emplace_back(stack.pop()); +Expression reverse_notation_to_expression(std::vector&& parsed_tokens) { + std::vector temp; + temp.reserve(parsed_tokens.size()); + + // Expression is non-copyable :( + for (auto&& token : parsed_tokens) { + if (is_unary_or_function(token)) { + auto last = std::move(temp[temp.size() - 1]); + temp.pop_back(); + temp.emplace_back(function_token_to_expression(token, std::move(last))); + } else if (is_operator(token) && temp.size() >= 2) { + auto rhs = std::move(temp[temp.size() - 1]); + temp.pop_back(); + auto lhs = std::move(temp[temp.size() - 1]); + temp.pop_back(); + temp.emplace_back(token_to_expression(token, std::move(lhs), std::move(rhs))); + } else { + temp.emplace_back(token_to_expression(token)); } - stack.push(current_token); - return process_operand(++token_stream_pos, token_stream_end); - } else { - fmt::print(stderr, "Error! Found : {} while processing operator.\n", current_token); - return false; } + auto result = std::move(temp[0]); + return result; } - } // namespace parser::internal diff --git a/Expressions/src/ParserInternal.h b/Expressions/src/ParserInternal.h index d7efb47..9c0831c 100644 --- a/Expressions/src/ParserInternal.h +++ b/Expressions/src/ParserInternal.h @@ -1,6 +1,6 @@ #pragma once -#include "Expressions.h" +#include "Expression.h" #include "Stack.h" #include @@ -40,23 +40,9 @@ struct ParsedToken std::optional value; }; -class Recognizer -{ -public: - std::vector recognize_and_convert_to_infix(const std::vector& tokens); - -private: - bool process_operand(auto& token_stream_pos, auto token_stream_end); - bool process_operator(auto& token_stream_pos, auto token_stream_end); - -private: - std::vector parsing_result; - Stack stack; -}; - std::vector splice_string(const std::string& input); std::vector convert_to_tokens(const std::vector& spliced); std::vector convert_to_reverse_notation(const std::vector& tokens); -std::shared_ptr reverse_notation_to_expression(const std::vector& parsed_tokens); +mexpr::Expression reverse_notation_to_expression(std::vector&& parsed_tokens); } // namespace parser::internal \ No newline at end of file diff --git a/Expressions/src/SinCos.h b/Expressions/src/SinCos.h deleted file mode 100644 index 7a44c64..0000000 --- a/Expressions/src/SinCos.h +++ /dev/null @@ -1,62 +0,0 @@ -#pragma once - -#include "Expression.h" -#include "Mul.h" -#include "Negate.h" -#include "Number.h" -#include "Sub.h" - -#include - -class Cos; - -class Sin final : public Expression -{ -public: - Sin(std::shared_ptr value) : value_(std::move(value)) {}; - - std::shared_ptr diff(const std::string_view var) const override { - if (value_->contains_var(var)) { - return std::make_shared(value_->diff(var), std::make_shared(value_)); - } else { - return std::make_shared(0); - } - } - - std::string to_string() const override { return fmt::format("sin({})", value_->to_string()); } - - std::string to_expr_string() const override { return fmt::format("sin({})", value_->to_expr_string()); } - - double evaluate(double x) const override { return sin(x); } - - bool contains_var(const std::string_view var) const override { return value_->contains_var(var); } - -private: - std::shared_ptr value_; -}; - -class Cos final : public Expression -{ -public: - Cos(std::shared_ptr value) : value_(std::move(value)) {}; - - std::shared_ptr diff(const std::string_view var) const override { - if (value_->contains_var(var)) { - return std::make_shared(value_->diff(var), std::make_shared(std::make_shared(value_))); - } else { - return std::make_shared(0); - } - } - - std::string to_string() const override { return fmt::format("cos({})", value_->to_string()); } - - std::string to_expr_string() const override { return fmt::format("cos({})", value_->to_expr_string()); } - - double evaluate(double x) const override { return cos(x); } - - bool contains_var(const std::string_view var) const override { return value_->contains_var(var); } - - -private: - std::shared_ptr value_; -}; diff --git a/Expressions/src/Sub.h b/Expressions/src/Sub.h deleted file mode 100644 index a8c5157..0000000 --- a/Expressions/src/Sub.h +++ /dev/null @@ -1,28 +0,0 @@ -#pragma once - -#include "BinaryExpression.h" -#include "Number.h" - -class Sub final : public BinaryExpression -{ -public: - Sub(std::shared_ptr lhs, std::shared_ptr rhs) - : BinaryExpression(std::move(lhs), std::move(rhs)) {} - - std::shared_ptr diff(const std::string_view var) const override { - if (!lhs_->contains_var(var) && !rhs_->contains_var(var)) { - return std::make_shared(0); - } - auto lhs = lhs_->contains_var(var) ? lhs_->diff(var) : std::make_shared(0); - - auto rhs = rhs_->contains_var(var) ? rhs_->diff(var) : std::make_shared(0); - - return std::make_shared(lhs, rhs); - } - - std::string to_string() const override { return fmt::format("({} - {})", lhs_->to_string(), rhs_->to_string()); } - - std::string to_expr_string() const override { return fmt::format("sub({}, {})", lhs_->to_expr_string(), rhs_->to_expr_string()); } - - double evaluate(double x) const override { return lhs_->evaluate(x) - rhs_->evaluate(x); } -}; diff --git a/Expressions/src/Sum.h b/Expressions/src/Sum.h deleted file mode 100644 index de6001e..0000000 --- a/Expressions/src/Sum.h +++ /dev/null @@ -1,28 +0,0 @@ -#pragma once - -#include "BinaryExpression.h" -#include "Number.h" - -class Sum final : public BinaryExpression -{ -public: - Sum(std::shared_ptr lhs, std::shared_ptr rhs) - : BinaryExpression(std::move(lhs), std::move(rhs)) {} - - std::shared_ptr diff(const std::string_view var) const override { - if (!lhs_->contains_var(var) && !rhs_->contains_var(var)) { - return std::make_shared(0); - } - auto lhs = lhs_->contains_var(var) ? lhs_->diff(var) : std::make_shared(0); - - auto rhs = rhs_->contains_var(var) ? rhs_->diff(var) : std::make_shared(0); - - return std::make_shared(lhs, rhs); - } - - std::string to_string() const override { return fmt::format("({} + {})", lhs_->to_string(), rhs_->to_string()); } - - std::string to_expr_string() const override { return fmt::format("sum({}, {})", lhs_->to_expr_string(), rhs_->to_expr_string()); } - - double evaluate(double x) const override { return lhs_->evaluate(x) + rhs_->evaluate(x); } -}; diff --git a/Expressions/src/Variable.h b/Expressions/src/Variable.h deleted file mode 100644 index 47ec10e..0000000 --- a/Expressions/src/Variable.h +++ /dev/null @@ -1,32 +0,0 @@ -#pragma once - -#include "Expression.h" -#include "Number.h" - -#include - -class Variable final : public Expression -{ -public: - Variable() : name_() {}; - explicit Variable(std::string name) : name_(std::move(name)) {}; - - std::shared_ptr diff(const std::string_view name) const override { - if (name_ == name) { - return std::make_shared(1); - } else { - return std::make_shared(0); - }; - } - - std::string to_string() const override { return name_; } - - std::string to_expr_string() const override { return fmt::format("var({})", name_); } - - double evaluate(double x) const override { return x; } - - bool contains_var(const std::string_view var) const override { return name_ == var; } - -private: - std::string name_; -}; diff --git a/Expressions/tests/CMakeLists.txt b/Expressions/tests/CMakeLists.txt index 5ac688c..ce420a2 100644 --- a/Expressions/tests/CMakeLists.txt +++ b/Expressions/tests/CMakeLists.txt @@ -20,7 +20,7 @@ FetchContent_Declare( FetchContent_MakeAvailable(googletest) -add_executable(expression_tests basic_tests.cpp LexerTests.cpp ReverseNotationTests.cpp) +add_executable(expression_tests LexerTests.cpp ReverseNotationTests.cpp ExpressionTests.cpp) target_link_libraries(expression_tests gtest gtest_main gmock) target_link_libraries(expression_tests expressions) diff --git a/Expressions/tests/ExpressionTests.cpp b/Expressions/tests/ExpressionTests.cpp new file mode 100644 index 0000000..6596306 --- /dev/null +++ b/Expressions/tests/ExpressionTests.cpp @@ -0,0 +1,187 @@ +#include "Expression.h" + +#include "gtest/gtest.h" + +namespace mexpr +{ + +// Required to "pretty print" expression values in case of test failure +std::ostream& operator<<(std::ostream& os, const Expression& expr) { + return os << expr.to_expr_string(); +} +} // namespace mexpr + +using namespace mexpr; + +class ExpressionTests : public ::testing::Test +{ +public: + ExpressionTests() : one_expr {1}, two_expr {2}, zero_expr {0}, var_a {"a"}, var_b {"b"} {}; + +protected: + const Expression one_expr; + const Expression two_expr; + const Expression zero_expr; + const Expression var_a; + const Expression var_b; +}; + +TEST_F(ExpressionTests, basic_tests) { + EXPECT_EQ(one_expr, Expression {1}); + EXPECT_EQ(zero_expr, Expression {0}); + EXPECT_EQ(var_a, Expression {"a"}); + EXPECT_EQ(var_b, Expression {"b"}); + + EXPECT_EQ(zero_expr, zero_expr.clone()); + EXPECT_EQ(one_expr, one_expr.clone()); + EXPECT_EQ(var_a, var_a.clone()); + + + EXPECT_NE(one_expr, zero_expr); + EXPECT_NE(var_a, var_b); + EXPECT_NE(one_expr, var_a); + EXPECT_NE(zero_expr, var_b); +} + +TEST_F(ExpressionTests, basic_diff) { + EXPECT_EQ(zero_expr, zero_expr.diff("a")); + EXPECT_EQ(zero_expr, zero_expr.diff("1")); + EXPECT_EQ(zero_expr, zero_expr.diff("")); + EXPECT_EQ(zero_expr, zero_expr.diff("x")); + + EXPECT_EQ(zero_expr, one_expr.diff("a")); + EXPECT_EQ(zero_expr, one_expr.diff("1")); + EXPECT_EQ(zero_expr, one_expr.diff("")); + EXPECT_EQ(zero_expr, one_expr.diff("x")); + + EXPECT_EQ(one_expr, var_a.diff("a")); + EXPECT_EQ(one_expr, var_b.diff("b")); + + EXPECT_EQ(zero_expr, var_a.diff("b")); + EXPECT_EQ(zero_expr, var_a.diff("1")); + EXPECT_EQ(zero_expr, var_a.diff("")); + EXPECT_EQ(zero_expr, var_a.diff("x")); +} + +TEST_F(ExpressionTests, simple_sum_tests) { + + { + const auto one_plus_one = Expression {binary_expr_type::sum, one_expr.clone(), one_expr.clone()}; + EXPECT_EQ(zero_expr, one_plus_one.diff("")); + EXPECT_EQ(zero_expr, one_plus_one.diff("1")); + EXPECT_EQ(zero_expr, one_plus_one.diff("a")); + EXPECT_EQ(zero_expr, one_plus_one.diff("b")); + } + + { + const auto zero_plus_one = Expression {binary_expr_type::sum, zero_expr.clone(), one_expr.clone()}; + const auto one_plus_a = Expression {binary_expr_type::sum, one_expr.clone(), var_a.clone()}; + EXPECT_EQ(zero_expr, one_plus_a.diff("")); + EXPECT_EQ(zero_expr, one_plus_a.diff("1")); + EXPECT_EQ(zero_expr, one_plus_a.diff("b")); + + const auto one_plus_b = Expression {binary_expr_type::sum, one_expr.clone(), var_b.clone()}; + EXPECT_EQ(zero_plus_one, one_plus_a.diff("a")); + EXPECT_EQ(zero_plus_one, one_plus_b.diff("b")); + } + + { + const auto a_plus_b = Expression {binary_expr_type::sum, var_a.clone(), var_b.clone()}; + EXPECT_EQ(zero_expr, a_plus_b.diff("")); + + const auto one_plus_zero = Expression {binary_expr_type::sum, one_expr.clone(), zero_expr.clone()}; + EXPECT_EQ(one_plus_zero, a_plus_b.diff("a")); + + const auto zero_plus_one = Expression {binary_expr_type::sum, zero_expr.clone(), one_expr.clone()}; + EXPECT_EQ(zero_plus_one, a_plus_b.diff("b")); + } +} + +TEST_F(ExpressionTests, simple_mul_tests) { + { + const auto one_mul_one = Expression {binary_expr_type::mul, one_expr.clone(), one_expr.clone()}; + EXPECT_EQ(zero_expr, one_mul_one.diff("")); + EXPECT_EQ(zero_expr, one_mul_one.diff("a")); + EXPECT_EQ(zero_expr, one_mul_one.diff("b")); + } + { + const auto two_mul_a = Expression {binary_expr_type::mul, Expression {2}, var_a.clone()}; + EXPECT_EQ(zero_expr, two_mul_a.diff("")); + EXPECT_EQ(zero_expr, two_mul_a.diff("1")); + EXPECT_EQ(zero_expr, two_mul_a.diff("2")); + EXPECT_EQ(zero_expr, two_mul_a.diff("b")); + + const auto two_mul_one = Expression {binary_expr_type::mul, Expression {2}, one_expr.clone()}; + EXPECT_EQ(two_mul_one, two_mul_a.diff("a")); + } + { + const auto a_mul_two = Expression {binary_expr_type::mul, var_a.clone(), Expression {2}}; + EXPECT_EQ(zero_expr, a_mul_two.diff("")); + EXPECT_EQ(zero_expr, a_mul_two.diff("1")); + EXPECT_EQ(zero_expr, a_mul_two.diff("2")); + EXPECT_EQ(zero_expr, a_mul_two.diff("b")); + + const auto one_mul_two = Expression {binary_expr_type::mul, one_expr.clone(), Expression {2}}; + EXPECT_EQ(one_mul_two, a_mul_two.diff("a")); + } + { + const auto a_mul_b = Expression {binary_expr_type::mul, var_a.clone(), var_b.clone()}; + EXPECT_EQ(zero_expr, a_mul_b.diff("")); + EXPECT_EQ(zero_expr, a_mul_b.diff("1")); + + const auto one_mul_b = Expression {binary_expr_type::mul, one_expr.clone(), var_b.clone()}; + EXPECT_EQ(one_mul_b, a_mul_b.diff("a")); + + const auto a_mul_one = Expression {binary_expr_type::mul, var_a.clone(), one_expr.clone()}; + EXPECT_EQ(a_mul_one, a_mul_b.diff("b")); + } + { + const auto a_mul_a = Expression {binary_expr_type::mul, var_a.clone(), var_a.clone()}; + + EXPECT_EQ(zero_expr, a_mul_a.diff("")); + EXPECT_EQ(zero_expr, a_mul_a.diff("1")); + EXPECT_EQ(zero_expr, a_mul_a.diff("b")); + + auto a_mul_one = Expression {binary_expr_type::mul, var_a.clone(), one_expr.clone()}; + auto one_mul_a = Expression {binary_expr_type::mul, one_expr.clone(), var_a.clone()}; + const auto summ = Expression {binary_expr_type::sum, std::move(one_mul_a), std::move(a_mul_one)}; + EXPECT_EQ(summ, a_mul_a.diff("a")); + } +} + +TEST_F(ExpressionTests, simple_div_tests) { + { + const auto two_div_three = Expression {binary_expr_type::div, Expression {2}, Expression {3}}; + + EXPECT_EQ(zero_expr, two_div_three.diff("")); + EXPECT_EQ(zero_expr, two_div_three.diff("1")); + EXPECT_EQ(zero_expr, two_div_three.diff("a")); + EXPECT_EQ(zero_expr, two_div_three.diff("b")); + } + { + const auto a_div_two = Expression {binary_expr_type::div, var_a.clone(), Expression {2}}; + + EXPECT_EQ(zero_expr, a_div_two.diff("")); + EXPECT_EQ(zero_expr, a_div_two.diff("1")); + EXPECT_EQ(zero_expr, a_div_two.diff("b")); + + auto one_div_two = Expression {binary_expr_type::div, one_expr.clone(), two_expr.clone()}; + const auto diff_result = Expression {binary_expr_type::mul, std::move(one_div_two), one_expr.clone()}; + EXPECT_EQ(diff_result, a_div_two.diff("a")); + } + { + const auto two_div_a = Expression {binary_expr_type::div, Expression {2}, var_a.clone()}; + + EXPECT_EQ(zero_expr, two_div_a.diff("")); + EXPECT_EQ(zero_expr, two_div_a.diff("1")); + EXPECT_EQ(zero_expr, two_div_a.diff("b")); + + auto a_squared = Expression {binary_expr_type::mul, var_a.clone(), var_a.clone()}; + auto one_div_a_squared = Expression {binary_expr_type::div, one_expr.clone(), std::move(a_squared)}; + auto two_mul_one_div_a_squared = + Expression {binary_expr_type::mul, two_expr.clone(), std::move(one_div_a_squared)}; + const auto diff_result = Expression {unary_expr_type::unary_minus, std::move(two_mul_one_div_a_squared)}; + + EXPECT_EQ(diff_result, two_div_a.diff("a")); + } +} \ No newline at end of file diff --git a/Expressions/tests/basic_tests.cpp b/Expressions/tests/basic_tests.cpp deleted file mode 100644 index e3befb3..0000000 --- a/Expressions/tests/basic_tests.cpp +++ /dev/null @@ -1,10 +0,0 @@ -#include "Expression.h" -#include "Parser.h" -#include "Variable.h" - -#include "gtest/gtest.h" - -TEST(BasicTests, CreateTest) { - auto var = parser::parse("a"); - ASSERT_STREQ(var->to_string().c_str(), "a"); -} diff --git a/README.md b/README.md index 5d36cd3..6c9405c 100644 --- a/README.md +++ b/README.md @@ -16,7 +16,8 @@ https://github.com/rudlorenz/mathParser ``` To use Expressions in your project add src folder with `add_subdirectory()` command to your `CmakeLists.txt` file and -include `Expressions.h` and `Parser.h` in source files. +include `Expression.h` and `Parser.h` in source files. +Cmake will automatically fetch gtest and fmt library. Alternatively you can use supplied `main.cpp`. @@ -26,11 +27,16 @@ Parsing expression from string: ```c++ #include +#include -auto result = parser::parse("x + y"); -if (result != nullptr) { - std::cout << "result : " << result->to_string(); +std::optional result = parser::parse("x + y"); +if (!result.has_value()) { + fmt::print(stderr, "Something wrong!"); + return 0; } + +fmt::print(stdout, "input : {}\n", result->to_string()); +fmt::print(stdout, "as expressions : {}\n", result->to_expr_string()); ``` Parse expression from string and find derivative: @@ -38,39 +44,59 @@ Parse expression from string and find derivative: ```c++ #include -auto result = parser::parse("x * x + 2*x + 10"); -if (result != nullptr) -{ - auto derivative = result->diff("x"); - std::cout << "expression : " << result->to_string() << "\n" - << "derivative : " << derivative->to_string(); +std::optional result = parser::parse("x * x + 2*x + 10"); +if (!result.has_value()) { + fmt::print(stderr, "Something wrong!"); + return 0; } + +fmt::print(stdout, "input : {}\n", input); +fmt::print(stdout, "as expressions : {}\n", result->to_expr_string()); +fmt::print(stdout, "d/dx : {}\n", result->diff("x").to_string()); ``` Calls are chainable: ```c++ auto result = parser::parse("x*x*y + x*y*y"); -auto as_string = result != nullptr - ? result->diff(x)->diff(y)->to_string(); +auto as_string = result + ? result->diff(x).diff(y).to_string(); + : "empty"; ``` You can create expressions directly, but it's highly discouraged: ```c++ #include "Expressions.h" - // x * x + y - auto var_x = std::make_shared("x"); - auto var_y = std::make_shared("y"); - auto result = std::make_shared( - std::make_shared(var_x, var_x), - var_y - ); +using namespace mexpr; + +// x * x + y +auto var_x = Expression{"x"}; +auto var_y = Expression{"y"}; +auto two_expr = Expression{2}; +auto var_x_squared = Expression{ + binary_expr_type::mul, + var_x.clone(), + var_x.clone(), +} +auto result = Expression{ + binary_expr_type::sub, + Expression{ + binary_expr_type::sum, + std::move(var_x_squared), + std::move(var_y) + }, + std::move(two_expr) +} ``` ## TODO 1. Unit testing. -2. Proper lexical and synax analysis i.e. input expression validation. Right now it works "garbage in --> garbage out". +2. ~~Proper lexical and syntax analysis i.e. input expression validation. Right now it works "garbage in --> garbage out".~~ 3. Expression simplification. -4. Proper parser errors. +4. ~~Proper parser errors.~~ +5. More unit tests. +6. POW operation. +7. Check for proper "unary" function parsing. +8. Expression evaluation. \ No newline at end of file