From 965a53f141523ccb563420d32b5759783836c725 Mon Sep 17 00:00:00 2001 From: Adel KARA SLIMANE Date: Sun, 8 Oct 2023 00:40:44 +0200 Subject: [PATCH 1/2] Add zecalculator library folder --- zecalculator/error.h | 128 + zecalculator/evaluation/ast/decl/evaluation.h | 83 + zecalculator/evaluation/ast/evaluation.h | 24 + zecalculator/evaluation/ast/impl/evaluation.h | 145 + zecalculator/evaluation/evaluation.h | 24 + zecalculator/evaluation/rpn/decl/evaluation.h | 85 + zecalculator/evaluation/rpn/evaluation.h | 24 + zecalculator/evaluation/rpn/impl/evaluation.h | 188 ++ zecalculator/external/expected.h | 2368 +++++++++++++++++ zecalculator/math_objects/aliases.h | 41 + .../math_objects/builtin_binary_functions.h | 109 + .../math_objects/builtin_unary_functions.h | 106 + zecalculator/math_objects/decl/expression.h | 71 + zecalculator/math_objects/decl/function.h | 149 ++ zecalculator/math_objects/decl/sequence.h | 93 + zecalculator/math_objects/expression.h | 24 + zecalculator/math_objects/forward_declares.h | 41 + zecalculator/math_objects/function.h | 24 + zecalculator/math_objects/global_constant.h | 55 + zecalculator/math_objects/global_variable.h | 37 + zecalculator/math_objects/impl/expression.h | 66 + zecalculator/math_objects/impl/function.h | 298 +++ zecalculator/math_objects/impl/sequence.h | 76 + zecalculator/math_objects/object_list.h | 17 + zecalculator/math_objects/sequence.h | 24 + zecalculator/mathworld/decl/mathworld.h | 197 ++ zecalculator/mathworld/impl/mathworld.h | 58 + zecalculator/mathworld/mathworld.h | 24 + .../parsing/data_structures/decl/node.h | 131 + .../parsing/data_structures/impl/node.h | 205 ++ zecalculator/parsing/data_structures/node.h | 24 + zecalculator/parsing/data_structures/token.h | 171 ++ zecalculator/parsing/decl/parser.h | 67 + zecalculator/parsing/impl/parser.h | 578 ++++ zecalculator/parsing/parser.h | 24 + zecalculator/parsing/shared.h | 29 + zecalculator/utils/name_map.h | 43 + zecalculator/utils/refs.h | 13 + zecalculator/utils/slotted_deque.h | 95 + zecalculator/utils/substr_info.h | 87 + zecalculator/utils/tuple.h | 102 + zecalculator/utils/utils.h | 31 + zecalculator/zecalculator-single.h | 27 + zecalculator/zecalculator.h | 27 + 44 files changed, 6233 insertions(+) create mode 100644 zecalculator/error.h create mode 100644 zecalculator/evaluation/ast/decl/evaluation.h create mode 100644 zecalculator/evaluation/ast/evaluation.h create mode 100644 zecalculator/evaluation/ast/impl/evaluation.h create mode 100644 zecalculator/evaluation/evaluation.h create mode 100644 zecalculator/evaluation/rpn/decl/evaluation.h create mode 100644 zecalculator/evaluation/rpn/evaluation.h create mode 100644 zecalculator/evaluation/rpn/impl/evaluation.h create mode 100644 zecalculator/external/expected.h create mode 100644 zecalculator/math_objects/aliases.h create mode 100644 zecalculator/math_objects/builtin_binary_functions.h create mode 100644 zecalculator/math_objects/builtin_unary_functions.h create mode 100644 zecalculator/math_objects/decl/expression.h create mode 100644 zecalculator/math_objects/decl/function.h create mode 100644 zecalculator/math_objects/decl/sequence.h create mode 100644 zecalculator/math_objects/expression.h create mode 100644 zecalculator/math_objects/forward_declares.h create mode 100644 zecalculator/math_objects/function.h create mode 100644 zecalculator/math_objects/global_constant.h create mode 100644 zecalculator/math_objects/global_variable.h create mode 100644 zecalculator/math_objects/impl/expression.h create mode 100644 zecalculator/math_objects/impl/function.h create mode 100644 zecalculator/math_objects/impl/sequence.h create mode 100644 zecalculator/math_objects/object_list.h create mode 100644 zecalculator/math_objects/sequence.h create mode 100644 zecalculator/mathworld/decl/mathworld.h create mode 100644 zecalculator/mathworld/impl/mathworld.h create mode 100644 zecalculator/mathworld/mathworld.h create mode 100644 zecalculator/parsing/data_structures/decl/node.h create mode 100644 zecalculator/parsing/data_structures/impl/node.h create mode 100644 zecalculator/parsing/data_structures/node.h create mode 100644 zecalculator/parsing/data_structures/token.h create mode 100644 zecalculator/parsing/decl/parser.h create mode 100644 zecalculator/parsing/impl/parser.h create mode 100644 zecalculator/parsing/parser.h create mode 100644 zecalculator/parsing/shared.h create mode 100644 zecalculator/utils/name_map.h create mode 100644 zecalculator/utils/refs.h create mode 100644 zecalculator/utils/slotted_deque.h create mode 100644 zecalculator/utils/substr_info.h create mode 100644 zecalculator/utils/tuple.h create mode 100644 zecalculator/utils/utils.h create mode 100644 zecalculator/zecalculator-single.h create mode 100644 zecalculator/zecalculator.h diff --git a/zecalculator/error.h b/zecalculator/error.h new file mode 100644 index 0000000..831e80f --- /dev/null +++ b/zecalculator/error.h @@ -0,0 +1,128 @@ +/**************************************************************************** +** Copyright (c) 2023, Adel Kara Slimane +** +** This file is part of ZeCalculator. +** +** ZeCalculators is free software: you may copy, redistribute and/or modify it +** under the terms of the GNU Affero General Public License as published by the +** Free Software Foundation, either version 3 of the License, or (at your +** option) any later version. +** +** This file is distributed in the hope that it will be useful, but +** WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +** General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +** +****************************************************************************/ + +#pragma once + +#include +#include + +namespace zc { + +struct Error +{ + // type of error + enum Type : uint8_t + { + UNKNOWN = 0, + WRONG_FORMAT, + UNEXPECTED, + MISSING, + UNDEFINED_VARIABLE, + UNDEFINED_FUNCTION, + CALLING_FUN_ARG_COUNT_MISMATCH, + CALLED_FUN_ARG_COUNT_MISMATCH, + NOT_IMPLEMENTED, + EMPTY_EXPRESSION, + INVALID_FUNCTION, + CALLING_INVALID_FUNCTION, // expression that contains a function who cannot return values + RECURSION_DEPTH_OVERFLOW, // maximum recursion depth has been reached + WRONG_OBJECT_TYPE, // object has been used as a different type as it actually is, example "2+cos" (where cos is a function used here as variable) + }; + + static Error unexpected(parsing::tokens::Text token) + { + return Error {.error_type = UNEXPECTED, .token = token}; + } + + static Error wrong_format(parsing::tokens::Text token) + { + return Error {.error_type = WRONG_FORMAT, .token = token}; + } + + static Error missing(parsing::tokens::Text token) + { + return Error {.error_type = MISSING, .token = token}; + } + + static Error unkown() + { + return Error {UNKNOWN}; + } + + static Error undefined_variable(parsing::tokens::Text tokenTxt) + { + return Error {UNDEFINED_VARIABLE, tokenTxt}; + } + + static Error undefined_function(parsing::tokens::Text tokenTxt) + { + return Error {UNDEFINED_FUNCTION, tokenTxt}; + } + + static Error mismatched_fun_args(parsing::tokens::Text tokenTxt) + { + return Error {CALLING_FUN_ARG_COUNT_MISMATCH, tokenTxt}; + } + + static Error mismatched_fun_args() + { + return Error {CALLED_FUN_ARG_COUNT_MISMATCH}; + } + + static Error not_implemented(parsing::tokens::Text tokenTxt) + { + return Error {NOT_IMPLEMENTED, tokenTxt}; + } + + static Error empty_expression() + { + return Error {EMPTY_EXPRESSION}; + } + + static Error invalid_function() + { + return Error {INVALID_FUNCTION}; + } + + static Error calling_invalid_function(parsing::tokens::Text tokenTxt) + { + return Error {CALLING_INVALID_FUNCTION, tokenTxt}; + } + + static Error recursion_depth_overflow() + { + return Error{RECURSION_DEPTH_OVERFLOW}; + } + + static Error wrong_object_type(parsing::tokens::Text tokenTxt) + { + return Error {WRONG_OBJECT_TYPE, tokenTxt}; + } + + // kind of error + Type error_type = UNKNOWN; + + // on what token + parsing::tokens::Text token = {}; + + bool operator == (const Error& other) const = default; +}; + +} diff --git a/zecalculator/evaluation/ast/decl/evaluation.h b/zecalculator/evaluation/ast/decl/evaluation.h new file mode 100644 index 0000000..60d377a --- /dev/null +++ b/zecalculator/evaluation/ast/decl/evaluation.h @@ -0,0 +1,83 @@ +#pragma once + +/**************************************************************************** +** Copyright (c) 2023, Adel Kara Slimane +** +** This file is part of ZeCalculator's source code. +** +** ZeCalculators is free software: you may copy, redistribute and/or modify it +** under the terms of the GNU Affero General Public License as published by the +** Free Software Foundation, either version 3 of the License, or (at your +** option) any later version. +** +** This file is distributed in the hope that it will be useful, but +** WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +** General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +** +****************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace zc { +namespace eval { +namespace ast { + +struct Evaluator +{ + const std::span input_vars; + const size_t current_recursion_depth = 0; + + using ReturnType = tl::expected; + + // using T = std::remove_cvref_t; + + ReturnType operator () (std::monostate); + + ReturnType operator () (const zc::parsing::node::ast::Function&); + + ReturnType operator () (const zc::parsing::node::ast::Sequence&); + + ReturnType operator () (const zc::parsing::node::InputVariable&); + + ReturnType operator () (const zc::parsing::node::Number&); + + ReturnType operator () (const zc::parsing::node::ast::CppUnaryFunction&); + + ReturnType operator () (const zc::parsing::node::ast::CppBinaryFunction&); + + ReturnType operator () (const zc::parsing::node::GlobalConstant&); + + ReturnType operator () (const zc::parsing::node::GlobalVariable&); + +}; + +} +} + +/// @brief evaluates a syntax tree using a given math world +/// @param tree: tree to evaluate +/// @param input_vars: variables that are given as input to the tree, will shadow any variable in the math world +/// @param world: math world (contains functions, global constants... etc) +inline tl::expected evaluate(const parsing::Tree& tree, + std::span input_vars, + size_t current_recursion_depth); + +/// @brief evaluates a syntax tree using a given math world +inline tl::expected evaluate(const parsing::Tree& tree, + std::span input_vars); + +/// @brief evaluates a syntax tree using a given math world +inline tl::expected evaluate(const parsing::Tree& tree); + +} // namespace zc diff --git a/zecalculator/evaluation/ast/evaluation.h b/zecalculator/evaluation/ast/evaluation.h new file mode 100644 index 0000000..1001896 --- /dev/null +++ b/zecalculator/evaluation/ast/evaluation.h @@ -0,0 +1,24 @@ +#pragma once + +/**************************************************************************** +** Copyright (c) 2023, Adel Kara Slimane +** +** This file is part of ZeCalculator's source code. +** +** ZeCalculators is free software: you may copy, redistribute and/or modify it +** under the terms of the GNU Affero General Public License as published by the +** Free Software Foundation, either version 3 of the License, or (at your +** option) any later version. +** +** This file is distributed in the hope that it will be useful, but +** WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +** General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +** +****************************************************************************/ + +#include +#include diff --git a/zecalculator/evaluation/ast/impl/evaluation.h b/zecalculator/evaluation/ast/impl/evaluation.h new file mode 100644 index 0000000..c0c6984 --- /dev/null +++ b/zecalculator/evaluation/ast/impl/evaluation.h @@ -0,0 +1,145 @@ +#pragma once + +/**************************************************************************** +** Copyright (c) 2023, Adel Kara Slimane +** +** This file is part of ZeCalculator's source code. +** +** ZeCalculators is free software: you may copy, redistribute and/or modify it +** under the terms of the GNU Affero General Public License as published by the +** Free Software Foundation, either version 3 of the License, or (at your +** option) any later version. +** +** This file is distributed in the hope that it will be useful, but +** WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +** General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +** +****************************************************************************/ + +#include +#include + +namespace zc { +namespace eval { +namespace ast { + +inline Evaluator::ReturnType Evaluator::operator () (std::monostate) +{ + return tl::unexpected(Error::empty_expression()); +} + +inline Evaluator::ReturnType + Evaluator::operator()(const zc::parsing::node::ast::Function& node) +{ + if (node.f.mathworld->max_recursion_depth < current_recursion_depth) + return tl::unexpected(Error::recursion_depth_overflow()); + + std::vector evaluations; + for (const auto& operand : node.operands) + { + auto eval = evaluate(operand, input_vars, current_recursion_depth + 1); + if (eval) [[likely]] + evaluations.push_back(*eval); + else [[unlikely]] + return eval; + } + + return node.f.evaluate(evaluations, current_recursion_depth + 1); +} + +inline Evaluator::ReturnType + Evaluator::operator()(const zc::parsing::node::ast::Sequence& node) +{ + if (node.u.mathworld->max_recursion_depth < current_recursion_depth) + return tl::unexpected(Error::recursion_depth_overflow()); + + auto eval = evaluate(node.operand, input_vars, current_recursion_depth + 1); + if (eval) [[likely]] + return node.u.evaluate(*eval, current_recursion_depth + 1); + else [[unlikely]] + return eval; +} + +inline Evaluator::ReturnType Evaluator::operator()( + const zc::parsing::node::ast::CppUnaryFunction& node) +{ + auto operand = evaluate(node.operand, input_vars, current_recursion_depth + 1); + if (operand) [[likely]] + return node.f(*operand); + else [[unlikely]] + return operand; +} + +inline Evaluator::ReturnType Evaluator::operator()( + const zc::parsing::node::ast::CppBinaryFunction& node) +{ + auto operand1 = evaluate(node.operand1, input_vars, current_recursion_depth + 1); + auto operand2 = evaluate(node.operand2, input_vars, current_recursion_depth + 1); + if (operand1 and operand2) [[likely]] + return node.f(*operand1, *operand2); + else [[unlikely]] + return operand1 ? operand2 : operand1; +} + +inline Evaluator::ReturnType + Evaluator::operator()(const zc::parsing::node::GlobalVariable& node) +{ + if (node.var.mathworld->max_recursion_depth < current_recursion_depth) + return tl::unexpected(Error::recursion_depth_overflow()); + + return node.var.evaluate(current_recursion_depth + 1); +} + +inline Evaluator::ReturnType Evaluator::operator () (const zc::parsing::node::GlobalConstant& node) +{ + return node.constant.value; +} + +inline Evaluator::ReturnType Evaluator::operator () (const zc::parsing::node::InputVariable& node) +{ + // node.index should never be bigger than input_vars.size() + assert(node.index < input_vars.size()); + + return input_vars[node.index]; +} + +inline Evaluator::ReturnType Evaluator::operator () (const zc::parsing::node::Number& node) +{ + return node.value; +} + +} +} + +/// @brief evaluates a syntax tree using a given math world +/// @param tree: tree to evaluate +/// @param input_vars: variables that are given as input to the tree, will shadow any variable in the math world +/// @param world: math world (contains functions, global constants... etc) +inline tl::expected evaluate(const parsing::Tree& tree, + std::span input_vars, + size_t current_recursion_depth) +{ + return std::visit(eval::ast::Evaluator{.input_vars = input_vars, + .current_recursion_depth = current_recursion_depth}, + *tree); +} + +/// @brief evaluates a syntax tree using a given math world +inline tl::expected evaluate(const parsing::Tree& tree, + std::span input_vars) +{ + return evaluate(tree, input_vars, 0); +} + +/// @brief evaluates a syntax tree using a given math world +inline tl::expected evaluate(const parsing::Tree& tree) +{ + return evaluate(tree, std::span(), 0); +} + + +} diff --git a/zecalculator/evaluation/evaluation.h b/zecalculator/evaluation/evaluation.h new file mode 100644 index 0000000..e31b442 --- /dev/null +++ b/zecalculator/evaluation/evaluation.h @@ -0,0 +1,24 @@ +#pragma once + +/**************************************************************************** +** Copyright (c) 2023, Adel Kara Slimane +** +** This file is part of ZeCalculator's source code. +** +** ZeCalculators is free software: you may copy, redistribute and/or modify it +** under the terms of the GNU Affero General Public License as published by the +** Free Software Foundation, either version 3 of the License, or (at your +** option) any later version. +** +** This file is distributed in the hope that it will be useful, but +** WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +** General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +** +****************************************************************************/ + +#include +#include diff --git a/zecalculator/evaluation/rpn/decl/evaluation.h b/zecalculator/evaluation/rpn/decl/evaluation.h new file mode 100644 index 0000000..6dd0498 --- /dev/null +++ b/zecalculator/evaluation/rpn/decl/evaluation.h @@ -0,0 +1,85 @@ +#pragma once + +/**************************************************************************** +** Copyright (c) 2023, Adel Kara Slimane +** +** This file is part of ZeCalculator's source code. +** +** ZeCalculators is free software: you may copy, redistribute and/or modify it +** under the terms of the GNU Affero General Public License as published by the +** Free Software Foundation, either version 3 of the License, or (at your +** option) any later version. +** +** This file is distributed in the hope that it will be useful, but +** WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +** General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +** +****************************************************************************/ + +#include "zecalculator/external/expected.h" +#include "zecalculator/parsing/data_structures/decl/node.h" +#include +#include +#include +#include +#include +#include +#include +#include + +namespace zc { + +namespace eval { +namespace rpn { + +struct Evaluator +{ + const std::span input_vars; + tl::expected, Error> expected_eval_stack = {}; + const size_t current_recursion_depth = 0; + const size_t max_recursion_depth = 100; + + using ReturnType = tl::expected; + + void operator () (std::monostate); + + void operator () (const zc::parsing::node::Number&); + + void operator () (const zc::parsing::node::InputVariable&); + + void operator () (const zc::parsing::node::rpn::Function&); + + void operator () (const zc::parsing::node::rpn::Sequence&); + + void operator () (const zc::parsing::node::rpn::CppUnaryFunction&); + + void operator () (const zc::parsing::node::rpn::CppBinaryFunction&); + + void operator () (const zc::parsing::node::GlobalConstant&); + + void operator () (const zc::parsing::node::GlobalVariable&); + +}; + +} +} + +/// @brief evaluates a syntax tree using a given math world +/// @param tree: tree to evaluate +/// @param input_vars: variables that are given as input to the tree, will shadow any variable in the math world +/// @param world: math world (contains functions, global constants... etc) +inline tl::expected evaluate(const parsing::RPN& expr, + std::span input_vars, + size_t current_recursion_depth); + +/// @brief evaluates a syntax tree using a given math world +inline tl::expected evaluate(const parsing::RPN& expr, + std::spaninput_vars); + +/// @brief evaluates a syntax tree using a given math world +inline tl::expected evaluate(const parsing::RPN& tree); +} diff --git a/zecalculator/evaluation/rpn/evaluation.h b/zecalculator/evaluation/rpn/evaluation.h new file mode 100644 index 0000000..4d8e97f --- /dev/null +++ b/zecalculator/evaluation/rpn/evaluation.h @@ -0,0 +1,24 @@ +#pragma once + +/**************************************************************************** +** Copyright (c) 2023, Adel Kara Slimane +** +** This file is part of ZeCalculator's source code. +** +** ZeCalculators is free software: you may copy, redistribute and/or modify it +** under the terms of the GNU Affero General Public License as published by the +** Free Software Foundation, either version 3 of the License, or (at your +** option) any later version. +** +** This file is distributed in the hope that it will be useful, but +** WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +** General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +** +****************************************************************************/ + +#include +#include diff --git a/zecalculator/evaluation/rpn/impl/evaluation.h b/zecalculator/evaluation/rpn/impl/evaluation.h new file mode 100644 index 0000000..1e8a4d3 --- /dev/null +++ b/zecalculator/evaluation/rpn/impl/evaluation.h @@ -0,0 +1,188 @@ +#pragma once + +/**************************************************************************** +** Copyright (c) 2023, Adel Kara Slimane +** +** This file is part of ZeCalculator's source code. +** +** ZeCalculators is free software: you may copy, redistribute and/or modify it +** under the terms of the GNU Affero General Public License as published by the +** Free Software Foundation, either version 3 of the License, or (at your +** option) any later version. +** +** This file is distributed in the hope that it will be useful, but +** WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +** General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +** +****************************************************************************/ + +#include +#include +#include +#include + +namespace zc { +namespace eval { +namespace rpn { + +inline void Evaluator::operator () (std::monostate) +{ + expected_eval_stack = tl::unexpected(Error::empty_expression()); +} + +inline void Evaluator::operator () (const zc::parsing::node::Number& val) +{ + expected_eval_stack->push_back(val.value); +} + +inline void Evaluator::operator()(const zc::parsing::node::rpn::Function& node) +{ + const size_t args_num = node.f.argument_size().value(); + if (max_recursion_depth < current_recursion_depth) [[unlikely]] + expected_eval_stack = tl::unexpected(Error::recursion_depth_overflow()); + if (not bool(node.f)) [[unlikely]] + expected_eval_stack = tl::unexpected(Error::calling_invalid_function(node)); + else + { + const auto evaluations = std::span(expected_eval_stack->end() - args_num, + expected_eval_stack->end()); + + auto expected_res = node.f.evaluate(evaluations, current_recursion_depth + 1); + + // when done, remove args_num from the back of the evaluation stack + // note: the resize needs to be done before pushing in the new result + expected_eval_stack->resize(expected_eval_stack->size() - args_num); + + if (expected_res) + // push the result to the evaluation stack if correct + expected_eval_stack->push_back(*expected_res); + + // save error in the expected otherwise + else expected_eval_stack = tl::unexpected(expected_res.error()); + } +} + +inline void Evaluator::operator()(const zc::parsing::node::rpn::CppUnaryFunction& node) +{ + if (expected_eval_stack->empty()) + expected_eval_stack = tl::unexpected(Error::mismatched_fun_args(node)); + else + { + double& back_val = expected_eval_stack->back(); + + // overwrite the last value with the function evaluation on it + back_val = node.f(back_val); + } +} + +inline void Evaluator::operator()(const zc::parsing::node::rpn::CppBinaryFunction& node) +{ + if (expected_eval_stack->size() < 2) [[unlikely]] + expected_eval_stack = tl::unexpected(Error::mismatched_fun_args(node)); + else + { + // points on the before last value on the stack + const auto it = expected_eval_stack->end() - 2; + + // since the function pops two elements, then pushes back one + // we can overwrite directly the value that will get replaced + *it = node.f(*it, *(it+1)); + + // remove the last value, i.e. at it+1, as it got consumed + expected_eval_stack->pop_back(); + } +} + +inline void Evaluator::operator()(const zc::parsing::node::rpn::Sequence& node) +{ + // std::cout << "Evaluating zc function: " << node.name << std::endl; + if (not bool(node.u)) + expected_eval_stack = tl::unexpected(Error::calling_invalid_function(node)); + else if (expected_eval_stack->empty()) + expected_eval_stack = tl::unexpected(Error::mismatched_fun_args(node)); + else + { + // sequence handles only one argument + double& back_val = expected_eval_stack->back(); + + auto expected_res = node.u.evaluate(back_val, current_recursion_depth + 1); + + if (expected_res) + // overwrite the top of the stack on computation success + back_val = *expected_res; + + // save error in the expected otherwise + else expected_eval_stack = tl::unexpected(expected_res.error()); + } +} + +inline void Evaluator::operator () (const zc::parsing::node::InputVariable& in_var) +{ + // node.index should never be bigger than input_vars.size() + assert(in_var.index < input_vars.size()); + + expected_eval_stack->push_back(input_vars[in_var.index]); +} + +inline void Evaluator::operator()(const zc::parsing::node::GlobalConstant& node) +{ + expected_eval_stack->push_back(node.constant.value); +} + +inline void Evaluator::operator()(const zc::parsing::node::GlobalVariable& node) +{ + auto expected_res = node.var.evaluate(current_recursion_depth + 1); + if (expected_res) + expected_eval_stack->push_back(*expected_res); + else expected_eval_stack = tl::unexpected(expected_res.error()); +} + +} +} + +/// @brief evaluates a syntax expr using a given math world +/// @param expr: expr to evaluate +/// @param input_vars: variables that are given as input to the expr, will shadow any variable in the math world +/// @param world: math world (contains functions, global constants... etc) +inline tl::expected evaluate(const parsing::RPN& expr, + std::span input_vars, + size_t current_recursion_depth) +{ + eval::rpn::Evaluator stateful_evaluator{.input_vars = input_vars, + .current_recursion_depth = current_recursion_depth}; + + for (const parsing::node::rpn::Node& tok: expr) + { + std::visit(stateful_evaluator, tok); + if (not stateful_evaluator.expected_eval_stack) + break; + } + + if (stateful_evaluator.expected_eval_stack) + { + if (stateful_evaluator.expected_eval_stack->size() == 1) + return stateful_evaluator.expected_eval_stack->front(); + else return tl::unexpected(Error::unkown()); + } + else return tl::unexpected(stateful_evaluator.expected_eval_stack.error()); +} + +/// @brief evaluates a syntax expr using a given math world +inline tl::expected evaluate(const parsing::RPN& expr, + std::span input_vars) +{ + return evaluate(expr, input_vars, 0); +} + +/// @brief evaluates a syntax expr using a given math world +inline tl::expected evaluate(const parsing::RPN& expr) +{ + return evaluate(expr, std::span(), 0); +} + + +} diff --git a/zecalculator/external/expected.h b/zecalculator/external/expected.h new file mode 100644 index 0000000..b49ff46 --- /dev/null +++ b/zecalculator/external/expected.h @@ -0,0 +1,2368 @@ +/// +// expected - An implementation of std::expected with extensions +// Written in 2017 by Sy Brand (tartanllama@gmail.com, @TartanLlama) +// +// Documentation available at http://tl.tartanllama.xyz/ +// +// To the extent possible under law, the author(s) have dedicated all +// copyright and related and neighboring rights to this software to the +// public domain worldwide. This software is distributed without any warranty. +// +// You should have received a copy of the CC0 Public Domain Dedication +// along with this software. If not, see +// . +/// + +#ifndef TL_EXPECTED_HPP +#define TL_EXPECTED_HPP + +#define TL_EXPECTED_VERSION_MAJOR 1 +#define TL_EXPECTED_VERSION_MINOR 0 +#define TL_EXPECTED_VERSION_PATCH 1 + +#include +#include +#include +#include + +#if defined(__EXCEPTIONS) || defined(_CPPUNWIND) +#define TL_EXPECTED_EXCEPTIONS_ENABLED +#endif + +#if (defined(_MSC_VER) && _MSC_VER == 1900) +#define TL_EXPECTED_MSVC2015 +#define TL_EXPECTED_MSVC2015_CONSTEXPR +#else +#define TL_EXPECTED_MSVC2015_CONSTEXPR constexpr +#endif + +#if (defined(__GNUC__) && __GNUC__ == 4 && __GNUC_MINOR__ <= 9 && \ + !defined(__clang__)) +#define TL_EXPECTED_GCC49 +#endif + +#if (defined(__GNUC__) && __GNUC__ == 5 && __GNUC_MINOR__ <= 4 && \ + !defined(__clang__)) +#define TL_EXPECTED_GCC54 +#endif + +#if (defined(__GNUC__) && __GNUC__ == 5 && __GNUC_MINOR__ <= 5 && \ + !defined(__clang__)) +#define TL_EXPECTED_GCC55 +#endif + +#if (defined(__GNUC__) && __GNUC__ == 4 && __GNUC_MINOR__ <= 9 && \ + !defined(__clang__)) +// GCC < 5 doesn't support overloading on const&& for member functions + +#define TL_EXPECTED_NO_CONSTRR +// GCC < 5 doesn't support some standard C++11 type traits +#define TL_EXPECTED_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(T) \ + std::has_trivial_copy_constructor +#define TL_EXPECTED_IS_TRIVIALLY_COPY_ASSIGNABLE(T) \ + std::has_trivial_copy_assign + +// This one will be different for GCC 5.7 if it's ever supported +#define TL_EXPECTED_IS_TRIVIALLY_DESTRUCTIBLE(T) \ + std::is_trivially_destructible + +// GCC 5 < v < 8 has a bug in is_trivially_copy_constructible which breaks +// std::vector for non-copyable types +#elif (defined(__GNUC__) && __GNUC__ < 8 && !defined(__clang__)) +#ifndef TL_GCC_LESS_8_TRIVIALLY_COPY_CONSTRUCTIBLE_MUTEX +#define TL_GCC_LESS_8_TRIVIALLY_COPY_CONSTRUCTIBLE_MUTEX +namespace tl { +namespace detail { +template +struct is_trivially_copy_constructible + : std::is_trivially_copy_constructible {}; +#ifdef _GLIBCXX_VECTOR +template +struct is_trivially_copy_constructible> : std::false_type {}; +#endif +} // namespace detail +} // namespace tl +#endif + +#define TL_EXPECTED_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(T) \ + tl::detail::is_trivially_copy_constructible +#define TL_EXPECTED_IS_TRIVIALLY_COPY_ASSIGNABLE(T) \ + std::is_trivially_copy_assignable +#define TL_EXPECTED_IS_TRIVIALLY_DESTRUCTIBLE(T) \ + std::is_trivially_destructible +#else +#define TL_EXPECTED_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(T) \ + std::is_trivially_copy_constructible +#define TL_EXPECTED_IS_TRIVIALLY_COPY_ASSIGNABLE(T) \ + std::is_trivially_copy_assignable +#define TL_EXPECTED_IS_TRIVIALLY_DESTRUCTIBLE(T) \ + std::is_trivially_destructible +#endif + +#if __cplusplus > 201103L +#define TL_EXPECTED_CXX14 +#endif + +#ifdef TL_EXPECTED_GCC49 +#define TL_EXPECTED_GCC49_CONSTEXPR +#else +#define TL_EXPECTED_GCC49_CONSTEXPR constexpr +#endif + +#if (__cplusplus == 201103L || defined(TL_EXPECTED_MSVC2015) || \ + defined(TL_EXPECTED_GCC49)) +#define TL_EXPECTED_11_CONSTEXPR +#else +#define TL_EXPECTED_11_CONSTEXPR constexpr +#endif + +namespace tl { +template class expected; + +#ifndef TL_MONOSTATE_INPLACE_MUTEX +#define TL_MONOSTATE_INPLACE_MUTEX +class monostate {}; + +struct in_place_t { + explicit in_place_t() = default; +}; +static constexpr in_place_t in_place{}; +#endif + +template class unexpected { +public: + static_assert(!std::is_same::value, "E must not be void"); + + unexpected() = delete; + constexpr explicit unexpected(const E &e) : m_val(e) {} + + constexpr explicit unexpected(E &&e) : m_val(std::move(e)) {} + + template ::value>::type * = nullptr> + constexpr explicit unexpected(Args &&...args) + : m_val(std::forward(args)...) {} + template < + class U, class... Args, + typename std::enable_if &, Args &&...>::value>::type * = nullptr> + constexpr explicit unexpected(std::initializer_list l, Args &&...args) + : m_val(l, std::forward(args)...) {} + + constexpr const E &value() const & { return m_val; } + TL_EXPECTED_11_CONSTEXPR E &value() & { return m_val; } + TL_EXPECTED_11_CONSTEXPR E &&value() && { return std::move(m_val); } + constexpr const E &&value() const && { return std::move(m_val); } + +private: + E m_val; +}; + +#ifdef __cpp_deduction_guides +template unexpected(E) -> unexpected; +#endif + +template +constexpr bool operator==(const unexpected &lhs, const unexpected &rhs) { + return lhs.value() == rhs.value(); +} +template +constexpr bool operator!=(const unexpected &lhs, const unexpected &rhs) { + return lhs.value() != rhs.value(); +} +template +constexpr bool operator<(const unexpected &lhs, const unexpected &rhs) { + return lhs.value() < rhs.value(); +} +template +constexpr bool operator<=(const unexpected &lhs, const unexpected &rhs) { + return lhs.value() <= rhs.value(); +} +template +constexpr bool operator>(const unexpected &lhs, const unexpected &rhs) { + return lhs.value() > rhs.value(); +} +template +constexpr bool operator>=(const unexpected &lhs, const unexpected &rhs) { + return lhs.value() >= rhs.value(); +} + +template +unexpected::type> make_unexpected(E &&e) { + return unexpected::type>(std::forward(e)); +} + +struct unexpect_t { + unexpect_t() = default; +}; +static constexpr unexpect_t unexpect{}; + +namespace detail { +template +[[noreturn]] TL_EXPECTED_11_CONSTEXPR void throw_exception(E &&e) { +#ifdef TL_EXPECTED_EXCEPTIONS_ENABLED + throw std::forward(e); +#else +#ifdef _MSC_VER + __assume(0); +#else + __builtin_unreachable(); +#endif +#endif +} + +#ifndef TL_TRAITS_MUTEX +#define TL_TRAITS_MUTEX +// C++14-style aliases for brevity +template using remove_const_t = typename std::remove_const::type; +template +using remove_reference_t = typename std::remove_reference::type; +template using decay_t = typename std::decay::type; +template +using enable_if_t = typename std::enable_if::type; +template +using conditional_t = typename std::conditional::type; + +// std::conjunction from C++17 +template struct conjunction : std::true_type {}; +template struct conjunction : B {}; +template +struct conjunction + : std::conditional, B>::type {}; + +#if defined(_LIBCPP_VERSION) && __cplusplus == 201103L +#define TL_TRAITS_LIBCXX_MEM_FN_WORKAROUND +#endif + +// In C++11 mode, there's an issue in libc++'s std::mem_fn +// which results in a hard-error when using it in a noexcept expression +// in some cases. This is a check to workaround the common failing case. +#ifdef TL_TRAITS_LIBCXX_MEM_FN_WORKAROUND +template +struct is_pointer_to_non_const_member_func : std::false_type {}; +template +struct is_pointer_to_non_const_member_func + : std::true_type {}; +template +struct is_pointer_to_non_const_member_func + : std::true_type {}; +template +struct is_pointer_to_non_const_member_func + : std::true_type {}; +template +struct is_pointer_to_non_const_member_func + : std::true_type {}; +template +struct is_pointer_to_non_const_member_func + : std::true_type {}; +template +struct is_pointer_to_non_const_member_func + : std::true_type {}; + +template struct is_const_or_const_ref : std::false_type {}; +template struct is_const_or_const_ref : std::true_type {}; +template struct is_const_or_const_ref : std::true_type {}; +#endif + +// std::invoke from C++17 +// https://stackoverflow.com/questions/38288042/c11-14-invoke-workaround +template < + typename Fn, typename... Args, +#ifdef TL_TRAITS_LIBCXX_MEM_FN_WORKAROUND + typename = enable_if_t::value && + is_const_or_const_ref::value)>, +#endif + typename = enable_if_t>::value>, int = 0> +constexpr auto invoke(Fn &&f, Args &&...args) noexcept( + noexcept(std::mem_fn(f)(std::forward(args)...))) + -> decltype(std::mem_fn(f)(std::forward(args)...)) { + return std::mem_fn(f)(std::forward(args)...); +} + +template >::value>> +constexpr auto invoke(Fn &&f, Args &&...args) noexcept( + noexcept(std::forward(f)(std::forward(args)...))) + -> decltype(std::forward(f)(std::forward(args)...)) { + return std::forward(f)(std::forward(args)...); +} + +// std::invoke_result from C++17 +template struct invoke_result_impl; + +template +struct invoke_result_impl< + F, + decltype(detail::invoke(std::declval(), std::declval()...), void()), + Us...> { + using type = + decltype(detail::invoke(std::declval(), std::declval()...)); +}; + +template +using invoke_result = invoke_result_impl; + +template +using invoke_result_t = typename invoke_result::type; + +#if defined(_MSC_VER) && _MSC_VER <= 1900 +// TODO make a version which works with MSVC 2015 +template struct is_swappable : std::true_type {}; + +template struct is_nothrow_swappable : std::true_type {}; +#else +// https://stackoverflow.com/questions/26744589/what-is-a-proper-way-to-implement-is-swappable-to-test-for-the-swappable-concept +namespace swap_adl_tests { +// if swap ADL finds this then it would call std::swap otherwise (same +// signature) +struct tag {}; + +template tag swap(T &, T &); +template tag swap(T (&a)[N], T (&b)[N]); + +// helper functions to test if an unqualified swap is possible, and if it +// becomes std::swap +template std::false_type can_swap(...) noexcept(false); +template (), std::declval()))> +std::true_type can_swap(int) noexcept(noexcept(swap(std::declval(), + std::declval()))); + +template std::false_type uses_std(...); +template +std::is_same(), std::declval())), tag> +uses_std(int); + +template +struct is_std_swap_noexcept + : std::integral_constant::value && + std::is_nothrow_move_assignable::value> {}; + +template +struct is_std_swap_noexcept : is_std_swap_noexcept {}; + +template +struct is_adl_swap_noexcept + : std::integral_constant(0))> {}; +} // namespace swap_adl_tests + +template +struct is_swappable + : std::integral_constant< + bool, + decltype(detail::swap_adl_tests::can_swap(0))::value && + (!decltype(detail::swap_adl_tests::uses_std(0))::value || + (std::is_move_assignable::value && + std::is_move_constructible::value))> {}; + +template +struct is_swappable + : std::integral_constant< + bool, + decltype(detail::swap_adl_tests::can_swap(0))::value && + (!decltype(detail::swap_adl_tests::uses_std( + 0))::value || + is_swappable::value)> {}; + +template +struct is_nothrow_swappable + : std::integral_constant< + bool, + is_swappable::value && + ((decltype(detail::swap_adl_tests::uses_std(0))::value && + detail::swap_adl_tests::is_std_swap_noexcept::value) || + (!decltype(detail::swap_adl_tests::uses_std(0))::value && + detail::swap_adl_tests::is_adl_swap_noexcept::value))> {}; +#endif +#endif + +// Trait for checking if a type is a tl::expected +template struct is_expected_impl : std::false_type {}; +template +struct is_expected_impl> : std::true_type {}; +template using is_expected = is_expected_impl>; + +template +using expected_enable_forward_value = detail::enable_if_t< + std::is_constructible::value && + !std::is_same, in_place_t>::value && + !std::is_same, detail::decay_t>::value && + !std::is_same, detail::decay_t>::value>; + +template +using expected_enable_from_other = detail::enable_if_t< + std::is_constructible::value && + std::is_constructible::value && + !std::is_constructible &>::value && + !std::is_constructible &&>::value && + !std::is_constructible &>::value && + !std::is_constructible &&>::value && + !std::is_convertible &, T>::value && + !std::is_convertible &&, T>::value && + !std::is_convertible &, T>::value && + !std::is_convertible &&, T>::value>; + +template +using is_void_or = conditional_t::value, std::true_type, U>; + +template +using is_copy_constructible_or_void = + is_void_or>; + +template +using is_move_constructible_or_void = + is_void_or>; + +template +using is_copy_assignable_or_void = is_void_or>; + +template +using is_move_assignable_or_void = is_void_or>; + +} // namespace detail + +namespace detail { +struct no_init_t {}; +static constexpr no_init_t no_init{}; + +// Implements the storage of the values, and ensures that the destructor is +// trivial if it can be. +// +// This specialization is for where neither `T` or `E` is trivially +// destructible, so the destructors must be called on destruction of the +// `expected` +template ::value, + bool = std::is_trivially_destructible::value> +struct expected_storage_base { + constexpr expected_storage_base() : m_val(T{}), m_has_val(true) {} + constexpr expected_storage_base(no_init_t) : m_no_init(), m_has_val(false) {} + + template ::value> * = + nullptr> + constexpr expected_storage_base(in_place_t, Args &&...args) + : m_val(std::forward(args)...), m_has_val(true) {} + + template &, Args &&...>::value> * = nullptr> + constexpr expected_storage_base(in_place_t, std::initializer_list il, + Args &&...args) + : m_val(il, std::forward(args)...), m_has_val(true) {} + template ::value> * = + nullptr> + constexpr explicit expected_storage_base(unexpect_t, Args &&...args) + : m_unexpect(std::forward(args)...), m_has_val(false) {} + + template &, Args &&...>::value> * = nullptr> + constexpr explicit expected_storage_base(unexpect_t, + std::initializer_list il, + Args &&...args) + : m_unexpect(il, std::forward(args)...), m_has_val(false) {} + + ~expected_storage_base() { + if (m_has_val) { + m_val.~T(); + } else { + m_unexpect.~unexpected(); + } + } + union { + T m_val; + unexpected m_unexpect; + char m_no_init; + }; + bool m_has_val; +}; + +// This specialization is for when both `T` and `E` are trivially-destructible, +// so the destructor of the `expected` can be trivial. +template struct expected_storage_base { + constexpr expected_storage_base() : m_val(T{}), m_has_val(true) {} + constexpr expected_storage_base(no_init_t) : m_no_init(), m_has_val(false) {} + + template ::value> * = + nullptr> + constexpr expected_storage_base(in_place_t, Args &&...args) + : m_val(std::forward(args)...), m_has_val(true) {} + + template &, Args &&...>::value> * = nullptr> + constexpr expected_storage_base(in_place_t, std::initializer_list il, + Args &&...args) + : m_val(il, std::forward(args)...), m_has_val(true) {} + template ::value> * = + nullptr> + constexpr explicit expected_storage_base(unexpect_t, Args &&...args) + : m_unexpect(std::forward(args)...), m_has_val(false) {} + + template &, Args &&...>::value> * = nullptr> + constexpr explicit expected_storage_base(unexpect_t, + std::initializer_list il, + Args &&...args) + : m_unexpect(il, std::forward(args)...), m_has_val(false) {} + + ~expected_storage_base() = default; + union { + T m_val; + unexpected m_unexpect; + char m_no_init; + }; + bool m_has_val; +}; + +// T is trivial, E is not. +template struct expected_storage_base { + constexpr expected_storage_base() : m_val(T{}), m_has_val(true) {} + TL_EXPECTED_MSVC2015_CONSTEXPR expected_storage_base(no_init_t) + : m_no_init(), m_has_val(false) {} + + template ::value> * = + nullptr> + constexpr expected_storage_base(in_place_t, Args &&...args) + : m_val(std::forward(args)...), m_has_val(true) {} + + template &, Args &&...>::value> * = nullptr> + constexpr expected_storage_base(in_place_t, std::initializer_list il, + Args &&...args) + : m_val(il, std::forward(args)...), m_has_val(true) {} + template ::value> * = + nullptr> + constexpr explicit expected_storage_base(unexpect_t, Args &&...args) + : m_unexpect(std::forward(args)...), m_has_val(false) {} + + template &, Args &&...>::value> * = nullptr> + constexpr explicit expected_storage_base(unexpect_t, + std::initializer_list il, + Args &&...args) + : m_unexpect(il, std::forward(args)...), m_has_val(false) {} + + ~expected_storage_base() { + if (!m_has_val) { + m_unexpect.~unexpected(); + } + } + + union { + T m_val; + unexpected m_unexpect; + char m_no_init; + }; + bool m_has_val; +}; + +// E is trivial, T is not. +template struct expected_storage_base { + constexpr expected_storage_base() : m_val(T{}), m_has_val(true) {} + constexpr expected_storage_base(no_init_t) : m_no_init(), m_has_val(false) {} + + template ::value> * = + nullptr> + constexpr expected_storage_base(in_place_t, Args &&...args) + : m_val(std::forward(args)...), m_has_val(true) {} + + template &, Args &&...>::value> * = nullptr> + constexpr expected_storage_base(in_place_t, std::initializer_list il, + Args &&...args) + : m_val(il, std::forward(args)...), m_has_val(true) {} + template ::value> * = + nullptr> + constexpr explicit expected_storage_base(unexpect_t, Args &&...args) + : m_unexpect(std::forward(args)...), m_has_val(false) {} + + template &, Args &&...>::value> * = nullptr> + constexpr explicit expected_storage_base(unexpect_t, + std::initializer_list il, + Args &&...args) + : m_unexpect(il, std::forward(args)...), m_has_val(false) {} + + ~expected_storage_base() { + if (m_has_val) { + m_val.~T(); + } + } + union { + T m_val; + unexpected m_unexpect; + char m_no_init; + }; + bool m_has_val; +}; + +// `T` is `void`, `E` is trivially-destructible +template struct expected_storage_base { + #if __GNUC__ <= 5 + //no constexpr for GCC 4/5 bug + #else + TL_EXPECTED_MSVC2015_CONSTEXPR + #endif + expected_storage_base() : m_has_val(true) {} + + constexpr expected_storage_base(no_init_t) : m_val(), m_has_val(false) {} + + constexpr expected_storage_base(in_place_t) : m_has_val(true) {} + + template ::value> * = + nullptr> + constexpr explicit expected_storage_base(unexpect_t, Args &&...args) + : m_unexpect(std::forward(args)...), m_has_val(false) {} + + template &, Args &&...>::value> * = nullptr> + constexpr explicit expected_storage_base(unexpect_t, + std::initializer_list il, + Args &&...args) + : m_unexpect(il, std::forward(args)...), m_has_val(false) {} + + ~expected_storage_base() = default; + struct dummy {}; + union { + unexpected m_unexpect; + dummy m_val; + }; + bool m_has_val; +}; + +// `T` is `void`, `E` is not trivially-destructible +template struct expected_storage_base { + constexpr expected_storage_base() : m_dummy(), m_has_val(true) {} + constexpr expected_storage_base(no_init_t) : m_dummy(), m_has_val(false) {} + + constexpr expected_storage_base(in_place_t) : m_dummy(), m_has_val(true) {} + + template ::value> * = + nullptr> + constexpr explicit expected_storage_base(unexpect_t, Args &&...args) + : m_unexpect(std::forward(args)...), m_has_val(false) {} + + template &, Args &&...>::value> * = nullptr> + constexpr explicit expected_storage_base(unexpect_t, + std::initializer_list il, + Args &&...args) + : m_unexpect(il, std::forward(args)...), m_has_val(false) {} + + ~expected_storage_base() { + if (!m_has_val) { + m_unexpect.~unexpected(); + } + } + + union { + unexpected m_unexpect; + char m_dummy; + }; + bool m_has_val; +}; + +// This base class provides some handy member functions which can be used in +// further derived classes +template +struct expected_operations_base : expected_storage_base { + using expected_storage_base::expected_storage_base; + + template void construct(Args &&...args) noexcept { + new (std::addressof(this->m_val)) T(std::forward(args)...); + this->m_has_val = true; + } + + template void construct_with(Rhs &&rhs) noexcept { + new (std::addressof(this->m_val)) T(std::forward(rhs).get()); + this->m_has_val = true; + } + + template void construct_error(Args &&...args) noexcept { + new (std::addressof(this->m_unexpect)) + unexpected(std::forward(args)...); + this->m_has_val = false; + } + +#ifdef TL_EXPECTED_EXCEPTIONS_ENABLED + + // These assign overloads ensure that the most efficient assignment + // implementation is used while maintaining the strong exception guarantee. + // The problematic case is where rhs has a value, but *this does not. + // + // This overload handles the case where we can just copy-construct `T` + // directly into place without throwing. + template ::value> + * = nullptr> + void assign(const expected_operations_base &rhs) noexcept { + if (!this->m_has_val && rhs.m_has_val) { + geterr().~unexpected(); + construct(rhs.get()); + } else { + assign_common(rhs); + } + } + + // This overload handles the case where we can attempt to create a copy of + // `T`, then no-throw move it into place if the copy was successful. + template ::value && + std::is_nothrow_move_constructible::value> + * = nullptr> + void assign(const expected_operations_base &rhs) noexcept { + if (!this->m_has_val && rhs.m_has_val) { + T tmp = rhs.get(); + geterr().~unexpected(); + construct(std::move(tmp)); + } else { + assign_common(rhs); + } + } + + // This overload is the worst-case, where we have to move-construct the + // unexpected value into temporary storage, then try to copy the T into place. + // If the construction succeeds, then everything is fine, but if it throws, + // then we move the old unexpected value back into place before rethrowing the + // exception. + template ::value && + !std::is_nothrow_move_constructible::value> + * = nullptr> + void assign(const expected_operations_base &rhs) { + if (!this->m_has_val && rhs.m_has_val) { + auto tmp = std::move(geterr()); + geterr().~unexpected(); + +#ifdef TL_EXPECTED_EXCEPTIONS_ENABLED + try { + construct(rhs.get()); + } catch (...) { + geterr() = std::move(tmp); + throw; + } +#else + construct(rhs.get()); +#endif + } else { + assign_common(rhs); + } + } + + // These overloads do the same as above, but for rvalues + template ::value> + * = nullptr> + void assign(expected_operations_base &&rhs) noexcept { + if (!this->m_has_val && rhs.m_has_val) { + geterr().~unexpected(); + construct(std::move(rhs).get()); + } else { + assign_common(std::move(rhs)); + } + } + + template ::value> + * = nullptr> + void assign(expected_operations_base &&rhs) { + if (!this->m_has_val && rhs.m_has_val) { + auto tmp = std::move(geterr()); + geterr().~unexpected(); +#ifdef TL_EXPECTED_EXCEPTIONS_ENABLED + try { + construct(std::move(rhs).get()); + } catch (...) { + geterr() = std::move(tmp); + throw; + } +#else + construct(std::move(rhs).get()); +#endif + } else { + assign_common(std::move(rhs)); + } + } + +#else + + // If exceptions are disabled then we can just copy-construct + void assign(const expected_operations_base &rhs) noexcept { + if (!this->m_has_val && rhs.m_has_val) { + geterr().~unexpected(); + construct(rhs.get()); + } else { + assign_common(rhs); + } + } + + void assign(expected_operations_base &&rhs) noexcept { + if (!this->m_has_val && rhs.m_has_val) { + geterr().~unexpected(); + construct(std::move(rhs).get()); + } else { + assign_common(rhs); + } + } + +#endif + + // The common part of move/copy assigning + template void assign_common(Rhs &&rhs) { + if (this->m_has_val) { + if (rhs.m_has_val) { + get() = std::forward(rhs).get(); + } else { + destroy_val(); + construct_error(std::forward(rhs).geterr()); + } + } else { + if (!rhs.m_has_val) { + geterr() = std::forward(rhs).geterr(); + } + } + } + + bool has_value() const { return this->m_has_val; } + + TL_EXPECTED_11_CONSTEXPR T &get() & { return this->m_val; } + constexpr const T &get() const & { return this->m_val; } + TL_EXPECTED_11_CONSTEXPR T &&get() && { return std::move(this->m_val); } +#ifndef TL_EXPECTED_NO_CONSTRR + constexpr const T &&get() const && { return std::move(this->m_val); } +#endif + + TL_EXPECTED_11_CONSTEXPR unexpected &geterr() & { + return this->m_unexpect; + } + constexpr const unexpected &geterr() const & { return this->m_unexpect; } + TL_EXPECTED_11_CONSTEXPR unexpected &&geterr() && { + return std::move(this->m_unexpect); + } +#ifndef TL_EXPECTED_NO_CONSTRR + constexpr const unexpected &&geterr() const && { + return std::move(this->m_unexpect); + } +#endif + + TL_EXPECTED_11_CONSTEXPR void destroy_val() { get().~T(); } +}; + +// This base class provides some handy member functions which can be used in +// further derived classes +template +struct expected_operations_base : expected_storage_base { + using expected_storage_base::expected_storage_base; + + template void construct() noexcept { this->m_has_val = true; } + + // This function doesn't use its argument, but needs it so that code in + // levels above this can work independently of whether T is void + template void construct_with(Rhs &&) noexcept { + this->m_has_val = true; + } + + template void construct_error(Args &&...args) noexcept { + new (std::addressof(this->m_unexpect)) + unexpected(std::forward(args)...); + this->m_has_val = false; + } + + template void assign(Rhs &&rhs) noexcept { + if (!this->m_has_val) { + if (rhs.m_has_val) { + geterr().~unexpected(); + construct(); + } else { + geterr() = std::forward(rhs).geterr(); + } + } else { + if (!rhs.m_has_val) { + construct_error(std::forward(rhs).geterr()); + } + } + } + + bool has_value() const { return this->m_has_val; } + + TL_EXPECTED_11_CONSTEXPR unexpected &geterr() & { + return this->m_unexpect; + } + constexpr const unexpected &geterr() const & { return this->m_unexpect; } + TL_EXPECTED_11_CONSTEXPR unexpected &&geterr() && { + return std::move(this->m_unexpect); + } +#ifndef TL_EXPECTED_NO_CONSTRR + constexpr const unexpected &&geterr() const && { + return std::move(this->m_unexpect); + } +#endif + + TL_EXPECTED_11_CONSTEXPR void destroy_val() { + // no-op + } +}; + +// This class manages conditionally having a trivial copy constructor +// This specialization is for when T and E are trivially copy constructible +template :: + value &&TL_EXPECTED_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(E)::value> +struct expected_copy_base : expected_operations_base { + using expected_operations_base::expected_operations_base; +}; + +// This specialization is for when T or E are not trivially copy constructible +template +struct expected_copy_base : expected_operations_base { + using expected_operations_base::expected_operations_base; + + expected_copy_base() = default; + expected_copy_base(const expected_copy_base &rhs) + : expected_operations_base(no_init) { + if (rhs.has_value()) { + this->construct_with(rhs); + } else { + this->construct_error(rhs.geterr()); + } + } + + expected_copy_base(expected_copy_base &&rhs) = default; + expected_copy_base &operator=(const expected_copy_base &rhs) = default; + expected_copy_base &operator=(expected_copy_base &&rhs) = default; +}; + +// This class manages conditionally having a trivial move constructor +// Unfortunately there's no way to achieve this in GCC < 5 AFAIK, since it +// doesn't implement an analogue to std::is_trivially_move_constructible. We +// have to make do with a non-trivial move constructor even if T is trivially +// move constructible +#ifndef TL_EXPECTED_GCC49 +template >::value + &&std::is_trivially_move_constructible::value> +struct expected_move_base : expected_copy_base { + using expected_copy_base::expected_copy_base; +}; +#else +template struct expected_move_base; +#endif +template +struct expected_move_base : expected_copy_base { + using expected_copy_base::expected_copy_base; + + expected_move_base() = default; + expected_move_base(const expected_move_base &rhs) = default; + + expected_move_base(expected_move_base &&rhs) noexcept( + std::is_nothrow_move_constructible::value) + : expected_copy_base(no_init) { + if (rhs.has_value()) { + this->construct_with(std::move(rhs)); + } else { + this->construct_error(std::move(rhs.geterr())); + } + } + expected_move_base &operator=(const expected_move_base &rhs) = default; + expected_move_base &operator=(expected_move_base &&rhs) = default; +}; + +// This class manages conditionally having a trivial copy assignment operator +template >::value + &&TL_EXPECTED_IS_TRIVIALLY_COPY_ASSIGNABLE(E)::value + &&TL_EXPECTED_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(E)::value + &&TL_EXPECTED_IS_TRIVIALLY_DESTRUCTIBLE(E)::value> +struct expected_copy_assign_base : expected_move_base { + using expected_move_base::expected_move_base; +}; + +template +struct expected_copy_assign_base : expected_move_base { + using expected_move_base::expected_move_base; + + expected_copy_assign_base() = default; + expected_copy_assign_base(const expected_copy_assign_base &rhs) = default; + + expected_copy_assign_base(expected_copy_assign_base &&rhs) = default; + expected_copy_assign_base &operator=(const expected_copy_assign_base &rhs) { + this->assign(rhs); + return *this; + } + expected_copy_assign_base & + operator=(expected_copy_assign_base &&rhs) = default; +}; + +// This class manages conditionally having a trivial move assignment operator +// Unfortunately there's no way to achieve this in GCC < 5 AFAIK, since it +// doesn't implement an analogue to std::is_trivially_move_assignable. We have +// to make do with a non-trivial move assignment operator even if T is trivially +// move assignable +#ifndef TL_EXPECTED_GCC49 +template , + std::is_trivially_move_constructible, + std::is_trivially_move_assignable>>:: + value &&std::is_trivially_destructible::value + &&std::is_trivially_move_constructible::value + &&std::is_trivially_move_assignable::value> +struct expected_move_assign_base : expected_copy_assign_base { + using expected_copy_assign_base::expected_copy_assign_base; +}; +#else +template struct expected_move_assign_base; +#endif + +template +struct expected_move_assign_base + : expected_copy_assign_base { + using expected_copy_assign_base::expected_copy_assign_base; + + expected_move_assign_base() = default; + expected_move_assign_base(const expected_move_assign_base &rhs) = default; + + expected_move_assign_base(expected_move_assign_base &&rhs) = default; + + expected_move_assign_base & + operator=(const expected_move_assign_base &rhs) = default; + + expected_move_assign_base & + operator=(expected_move_assign_base &&rhs) noexcept( + std::is_nothrow_move_constructible::value + &&std::is_nothrow_move_assignable::value) { + this->assign(std::move(rhs)); + return *this; + } +}; + +// expected_delete_ctor_base will conditionally delete copy and move +// constructors depending on whether T is copy/move constructible +template ::value && + std::is_copy_constructible::value), + bool EnableMove = (is_move_constructible_or_void::value && + std::is_move_constructible::value)> +struct expected_delete_ctor_base { + expected_delete_ctor_base() = default; + expected_delete_ctor_base(const expected_delete_ctor_base &) = default; + expected_delete_ctor_base(expected_delete_ctor_base &&) noexcept = default; + expected_delete_ctor_base & + operator=(const expected_delete_ctor_base &) = default; + expected_delete_ctor_base & + operator=(expected_delete_ctor_base &&) noexcept = default; +}; + +template +struct expected_delete_ctor_base { + expected_delete_ctor_base() = default; + expected_delete_ctor_base(const expected_delete_ctor_base &) = default; + expected_delete_ctor_base(expected_delete_ctor_base &&) noexcept = delete; + expected_delete_ctor_base & + operator=(const expected_delete_ctor_base &) = default; + expected_delete_ctor_base & + operator=(expected_delete_ctor_base &&) noexcept = default; +}; + +template +struct expected_delete_ctor_base { + expected_delete_ctor_base() = default; + expected_delete_ctor_base(const expected_delete_ctor_base &) = delete; + expected_delete_ctor_base(expected_delete_ctor_base &&) noexcept = default; + expected_delete_ctor_base & + operator=(const expected_delete_ctor_base &) = default; + expected_delete_ctor_base & + operator=(expected_delete_ctor_base &&) noexcept = default; +}; + +template +struct expected_delete_ctor_base { + expected_delete_ctor_base() = default; + expected_delete_ctor_base(const expected_delete_ctor_base &) = delete; + expected_delete_ctor_base(expected_delete_ctor_base &&) noexcept = delete; + expected_delete_ctor_base & + operator=(const expected_delete_ctor_base &) = default; + expected_delete_ctor_base & + operator=(expected_delete_ctor_base &&) noexcept = default; +}; + +// expected_delete_assign_base will conditionally delete copy and move +// constructors depending on whether T and E are copy/move constructible + +// assignable +template ::value && + std::is_copy_constructible::value && + is_copy_assignable_or_void::value && + std::is_copy_assignable::value), + bool EnableMove = (is_move_constructible_or_void::value && + std::is_move_constructible::value && + is_move_assignable_or_void::value && + std::is_move_assignable::value)> +struct expected_delete_assign_base { + expected_delete_assign_base() = default; + expected_delete_assign_base(const expected_delete_assign_base &) = default; + expected_delete_assign_base(expected_delete_assign_base &&) noexcept = + default; + expected_delete_assign_base & + operator=(const expected_delete_assign_base &) = default; + expected_delete_assign_base & + operator=(expected_delete_assign_base &&) noexcept = default; +}; + +template +struct expected_delete_assign_base { + expected_delete_assign_base() = default; + expected_delete_assign_base(const expected_delete_assign_base &) = default; + expected_delete_assign_base(expected_delete_assign_base &&) noexcept = + default; + expected_delete_assign_base & + operator=(const expected_delete_assign_base &) = default; + expected_delete_assign_base & + operator=(expected_delete_assign_base &&) noexcept = delete; +}; + +template +struct expected_delete_assign_base { + expected_delete_assign_base() = default; + expected_delete_assign_base(const expected_delete_assign_base &) = default; + expected_delete_assign_base(expected_delete_assign_base &&) noexcept = + default; + expected_delete_assign_base & + operator=(const expected_delete_assign_base &) = delete; + expected_delete_assign_base & + operator=(expected_delete_assign_base &&) noexcept = default; +}; + +template +struct expected_delete_assign_base { + expected_delete_assign_base() = default; + expected_delete_assign_base(const expected_delete_assign_base &) = default; + expected_delete_assign_base(expected_delete_assign_base &&) noexcept = + default; + expected_delete_assign_base & + operator=(const expected_delete_assign_base &) = delete; + expected_delete_assign_base & + operator=(expected_delete_assign_base &&) noexcept = delete; +}; + +// This is needed to be able to construct the expected_default_ctor_base which +// follows, while still conditionally deleting the default constructor. +struct default_constructor_tag { + explicit constexpr default_constructor_tag() = default; +}; + +// expected_default_ctor_base will ensure that expected has a deleted default +// consturctor if T is not default constructible. +// This specialization is for when T is default constructible +template ::value || std::is_void::value> +struct expected_default_ctor_base { + constexpr expected_default_ctor_base() noexcept = default; + constexpr expected_default_ctor_base( + expected_default_ctor_base const &) noexcept = default; + constexpr expected_default_ctor_base(expected_default_ctor_base &&) noexcept = + default; + expected_default_ctor_base & + operator=(expected_default_ctor_base const &) noexcept = default; + expected_default_ctor_base & + operator=(expected_default_ctor_base &&) noexcept = default; + + constexpr explicit expected_default_ctor_base(default_constructor_tag) {} +}; + +// This specialization is for when T is not default constructible +template struct expected_default_ctor_base { + constexpr expected_default_ctor_base() noexcept = delete; + constexpr expected_default_ctor_base( + expected_default_ctor_base const &) noexcept = default; + constexpr expected_default_ctor_base(expected_default_ctor_base &&) noexcept = + default; + expected_default_ctor_base & + operator=(expected_default_ctor_base const &) noexcept = default; + expected_default_ctor_base & + operator=(expected_default_ctor_base &&) noexcept = default; + + constexpr explicit expected_default_ctor_base(default_constructor_tag) {} +}; +} // namespace detail + +template class bad_expected_access : public std::exception { +public: + explicit bad_expected_access(E e) : m_val(std::move(e)) {} + + virtual const char *what() const noexcept override { + return "Bad expected access"; + } + + const E &error() const & { return m_val; } + E &error() & { return m_val; } + const E &&error() const && { return std::move(m_val); } + E &&error() && { return std::move(m_val); } + +private: + E m_val; +}; + +/// An `expected` object is an object that contains the storage for +/// another object and manages the lifetime of this contained object `T`. +/// Alternatively it could contain the storage for another unexpected object +/// `E`. The contained object may not be initialized after the expected object +/// has been initialized, and may not be destroyed before the expected object +/// has been destroyed. The initialization state of the contained object is +/// tracked by the expected object. +template +class expected : private detail::expected_move_assign_base, + private detail::expected_delete_ctor_base, + private detail::expected_delete_assign_base, + private detail::expected_default_ctor_base { + static_assert(!std::is_reference::value, "T must not be a reference"); + static_assert(!std::is_same::type>::value, + "T must not be in_place_t"); + static_assert(!std::is_same::type>::value, + "T must not be unexpect_t"); + static_assert( + !std::is_same>::type>::value, + "T must not be unexpected"); + static_assert(!std::is_reference::value, "E must not be a reference"); + + T *valptr() { return std::addressof(this->m_val); } + const T *valptr() const { return std::addressof(this->m_val); } + unexpected *errptr() { return std::addressof(this->m_unexpect); } + const unexpected *errptr() const { + return std::addressof(this->m_unexpect); + } + + template ::value> * = nullptr> + TL_EXPECTED_11_CONSTEXPR U &val() { + return this->m_val; + } + TL_EXPECTED_11_CONSTEXPR unexpected &err() { return this->m_unexpect; } + + template ::value> * = nullptr> + constexpr const U &val() const { + return this->m_val; + } + constexpr const unexpected &err() const { return this->m_unexpect; } + + using impl_base = detail::expected_move_assign_base; + using ctor_base = detail::expected_default_ctor_base; + +public: + typedef T value_type; + typedef E error_type; + typedef unexpected unexpected_type; + +#if defined(TL_EXPECTED_CXX14) && !defined(TL_EXPECTED_GCC49) && \ + !defined(TL_EXPECTED_GCC54) && !defined(TL_EXPECTED_GCC55) + template TL_EXPECTED_11_CONSTEXPR auto and_then(F &&f) & { + return and_then_impl(*this, std::forward(f)); + } + template TL_EXPECTED_11_CONSTEXPR auto and_then(F &&f) && { + return and_then_impl(std::move(*this), std::forward(f)); + } + template constexpr auto and_then(F &&f) const & { + return and_then_impl(*this, std::forward(f)); + } + +#ifndef TL_EXPECTED_NO_CONSTRR + template constexpr auto and_then(F &&f) const && { + return and_then_impl(std::move(*this), std::forward(f)); + } +#endif + +#else + template + TL_EXPECTED_11_CONSTEXPR auto + and_then(F &&f) & -> decltype(and_then_impl(std::declval(), + std::forward(f))) { + return and_then_impl(*this, std::forward(f)); + } + template + TL_EXPECTED_11_CONSTEXPR auto + and_then(F &&f) && -> decltype(and_then_impl(std::declval(), + std::forward(f))) { + return and_then_impl(std::move(*this), std::forward(f)); + } + template + constexpr auto and_then(F &&f) const & -> decltype(and_then_impl( + std::declval(), std::forward(f))) { + return and_then_impl(*this, std::forward(f)); + } + +#ifndef TL_EXPECTED_NO_CONSTRR + template + constexpr auto and_then(F &&f) const && -> decltype(and_then_impl( + std::declval(), std::forward(f))) { + return and_then_impl(std::move(*this), std::forward(f)); + } +#endif +#endif + +#if defined(TL_EXPECTED_CXX14) && !defined(TL_EXPECTED_GCC49) && \ + !defined(TL_EXPECTED_GCC54) && !defined(TL_EXPECTED_GCC55) + template TL_EXPECTED_11_CONSTEXPR auto map(F &&f) & { + return expected_map_impl(*this, std::forward(f)); + } + template TL_EXPECTED_11_CONSTEXPR auto map(F &&f) && { + return expected_map_impl(std::move(*this), std::forward(f)); + } + template constexpr auto map(F &&f) const & { + return expected_map_impl(*this, std::forward(f)); + } + template constexpr auto map(F &&f) const && { + return expected_map_impl(std::move(*this), std::forward(f)); + } +#else + template + TL_EXPECTED_11_CONSTEXPR decltype(expected_map_impl( + std::declval(), std::declval())) + map(F &&f) & { + return expected_map_impl(*this, std::forward(f)); + } + template + TL_EXPECTED_11_CONSTEXPR decltype(expected_map_impl(std::declval(), + std::declval())) + map(F &&f) && { + return expected_map_impl(std::move(*this), std::forward(f)); + } + template + constexpr decltype(expected_map_impl(std::declval(), + std::declval())) + map(F &&f) const & { + return expected_map_impl(*this, std::forward(f)); + } + +#ifndef TL_EXPECTED_NO_CONSTRR + template + constexpr decltype(expected_map_impl(std::declval(), + std::declval())) + map(F &&f) const && { + return expected_map_impl(std::move(*this), std::forward(f)); + } +#endif +#endif + +#if defined(TL_EXPECTED_CXX14) && !defined(TL_EXPECTED_GCC49) && \ + !defined(TL_EXPECTED_GCC54) && !defined(TL_EXPECTED_GCC55) + template TL_EXPECTED_11_CONSTEXPR auto transform(F &&f) & { + return expected_map_impl(*this, std::forward(f)); + } + template TL_EXPECTED_11_CONSTEXPR auto transform(F &&f) && { + return expected_map_impl(std::move(*this), std::forward(f)); + } + template constexpr auto transform(F &&f) const & { + return expected_map_impl(*this, std::forward(f)); + } + template constexpr auto transform(F &&f) const && { + return expected_map_impl(std::move(*this), std::forward(f)); + } +#else + template + TL_EXPECTED_11_CONSTEXPR decltype(expected_map_impl( + std::declval(), std::declval())) + transform(F &&f) & { + return expected_map_impl(*this, std::forward(f)); + } + template + TL_EXPECTED_11_CONSTEXPR decltype(expected_map_impl(std::declval(), + std::declval())) + transform(F &&f) && { + return expected_map_impl(std::move(*this), std::forward(f)); + } + template + constexpr decltype(expected_map_impl(std::declval(), + std::declval())) + transform(F &&f) const & { + return expected_map_impl(*this, std::forward(f)); + } + +#ifndef TL_EXPECTED_NO_CONSTRR + template + constexpr decltype(expected_map_impl(std::declval(), + std::declval())) + transform(F &&f) const && { + return expected_map_impl(std::move(*this), std::forward(f)); + } +#endif +#endif + +#if defined(TL_EXPECTED_CXX14) && !defined(TL_EXPECTED_GCC49) && \ + !defined(TL_EXPECTED_GCC54) && !defined(TL_EXPECTED_GCC55) + template TL_EXPECTED_11_CONSTEXPR auto map_error(F &&f) & { + return map_error_impl(*this, std::forward(f)); + } + template TL_EXPECTED_11_CONSTEXPR auto map_error(F &&f) && { + return map_error_impl(std::move(*this), std::forward(f)); + } + template constexpr auto map_error(F &&f) const & { + return map_error_impl(*this, std::forward(f)); + } + template constexpr auto map_error(F &&f) const && { + return map_error_impl(std::move(*this), std::forward(f)); + } +#else + template + TL_EXPECTED_11_CONSTEXPR decltype(map_error_impl(std::declval(), + std::declval())) + map_error(F &&f) & { + return map_error_impl(*this, std::forward(f)); + } + template + TL_EXPECTED_11_CONSTEXPR decltype(map_error_impl(std::declval(), + std::declval())) + map_error(F &&f) && { + return map_error_impl(std::move(*this), std::forward(f)); + } + template + constexpr decltype(map_error_impl(std::declval(), + std::declval())) + map_error(F &&f) const & { + return map_error_impl(*this, std::forward(f)); + } + +#ifndef TL_EXPECTED_NO_CONSTRR + template + constexpr decltype(map_error_impl(std::declval(), + std::declval())) + map_error(F &&f) const && { + return map_error_impl(std::move(*this), std::forward(f)); + } +#endif +#endif + template expected TL_EXPECTED_11_CONSTEXPR or_else(F &&f) & { + return or_else_impl(*this, std::forward(f)); + } + + template expected TL_EXPECTED_11_CONSTEXPR or_else(F &&f) && { + return or_else_impl(std::move(*this), std::forward(f)); + } + + template expected constexpr or_else(F &&f) const & { + return or_else_impl(*this, std::forward(f)); + } + +#ifndef TL_EXPECTED_NO_CONSTRR + template expected constexpr or_else(F &&f) const && { + return or_else_impl(std::move(*this), std::forward(f)); + } +#endif + constexpr expected() = default; + constexpr expected(const expected &rhs) = default; + constexpr expected(expected &&rhs) = default; + expected &operator=(const expected &rhs) = default; + expected &operator=(expected &&rhs) = default; + + template ::value> * = + nullptr> + constexpr expected(in_place_t, Args &&...args) + : impl_base(in_place, std::forward(args)...), + ctor_base(detail::default_constructor_tag{}) {} + + template &, Args &&...>::value> * = nullptr> + constexpr expected(in_place_t, std::initializer_list il, Args &&...args) + : impl_base(in_place, il, std::forward(args)...), + ctor_base(detail::default_constructor_tag{}) {} + + template ::value> * = + nullptr, + detail::enable_if_t::value> * = + nullptr> + explicit constexpr expected(const unexpected &e) + : impl_base(unexpect, e.value()), + ctor_base(detail::default_constructor_tag{}) {} + + template < + class G = E, + detail::enable_if_t::value> * = + nullptr, + detail::enable_if_t::value> * = nullptr> + constexpr expected(unexpected const &e) + : impl_base(unexpect, e.value()), + ctor_base(detail::default_constructor_tag{}) {} + + template < + class G = E, + detail::enable_if_t::value> * = nullptr, + detail::enable_if_t::value> * = nullptr> + explicit constexpr expected(unexpected &&e) noexcept( + std::is_nothrow_constructible::value) + : impl_base(unexpect, std::move(e.value())), + ctor_base(detail::default_constructor_tag{}) {} + + template < + class G = E, + detail::enable_if_t::value> * = nullptr, + detail::enable_if_t::value> * = nullptr> + constexpr expected(unexpected &&e) noexcept( + std::is_nothrow_constructible::value) + : impl_base(unexpect, std::move(e.value())), + ctor_base(detail::default_constructor_tag{}) {} + + template ::value> * = + nullptr> + constexpr explicit expected(unexpect_t, Args &&...args) + : impl_base(unexpect, std::forward(args)...), + ctor_base(detail::default_constructor_tag{}) {} + + template &, Args &&...>::value> * = nullptr> + constexpr explicit expected(unexpect_t, std::initializer_list il, + Args &&...args) + : impl_base(unexpect, il, std::forward(args)...), + ctor_base(detail::default_constructor_tag{}) {} + + template ::value && + std::is_convertible::value)> * = + nullptr, + detail::expected_enable_from_other + * = nullptr> + explicit TL_EXPECTED_11_CONSTEXPR expected(const expected &rhs) + : ctor_base(detail::default_constructor_tag{}) { + if (rhs.has_value()) { + this->construct(*rhs); + } else { + this->construct_error(rhs.error()); + } + } + + template ::value && + std::is_convertible::value)> * = + nullptr, + detail::expected_enable_from_other + * = nullptr> + TL_EXPECTED_11_CONSTEXPR expected(const expected &rhs) + : ctor_base(detail::default_constructor_tag{}) { + if (rhs.has_value()) { + this->construct(*rhs); + } else { + this->construct_error(rhs.error()); + } + } + + template < + class U, class G, + detail::enable_if_t::value && + std::is_convertible::value)> * = nullptr, + detail::expected_enable_from_other * = nullptr> + explicit TL_EXPECTED_11_CONSTEXPR expected(expected &&rhs) + : ctor_base(detail::default_constructor_tag{}) { + if (rhs.has_value()) { + this->construct(std::move(*rhs)); + } else { + this->construct_error(std::move(rhs.error())); + } + } + + template < + class U, class G, + detail::enable_if_t<(std::is_convertible::value && + std::is_convertible::value)> * = nullptr, + detail::expected_enable_from_other * = nullptr> + TL_EXPECTED_11_CONSTEXPR expected(expected &&rhs) + : ctor_base(detail::default_constructor_tag{}) { + if (rhs.has_value()) { + this->construct(std::move(*rhs)); + } else { + this->construct_error(std::move(rhs.error())); + } + } + + template < + class U = T, + detail::enable_if_t::value> * = nullptr, + detail::expected_enable_forward_value * = nullptr> + explicit TL_EXPECTED_MSVC2015_CONSTEXPR expected(U &&v) + : expected(in_place, std::forward(v)) {} + + template < + class U = T, + detail::enable_if_t::value> * = nullptr, + detail::expected_enable_forward_value * = nullptr> + TL_EXPECTED_MSVC2015_CONSTEXPR expected(U &&v) + : expected(in_place, std::forward(v)) {} + + template < + class U = T, class G = T, + detail::enable_if_t::value> * = + nullptr, + detail::enable_if_t::value> * = nullptr, + detail::enable_if_t< + (!std::is_same, detail::decay_t>::value && + !detail::conjunction, + std::is_same>>::value && + std::is_constructible::value && + std::is_assignable::value && + std::is_nothrow_move_constructible::value)> * = nullptr> + expected &operator=(U &&v) { + if (has_value()) { + val() = std::forward(v); + } else { + err().~unexpected(); + ::new (valptr()) T(std::forward(v)); + this->m_has_val = true; + } + + return *this; + } + + template < + class U = T, class G = T, + detail::enable_if_t::value> * = + nullptr, + detail::enable_if_t::value> * = nullptr, + detail::enable_if_t< + (!std::is_same, detail::decay_t>::value && + !detail::conjunction, + std::is_same>>::value && + std::is_constructible::value && + std::is_assignable::value && + std::is_nothrow_move_constructible::value)> * = nullptr> + expected &operator=(U &&v) { + if (has_value()) { + val() = std::forward(v); + } else { + auto tmp = std::move(err()); + err().~unexpected(); + +#ifdef TL_EXPECTED_EXCEPTIONS_ENABLED + try { + ::new (valptr()) T(std::forward(v)); + this->m_has_val = true; + } catch (...) { + err() = std::move(tmp); + throw; + } +#else + ::new (valptr()) T(std::forward(v)); + this->m_has_val = true; +#endif + } + + return *this; + } + + template ::value && + std::is_assignable::value> * = nullptr> + expected &operator=(const unexpected &rhs) { + if (!has_value()) { + err() = rhs; + } else { + this->destroy_val(); + ::new (errptr()) unexpected(rhs); + this->m_has_val = false; + } + + return *this; + } + + template ::value && + std::is_move_assignable::value> * = nullptr> + expected &operator=(unexpected &&rhs) noexcept { + if (!has_value()) { + err() = std::move(rhs); + } else { + this->destroy_val(); + ::new (errptr()) unexpected(std::move(rhs)); + this->m_has_val = false; + } + + return *this; + } + + template ::value> * = nullptr> + void emplace(Args &&...args) { + if (has_value()) { + val().~T(); + } else { + err().~unexpected(); + this->m_has_val = true; + } + ::new (valptr()) T(std::forward(args)...); + } + + template ::value> * = nullptr> + void emplace(Args &&...args) { + if (has_value()) { + val().~T(); + ::new (valptr()) T(std::forward(args)...); + } else { + auto tmp = std::move(err()); + err().~unexpected(); + +#ifdef TL_EXPECTED_EXCEPTIONS_ENABLED + try { + ::new (valptr()) T(std::forward(args)...); + this->m_has_val = true; + } catch (...) { + err() = std::move(tmp); + throw; + } +#else + ::new (valptr()) T(std::forward(args)...); + this->m_has_val = true; +#endif + } + } + + template &, Args &&...>::value> * = nullptr> + void emplace(std::initializer_list il, Args &&...args) { + if (has_value()) { + T t(il, std::forward(args)...); + val() = std::move(t); + } else { + err().~unexpected(); + ::new (valptr()) T(il, std::forward(args)...); + this->m_has_val = true; + } + } + + template &, Args &&...>::value> * = nullptr> + void emplace(std::initializer_list il, Args &&...args) { + if (has_value()) { + T t(il, std::forward(args)...); + val() = std::move(t); + } else { + auto tmp = std::move(err()); + err().~unexpected(); + +#ifdef TL_EXPECTED_EXCEPTIONS_ENABLED + try { + ::new (valptr()) T(il, std::forward(args)...); + this->m_has_val = true; + } catch (...) { + err() = std::move(tmp); + throw; + } +#else + ::new (valptr()) T(il, std::forward(args)...); + this->m_has_val = true; +#endif + } + } + +private: + using t_is_void = std::true_type; + using t_is_not_void = std::false_type; + using t_is_nothrow_move_constructible = std::true_type; + using move_constructing_t_can_throw = std::false_type; + using e_is_nothrow_move_constructible = std::true_type; + using move_constructing_e_can_throw = std::false_type; + + void swap_where_both_have_value(expected & /*rhs*/, t_is_void) noexcept { + // swapping void is a no-op + } + + void swap_where_both_have_value(expected &rhs, t_is_not_void) { + using std::swap; + swap(val(), rhs.val()); + } + + void swap_where_only_one_has_value(expected &rhs, t_is_void) noexcept( + std::is_nothrow_move_constructible::value) { + ::new (errptr()) unexpected_type(std::move(rhs.err())); + rhs.err().~unexpected_type(); + std::swap(this->m_has_val, rhs.m_has_val); + } + + void swap_where_only_one_has_value(expected &rhs, t_is_not_void) { + swap_where_only_one_has_value_and_t_is_not_void( + rhs, typename std::is_nothrow_move_constructible::type{}, + typename std::is_nothrow_move_constructible::type{}); + } + + void swap_where_only_one_has_value_and_t_is_not_void( + expected &rhs, t_is_nothrow_move_constructible, + e_is_nothrow_move_constructible) noexcept { + auto temp = std::move(val()); + val().~T(); + ::new (errptr()) unexpected_type(std::move(rhs.err())); + rhs.err().~unexpected_type(); + ::new (rhs.valptr()) T(std::move(temp)); + std::swap(this->m_has_val, rhs.m_has_val); + } + + void swap_where_only_one_has_value_and_t_is_not_void( + expected &rhs, t_is_nothrow_move_constructible, + move_constructing_e_can_throw) { + auto temp = std::move(val()); + val().~T(); +#ifdef TL_EXPECTED_EXCEPTIONS_ENABLED + try { + ::new (errptr()) unexpected_type(std::move(rhs.err())); + rhs.err().~unexpected_type(); + ::new (rhs.valptr()) T(std::move(temp)); + std::swap(this->m_has_val, rhs.m_has_val); + } catch (...) { + val() = std::move(temp); + throw; + } +#else + ::new (errptr()) unexpected_type(std::move(rhs.err())); + rhs.err().~unexpected_type(); + ::new (rhs.valptr()) T(std::move(temp)); + std::swap(this->m_has_val, rhs.m_has_val); +#endif + } + + void swap_where_only_one_has_value_and_t_is_not_void( + expected &rhs, move_constructing_t_can_throw, + e_is_nothrow_move_constructible) { + auto temp = std::move(rhs.err()); + rhs.err().~unexpected_type(); +#ifdef TL_EXPECTED_EXCEPTIONS_ENABLED + try { + ::new (rhs.valptr()) T(std::move(val())); + val().~T(); + ::new (errptr()) unexpected_type(std::move(temp)); + std::swap(this->m_has_val, rhs.m_has_val); + } catch (...) { + rhs.err() = std::move(temp); + throw; + } +#else + ::new (rhs.valptr()) T(std::move(val())); + val().~T(); + ::new (errptr()) unexpected_type(std::move(temp)); + std::swap(this->m_has_val, rhs.m_has_val); +#endif + } + +public: + template + detail::enable_if_t::value && + detail::is_swappable::value && + (std::is_nothrow_move_constructible::value || + std::is_nothrow_move_constructible::value)> + swap(expected &rhs) noexcept( + std::is_nothrow_move_constructible::value + &&detail::is_nothrow_swappable::value + &&std::is_nothrow_move_constructible::value + &&detail::is_nothrow_swappable::value) { + if (has_value() && rhs.has_value()) { + swap_where_both_have_value(rhs, typename std::is_void::type{}); + } else if (!has_value() && rhs.has_value()) { + rhs.swap(*this); + } else if (has_value()) { + swap_where_only_one_has_value(rhs, typename std::is_void::type{}); + } else { + using std::swap; + swap(err(), rhs.err()); + } + } + + constexpr const T *operator->() const { return valptr(); } + TL_EXPECTED_11_CONSTEXPR T *operator->() { return valptr(); } + + template ::value> * = nullptr> + constexpr const U &operator*() const & { + return val(); + } + template ::value> * = nullptr> + TL_EXPECTED_11_CONSTEXPR U &operator*() & { + return val(); + } + template ::value> * = nullptr> + constexpr const U &&operator*() const && { + return std::move(val()); + } + template ::value> * = nullptr> + TL_EXPECTED_11_CONSTEXPR U &&operator*() && { + return std::move(val()); + } + + constexpr bool has_value() const noexcept { return this->m_has_val; } + constexpr explicit operator bool() const noexcept { return this->m_has_val; } + + template ::value> * = nullptr> + TL_EXPECTED_11_CONSTEXPR const U &value() const & { + if (!has_value()) + detail::throw_exception(bad_expected_access(err().value())); + return val(); + } + template ::value> * = nullptr> + TL_EXPECTED_11_CONSTEXPR U &value() & { + if (!has_value()) + detail::throw_exception(bad_expected_access(err().value())); + return val(); + } + template ::value> * = nullptr> + TL_EXPECTED_11_CONSTEXPR const U &&value() const && { + if (!has_value()) + detail::throw_exception(bad_expected_access(std::move(err()).value())); + return std::move(val()); + } + template ::value> * = nullptr> + TL_EXPECTED_11_CONSTEXPR U &&value() && { + if (!has_value()) + detail::throw_exception(bad_expected_access(std::move(err()).value())); + return std::move(val()); + } + + constexpr const E &error() const & { return err().value(); } + TL_EXPECTED_11_CONSTEXPR E &error() & { return err().value(); } + constexpr const E &&error() const && { return std::move(err().value()); } + TL_EXPECTED_11_CONSTEXPR E &&error() && { return std::move(err().value()); } + + template constexpr T value_or(U &&v) const & { + static_assert(std::is_copy_constructible::value && + std::is_convertible::value, + "T must be copy-constructible and convertible to from U&&"); + return bool(*this) ? **this : static_cast(std::forward(v)); + } + template TL_EXPECTED_11_CONSTEXPR T value_or(U &&v) && { + static_assert(std::is_move_constructible::value && + std::is_convertible::value, + "T must be move-constructible and convertible to from U&&"); + return bool(*this) ? std::move(**this) : static_cast(std::forward(v)); + } +}; + +namespace detail { +template using exp_t = typename detail::decay_t::value_type; +template using err_t = typename detail::decay_t::error_type; +template using ret_t = expected>; + +#ifdef TL_EXPECTED_CXX14 +template >::value> * = nullptr, + class Ret = decltype(detail::invoke(std::declval(), + *std::declval()))> +constexpr auto and_then_impl(Exp &&exp, F &&f) { + static_assert(detail::is_expected::value, "F must return an expected"); + + return exp.has_value() + ? detail::invoke(std::forward(f), *std::forward(exp)) + : Ret(unexpect, std::forward(exp).error()); +} + +template >::value> * = nullptr, + class Ret = decltype(detail::invoke(std::declval()))> +constexpr auto and_then_impl(Exp &&exp, F &&f) { + static_assert(detail::is_expected::value, "F must return an expected"); + + return exp.has_value() ? detail::invoke(std::forward(f)) + : Ret(unexpect, std::forward(exp).error()); +} +#else +template struct TC; +template (), + *std::declval())), + detail::enable_if_t>::value> * = nullptr> +auto and_then_impl(Exp &&exp, F &&f) -> Ret { + static_assert(detail::is_expected::value, "F must return an expected"); + + return exp.has_value() + ? detail::invoke(std::forward(f), *std::forward(exp)) + : Ret(unexpect, std::forward(exp).error()); +} + +template ())), + detail::enable_if_t>::value> * = nullptr> +constexpr auto and_then_impl(Exp &&exp, F &&f) -> Ret { + static_assert(detail::is_expected::value, "F must return an expected"); + + return exp.has_value() ? detail::invoke(std::forward(f)) + : Ret(unexpect, std::forward(exp).error()); +} +#endif + +#ifdef TL_EXPECTED_CXX14 +template >::value> * = nullptr, + class Ret = decltype(detail::invoke(std::declval(), + *std::declval())), + detail::enable_if_t::value> * = nullptr> +constexpr auto expected_map_impl(Exp &&exp, F &&f) { + using result = ret_t>; + return exp.has_value() ? result(detail::invoke(std::forward(f), + *std::forward(exp))) + : result(unexpect, std::forward(exp).error()); +} + +template >::value> * = nullptr, + class Ret = decltype(detail::invoke(std::declval(), + *std::declval())), + detail::enable_if_t::value> * = nullptr> +auto expected_map_impl(Exp &&exp, F &&f) { + using result = expected>; + if (exp.has_value()) { + detail::invoke(std::forward(f), *std::forward(exp)); + return result(); + } + + return result(unexpect, std::forward(exp).error()); +} + +template >::value> * = nullptr, + class Ret = decltype(detail::invoke(std::declval())), + detail::enable_if_t::value> * = nullptr> +constexpr auto expected_map_impl(Exp &&exp, F &&f) { + using result = ret_t>; + return exp.has_value() ? result(detail::invoke(std::forward(f))) + : result(unexpect, std::forward(exp).error()); +} + +template >::value> * = nullptr, + class Ret = decltype(detail::invoke(std::declval())), + detail::enable_if_t::value> * = nullptr> +auto expected_map_impl(Exp &&exp, F &&f) { + using result = expected>; + if (exp.has_value()) { + detail::invoke(std::forward(f)); + return result(); + } + + return result(unexpect, std::forward(exp).error()); +} +#else +template >::value> * = nullptr, + class Ret = decltype(detail::invoke(std::declval(), + *std::declval())), + detail::enable_if_t::value> * = nullptr> + +constexpr auto expected_map_impl(Exp &&exp, F &&f) + -> ret_t> { + using result = ret_t>; + + return exp.has_value() ? result(detail::invoke(std::forward(f), + *std::forward(exp))) + : result(unexpect, std::forward(exp).error()); +} + +template >::value> * = nullptr, + class Ret = decltype(detail::invoke(std::declval(), + *std::declval())), + detail::enable_if_t::value> * = nullptr> + +auto expected_map_impl(Exp &&exp, F &&f) -> expected> { + if (exp.has_value()) { + detail::invoke(std::forward(f), *std::forward(exp)); + return {}; + } + + return unexpected>(std::forward(exp).error()); +} + +template >::value> * = nullptr, + class Ret = decltype(detail::invoke(std::declval())), + detail::enable_if_t::value> * = nullptr> + +constexpr auto expected_map_impl(Exp &&exp, F &&f) + -> ret_t> { + using result = ret_t>; + + return exp.has_value() ? result(detail::invoke(std::forward(f))) + : result(unexpect, std::forward(exp).error()); +} + +template >::value> * = nullptr, + class Ret = decltype(detail::invoke(std::declval())), + detail::enable_if_t::value> * = nullptr> + +auto expected_map_impl(Exp &&exp, F &&f) -> expected> { + if (exp.has_value()) { + detail::invoke(std::forward(f)); + return {}; + } + + return unexpected>(std::forward(exp).error()); +} +#endif + +#if defined(TL_EXPECTED_CXX14) && !defined(TL_EXPECTED_GCC49) && \ + !defined(TL_EXPECTED_GCC54) && !defined(TL_EXPECTED_GCC55) +template >::value> * = nullptr, + class Ret = decltype(detail::invoke(std::declval(), + std::declval().error())), + detail::enable_if_t::value> * = nullptr> +constexpr auto map_error_impl(Exp &&exp, F &&f) { + using result = expected, detail::decay_t>; + return exp.has_value() + ? result(*std::forward(exp)) + : result(unexpect, detail::invoke(std::forward(f), + std::forward(exp).error())); +} +template >::value> * = nullptr, + class Ret = decltype(detail::invoke(std::declval(), + std::declval().error())), + detail::enable_if_t::value> * = nullptr> +auto map_error_impl(Exp &&exp, F &&f) { + using result = expected, monostate>; + if (exp.has_value()) { + return result(*std::forward(exp)); + } + + detail::invoke(std::forward(f), std::forward(exp).error()); + return result(unexpect, monostate{}); +} +template >::value> * = nullptr, + class Ret = decltype(detail::invoke(std::declval(), + std::declval().error())), + detail::enable_if_t::value> * = nullptr> +constexpr auto map_error_impl(Exp &&exp, F &&f) { + using result = expected, detail::decay_t>; + return exp.has_value() + ? result() + : result(unexpect, detail::invoke(std::forward(f), + std::forward(exp).error())); +} +template >::value> * = nullptr, + class Ret = decltype(detail::invoke(std::declval(), + std::declval().error())), + detail::enable_if_t::value> * = nullptr> +auto map_error_impl(Exp &&exp, F &&f) { + using result = expected, monostate>; + if (exp.has_value()) { + return result(); + } + + detail::invoke(std::forward(f), std::forward(exp).error()); + return result(unexpect, monostate{}); +} +#else +template >::value> * = nullptr, + class Ret = decltype(detail::invoke(std::declval(), + std::declval().error())), + detail::enable_if_t::value> * = nullptr> +constexpr auto map_error_impl(Exp &&exp, F &&f) + -> expected, detail::decay_t> { + using result = expected, detail::decay_t>; + + return exp.has_value() + ? result(*std::forward(exp)) + : result(unexpect, detail::invoke(std::forward(f), + std::forward(exp).error())); +} + +template >::value> * = nullptr, + class Ret = decltype(detail::invoke(std::declval(), + std::declval().error())), + detail::enable_if_t::value> * = nullptr> +auto map_error_impl(Exp &&exp, F &&f) -> expected, monostate> { + using result = expected, monostate>; + if (exp.has_value()) { + return result(*std::forward(exp)); + } + + detail::invoke(std::forward(f), std::forward(exp).error()); + return result(unexpect, monostate{}); +} + +template >::value> * = nullptr, + class Ret = decltype(detail::invoke(std::declval(), + std::declval().error())), + detail::enable_if_t::value> * = nullptr> +constexpr auto map_error_impl(Exp &&exp, F &&f) + -> expected, detail::decay_t> { + using result = expected, detail::decay_t>; + + return exp.has_value() + ? result() + : result(unexpect, detail::invoke(std::forward(f), + std::forward(exp).error())); +} + +template >::value> * = nullptr, + class Ret = decltype(detail::invoke(std::declval(), + std::declval().error())), + detail::enable_if_t::value> * = nullptr> +auto map_error_impl(Exp &&exp, F &&f) -> expected, monostate> { + using result = expected, monostate>; + if (exp.has_value()) { + return result(); + } + + detail::invoke(std::forward(f), std::forward(exp).error()); + return result(unexpect, monostate{}); +} +#endif + +#ifdef TL_EXPECTED_CXX14 +template (), + std::declval().error())), + detail::enable_if_t::value> * = nullptr> +constexpr auto or_else_impl(Exp &&exp, F &&f) { + static_assert(detail::is_expected::value, "F must return an expected"); + return exp.has_value() ? std::forward(exp) + : detail::invoke(std::forward(f), + std::forward(exp).error()); +} + +template (), + std::declval().error())), + detail::enable_if_t::value> * = nullptr> +detail::decay_t or_else_impl(Exp &&exp, F &&f) { + return exp.has_value() ? std::forward(exp) + : (detail::invoke(std::forward(f), + std::forward(exp).error()), + std::forward(exp)); +} +#else +template (), + std::declval().error())), + detail::enable_if_t::value> * = nullptr> +auto or_else_impl(Exp &&exp, F &&f) -> Ret { + static_assert(detail::is_expected::value, "F must return an expected"); + return exp.has_value() ? std::forward(exp) + : detail::invoke(std::forward(f), + std::forward(exp).error()); +} + +template (), + std::declval().error())), + detail::enable_if_t::value> * = nullptr> +detail::decay_t or_else_impl(Exp &&exp, F &&f) { + return exp.has_value() ? std::forward(exp) + : (detail::invoke(std::forward(f), + std::forward(exp).error()), + std::forward(exp)); +} +#endif +} // namespace detail + +template +constexpr bool operator==(const expected &lhs, + const expected &rhs) { + return (lhs.has_value() != rhs.has_value()) + ? false + : (!lhs.has_value() ? lhs.error() == rhs.error() : *lhs == *rhs); +} +template +constexpr bool operator!=(const expected &lhs, + const expected &rhs) { + return (lhs.has_value() != rhs.has_value()) + ? true + : (!lhs.has_value() ? lhs.error() != rhs.error() : *lhs != *rhs); +} +template +constexpr bool operator==(const expected &lhs, + const expected &rhs) { + return (lhs.has_value() != rhs.has_value()) + ? false + : (!lhs.has_value() ? lhs.error() == rhs.error() : true); +} +template +constexpr bool operator!=(const expected &lhs, + const expected &rhs) { + return (lhs.has_value() != rhs.has_value()) + ? true + : (!lhs.has_value() ? lhs.error() == rhs.error() : false); +} + +template +constexpr bool operator==(const expected &x, const U &v) { + return x.has_value() ? *x == v : false; +} +template +constexpr bool operator==(const U &v, const expected &x) { + return x.has_value() ? *x == v : false; +} +template +constexpr bool operator!=(const expected &x, const U &v) { + return x.has_value() ? *x != v : true; +} +template +constexpr bool operator!=(const U &v, const expected &x) { + return x.has_value() ? *x != v : true; +} + +template +constexpr bool operator==(const expected &x, const unexpected &e) { + return x.has_value() ? false : x.error() == e.value(); +} +template +constexpr bool operator==(const unexpected &e, const expected &x) { + return x.has_value() ? false : x.error() == e.value(); +} +template +constexpr bool operator!=(const expected &x, const unexpected &e) { + return x.has_value() ? true : x.error() != e.value(); +} +template +constexpr bool operator!=(const unexpected &e, const expected &x) { + return x.has_value() ? true : x.error() != e.value(); +} + +template ::value || + std::is_move_constructible::value) && + detail::is_swappable::value && + std::is_move_constructible::value && + detail::is_swappable::value> * = nullptr> +void swap(expected &lhs, + expected &rhs) noexcept(noexcept(lhs.swap(rhs))) { + lhs.swap(rhs); +} +} // namespace tl + +#endif diff --git a/zecalculator/math_objects/aliases.h b/zecalculator/math_objects/aliases.h new file mode 100644 index 0000000..5d836f8 --- /dev/null +++ b/zecalculator/math_objects/aliases.h @@ -0,0 +1,41 @@ +#pragma once + +/**************************************************************************** +** Copyright (c) 2023, Adel Kara Slimane +** +** This file is part of ZeCalculator's source code. +** +** ZeCalculators is free software: you may copy, redistribute and/or modify it +** under the terms of the GNU Affero General Public License as published by the +** Free Software Foundation, either version 3 of the License, or (at your +** option) any later version. +** +** This file is distributed in the hope that it will be useful, but +** WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +** General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +** +****************************************************************************/ + +#include + +namespace zc { + +namespace ast { + using Function = zc::Function; + using Sequence = zc::Sequence; + using Expression = zc::Expression; + using GlobalVariable = zc::GlobalVariable; +} + +namespace rpn { + using Function = zc::Function; + using Sequence = zc::Sequence; + using Expression = zc::Expression; + using GlobalVariable = zc::GlobalVariable; +} + +} diff --git a/zecalculator/math_objects/builtin_binary_functions.h b/zecalculator/math_objects/builtin_binary_functions.h new file mode 100644 index 0000000..21c905f --- /dev/null +++ b/zecalculator/math_objects/builtin_binary_functions.h @@ -0,0 +1,109 @@ +#pragma once + +/**************************************************************************** +** Copyright (c) 2023, Adel Kara Slimane +** +** This file is part of ZeCalculator's source code. +** +** ZeCalculators is free software: you may copy, redistribute and/or modify it +** under the terms of the GNU Affero General Public License as published by the +** Free Software Foundation, either version 3 of the License, or (at your +** option) any later version. +** +** This file is distributed in the hope that it will be useful, but +** WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +** General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +** +****************************************************************************/ + +#include +#include +#include +#include + +#include + +namespace zc { + +using CppBinaryFunctionPtr = double (*) (double, double); + +class CppBinaryFunction +{ +public: + constexpr CppBinaryFunction() = default; + + constexpr CppBinaryFunction(CppBinaryFunctionPtr f_ptr) : f_ptr(f_ptr) {}; + + void set_name(std::string name) { this->name = std::move(name); } + + const std::string& get_name() const { return name; } + + constexpr void set(CppBinaryFunction f) {f_ptr = f.f_ptr; } + + double operator()(double a, double b) const + { + return f_ptr(a ,b); + } + + bool operator == (const CppBinaryFunction&) const = default; + +protected: + CppBinaryFunctionPtr f_ptr = nullptr; + std::string name; +}; + +inline double plus(const double a, const double b) +{ + return a + b; +} + +inline double minus(const double a, const double b) +{ + return a - b; +} + +inline double multiply(const double a, const double b) +{ + return a * b; +} + +inline double divide(const double a, const double b) +{ + return a / b; +} + + +// we save the names along with the function pointers for convenience +// we could save only the function pointers, and the names only in the inventory +inline std::array, 5> builtin_binary_functions = {{ + {parsing::tokens::Operator::name_of('+'), plus}, + {parsing::tokens::Operator::name_of('-'), minus}, + {parsing::tokens::Operator::name_of('*'), multiply}, + {parsing::tokens::Operator::name_of('/'), divide}, + {parsing::tokens::Operator::name_of('^'), CppBinaryFunction(std::pow)}, +}}; + +inline CppBinaryFunction binary_func_from_op(char op) +{ + switch (op) + { + case '+': + return plus; + case '-': + return minus; + case '*': + return multiply; + case '/': + return divide; + case '^': + return CppBinaryFunction(std::pow); + default: + return nullptr; + } +} + +} diff --git a/zecalculator/math_objects/builtin_unary_functions.h b/zecalculator/math_objects/builtin_unary_functions.h new file mode 100644 index 0000000..e9a8411 --- /dev/null +++ b/zecalculator/math_objects/builtin_unary_functions.h @@ -0,0 +1,106 @@ +#pragma once + +/**************************************************************************** +** Copyright (c) 2023, Adel Kara Slimane +** +** This file is part of ZeCalculator's source code. +** +** ZeCalculators is free software: you may copy, redistribute and/or modify it +** under the terms of the GNU Affero General Public License as published by the +** Free Software Foundation, either version 3 of the License, or (at your +** option) any later version. +** +** This file is distributed in the hope that it will be useful, but +** WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +** General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +** +****************************************************************************/ + +#include +#include +#include +#include + +namespace zc { + +using CppUnaryFunctionPtr = double (*) (double); + +class CppUnaryFunction +{ +public: + constexpr CppUnaryFunction() = default; + + constexpr CppUnaryFunction(CppUnaryFunctionPtr f_ptr) : f_ptr(f_ptr) {}; + + constexpr void set(CppUnaryFunction f) {f_ptr = f.f_ptr; } + + void set_name(std::string name) { this->name = std::move(name); } + + const std::string& get_name() const { return name; } + + double operator()(double a) const + { + return f_ptr(a); + } + +protected: + CppUnaryFunctionPtr f_ptr = nullptr; + std::string name; +}; + +// we save the names along with the function pointers for convenience +// we could save only the function pointers, and the names only in the inventory +inline std::array, 30> builtin_unary_functions = {{ + {"cos", CppUnaryFunction(std::cos)}, + {"sin", CppUnaryFunction(std::sin)}, + {"tan", CppUnaryFunction(std::tan)}, + + {"acos", CppUnaryFunction(std::acos)}, + {"asin", CppUnaryFunction(std::asin)}, + {"atan", CppUnaryFunction(std::atan)}, + + {"cosh", CppUnaryFunction(std::cosh)}, + {"sinh", CppUnaryFunction(std::sinh)}, + {"tanh", CppUnaryFunction(std::tanh)}, + + {"ch", CppUnaryFunction(std::cosh)}, + {"sh", CppUnaryFunction(std::sinh)}, + {"th", CppUnaryFunction(std::tanh)}, + + {"acosh", CppUnaryFunction(std::acosh)}, + {"asinh", CppUnaryFunction(std::asinh)}, + {"atanh", CppUnaryFunction(std::atanh)}, + + {"ach", CppUnaryFunction(std::acosh)}, + {"ash", CppUnaryFunction(std::asinh)}, + {"ath", CppUnaryFunction(std::atanh)}, + + {"sqrt", CppUnaryFunction(std::sqrt)}, + {"log", CppUnaryFunction(std::log10)}, + {"lg", CppUnaryFunction(std::log2)}, + {"ln", CppUnaryFunction(std::log)}, + {"abs", CppUnaryFunction(std::abs)}, + {"exp", CppUnaryFunction(std::exp)}, + {"floor", CppUnaryFunction(std::floor)}, + {"ceil", CppUnaryFunction(std::ceil)}, + {"erf", CppUnaryFunction(std::erf)}, + {"erfc", CppUnaryFunction(std::erfc)}, + {"gamma", CppUnaryFunction(std::tgamma)}, + {"Γ", CppUnaryFunction(std::tgamma)}, +}}; + +inline CppUnaryFunction unary_func_from_name(std::string_view name) +{ + for(auto&& [f_name, f]: builtin_unary_functions) + { + if (f_name == name) + return f; + }; + return nullptr; +} + +} diff --git a/zecalculator/math_objects/decl/expression.h b/zecalculator/math_objects/decl/expression.h new file mode 100644 index 0000000..7f06316 --- /dev/null +++ b/zecalculator/math_objects/decl/expression.h @@ -0,0 +1,71 @@ +/**************************************************************************** +** Copyright (c) 2023, Adel Kara Slimane +** +** This file is part of ZeCalculator's source code. +** +** ZeCalculators is free software: you may copy, redistribute and/or modify it +** under the terms of the GNU Affero General Public License as published by the +** Free Software Foundation, either version 3 of the License, or (at your +** option) any later version. +** +** This file is distributed in the hope that it will be useful, but +** WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +** General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +** +****************************************************************************/ + +#pragma once + +#include +#include +#include +#include + +/* TODO: update approach as the following: + - Check for validity + - Enable setting custom names for functions and variables + - Performance improvement: flatten trees +*/ + +namespace zc { + +template +class MathWorld; + +/// @brief a class that represents a general expression +/// @note an expression is a function that does not have any input +template +class Expression: public Function +{ +public: + + void set(std::string expression); + + tl::expected evaluate() const; + + tl::expected operator ()() const; + +protected: + + // constructor reserved for MathWorld when using add() function + Expression(const MathWorld* mathworld); + + tl::expected evaluate(size_t current_recursion_depth) const; + + friend struct eval::rpn::Evaluator; + friend struct eval::ast::Evaluator; + + // hide functions that are not needed from Function + using Function::evaluate; + using Function::set_input_vars; + + template + friend class MathWorld; + +}; + +} diff --git a/zecalculator/math_objects/decl/function.h b/zecalculator/math_objects/decl/function.h new file mode 100644 index 0000000..a7472a9 --- /dev/null +++ b/zecalculator/math_objects/decl/function.h @@ -0,0 +1,149 @@ +/**************************************************************************** +** Copyright (c) 2023, Adel Kara Slimane +** +** This file is part of ZeCalculator's source code. +** +** ZeCalculators is free software: you may copy, redistribute and/or modify it +** under the terms of the GNU Affero General Public License as published by the +** Free Software Foundation, either version 3 of the License, or (at your +** option) any later version. +** +** This file is distributed in the hope that it will be useful, but +** WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +** General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +** +****************************************************************************/ + +#pragma once + +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +/* TODO: update approach as the following: + - Check for validity + - Enable setting custom names for functions and variables + - Performance improvement: flatten trees +*/ + +namespace zc { + +template +class MathWorld; + +namespace eval { +namespace rpn { + struct Evaluator; +} + +namespace ast { + struct Evaluator; +} +} + +namespace parsing { + struct RpnMaker; +} + +struct Ok {}; +struct Empty {}; + +struct InvalidInputVar +{ + std::string var_name; +}; + +using Vars = std::vector; + +template +class Function +{ +public: + + Function(Function&& f) = default; + Function& operator = (Function&& f) = default; + + const std::string& get_name() const; + + /// @brief sets the names of the input variables + /// @note the order of the input variables is important when calling the function + /// with positional arguments + void set_input_vars(std::vector input_vars); + + /// \brief set the expression + void set_expression(std::string expr); + + /// @brief sets both the input_vars and the expression + void set(std::vector input_vars, std::string expr); + + /// @brief returns the number of input variables, if they are valid + std::optional argument_size() const; + + /// @brief gives the math objects that are present in this function's expression + auto direct_dependencies() const; + + /// @brief tests if the function is valid, i.e. has a valid expression and input vars + operator bool () const; + + std::variant parsing_status() const; + + const tl::expected, Error>& get_parsing() const; + + /// @brief evaluation on a given math world with the given input + tl::expected evaluate(const std::vector& args) const; + + // span version + tl::expected evaluate(std::span args) const; + + /// @brief evaluation on a given math world with the given input + /// @note operator style + tl::expected operator()(const std::vector& args) const; + + // span version + tl::expected operator()(std::span args) const; + +protected: + + // constructor reserved for MathWorld when using add() function + Function(const MathWorld* mathworld) + requires(type == parsing::Type::AST); + + // constructor reserved for MathWorld when using add() function + Function(const MathWorld* mathworld) + requires (type == parsing::Type::RPN); + + /// @note version that tracks the current recursion depth + tl::expected evaluate(std::span args, + size_t current_recursion_depth) const; + + void set_name(std::string name); + + std::string name; + std::string expression; + + friend struct eval::rpn::Evaluator; + friend struct eval::ast::Evaluator; + friend struct parsing::RpnMaker; + + tl::expected, Error> parsed_expr; + tl::expected, InvalidInputVar> vars; + + // non-owning pointer to the mathworld that contains this object + const MathWorld* mathworld; + + template + friend class MathWorld; +}; + +} diff --git a/zecalculator/math_objects/decl/sequence.h b/zecalculator/math_objects/decl/sequence.h new file mode 100644 index 0000000..0523bad --- /dev/null +++ b/zecalculator/math_objects/decl/sequence.h @@ -0,0 +1,93 @@ +#pragma once + +/**************************************************************************** +** Copyright (c) 2023, Adel Kara Slimane +** +** This file is part of ZeCalculator's source code. +** +** ZeCalculators is free software: you may copy, redistribute and/or modify it +** under the terms of the GNU Affero General Public License as published by the +** Free Software Foundation, either version 3 of the License, or (at your +** option) any later version. +** +** This file is distributed in the hope that it will be useful, but +** WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +** General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +** +****************************************************************************/ + +#include + +/* TODO: update approach as the following: + - Check for validity + - Enable setting custom names for functions and variables + - Performance improvement: flatten trees +*/ + +namespace zc { + +template +class MathWorld; + +using Vals = std::vector; + +/// @brief a class that represents a Sequence of single argument +template +class Sequence: public zc::Function +{ +public: + + void set(std::string var_name, + const std::string& expr, + std::vector first_vals = {}); + + /// \brief set the expression + void set_expression(const std::string& expr); + + void set_input_var(std::string var_name); + + void set_first_values(std::vector first_vals); + + void set_first_val_index(int index); + + constexpr int get_first_val_index() const; + + /// @brief evaluates the sequence at the given index + /// @note evaluation modifies the state of the sequence, as values get saved within + /// the instance, and a locking mechanism is triggered to detect ill-formed seqs + tl::expected evaluate(double index) const; + + /// @brief operator version of evaluate + tl::expected operator () (double index) const; + +protected: + + // constructor reserved for MathWorld when using add() function + Sequence(const MathWorld* mathworld); + + /// @brief evaluation with recursion depth tracking + tl::expected evaluate(double index, size_t current_recursion_depth) const; + + friend struct eval::rpn::Evaluator; + friend struct eval::ast::Evaluator; + + // hide functions that are not needed from Function + using Function::evaluate; + using Function::set_input_vars; + + // index of the first value + int first_val_index = 0; + + // first values of the sequence + std::vector values; + + template + friend class MathWorld; + +}; + +} diff --git a/zecalculator/math_objects/expression.h b/zecalculator/math_objects/expression.h new file mode 100644 index 0000000..6a28b2f --- /dev/null +++ b/zecalculator/math_objects/expression.h @@ -0,0 +1,24 @@ +#pragma once + +/**************************************************************************** +** Copyright (c) 2023, Adel Kara Slimane +** +** This file is part of ZeCalculator's source code. +** +** ZeCalculators is free software: you may copy, redistribute and/or modify it +** under the terms of the GNU Affero General Public License as published by the +** Free Software Foundation, either version 3 of the License, or (at your +** option) any later version. +** +** This file is distributed in the hope that it will be useful, but +** WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +** General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +** +****************************************************************************/ + +#include +#include diff --git a/zecalculator/math_objects/forward_declares.h b/zecalculator/math_objects/forward_declares.h new file mode 100644 index 0000000..8e4c8e1 --- /dev/null +++ b/zecalculator/math_objects/forward_declares.h @@ -0,0 +1,41 @@ +#pragma once + +#include + +/**************************************************************************** +** Copyright (c) 2023, Adel Kara Slimane +** +** This file is part of ZeCalculator's source code. +** +** ZeCalculators is free software: you may copy, redistribute and/or modify it +** under the terms of the GNU Affero General Public License as published by the +** Free Software Foundation, either version 3 of the License, or (at your +** option) any later version. +** +** This file is distributed in the hope that it will be useful, but +** WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +** General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +** +****************************************************************************/ + +namespace zc { + +template +class Function; + +template +class Sequence; + +template +class Expression; + +template +using GlobalVariable = Expression; + +struct GlobalConstant; + +} diff --git a/zecalculator/math_objects/function.h b/zecalculator/math_objects/function.h new file mode 100644 index 0000000..30df9d2 --- /dev/null +++ b/zecalculator/math_objects/function.h @@ -0,0 +1,24 @@ +#pragma once + +/**************************************************************************** +** Copyright (c) 2023, Adel Kara Slimane +** +** This file is part of ZeCalculator's source code. +** +** ZeCalculators is free software: you may copy, redistribute and/or modify it +** under the terms of the GNU Affero General Public License as published by the +** Free Software Foundation, either version 3 of the License, or (at your +** option) any later version. +** +** This file is distributed in the hope that it will be useful, but +** WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +** General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +** +****************************************************************************/ + +#include +#include diff --git a/zecalculator/math_objects/global_constant.h b/zecalculator/math_objects/global_constant.h new file mode 100644 index 0000000..5029c61 --- /dev/null +++ b/zecalculator/math_objects/global_constant.h @@ -0,0 +1,55 @@ +#pragma once + +/**************************************************************************** +** Copyright (c) 2023, Adel Kara Slimane +** +** This file is part of ZeCalculator's source code. +** +** ZeCalculators is free software: you may copy, redistribute and/or modify it +** under the terms of the GNU Affero General Public License as published by the +** Free Software Foundation, either version 3 of the License, or (at your +** option) any later version. +** +** This file is distributed in the hope that it will be useful, but +** WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +** General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +** +****************************************************************************/ + +#include +#include +#include +#include + +namespace zc { + +struct GlobalConstant +{ + constexpr GlobalConstant() = default; + + constexpr GlobalConstant(double val): value(val) {} + + constexpr void set(GlobalConstant cst) {value = cst.value;} + + void set_name(std::string name) { this->name = std::move(name); } + + const std::string& get_name() const { return name; } + + double value = 0; + std::string name; +}; + +inline std::array, 5> builtin_global_constants = +{{ + {"math::pi", std::numbers::pi}, + {"math::π", std::numbers::pi}, + {"physics::kB", 1.380649e-23}, // Blotzmann constant, SI units + {"physics::h", 6.62607015e-34}, // Plank constant, SI units + {"physics::c", 299792458}, // Speed of light in vacuum, SI units +}}; + +} diff --git a/zecalculator/math_objects/global_variable.h b/zecalculator/math_objects/global_variable.h new file mode 100644 index 0000000..1be736e --- /dev/null +++ b/zecalculator/math_objects/global_variable.h @@ -0,0 +1,37 @@ +#pragma once + +/**************************************************************************** +** Copyright (c) 2023, Adel Kara Slimane +** +** This file is part of ZeCalculator's source code. +** +** ZeCalculators is free software: you may copy, redistribute and/or modify it +** under the terms of the GNU Affero General Public License as published by the +** Free Software Foundation, either version 3 of the License, or (at your +** option) any later version. +** +** This file is distributed in the hope that it will be useful, but +** WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +** General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +** +****************************************************************************/ + +#include + +namespace zc { + +template +class Expression; + +/// @brief there's not mathematical difference between a global variable +/// and a simple mathematical expression. It just makes more sense +/// when we add one to a math world: a global variable is an expression +/// that has a name +template +using GlobalVariable = Expression; + +} diff --git a/zecalculator/math_objects/impl/expression.h b/zecalculator/math_objects/impl/expression.h new file mode 100644 index 0000000..6b46068 --- /dev/null +++ b/zecalculator/math_objects/impl/expression.h @@ -0,0 +1,66 @@ +/**************************************************************************** +** Copyright (c) 2023, Adel Kara Slimane +** +** This file is part of ZeCalculator's source code. +** +** ZeCalculators is free software: you may copy, redistribute and/or modify it +** under the terms of the GNU Affero General Public License as published by the +** Free Software Foundation, either version 3 of the License, or (at your +** option) any later version. +** +** This file is distributed in the hope that it will be useful, but +** WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +** General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +** +****************************************************************************/ + +#pragma once + +#include +#include + +/* TODO: update approach as the following: + - Check for validity + - Enable setting custom names for functions and variables + - Performance improvement: flatten trees +*/ + +namespace zc { + +namespace eval { + struct Variable; +} + +template +Expression::Expression(const MathWorld* mathworld) : Function(mathworld) +{} + +template +void Expression::set(std::string expression) +{ + Function::set_expression(std::move(expression)); +} + +template +tl::expected Expression::evaluate() const +{ + return Function::evaluate(std::span()); +} + +template +tl::expected Expression::operator ()() const +{ + return Function::evaluate(std::span()); +} + +template +tl::expected Expression::evaluate(size_t current_recursion_depth) const +{ + return Function::evaluate(std::span(), current_recursion_depth); +} + +} diff --git a/zecalculator/math_objects/impl/function.h b/zecalculator/math_objects/impl/function.h new file mode 100644 index 0000000..d657546 --- /dev/null +++ b/zecalculator/math_objects/impl/function.h @@ -0,0 +1,298 @@ +#pragma once + +/**************************************************************************** +** Copyright (c) 2023, Adel Kara Slimane +** +** This file is part of ZeCalculator's source code. +** +** ZeCalculators is free software: you may copy, redistribute and/or modify it +** under the terms of the GNU Affero General Public License as published by the +** Free Software Foundation, either version 3 of the License, or (at your +** option) any later version. +** +** This file is distributed in the hope that it will be useful, but +** WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +** General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +** +****************************************************************************/ + +#include +#include +#include +#include + +namespace zc { + +template +Function::Function(const MathWorld* mathworld) + requires(type == parsing::Type::AST) + : mathworld(mathworld) +{} + +template +Function::Function(const MathWorld* mathworld) + requires(type == parsing::Type::RPN) + : parsed_expr({std::monostate()}), mathworld(mathworld) +{} + +template +void Function::set_name(std::string name) +{ + this->name = std::move(name); +} + +template +const std::string& Function::get_name() const +{ + return name; +} + +template +void Function::set_input_vars(std::vector input_vars) +{ + auto it = std::ranges::find_if_not(input_vars, parsing::is_valid_name); + if (it != input_vars.end()) + vars = tl::unexpected(InvalidInputVar{*it}); + else vars = std::move(input_vars); +} + +template +void Function::set_expression(std::string expr) +{ + // do nothing if it's the same expression + if (expression == expr) + return; + + expression = std::move(expr); + + if (expression.empty()) + { + if constexpr (type == parsing::Type::AST) + parsed_expr = parsing::Tree(std::monostate()); + else parsed_expr = {std::monostate()}; + } + else + { + // just a shortcut to have tokens -> make_tree(tokens, mathworld, vars.value) + using namespace std::placeholders; + auto bound_make_tree = [&](const std::vector& vec) + { + return parsing::make_tree(vec, *mathworld, vars.value()); + }; + + if constexpr (type == parsing::Type::AST) + parsed_expr = parsing::tokenize(expression).and_then(bound_make_tree); + else + parsed_expr + = parsing::tokenize(expression).and_then(bound_make_tree).transform(parsing::make_RPN); + } +} + +template +void Function::set(std::vector input_vars, std::string expr) +{ + set_input_vars(std::move(input_vars)); + set_expression(std::move(expr)); +} + +template +std::optional Function::argument_size() const +{ + if (vars) + return vars->size(); + else return {}; +} + +template +Function::operator bool () const +{ + if constexpr (type == parsing::Type::AST) + return bool(parsed_expr) and (not std::holds_alternative(**parsed_expr)) and bool(vars); + else return bool(parsed_expr) and (not std::holds_alternative(parsed_expr.value().front())) and bool(vars); +} + +template +std::variant Function::parsing_status() const +{ + if constexpr (type == parsing::Type::AST) + { + if (not parsed_expr.has_value()) + return parsed_expr.error(); + else if (std::holds_alternative(*(parsed_expr.value()))) + return Empty(); + else return Ok(); + } + else + { + if (not parsed_expr.has_value()) + return parsed_expr.error(); + else if (std::holds_alternative(parsed_expr.value().front())) + return Empty(); + else return Ok(); + } + +} + +template +const tl::expected, Error>& Function::get_parsing() const +{ + return parsed_expr; +} + +template +struct DepVisitor +{ + using ObjectType = typename MathWorld::ConstDynMathObject; + std::vector deps; + + template + void insert(const T& obj) + { + // tests for same alternative then same address + auto test = [&](auto&& val) + { + cref* ptr = std::get_if>(&val); + return ptr and &ptr->get() == &obj; + }; + + if (not std::ranges::any_of(deps, test)) + deps.push_back(std::cref(obj)); + } + + + template + void operator()(const T& alt) + { + // AST + + if constexpr (std::is_same_v>) + { + insert(alt.f); + std::visit(*this, *alt.operand); + } + else if constexpr (std::is_same_v>) + { + insert(alt.f); + std::visit(*this, *alt.operand1); + std::visit(*this, *alt.operand2); + } + else if constexpr (std::is_same_v>) + { + insert(alt.f); + std::ranges::for_each(alt.operands, [this](auto&& v){ std::visit(*this, *v); }); + } + else if constexpr (std::is_same_v>) + { + insert(alt.u); + std::visit(*this, *alt.operand); + } + + // RPN + + else if constexpr (std::is_same_v) + { + insert(alt.f); + } + else if constexpr (std::is_same_v) + { + insert(alt.f); + } + else if constexpr (std::is_same_v) + { + insert(alt.f); + } + else if constexpr (std::is_same_v) + { + insert(alt.u); + } + + // shared / templated + + else if constexpr (std::is_same_v>) + { + insert(alt.var); + } + else if constexpr (std::is_same_v) + { + insert(alt.constant); + } + + // we do nothing with every other possibility + } +}; + +template +auto Function::direct_dependencies() const +{ + DepVisitor visitor; + + if (not bool(*this)) + return visitor.deps; + + if constexpr (type == parsing::Type::RPN) + std::ranges::for_each(parsed_expr.value(), [&](auto&& v){ std::visit(visitor, v); }); + else + { + auto& val = *parsed_expr.value(); + std::visit(visitor, val); + } + + return visitor.deps; +} + +template +tl::expected Function::evaluate(std::span args, + size_t current_recursion_depth) const +{ + if (mathworld->max_recursion_depth < current_recursion_depth) [[unlikely]] + return tl::unexpected(Error::recursion_depth_overflow()); + else if (not bool(*this)) [[unlikely]] + return tl::unexpected(Error::invalid_function()); + else if (args.size() != vars->size()) [[unlikely]] + return tl::unexpected(Error::mismatched_fun_args()); + + if constexpr (type == parsing::Type::AST) + { + if (std::holds_alternative(*parsed_expr.value())) [[unlikely]] + return tl::unexpected(Error::empty_expression()); + } + else + { + if (std::holds_alternative(parsed_expr.value().front())) [[unlikely]] + return tl::unexpected(Error::empty_expression()); + } + + return zc::evaluate(*parsed_expr, args, current_recursion_depth); +} + +template +tl::expected Function::evaluate(const std::vector& args) const +{ + // this function is user called, so the recursion depth is zero + return evaluate(args, 0); +} + +template +tl::expected Function::evaluate(std::span args) const +{ + // this function is user called, so the recursion depth is zero + return evaluate(args, 0); +} + +template +tl::expected Function::operator()(const std::vector& args) const +{ + return evaluate(args, 0); +} + +template +tl::expected Function::operator()(std::span args) const +{ + return evaluate(args, 0); +} + +} // namespace zc diff --git a/zecalculator/math_objects/impl/sequence.h b/zecalculator/math_objects/impl/sequence.h new file mode 100644 index 0000000..aa3136e --- /dev/null +++ b/zecalculator/math_objects/impl/sequence.h @@ -0,0 +1,76 @@ +#pragma once + +#include + +namespace zc { + +template +Sequence::Sequence(const MathWorld* mathworld) : Function(mathworld) +{} + +template +void Sequence::set(std::string var_name, + const std::string& expr, + std::vector first_vals) +{ + values = std::move(first_vals); + Function::set(std::vector{var_name}, expr); +} + +template +void Sequence::set_expression(const std::string& expr) +{ + Function::set_expression(expr); +} + +template +void Sequence::set_input_var(std::string var_name) +{ + this->set_input_vars(std::vector {var_name}); +} + +template +void Sequence::set_first_values(std::vector first_vals) +{ + values = std::move(first_vals); +} + +template +void Sequence::set_first_val_index(int index) +{ + first_val_index = index; + values.clear(); +} + +template +constexpr int Sequence::get_first_val_index() const { return first_val_index; }; + +template +tl::expected Sequence::evaluate(double index, + size_t current_recursion_depth) const +{ + if (this->mathworld->max_recursion_depth < current_recursion_depth) [[unlikely]] + return tl::unexpected(Error::recursion_depth_overflow()); + // round double to nearest integer + int integer_index = std::lround(index); + + if (integer_index < first_val_index) [[unlikely]] + return std::nan(""); + else if (size_t(integer_index - first_val_index) < values.size()) + return values[size_t(integer_index - first_val_index)]; + else return Function::evaluate(std::vector{double(integer_index)}, current_recursion_depth); +} + +template +tl::expected Sequence::evaluate(double index) const +{ + return evaluate(index, 0); +} + +template +tl::expected Sequence::operator ()(double index) const +{ + return evaluate(index); +} + +} diff --git a/zecalculator/math_objects/object_list.h b/zecalculator/math_objects/object_list.h new file mode 100644 index 0000000..2666070 --- /dev/null +++ b/zecalculator/math_objects/object_list.h @@ -0,0 +1,17 @@ +#pragma once + +#include +#include + +#include + +namespace zc { + +template +using MathObjects = std::tuple, + GlobalVariable, + Sequence>; +} diff --git a/zecalculator/math_objects/sequence.h b/zecalculator/math_objects/sequence.h new file mode 100644 index 0000000..7f18f6f --- /dev/null +++ b/zecalculator/math_objects/sequence.h @@ -0,0 +1,24 @@ +#pragma once + +/**************************************************************************** +** Copyright (c) 2023, Adel Kara Slimane +** +** This file is part of ZeCalculator's source code. +** +** ZeCalculators is free software: you may copy, redistribute and/or modify it +** under the terms of the GNU Affero General Public License as published by the +** Free Software Foundation, either version 3 of the License, or (at your +** option) any later version. +** +** This file is distributed in the hope that it will be useful, but +** WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +** General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +** +****************************************************************************/ + +#include +#include diff --git a/zecalculator/mathworld/decl/mathworld.h b/zecalculator/mathworld/decl/mathworld.h new file mode 100644 index 0000000..7ed6b5b --- /dev/null +++ b/zecalculator/mathworld/decl/mathworld.h @@ -0,0 +1,197 @@ +#pragma once + +/**************************************************************************** +** Copyright (c) 2023, Adel Kara Slimane +** +** This file is part of ZeCalculator's source code. +** +** ZeCalculators is free software: you may copy, redistribute and/or modify it +** under the terms of the GNU Affero General Public License as published by the +** Free Software Foundation, either version 3 of the License, or (at your +** option) any later version. +** +** This file is distributed in the hope that it will be useful, but +** WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +** General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +** +****************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +namespace zc { + +template +class MathWorld; + +namespace ast { + using MathWorld = zc::MathWorld; +} + +namespace rpn { + using MathWorld = zc::MathWorld; +} + + +struct NameError +{ + enum Type {ALREADY_TAKEN, INVALID_FORMAT}; + + static NameError already_taken(std::string_view name) + { + return NameError{.type = ALREADY_TAKEN, .name = std::string(name)}; + } + + static NameError invalid_format(std::string_view name) + { + return NameError{.type = INVALID_FORMAT, .name = std::string(name)}; + } + + Type type; + std::string name; +}; + +template +class MathWorld +{ +public: + + class UnregisteredObject {}; + + /// @brief type used when looking up a match object with a name at runtime + using DynMathObject = to_variant_t, tuple_transform_t>>>; + + /// @brief const version of the one above + using ConstDynMathObject = to_variant_t, tuple_transform_t>>>; + + /// @brief default constructor that defines the usual functions and global constants + MathWorld() + : MathWorld(builtin_unary_functions, builtin_binary_functions, builtin_global_constants){}; + + template + MathWorld(const std::array, size1>& objects1, + const std::array, sizeN>&... objectsN) + : MathWorld(objectsN...) + { + for(auto [name, obj]: objects1) + add(name, obj); + } + + template + MathWorld(const std::array, size>& objects) + { + for(auto [name, obj]: objects) + add(name, obj); + } + + /// @brief get object from name, the underlying type is to be dynamically resolved at runtime + /// @note const version + ConstDynMathObject get(std::string_view name) const + { + auto it = inventory.find(name); + return it != inventory.end() ? to_const(it->second) : ConstDynMathObject(); + } + + /// @brief get object from name, the underlying type is to be dynamically resolved at runtime + DynMathObject get(std::string_view name) + { + auto it = inventory.find(name); + return it != inventory.end() ? it->second : DynMathObject(); + } + + /// @brief get object 'ObjectType' from name, if it exists, nullptr otherwise + template + requires tuple_contains_v, ObjectType> + ObjectType* get(std::string_view name) + { + DynMathObject dyn_obj = get(name); + if (std::holds_alternative>(dyn_obj)) + return &std::get>(dyn_obj).get(); + else return nullptr; + } + + /// @brief get object 'ObjectType' from name, if it exists, nullptr otherwise + /// @note const version + template + requires tuple_contains_v, ObjectType> + const ObjectType* get(std::string_view name) const + { + ConstDynMathObject dyn_obj = get(name); + if (std::holds_alternative>(dyn_obj)) + return &std::get>(dyn_obj).get(); + else return nullptr; + } + + /// @brief get object 'ObjectType' from id + /// @note ids are not unique, they live in different sets between ObjectTypes + template + requires tuple_contains_v, ObjectType> + ObjectType& get(size_t id) + { + return std::get>(math_objects).at(id); + } + + /// @brief get object 'ObjectType' from id + /// @note const version + template + requires tuple_contains_v, ObjectType> + const ObjectType& get(size_t id) const + { + return std::get>(math_objects).at(id); + } + + /// @brief default constructs an ObjectType in the world, under the name 'name' + /// then, if there are extra args, forwards them the member function .set() of the newly added object + /// @param arg...: arguments passed to the set() member function of the object + /// @note returns a NameError if the name is already taken or has the wrong format, leaves the world unchanged. + template + requires(tuple_contains_v, ObjectType> + and (sizeof...(Arg) == 0 or requires(ObjectType o) { o.set(std::declval()...); })) + tl::expected, NameError> add(std::string_view name, Arg&&... arg); + + /// @brief says if an object with the given name exists within the world + bool contains(std::string_view name) const + { + return inventory.find(name) != inventory.end(); + } + + /// @brief maximum recursion depth to reach before returning an error + size_t max_recursion_depth = 20; + + +protected: + + /// @brief converts a DynMathObject to a ConstDynMathObject + ConstDynMathObject to_const(DynMathObject obj) const + { + return std::visit( + overloaded{ + [](UnregisteredObject) -> ConstDynMathObject { return UnregisteredObject(); }, + [](auto&& val) -> ConstDynMathObject { return std::cref(val.get()); } + }, + obj); + } + + /// @brief maps an object name to its type and ID (index within the container that holds it) + name_map inventory; + + tuple_transform_t> math_objects; + +}; + +} diff --git a/zecalculator/mathworld/impl/mathworld.h b/zecalculator/mathworld/impl/mathworld.h new file mode 100644 index 0000000..f7670a7 --- /dev/null +++ b/zecalculator/mathworld/impl/mathworld.h @@ -0,0 +1,58 @@ +#pragma once + +/**************************************************************************** +** Copyright (c) 2023, Adel Kara Slimane +** +** This file is part of ZeCalculator's source code. +** +** ZeCalculators is free software: you may copy, redistribute and/or modify it +** under the terms of the GNU Affero General Public License as published by the +** Free Software Foundation, either version 3 of the License, or (at your +** option) any later version. +** +** This file is distributed in the hope that it will be useful, but +** WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +** General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +** +****************************************************************************/ + +#include +#include + +namespace zc { + +template +template + requires(tuple_contains_v, ObjectType> + and (sizeof...(Arg) == 0 or requires(ObjectType o) { o.set(std::declval()...); })) +tl::expected, NameError> MathWorld::add(std::string_view name, Arg &&...arg) +{ + if (not parsing::is_valid_name(name)) + return tl::unexpected(NameError::invalid_format(name)); + else if (contains(name)) + return tl::unexpected(NameError::already_taken(name)); + + SlottedDeque &object_container = std::get>(math_objects); + + size_t id; + // compile time check if objects needs MathWorld pointer + if constexpr (requires { ObjectType(this); }) + id = object_container.push(ObjectType(this)); + else id = object_container.push(ObjectType()); + + ObjectType& world_object = object_container[id]; + world_object.set_name(std::string(name)); + + inventory[std::string(name)] = std::ref(world_object); + + if constexpr (sizeof...(Arg) > 0) + world_object.set(std::forward(arg)...); + + return world_object; +} + +} diff --git a/zecalculator/mathworld/mathworld.h b/zecalculator/mathworld/mathworld.h new file mode 100644 index 0000000..465d800 --- /dev/null +++ b/zecalculator/mathworld/mathworld.h @@ -0,0 +1,24 @@ +#pragma once + +/**************************************************************************** +** Copyright (c) 2023, Adel Kara Slimane +** +** This file is part of ZeCalculator's source code. +** +** ZeCalculators is free software: you may copy, redistribute and/or modify it +** under the terms of the GNU Affero General Public License as published by the +** Free Software Foundation, either version 3 of the License, or (at your +** option) any later version. +** +** This file is distributed in the hope that it will be useful, but +** WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +** General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +** +****************************************************************************/ + +#include +#include diff --git a/zecalculator/parsing/data_structures/decl/node.h b/zecalculator/parsing/data_structures/decl/node.h new file mode 100644 index 0000000..1a3230c --- /dev/null +++ b/zecalculator/parsing/data_structures/decl/node.h @@ -0,0 +1,131 @@ +/**************************************************************************** +** Copyright (c) 2023, Adel Kara Slimane +** +** This file is part of ZeCalculator. +** +** ZeCalculators is free software: you may copy, redistribute and/or modify it +** under the terms of the GNU Affero General Public License as published by the +** Free Software Foundation, either version 3 of the License, or (at your +** option) any later version. +** +** This file is distributed in the hope that it will be useful, but +** WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +** General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +** +****************************************************************************/ + +#pragma once + +#include +#include +#include +#include +#include + +namespace zc { + +struct SubstrInfo; + +namespace parsing { + namespace tokens { + + struct Variable; + struct Number; + struct Text; + + } // namespace tokens + + namespace node { + + struct InputVariable; + struct GlobalConstant; + + template + struct GlobalVariable; + + using Number = zc::parsing::tokens::Number; + + namespace rpn { + + struct Function; + struct Sequence; + struct CppBinaryFunction; + struct CppUnaryFunction; + + using GlobalVariable = node::GlobalVariable; + + using Node = std::variant; + + } // namespace rpn + + namespace ast { + + template + struct Function; + + template + struct Sequence; + + template + struct CppBinaryFunction; + + template + struct CppUnaryFunction; + + template + using Node = std::variant, + CppBinaryFunction, + GlobalConstant, + Function, + GlobalVariable, + Sequence>; + + template + struct NodePtr; + + } // namespace ast + + } // namespace node + + /// @brief A tree representation in an AST or RPN world + /// @note when the math world is RPN based, this Tree is simply an intermediate form + /// before being transformed into an RPN representation + template + using Tree = node::ast::NodePtr; + + using RPN = std::vector; + + template + using Parsing = std::conditional_t, RPN>; + + template + requires(is_any_of, + node::ast::Node, + node::rpn::Node>) + parsing::tokens::Text text_token(const NodeType& token); + + template + requires(is_any_of, + node::ast::Node, + node::rpn::Node>) + SubstrInfo substr_info(const NodeType& token); + + } // namespace parsing +} // namespace zc diff --git a/zecalculator/parsing/data_structures/impl/node.h b/zecalculator/parsing/data_structures/impl/node.h new file mode 100644 index 0000000..abb52ba --- /dev/null +++ b/zecalculator/parsing/data_structures/impl/node.h @@ -0,0 +1,205 @@ +/**************************************************************************** +** Copyright (c) 2023, Adel Kara Slimane +** +** This file is part of ZeCalculator. +** +** ZeCalculators is free software: you may copy, redistribute and/or modify it +** under the terms of the GNU Affero General Public License as published by the +** Free Software Foundation, either version 3 of the License, or (at your +** option) any later version. +** +** This file is distributed in the hope that it will be useful, but +** WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +** General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +** +****************************************************************************/ + +#pragma once + +#include +#include +#include +#include +#include +#include +#include + +namespace zc { + namespace parsing { + namespace node { + + using zc::parsing::tokens::Text; + + struct InputVariable: Text + { + InputVariable(const Text& txt, size_t index) + : Text(txt), index(index) {} + + size_t index; + }; + + struct GlobalConstant: Text + { + GlobalConstant(const Text& txt, const zc::GlobalConstant& constant) + : Text(txt), constant(constant) {} + + const zc::GlobalConstant& constant; + }; + + template + struct GlobalVariable: Text + { + GlobalVariable(const Text& txt, const zc::GlobalVariable& var) + : Text(txt), var(var) {} + + const zc::GlobalVariable& var; + }; + + namespace rpn { + + using parsing::Type::RPN; + + struct Function: Text + { + Function(const Text& txt, const zc::Function& f) + : Text(txt), f(f) {} + + const zc::Function& f; + }; + + struct Sequence: Text + { + Sequence(const Text& txt, const zc::Sequence& u) + : Text(txt), u(u) {} + + const zc::Sequence& u; + }; + + struct CppBinaryFunction: Text + { + CppBinaryFunction(const Text& txt, const zc::CppBinaryFunction& f) + : Text(txt), f(f) {} + + const zc::CppBinaryFunction& f; + }; + + struct CppUnaryFunction: Text + { + CppUnaryFunction(const Text& txt, const zc::CppUnaryFunction& f) + : Text(txt), f(f) {} + + const zc::CppUnaryFunction& f; + }; + + } // namespace rpn + + namespace ast { + + using parsing::Type::AST; + + template + struct NodePtr: std::unique_ptr> + { + using Parent = std::unique_ptr>; + + NodePtr() : Parent(std::make_unique>()) {} + + template + requires(not std::is_same_v, NodePtr> + and std::is_convertible_v>) + NodePtr(T&& val) : Parent(std::make_unique>(std::forward(val))) + {} + + NodePtr(Parent&& parent_rval) + : Parent(std::move(parent_rval)) + {} + + NodePtr(NodePtr&&) = default; + NodePtr& operator = (NodePtr&&) = default; + + bool operator == (const NodePtr& other) const + { + assert(*this and other); // cannot be nullptrs, invariant of this class + return **this == *other; + } + }; + + template + struct Function: Text + { + Function(const Text& txt, const zc::Function& f, std::vector> operands) + : Text(txt), f(f), operands(std::move(operands)) {} + + const zc::Function& f; + std::vector> operands; + }; + + template + struct Sequence: Text + { + Sequence(const Text& txt, const zc::Sequence& u, NodePtr operand) + : Text(txt), u(u), operand(std::move(operand)) {} + + const zc::Sequence& u; + NodePtr operand; + }; + + template + struct CppBinaryFunction: rpn::CppBinaryFunction + { + CppBinaryFunction(const Text& txt, + const zc::CppBinaryFunction& f, + NodePtr operand1, + NodePtr operand2) + : rpn::CppBinaryFunction(txt, f), operand1(std::move(operand1)), + operand2(std::move(operand2)) + {} + + NodePtr operand1, operand2; + }; + + template + struct CppUnaryFunction: rpn::CppUnaryFunction + { + CppUnaryFunction(const Text& txt, + const zc::CppUnaryFunction& f, + NodePtr operand) + : rpn::CppUnaryFunction(txt, f), operand(std::move(operand)) + {} + + NodePtr operand; + }; + + } // namespace rpn + } // namespace node + + template + requires(is_any_of, + node::ast::Node, + node::rpn::Node>) + inline parsing::tokens::Text text_token(const NodeType& token) + { + return std::visit(overloaded{[](const std::monostate&) -> parsing::tokens::Text + { return parsing::tokens::Text(); }, + [](const auto& tk) -> parsing::tokens::Text { return tk; }}, + token); + } + + template + requires(is_any_of, + node::ast::Node, + node::rpn::Node>) + inline SubstrInfo substr_info(const NodeType& token) + { + return text_token(token).substr_info; + + } + + } // namespace parsing +} // namespace zc diff --git a/zecalculator/parsing/data_structures/node.h b/zecalculator/parsing/data_structures/node.h new file mode 100644 index 0000000..04414cd --- /dev/null +++ b/zecalculator/parsing/data_structures/node.h @@ -0,0 +1,24 @@ +#pragma once + +/**************************************************************************** +** Copyright (c) 2023, Adel Kara Slimane +** +** This file is part of ZeCalculator. +** +** ZeCalculators is free software: you may copy, redistribute and/or modify it +** under the terms of the GNU Affero General Public License as published by the +** Free Software Foundation, either version 3 of the License, or (at your +** option) any later version. +** +** This file is distributed in the hope that it will be useful, but +** WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +** General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +** +****************************************************************************/ + +#include +#include diff --git a/zecalculator/parsing/data_structures/token.h b/zecalculator/parsing/data_structures/token.h new file mode 100644 index 0000000..9ec8c4f --- /dev/null +++ b/zecalculator/parsing/data_structures/token.h @@ -0,0 +1,171 @@ +/**************************************************************************** +** Copyright (c) 2023, Adel Kara Slimane +** +** This file is part of ZeCalculator. +** +** ZeCalculators is free software: you may copy, redistribute and/or modify it +** under the terms of the GNU Affero General Public License as published by the +** Free Software Foundation, either version 3 of the License, or (at your +** option) any later version. +** +** This file is distributed in the hope that it will be useful, but +** WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +** General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +** +****************************************************************************/ + +#pragma once + +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace zc { +namespace parsing { + +namespace tokens { + +struct Text +{ + Text() = default; + + Text(std::string_view substr, std::string_view original_expr) + : name(std::string(substr)), substr_info(SubstrInfo::from_views(substr, original_expr)) + {} + + Text(std::string_view name, size_t begin, size_t size) : name(std::string(name)), substr_info{begin, size} {} + + Text(std::string_view name, SubstrInfo substr_info) : name(std::string(name)), substr_info(substr_info) {} + + /// @brief name of the token, can different from what appears in the expressions + /// @example '+' is replaced with 'internal::plus' (a valid function name) + std::string name = {}; + + /// @brief information about the location of the original token with the original expression + /// @example token '+' in '2+2*2' will have: begin=1, size=1 + SubstrInfo substr_info = {}; + + bool operator == (const Text& other) const = default; +}; + +struct Unkown: Text +{ + explicit Unkown(const Text& txt) : Text(txt) {} + using Text::Text; +}; + +struct Number: Text +{ + Number(double value, const tokens::Text& text_token): Text(text_token), value(value) {} + double value = std::nan(""); +}; + +struct Variable: Text +{ + explicit Variable(const Text& txtTok): Text(txtTok) {} + using Text::Text; +}; + +struct Function: Text +{ + explicit Function(const Text& txtTok): Text(txtTok) {} + using Text::Text; +}; + +struct Operator: Function +{ + using pair_type = std::pair; + // operators ordered in increasing order of priority + static constexpr std::array operators = {{{'+', "internal::plus"}, + {'-', "internal::minus"}, + {'*', "internal::multiply"}, + {'/', "internal::divide"}, + {'^', "internal::power"}}}; + + constexpr static std::string_view name_of(char op) + { + assert(std::ranges::count(operators, op, &pair_type::first) == 1); + return std::ranges::find(operators, op, &pair_type::first)->second; + } + + constexpr static bool is_operator(const char ch) + { + return std::ranges::any_of( + operators, [&ch](const char op) { return op == ch; }, &pair_type::first); + } + + Operator(char op, size_t begin): Function(name_of(op), begin, 1) {} + + Operator(std::string_view op_v, std::string_view original_expr) + : Function(name_of(op_v.front()), SubstrInfo::from_views(op_v, original_expr)) + {} +}; + +struct OpeningParenthesis: Text +{ + using Text::Text; +}; + +struct ClosingParenthesis: Text +{ + using Text::Text; +}; + +struct FunctionCallStart: Text +{ + using Text::Text; +}; + +struct FunctionCallEnd: Text +{ + using Text::Text; +}; + +struct FunctionArgumentSeparator: Text +{ + using Text::Text; +}; + +struct EndOfExpression: Text // will be used only to signal errors +{ + using Text::Text; +}; + +} + +/// @brief represents a in a parsed expression +/// @example an operatr '+', a function name 'cos', a variable 'x', a number '-3.14E+2' +using TokenType = + std::variant; + +struct Token: TokenType +{ + using TokenType::TokenType; +}; + +inline tokens::Text text_token(const Token& token) +{ + return std::visit([](const auto& tk) -> tokens::Text { return tk; }, token); +} + +inline SubstrInfo substr_info(const Token& token) +{ + return text_token(token).substr_info; +} + +} +} diff --git a/zecalculator/parsing/decl/parser.h b/zecalculator/parsing/decl/parser.h new file mode 100644 index 0000000..d42576d --- /dev/null +++ b/zecalculator/parsing/decl/parser.h @@ -0,0 +1,67 @@ +/**************************************************************************** +** Copyright (c) 2023, Adel Kara Slimane +** +** This file is part of ZeCalculator. +** +** ZeCalculators is free software: you may copy, redistribute and/or modify it +** under the terms of the GNU Affero General Public License as published by the +** Free Software Foundation, either version 3 of the License, or (at your +** option) any later version. +** +** This file is distributed in the hope that it will be useful, but +** WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +** General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +** +****************************************************************************/ + +#pragma once + +#include +#include +#include +#include + +#include + +/* TODO: update approach as the following: + - Parse: aka cut each atom in a formula + - Evaluate type of atom: separator, number + - Treat number in a special as to make 1.2E+33 as one atom + - Check for validity + - Enable setting custom names for functions and variables + - Performance improvement: flatten trees +*/ + +namespace zc { +namespace parsing { + +/// @brief interprets "view" as a floating number +/// @returns if successful, the interpreted double and the number of characters interpreted, otherwise empty +std::optional> to_double(std::string_view view); + +/// @brief parses the expression into a list of tokens +/// @note the string that is void must remain valid for for the returned instance +/// to remain valid (for both a successful or unsuccessful parsing) +/// as they contain sub-string views of the input view +tl::expected, Error> tokenize(std::string_view expression); + +/// @brief tells if the string_view contains a valid math object name +bool is_valid_name(std::string_view name); + +/// @brief makes a syntax tree from from a sequence of tokens +/// @param input_vars: variable names that are considered as input (for functions) +/// e.g."x" in the function "f" such as "f(x) = cos(x)" +template +tl::expected, Error> make_tree(std::span tokens, + const MathWorld& math_world, + const std::vector& input_vars = {}); + +/// @brief transforms a syntax tree to a flat Reverse Polish / postfix notation representation +RPN make_RPN(const Tree& tree); + +} +} diff --git a/zecalculator/parsing/impl/parser.h b/zecalculator/parsing/impl/parser.h new file mode 100644 index 0000000..05cffdc --- /dev/null +++ b/zecalculator/parsing/impl/parser.h @@ -0,0 +1,578 @@ +#pragma once + +/**************************************************************************** +** Copyright (c) 2023, Adel Kara Slimane +** +** This file is part of ZeCalculator. +** +** ZeCalculators is free software: you may copy, redistribute and/or modify it +** under the terms of the GNU Affero General Public License as published by the +** Free Software Foundation, either version 3 of the License, or (at your +** option) any later version. +** +** This file is distributed in the hope that it will be useful, but +** WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +** General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +** +****************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +namespace zc { +namespace parsing { + +inline std::optional> to_double(std::string_view view) +{ + std::optional> result = std::make_pair(0.0, 0); + char* charAfter = const_cast(view.data()); // char that comes right after the number + result->first = std::strtod(view.data(), &charAfter); + + if (charAfter == view.data() or result->first == HUGE_VAL) + result.reset(); + else result->second = size_t(charAfter - view.data()); + + return result; +} + +tl::expected, Error> tokenize(std::string_view expression) +{ + const std::string_view orig_expr = expression; + std::vector parsing; + + auto is_seperator = [](const char ch) { + static constexpr std::array separators = {'+', '-', '*', '/', '^', ' ', '(', ')'}; + return std::ranges::any_of(separators, [&ch](const char op){ return op == ch; }); + }; + + auto is_digit = [](const char ch) + { + return std::isdigit(static_cast(ch)); + }; + + auto is_argument_separator = [](const char ch) + { + return ch == ',' or ch == ';'; + }; + + bool openingParenthesis = true, numberSign = true, value = true, canEnd = false, + ope = false, closingParenthesis = false; + + enum : bool { FUNCTION_CALL_PTH, NORMAL_PTH}; + std::stack last_opened_pth; + + auto it = expression.cbegin(); + while (it != expression.cend()) + { + const std::optional next_char = it+1 != expression.cend() ? *(it+1) : std::optional(); + + // view on the single character pointed to by 'it' + const std::string_view char_v = std::string_view(it, 1); + + if (is_digit(*it) or (numberSign and *it == '-' and next_char and is_digit(next_char.value()))) + { + auto double_val = to_double(std::string_view(it, expression.cend())); + + if (double_val) + { + const auto& [double_opt_val, processed_char_num] = *double_val; + const std::string_view val_str_v(it, processed_char_num); + // parsing successful + parsing.push_back(tokens::Number(double_opt_val, tokens::Text(val_str_v, orig_expr))); + it += processed_char_num; + + openingParenthesis = value = numberSign = false; + ope = canEnd = closingParenthesis = true; + } + else + return tl::unexpected(Error::wrong_format( + tokens::Number(std::nan(""), tokens::Text(char_v, orig_expr)))); + } + else if (tokens::Operator::is_operator(*it)) + { + if (ope) + { + parsing.push_back(tokens::Operator(char_v, orig_expr)); + + openingParenthesis = value = true; + ope = numberSign = closingParenthesis = canEnd = false; + it++; + } + else return tl::unexpected(Error::unexpected(tokens::Operator(char_v, orig_expr))); + } + else if (*it == '(') + { + if (openingParenthesis) + { + if (not parsing.empty() and std::holds_alternative(parsing.back())) + { + parsing.emplace_back(tokens::FunctionCallStart(char_v, orig_expr)); + last_opened_pth.push(FUNCTION_CALL_PTH); + } + else + { + parsing.emplace_back(tokens::OpeningParenthesis(char_v, orig_expr)); + last_opened_pth.push(NORMAL_PTH); + } + + numberSign = value = openingParenthesis = true; + ope = closingParenthesis = canEnd = false; + it++; + } + else return tl::unexpected(Error::unexpected(tokens::OpeningParenthesis(char_v, orig_expr))); + } + else if (*it == ')') + { + if (closingParenthesis and not last_opened_pth.empty()) + { + if (last_opened_pth.top() == FUNCTION_CALL_PTH) + { + parsing.emplace_back(tokens::FunctionCallEnd(char_v, orig_expr)); + } + else parsing.emplace_back(tokens::ClosingParenthesis(char_v, orig_expr)); + + last_opened_pth.pop(); + + ope = canEnd = closingParenthesis = true; + value = numberSign = openingParenthesis = false; + it++; + } + else return tl::unexpected(Error::unexpected(tokens::ClosingParenthesis(char_v, orig_expr))); + } + else if (*it == ' ') + // spaces are skipped + it++; + else if (is_argument_separator(*it)) + { + if (not last_opened_pth.empty() and last_opened_pth.top() == FUNCTION_CALL_PTH) + parsing.emplace_back(tokens::FunctionArgumentSeparator(char_v, orig_expr)); + else return tl::unexpected(Error::unexpected(tokens::FunctionArgumentSeparator(char_v, orig_expr))); + + openingParenthesis = numberSign = value = true; + canEnd = ope = closingParenthesis = false; + it++; + } + else + { + if (value) + { + // the only possibilities left are variables and functions + // to differentiate between them, functions always are followed by an opening parenthesis + // e.g. "cos(" + + const auto token_begin = it; + while (it != expression.cend() and + not is_seperator(*it) and + not is_argument_separator(*it)) { it++; } + + const std::string_view token_v(token_begin, it); + + if (it == expression.cend() or *it != '(') + { + // can only be a variable when we reach the end of the expression + parsing.emplace_back(tokens::Variable(token_v, orig_expr)); + + openingParenthesis = numberSign = value = false; + canEnd = ope = closingParenthesis = true; + } + else + { + parsing.emplace_back(tokens::Function(token_v, orig_expr)); + + canEnd = closingParenthesis = ope = numberSign = value = false; + openingParenthesis = true; + } + } + else return tl::unexpected(Error::unexpected(tokens::Unkown(char_v, orig_expr))); + } + } + + if (not last_opened_pth.empty()) + { + std::string_view expr_cend = std::string_view(it, 0); + if (last_opened_pth.top() == FUNCTION_CALL_PTH) + return tl::unexpected(Error::missing(tokens::FunctionCallEnd(expr_cend, orig_expr))); + else return tl::unexpected(Error::missing(tokens::ClosingParenthesis(expr_cend, orig_expr))); + } + + if (not canEnd) + { + std::string_view expr_cend = std::string_view(it, 0); + return tl::unexpected(Error::unexpected(tokens::EndOfExpression(expr_cend, orig_expr))); + } + + return parsing; +} + + +bool is_valid_name(std::string_view name) +{ + auto parsing = tokenize(name); + return parsing and parsing->size() == 1 + and std::holds_alternative(parsing->front()); +} + +tl::expected< + std::vector::iterator>, + Error +> get_non_pth_enclosed_tokens(std::span tokens) +{ + std::vector::iterator> non_pth_enclosed_tokens; + + enum : bool { FUNCTION_CALL_PTH, NORMAL_PTH}; + std::stack last_opened_pth; + + // search for parentheses + for (auto tokenIt = tokens.begin() ; tokenIt != tokens.end() ; tokenIt++) + { + if (std::holds_alternative(*tokenIt)) + { + last_opened_pth.push(NORMAL_PTH); + } + else if (std::holds_alternative(*tokenIt)) + { + last_opened_pth.push(FUNCTION_CALL_PTH); + } + else if (std::holds_alternative(*tokenIt)) + { + if (not last_opened_pth.empty() and last_opened_pth.top() == FUNCTION_CALL_PTH) + last_opened_pth.pop(); + else return tl::unexpected(Error::unexpected(text_token(*tokenIt))); + } + else if (std::holds_alternative(*tokenIt)) + { + if (not last_opened_pth.empty() and last_opened_pth.top() == NORMAL_PTH) + last_opened_pth.pop(); + else return tl::unexpected(Error::unexpected(text_token(*tokenIt))); + } + // if not a parenthesis, and the token is not enclosed within parentheses, push it + else if (last_opened_pth.empty()) + non_pth_enclosed_tokens.push_back(tokenIt); + } + + return non_pth_enclosed_tokens; +} + +/// @brief functor that maps a MathWorld::ConstDynMathObject to tl::expected +template +struct VariableVisiter +{ + using Ret = tl::expected, Error>; + + const tokens::Text& var_txt_token; + + Ret operator()(cref global_constant) + { + return std::make_unique>( + node::GlobalConstant(var_txt_token, global_constant)); + } + Ret operator()(cref> global_variable) + { + return std::make_unique>( + node::GlobalVariable(var_txt_token, global_variable)); + } + Ret operator()(MathWorld::UnregisteredObject) + { + return tl::unexpected(Error::undefined_variable(var_txt_token)); + } + Ret operator()(auto&&) + { + return tl::unexpected(Error::wrong_object_type(var_txt_token)); + } +}; + +template +struct FunctionVisiter +{ + using Ret = tl::expected, Error>; + + const tokens::Text& func_txt_token; + std::vector> subnodes; + + Ret operator()(cref f) + { + if (subnodes.size() != 1) [[unlikely]] + return tl::unexpected(Error::mismatched_fun_args(func_txt_token)); + + return std::make_unique>( + node::ast::CppUnaryFunction(func_txt_token, f.get(), std::move(subnodes.back()))); + } + Ret operator()(cref f) + { + if (subnodes.size() != 2) [[unlikely]] + return tl::unexpected(Error::mismatched_fun_args(func_txt_token)); + + return std::make_unique>( + node::ast::CppBinaryFunction(func_txt_token, + f.get(), + std::move(subnodes.front()), + std::move(subnodes.back()))); + } + Ret operator()(cref> f) + { + if (subnodes.size() != f.get().argument_size()) [[unlikely]] + return tl::unexpected(Error::mismatched_fun_args(func_txt_token)); + + return std::make_unique>( + node::ast::Function(func_txt_token, f.get(), std::move(subnodes))); + } + Ret operator()(cref> u) + { + if (subnodes.size() != 1) [[unlikely]] + return tl::unexpected(Error::mismatched_fun_args(func_txt_token)); + + return std::make_unique>( + node::ast::Sequence(func_txt_token, u.get(), std::move(subnodes.front()))); + } + Ret operator()(MathWorld::UnregisteredObject) + { + return tl::unexpected(Error::undefined_function(func_txt_token)); + } + Ret operator()(auto&&) + { + return tl::unexpected(Error::wrong_object_type(func_txt_token)); + } +}; + +template +tl::expected, Error> make_tree(std::span tokens, + const MathWorld& world, + const std::vector& input_vars) +{ + using Ret = tl::expected, Error>; + + // when there's only a single token, it can only be number or a variable + if (tokens.size() == 1) + { + return std::visit(overloaded{[&](const tokens::Number& num) -> Ret { return Tree(num); }, + [&](const tokens::Variable& var) -> Ret + { + // if variable is in 'input_vars' then treat it as such + // this will avoid name lookup when we evaluate + auto it = std::ranges::find(input_vars, var.name); + if (it != input_vars.end()) + // the index is computed with the distance between begin() and 'it' + return Tree( + node::InputVariable(var, + std::distance(input_vars.begin(), it))); + else + return std::visit(VariableVisiter{var}, + world.get(var.name)); + }, + [&](const auto& anything_else) -> Ret + { return tl::unexpected(Error::unexpected(anything_else)); }}, + tokens.back()); + } + + auto expected_non_pth_wrapped_tokens = get_non_pth_enclosed_tokens(tokens); + if (not expected_non_pth_wrapped_tokens.has_value()) + return tl::unexpected(expected_non_pth_wrapped_tokens.error()); + + const auto& non_pth_enclosed_tokens = expected_non_pth_wrapped_tokens.value(); + + // expression of the type "(...)" + if (non_pth_enclosed_tokens.empty() and tokens.size() > 2 and + std::holds_alternative(tokens.front()) and + std::holds_alternative(tokens.back())) + { + return make_tree(std::span(tokens.begin()+1, tokens.end()-1), world, input_vars); + } + + // expression of the type "function(...)" + else if (non_pth_enclosed_tokens.size() == 1 and tokens.size() > 3 and + std::holds_alternative(tokens.front()) and + std::holds_alternative(*(tokens.begin()+1)) and + std::holds_alternative(tokens.back())) + { + /// @todo needs changing to support multi-argument functions + /// @note here we expect functions that receive that receive a single argument + auto non_pth_wrapped_args = get_non_pth_enclosed_tokens( + std::span(tokens.begin() + 2, tokens.end() - 1)); + + if (not non_pth_wrapped_args.has_value()) + return tl::unexpected(non_pth_wrapped_args.error()); + + // add the FunctionCallEnd token so we handle it in the loop + non_pth_wrapped_args->push_back(tokens.end()-1); + + std::vector> subnodes; + auto last_non_coma_token_it = tokens.begin()+2; + for (auto tokenIt: *non_pth_wrapped_args) + { + if (std::holds_alternative(*tokenIt) or + std::holds_alternative(*tokenIt)) + { + auto expected_func_argument = make_tree(std::span(last_non_coma_token_it, tokenIt), world, input_vars); + if (not expected_func_argument.has_value()) + return expected_func_argument; + else subnodes.push_back(std::move(expected_func_argument.value())); + last_non_coma_token_it = tokenIt+1; + } + } + + const auto func_txt_token = text_token(tokens.front()); + return std::visit(FunctionVisiter{func_txt_token, std::move(subnodes)}, + world.get(func_txt_token.name)); + + // return ast::node::Function(text_token(tokens.front()), std::move(subnodes)); + } + + // there are tokens that are not within parentheses + else if (not non_pth_enclosed_tokens.empty()) + { + // we check for operations + // loop through the expression by increasing operator priority + // -> because the deepest parts of the syntax tree are to be calculated first + for (const auto& [op, op_str]: tokens::Operator::operators) + { + for (auto tokenIt: non_pth_enclosed_tokens) + { + if (std::holds_alternative(*tokenIt) and + std::get(*tokenIt).name == op_str) + { + // we are not within parentheses, and we are at the right operator priority + if (tokenIt == tokens.begin() or tokenIt+1 == tokens.end()) + return tl::unexpected(Error::unexpected(text_token(*tokenIt))); + + auto left_hand_side = make_tree(std::span(tokens.begin(), tokenIt), world, input_vars); + if (not left_hand_side.has_value()) + return left_hand_side; + + auto right_hand_side = make_tree(std::span(tokenIt+1, tokens.end()), world, input_vars); + if (not right_hand_side.has_value()) + return right_hand_side; + + const zc::CppBinaryFunction* cpp_bin_f = world.template get(op_str); + + assert(cpp_bin_f); + + return node::ast::CppBinaryFunction(text_token(*tokenIt), + *cpp_bin_f, + std::move(left_hand_side.value()), + std::move(right_hand_side.value())); + } + } + } + } + + // if we reach the end of this function, something is not right + const SubstrInfo substrinfo + = std::accumulate(tokens.begin(), + tokens.end(), + substr_info(tokens.front()), + [](const SubstrInfo& info, const Token& t1) + { return substr_info(t1) + info; }); + + return tl::unexpected(Error::unexpected(tokens::Unkown("", substrinfo))); +} + +struct RpnMaker +{ + RPN operator () (std::monostate) + { + return RPN{std::monostate()}; + } + + static bool check_for_monostate(const RPN& rpn) + { + return std::ranges::any_of(rpn, + [](const node::rpn::Node& node) + { return std::holds_alternative(node); }); + } + + RPN operator()(const node::ast::CppUnaryFunction& func) + { + RPN res = std::visit(*this, *func.operand); + + if (check_for_monostate(res)) [[unlikely]] + return RPN{std::monostate()}; + + res.push_back(func); + return res; + } + + RPN operator()(const node::ast::Sequence& seq) + { + RPN res = std::visit(*this, *seq.operand); + + if (check_for_monostate(res)) [[unlikely]] + return RPN{std::monostate()}; + + res.push_back(node::rpn::Sequence(seq, seq.u)); + return res; + } + + RPN operator()(const node::ast::CppBinaryFunction& func) + { + RPN res = std::visit(*this, *func.operand1); + RPN tmp = std::visit(*this, *func.operand2); + + std::ranges::move(tmp, std::back_inserter(res)); + + if (check_for_monostate(res)) [[unlikely]] + return RPN{std::monostate()}; + + res.push_back(func); + return res; + } + + RPN operator () (const node::ast::Function& func) + { + RPN res; + for (const Tree& sub_node : func.operands) + { + RPN tmp = std::visit(*this, *sub_node); + if (check_for_monostate(tmp)) [[unlikely]] + return RPN{std::monostate()}; + else [[likely]] + std::ranges::move(tmp, std::back_inserter(res)); + } + + res.push_back(node::rpn::Function(func, func.f)); + return res; + } + + RPN operator () (const node::InputVariable& in_var) + { + return RPN(1, in_var); + } + + RPN operator () (const node::GlobalConstant& var) + { + return RPN(1, var); + } + + RPN operator () (const node::GlobalVariable& var) + { + return RPN(1, node::rpn::GlobalVariable(var, var.var)); + } + + RPN operator () (const node::Number& number) + { + return RPN(1, number); + } + +}; + +RPN make_RPN(const Tree& tree) +{ + return std::visit(RpnMaker{}, *tree); +} + +} // namespace parsing +} // namespace zc diff --git a/zecalculator/parsing/parser.h b/zecalculator/parsing/parser.h new file mode 100644 index 0000000..44c90c6 --- /dev/null +++ b/zecalculator/parsing/parser.h @@ -0,0 +1,24 @@ +#pragma once + +/**************************************************************************** +** Copyright (c) 2023, Adel Kara Slimane +** +** This file is part of ZeCalculator. +** +** ZeCalculators is free software: you may copy, redistribute and/or modify it +** under the terms of the GNU Affero General Public License as published by the +** Free Software Foundation, either version 3 of the License, or (at your +** option) any later version. +** +** This file is distributed in the hope that it will be useful, but +** WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +** General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +** +****************************************************************************/ + +#include +#include diff --git a/zecalculator/parsing/shared.h b/zecalculator/parsing/shared.h new file mode 100644 index 0000000..041a13c --- /dev/null +++ b/zecalculator/parsing/shared.h @@ -0,0 +1,29 @@ +#pragma once + +/**************************************************************************** +** Copyright (c) 2023, Adel Kara Slimane +** +** This file is part of ZeCalculator. +** +** ZeCalculators is free software: you may copy, redistribute and/or modify it +** under the terms of the GNU Affero General Public License as published by the +** Free Software Foundation, either version 3 of the License, or (at your +** option) any later version. +** +** This file is distributed in the hope that it will be useful, but +** WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +** General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +** +****************************************************************************/ + +namespace zc { +namespace parsing { + +enum struct Type {AST, RPN}; + +} +} diff --git a/zecalculator/utils/name_map.h b/zecalculator/utils/name_map.h new file mode 100644 index 0000000..5216dec --- /dev/null +++ b/zecalculator/utils/name_map.h @@ -0,0 +1,43 @@ +#pragma once + +/**************************************************************************** +** Copyright (c) 2023, Adel Kara Slimane +** +** This file is part of ZeCalculator's source code. +** +** ZeCalculators is free software: you may copy, redistribute and/or modify it +** under the terms of the GNU Affero General Public License as published by the +** Free Software Foundation, either version 3 of the License, or (at your +** option) any later version. +** +** This file is distributed in the hope that it will be useful, but +** WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +** General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +** +****************************************************************************/ + +#include +#include +#include + +namespace zc { + +struct string_hash +{ + using hash_type = std::hash; + using is_transparent = void; + + std::size_t operator()(const char* str) const { return hash_type{}(str); } + std::size_t operator()(std::string_view str) const { return hash_type{}(str); } + std::size_t operator()(std::string const& str) const { return hash_type{}(str); } +}; + +/// @brief a map that accepts searching a string_view key +template +using name_map = std::unordered_map>; + +} diff --git a/zecalculator/utils/refs.h b/zecalculator/utils/refs.h new file mode 100644 index 0000000..0ec6127 --- /dev/null +++ b/zecalculator/utils/refs.h @@ -0,0 +1,13 @@ +#pragma once + +#include + +namespace zc { + +template +using cref = std::reference_wrapper; + +template +using ref = std::reference_wrapper; + +} diff --git a/zecalculator/utils/slotted_deque.h b/zecalculator/utils/slotted_deque.h new file mode 100644 index 0000000..af03301 --- /dev/null +++ b/zecalculator/utils/slotted_deque.h @@ -0,0 +1,95 @@ +#pragma once + +/**************************************************************************** +** Copyright (c) 2023, Adel Kara Slimane +** +** This file is part of ZeCalculator's source code. +** +** ZeCalculators is free software: you may copy, redistribute and/or modify it +** under the terms of the GNU Affero General Public License as published by the +** Free Software Foundation, either version 3 of the License, or (at your +** option) any later version. +** +** This file is distributed in the hope that it will be useful, but +** WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +** General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +** +****************************************************************************/ + +#include +#include +#include +#include + +namespace zc { + +/// @brief a vector of T, where each element keeps its index +/// during its whole lifetime +template +class SlottedDeque: protected std::deque> +{ + using Parent = std::deque>; + +public: + SlottedDeque() = default; + + using Parent::size; + + /// @brief finds the a free slot, puts 'val' in it, then returns the slot index + size_t push(T val) + { + if (free_slots.empty()) + { + const size_t slot = size(); + Parent::push_back(std::move(val)); + return slot; + } + else + { + const size_t slot = free_slots.top(); + free_slots.pop(); + Parent::operator[](slot).emplace(std::move(val)); + return slot; + } + } + + ///@brief frees the slot 'slot' + void pop(const size_t slot) + { + (*this)[slot].reset(); + free_slots.push(slot); + } + + /// @brief returns the element T at 'slot', bounds checked + const T& at(size_t slot) const + { + return Parent::at(slot).value(); + } + + /// @brief returns the element T at 'slot', bounds checked + T& at(size_t slot) + { + return Parent::at(slot).value(); + } + + /// @brief returns the element T at 'slot' + const T& operator [] (size_t slot) const + { + return *Parent::operator [] (slot); + } + + /// @brief returns the element T at 'slot' + T& operator [] (size_t slot) + { + return *Parent::operator [] (slot); + } + +protected: + std::stack free_slots; +}; + +} diff --git a/zecalculator/utils/substr_info.h b/zecalculator/utils/substr_info.h new file mode 100644 index 0000000..ed3ecfa --- /dev/null +++ b/zecalculator/utils/substr_info.h @@ -0,0 +1,87 @@ +#pragma once + +/**************************************************************************** +** Copyright (c) 2023, Adel Kara Slimane +** +** This file is part of ZeCalculator's source code. +** +** ZeCalculators is free software: you may copy, redistribute and/or modify it +** under the terms of the GNU Affero General Public License as published by the +** Free Software Foundation, either version 3 of the License, or (at your +** option) any later version. +** +** This file is distributed in the hope that it will be useful, but +** WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +** General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +** +****************************************************************************/ + +#include +#include + +namespace zc { + + +/// @brief This class is some kind of meta std::string_view +/// it contains the beginning index and the size of a substring +/// without saving a reference/pointer/copy to the original string +/// a substring can be retrieved by giving it the original expression +struct SubstrInfo +{ + constexpr static SubstrInfo from_views(std::string_view substr, std::string_view full_str) + { + assert(full_str.data() <= substr.data() + and substr.data() + substr.size() <= full_str.data() + full_str.size()); + + return SubstrInfo{.begin = size_t(substr.data() - full_str.data()), .size = substr.size()}; + }; + + /// @brief returns the substring this class is refering to within the full string 'full_str' + /// @example *this == {2, 3}, full_str = "2+cos(2)" returns "cos" + /// @note if the full_str isn't long enough (wrong string given by the end-user), returns empty string_view + std::string_view substr(std::string_view full_str) const + { + if (begin + size > full_str.size()) + return std::string_view(); + return full_str.substr(begin, size); + } + + /// @brief returns the substring that is before substr(full_str) + /// @example *this == {2, 3}, full_str = "2+cos(3)" returns "2+" + std::string_view substr_before(std::string_view full_str) const + { + return full_str.substr(0, begin); + } + + /// @brief returns the substring that is after substr(full_str) + /// @example *this == {2, 3}, full_str = "2+cos(3)" returns "(3)" + std::string_view substr_after(std::string_view full_str) const + { + if (begin + size > full_str.size()) + return std::string_view(); + else return full_str.substr(begin + size); + } + + ///@brief begin position in the original string + size_t begin = 0; + + ///@brief substring size + size_t size = 0; + + bool operator == (const SubstrInfo& other) const = default; +}; + +inline SubstrInfo operator + (const SubstrInfo& a, const SubstrInfo& b) +{ + const size_t begin = std::min(a.begin, b.begin); + return SubstrInfo + { + .begin = begin, .size = std::max(a.begin + a.size, b.begin + b.size) - begin + }; +} + +} diff --git a/zecalculator/utils/tuple.h b/zecalculator/utils/tuple.h new file mode 100644 index 0000000..72feea9 --- /dev/null +++ b/zecalculator/utils/tuple.h @@ -0,0 +1,102 @@ +#pragma once + +#include +#include +#include + +namespace zc { + + +// ================== to_variant + +/// @brief transforms an std::tuple to an std::variant +template +struct to_variant; + +template +struct to_variant> +{ + using type = std::variant; +}; + +template +using to_variant_t = to_variant::type; + +// ================== to_variant + +// ================== tuple_map + +/// @brief transforms std::tuple to std::tuple::type, ...> +template