From 08f56460d078c914677bfd9de91d43ba8293cf3a Mon Sep 17 00:00:00 2001 From: Blake-Madden <66873089+Blake-Madden@users.noreply.github.com> Date: Sun, 23 Nov 2025 06:13:13 -0500 Subject: [PATCH 01/17] clang-tidy cleanup --- tinyexpr.cpp | 345 ++++++++++++++++++++++++--------------------------- tinyexpr.h | 103 ++++++++------- 2 files changed, 216 insertions(+), 232 deletions(-) diff --git a/tinyexpr.cpp b/tinyexpr.cpp index 78b0e99..70e3f6e 100644 --- a/tinyexpr.cpp +++ b/tinyexpr.cpp @@ -49,6 +49,8 @@ #include "tinyexpr.h" +// NOLINTBEGIN(readability-redundant-casting,readability-avoid-nested-conditional-operator) + // builtin functions namespace te_builtins { @@ -213,7 +215,7 @@ namespace te_builtins /// @warning This version of round emulates Excel's behavior of supporting /// negative decimal places (e.g., ROUND(21.5, -1) = 20). Be aware - /// of that if using this function outside of TinyExpr++. + /// of that if using this function outside TinyExpr++. [[nodiscard]] static te_type te_round(te_type val, te_type decimalPlaces) // NOLINT { @@ -222,8 +224,8 @@ namespace te_builtins 0 : static_cast(std::abs(decimalPlaces)) }; - const auto decimalPostition = static_cast(std::pow(10, adjustedDecimalPlaces)); - if (!std::isfinite(decimalPostition)) + const auto decimalPosition = static_cast(std::pow(10, adjustedDecimalPlaces)); + if (!std::isfinite(decimalPosition)) { return te_parser::te_nan; } @@ -233,24 +235,24 @@ namespace te_builtins { if (val < 0) { - return (decimalPostition == 0) ? + return (decimalPosition == 0) ? std::ceil(val - ROUND_EPSILON) : - std::ceil(static_cast(val * decimalPostition) - ROUND_EPSILON) / - decimalPostition; + std::ceil(static_cast(val * decimalPosition) - ROUND_EPSILON) / + decimalPosition; } - return (decimalPostition == 0) ? + return (decimalPosition == 0) ? std::floor(val + ROUND_EPSILON) : - std::floor(static_cast(val * decimalPostition) + ROUND_EPSILON) / - decimalPostition; + std::floor(static_cast(val * decimalPosition) + ROUND_EPSILON) / + decimalPosition; } // ROUND(21.5, -1) = 20 if (val < 0) { - return std::ceil(static_cast(val / decimalPostition) - ROUND_EPSILON) * - decimalPostition; + return std::ceil(static_cast(val / decimalPosition) - ROUND_EPSILON) * + decimalPosition; } - return std::floor(static_cast(val / decimalPostition) + ROUND_EPSILON) * - decimalPostition; + return std::floor(static_cast(val / decimalPosition) + ROUND_EPSILON) * + decimalPosition; } [[nodiscard]] @@ -264,36 +266,39 @@ namespace te_builtins } [[nodiscard]] - static te_type te_effect(te_type nomicalRate, te_type periods) + static te_type te_effect(te_type nominalRate, te_type periods) { - if (periods < 1 || nomicalRate <= 0) + if (periods < 1 || nominalRate <= 0) { return te_parser::te_nan; } - return std::pow(1 + (nomicalRate / periods), periods) - 1; + return std::pow(1 + (nominalRate / periods), periods) - 1; } [[nodiscard]] static te_type te_asset_depreciation(te_type cost, te_type salvage, te_type life, te_type period, te_type month) { + constexpr te_type NUM_OF_MONTHS{ 12 }; + constexpr te_type FRAC_TO_PERCENT{ 100 }; // month in the first year of depreciation is optional and defaults to a full year if (!std::isfinite(month)) { - month = 12; + month = NUM_OF_MONTHS; } - if (month < 1 || month > 12 || life <= 0 || cost <= 0 || period < 1 || period >= (life + 2)) + if (month < 1 || month > NUM_OF_MONTHS || life <= 0 || cost <= 0 || period < 1 || + period >= (life + 2)) { return te_parser::te_nan; } - te_type intPrefix; - te_type mantissa = std::modf(life, &intPrefix) * 100; + te_type intPrefix{ 0 }; + te_type mantissa = std::modf(life, &intPrefix) * FRAC_TO_PERCENT; if (mantissa > 0) { return te_parser::te_nan; } - mantissa = std::modf(period, &intPrefix) * 100; + mantissa = std::modf(period, &intPrefix) * FRAC_TO_PERCENT; if (mantissa > 0) { return te_parser::te_nan; @@ -310,22 +315,20 @@ namespace te_builtins const auto rate = te_round(1 - (std::pow((salvage / cost), (1 / life))), 3); if (period == 1) { - return cost * rate * (month / 12); + return cost * rate * (month / NUM_OF_MONTHS); } - else + te_type priorDepreciation{ 0.0 }; + te_type costAfterDepreciation{ cost }; + for (uint64_t i = 1; i < static_cast(period) - 1; ++i) { - te_type priorDepreciation{ 0.0 }; - te_type costAfterDepreciation{ cost }; - for (uint64_t i = 1; i < static_cast(period) - 1; ++i) - { - auto depreciation = (costAfterDepreciation * rate); - priorDepreciation += depreciation; - costAfterDepreciation -= depreciation; - } - priorDepreciation += costAfterDepreciation * rate * (month / 12); - return (period == life + 1) ? ((cost - priorDepreciation) * rate * (12 - month)) / 12 : - (cost - priorDepreciation) * rate; + const auto depreciation = (costAfterDepreciation * rate); + priorDepreciation += depreciation; + costAfterDepreciation -= depreciation; } + priorDepreciation += costAfterDepreciation * rate * (month / NUM_OF_MONTHS); + return (period == life + 1) ? + ((cost - priorDepreciation) * rate * (NUM_OF_MONTHS - month)) / NUM_OF_MONTHS : + (cost - priorDepreciation) * rate; } [[nodiscard]] @@ -580,7 +583,7 @@ namespace te_builtins { return te_parser::te_nan; } - if (val1 > ((std::numeric_limits::max)()) || + if (val1 > (std::numeric_limits::max)() || val2 > (std::numeric_limits::max)()) { return std::numeric_limits::infinity(); @@ -594,7 +597,7 @@ namespace te_builtins } for (decltype(usignR) i = 1; i <= usignR; i++) { - if (result > ((std::numeric_limits::max)()) / (usignN - usignR + i)) + if (result > (std::numeric_limits::max)() / (usignN - usignR + i)) { return std::numeric_limits::infinity(); } @@ -822,14 +825,12 @@ namespace te_builtins { return te_right_rotate64(val1, val2); } - else if constexpr (te_parser::supports_32bit()) + if constexpr (te_parser::supports_32bit()) { return te_right_rotate32(val1, val2); } - else - { - return te_right_rotate16(val1, val2); - } + + return te_right_rotate16(val1, val2); } //-------------------------------------------------- @@ -955,14 +956,12 @@ namespace te_builtins { return te_bitwise_not64(val); } - else if constexpr (te_parser::supports_32bit()) + if constexpr (te_parser::supports_32bit()) { return te_bitwise_not32(val); } - else - { - return te_bitwise_not16(val); - } + + return te_bitwise_not16(val); } //-------------------------------------------------- @@ -1064,8 +1063,8 @@ namespace te_builtins std::to_string(MAX_BITNESS_PARAM)); } - const auto multipler = (static_cast(1) << static_cast(val2)); - const auto maxBaseNumber = (std::numeric_limits::max() / multipler); + const auto multiplier = (static_cast(1) << static_cast(val2)); + const auto maxBaseNumber = (std::numeric_limits::max() / multiplier); if (static_cast(val1) > maxBaseNumber) { throw std::runtime_error( @@ -1110,7 +1109,7 @@ namespace te_builtins } /// @warning This emulates Excel, where a negative shift amount acts as a right shift.\n - /// Be aware of this if using this function outside of TinyExpr++. + /// Be aware of this if using this function outside TinyExpr++. //-------------------------------------------------- [[nodiscard]] static te_type te_left_shift_or_right(te_type val1, te_type val2) @@ -1119,7 +1118,7 @@ namespace te_builtins } /// @warning This emulates Excel, where a negative shift amount acts as a right shift.\n - /// Be aware of this if using this function outside of TinyExpr++. + /// Be aware of this if using this function outside TinyExpr++. //-------------------------------------------------- [[nodiscard]] static te_type te_right_shift_or_left(te_type val1, te_type val2) @@ -1521,7 +1520,7 @@ const std::set te_parser::m_functions = { // NOLINT }; //-------------------------------------------------- -void te_parser::next_token(te_parser::state* theState) +void te_parser::next_token(state* theState) { assert(theState); if (theState == nullptr) @@ -1529,13 +1528,13 @@ void te_parser::next_token(te_parser::state* theState) return; } - theState->m_type = te_parser::state::token_type::TOK_NULL; + theState->m_type = state::token_type::TOK_NULL; do // NOLINT { if (*theState->m_next == 0) { - theState->m_type = te_parser::state::token_type::TOK_END; + theState->m_type = state::token_type::TOK_END; return; } @@ -1552,7 +1551,7 @@ void te_parser::next_token(te_parser::state* theState) theState->m_value = static_cast(std::strtod(theState->m_next, &nEnd)); #endif theState->m_next = nEnd; - theState->m_type = te_parser::state::token_type::TOK_NUMBER; + theState->m_type = state::token_type::TOK_NUMBER; } else { @@ -1638,7 +1637,7 @@ void te_parser::next_token(te_parser::state* theState) if (!m_varFound) { - theState->m_type = te_parser::state::token_type::TOK_ERROR; + theState->m_type = state::token_type::TOK_ERROR; } else { @@ -1656,24 +1655,24 @@ void te_parser::next_token(te_parser::state* theState) if (is_constant(m_currentVar->m_value)) { - theState->m_type = te_parser::state::token_type::TOK_NUMBER; + theState->m_type = state::token_type::TOK_NUMBER; theState->m_value = m_currentVar->m_value; } else if (is_variable(m_currentVar->m_value)) { - theState->m_type = te_parser::state::token_type::TOK_VARIABLE; + theState->m_type = state::token_type::TOK_VARIABLE; theState->m_value = m_currentVar->m_value; } else if (is_function(m_currentVar->m_value)) { - theState->m_type = te_parser::state::token_type::TOK_FUNCTION; + theState->m_type = state::token_type::TOK_FUNCTION; theState->m_varType = m_currentVar->m_type; theState->m_value = m_currentVar->m_value; } else if (is_closure(m_currentVar->m_value)) { theState->context = m_currentVar->m_context; - theState->m_type = te_parser::state::token_type::TOK_FUNCTION; + theState->m_type = state::token_type::TOK_FUNCTION; theState->m_varType = m_currentVar->m_type; theState->m_value = m_currentVar->m_value; } @@ -1686,58 +1685,58 @@ void te_parser::next_token(te_parser::state* theState) std::advance(theState->m_next, 1); if (tok == '+') { - theState->m_type = te_parser::state::token_type::TOK_INFIX; + theState->m_type = state::token_type::TOK_INFIX; theState->m_value = te_builtins::te_add; } else if (tok == '-') { - theState->m_type = te_parser::state::token_type::TOK_INFIX; + theState->m_type = state::token_type::TOK_INFIX; theState->m_value = te_builtins::te_sub; } #ifndef TE_FLOAT else if (tok == '~') { - theState->m_type = te_parser::state::token_type::TOK_INFIX; + theState->m_type = state::token_type::TOK_INFIX; theState->m_value = te_builtins::te_bitwise_not; } #else else if (tok == '~') { - theState->m_type = te_parser::state::token_type::TOK_ERROR; + theState->m_type = state::token_type::TOK_ERROR; } #endif else if (tok == '*' && (*theState->m_next == '*')) { - theState->m_type = te_parser::state::token_type::TOK_INFIX; + theState->m_type = state::token_type::TOK_INFIX; theState->m_value = static_cast(te_builtins::te_pow); std::advance(theState->m_next, 1); } else if (tok == '*') { - theState->m_type = te_parser::state::token_type::TOK_INFIX; + theState->m_type = state::token_type::TOK_INFIX; theState->m_value = te_builtins::te_mul; } else if (tok == '/') { - theState->m_type = te_parser::state::token_type::TOK_INFIX; + theState->m_type = state::token_type::TOK_INFIX; theState->m_value = te_builtins::te_divide; } #if defined(TE_BITWISE_OPERATORS) && !defined(TE_FLOAT) else if (tok == '^') { - theState->m_type = te_parser::state::token_type::TOK_INFIX; + theState->m_type = state::token_type::TOK_INFIX; theState->m_value = static_cast(te_builtins::te_bitwise_xor); } #else else if (tok == '^') { - theState->m_type = te_parser::state::token_type::TOK_INFIX; + theState->m_type = state::token_type::TOK_INFIX; theState->m_value = static_cast(te_builtins::te_pow); } #endif else if (tok == '%') { - theState->m_type = te_parser::state::token_type::TOK_INFIX; + theState->m_type = state::token_type::TOK_INFIX; theState->m_value = te_builtins::te_modulus; } #ifdef TE_BRACKETS_AS_PARENS @@ -1746,7 +1745,7 @@ void te_parser::next_token(te_parser::state* theState) else if (tok == '(') #endif { - theState->m_type = te_parser::state::token_type::TOK_OPEN; + theState->m_type = state::token_type::TOK_OPEN; } #ifdef TE_BRACKETS_AS_PARENS else if (tok == ')' || tok == ']') @@ -1754,25 +1753,25 @@ void te_parser::next_token(te_parser::state* theState) else if (tok == ')') #endif { - theState->m_type = te_parser::state::token_type::TOK_CLOSE; + theState->m_type = state::token_type::TOK_CLOSE; } else if (tok == get_list_separator()) { - theState->m_type = te_parser::state::token_type::TOK_SEP; + theState->m_type = state::token_type::TOK_SEP; } #if __cplusplus >= 202002L && !defined(TE_FLOAT) // rotate (circular shift) operators (uses the 64-bit integer version) else if (tok == '<' && (*theState->m_next == '<') && (*std::next(theState->m_next) == '<')) { - theState->m_type = te_parser::state::token_type::TOK_INFIX; + theState->m_type = state::token_type::TOK_INFIX; theState->m_value = static_cast(te_builtins::te_left_rotate); std::advance(theState->m_next, 2); } else if (tok == '>' && (*theState->m_next == '>') && (*std::next(theState->m_next) == '>')) { - theState->m_type = te_parser::state::token_type::TOK_INFIX; + theState->m_type = state::token_type::TOK_INFIX; theState->m_value = static_cast(te_builtins::te_right_rotate); std::advance(theState->m_next, 2); } @@ -1783,20 +1782,20 @@ void te_parser::next_token(te_parser::state* theState) (tok == '>' && (*theState->m_next == '>') && (*std::next(theState->m_next) == '>'))) { - theState->m_type = te_parser::state::token_type::TOK_ERROR; + theState->m_type = state::token_type::TOK_ERROR; } #endif #ifndef TE_FLOAT // shift operators else if (tok == '<' && (*theState->m_next == '<')) { - theState->m_type = te_parser::state::token_type::TOK_INFIX; + theState->m_type = state::token_type::TOK_INFIX; theState->m_value = static_cast(te_builtins::te_left_shift); std::advance(theState->m_next, 1); } else if (tok == '>' && (*theState->m_next == '>')) { - theState->m_type = te_parser::state::token_type::TOK_INFIX; + theState->m_type = state::token_type::TOK_INFIX; theState->m_value = static_cast(te_builtins::te_right_shift); std::advance(theState->m_next, 1); } @@ -1805,90 +1804,90 @@ void te_parser::next_token(te_parser::state* theState) else if ((tok == '<' && (*theState->m_next == '<')) || (tok == '>' && (*theState->m_next == '>'))) { - theState->m_type = te_parser::state::token_type::TOK_ERROR; + theState->m_type = state::token_type::TOK_ERROR; } #endif // logical operators else if (tok == '=' && (*theState->m_next == '=')) { - theState->m_type = te_parser::state::token_type::TOK_INFIX; + theState->m_type = state::token_type::TOK_INFIX; theState->m_value = static_cast(te_builtins::te_equal); std::advance(theState->m_next, 1); } else if (tok == '=') { - theState->m_type = te_parser::state::token_type::TOK_INFIX; + theState->m_type = state::token_type::TOK_INFIX; theState->m_value = static_cast(te_builtins::te_equal); } else if (tok == '!' && (*theState->m_next == '=')) // NOLINT { - theState->m_type = te_parser::state::token_type::TOK_INFIX; + theState->m_type = state::token_type::TOK_INFIX; theState->m_value = static_cast(te_builtins::te_not_equal); std::advance(theState->m_next, 1); } else if (tok == '<' && (*theState->m_next == '>')) { - theState->m_type = te_parser::state::token_type::TOK_INFIX; + theState->m_type = state::token_type::TOK_INFIX; theState->m_value = static_cast(te_builtins::te_not_equal); std::advance(theState->m_next, 1); } else if (tok == '<' && (*theState->m_next == '=')) { - theState->m_type = te_parser::state::token_type::TOK_INFIX; + theState->m_type = state::token_type::TOK_INFIX; theState->m_value = static_cast(te_builtins::te_less_than_equal_to); std::advance(theState->m_next, 1); } else if (tok == '<') { - theState->m_type = te_parser::state::token_type::TOK_INFIX; + theState->m_type = state::token_type::TOK_INFIX; theState->m_value = static_cast(te_builtins::te_less_than); } else if (tok == '>' && (*theState->m_next == '=')) { - theState->m_type = te_parser::state::token_type::TOK_INFIX; + theState->m_type = state::token_type::TOK_INFIX; theState->m_value = static_cast(te_builtins::te_greater_than_equal_to); std::advance(theState->m_next, 1); } else if (tok == '>') { - theState->m_type = te_parser::state::token_type::TOK_INFIX; + theState->m_type = state::token_type::TOK_INFIX; theState->m_value = static_cast(te_builtins::te_greater_than); } else if (tok == '&' && (*theState->m_next == '&')) { - theState->m_type = te_parser::state::token_type::TOK_INFIX; + theState->m_type = state::token_type::TOK_INFIX; theState->m_value = static_cast(te_builtins::te_and); std::advance(theState->m_next, 1); } #if defined(TE_BITWISE_OPERATORS) && !defined(TE_FLOAT) else if (tok == '&') { - theState->m_type = te_parser::state::token_type::TOK_INFIX; + theState->m_type = state::token_type::TOK_INFIX; theState->m_value = static_cast(te_builtins::te_bitwise_and); } #else else if (tok == '&') { - theState->m_type = te_parser::state::token_type::TOK_INFIX; + theState->m_type = state::token_type::TOK_INFIX; theState->m_value = static_cast(te_builtins::te_and); } #endif else if (tok == '|' && (*theState->m_next == '|')) { - theState->m_type = te_parser::state::token_type::TOK_INFIX; + theState->m_type = state::token_type::TOK_INFIX; theState->m_value = static_cast(te_builtins::te_or); std::advance(theState->m_next, 1); } #if defined(TE_BITWISE_OPERATORS) && !defined(TE_FLOAT) else if (tok == '|') { - theState->m_type = te_parser::state::token_type::TOK_INFIX; + theState->m_type = state::token_type::TOK_INFIX; theState->m_value = static_cast(te_builtins::te_bitwise_or); } #else else if (tok == '|') { - theState->m_type = te_parser::state::token_type::TOK_INFIX; + theState->m_type = state::token_type::TOK_INFIX; theState->m_value = static_cast(te_builtins::te_or); } #endif @@ -1897,52 +1896,52 @@ void te_parser::next_token(te_parser::state* theState) } else { - theState->m_type = te_parser::state::token_type::TOK_ERROR; + theState->m_type = state::token_type::TOK_ERROR; } } } - } while (theState->m_type == te_parser::state::token_type::TOK_NULL); + } while (theState->m_type == state::token_type::TOK_NULL); } //-------------------------------------------------- -te_expr* te_parser::base(te_parser::state* theState) +te_expr* te_parser::base(state* theState) { /* = | | {"(" ")"} | | "(" {"," } ")" | "(" ")" */ te_expr* ret{ nullptr }; - if (theState->m_type == te_parser::state::token_type::TOK_OPEN) + if (theState->m_type == state::token_type::TOK_OPEN) { next_token(theState); ret = list(theState); - if (theState->m_type != te_parser::state::token_type::TOK_CLOSE) + if (theState->m_type != state::token_type::TOK_CLOSE) { - theState->m_type = te_parser::state::token_type::TOK_ERROR; + theState->m_type = state::token_type::TOK_ERROR; } else { next_token(theState); } } - else if (theState->m_type == te_parser::state::token_type::TOK_NUMBER) + else if (theState->m_type == state::token_type::TOK_NUMBER) { ret = new_expr(TE_DEFAULT, theState->m_value); next_token(theState); } - else if (theState->m_type == te_parser::state::token_type::TOK_VARIABLE) + else if (theState->m_type == state::token_type::TOK_VARIABLE) { ret = new_expr(TE_DEFAULT, theState->m_value); next_token(theState); } - else if (theState->m_type == te_parser::state::token_type::TOK_NULL || - theState->m_type == te_parser::state::token_type::TOK_ERROR || - theState->m_type == te_parser::state::token_type::TOK_END || - theState->m_type == te_parser::state::token_type::TOK_SEP || - theState->m_type == te_parser::state::token_type::TOK_CLOSE || - theState->m_type == te_parser::state::token_type::TOK_INFIX) + else if (theState->m_type == state::token_type::TOK_NULL || + theState->m_type == state::token_type::TOK_ERROR || + theState->m_type == state::token_type::TOK_END || + theState->m_type == state::token_type::TOK_SEP || + theState->m_type == state::token_type::TOK_CLOSE || + theState->m_type == state::token_type::TOK_INFIX) { ret = new_expr(TE_DEFAULT, te_variant_type{ te_nan }); - theState->m_type = te_parser::state::token_type::TOK_ERROR; + theState->m_type = state::token_type::TOK_ERROR; } else if (is_function0(theState->m_value) || is_closure0(theState->m_value)) { @@ -1952,12 +1951,12 @@ te_expr* te_parser::base(te_parser::state* theState) ret->m_parameters[0] = theState->context; } next_token(theState); - if (theState->m_type == te_parser::state::token_type::TOK_OPEN) + if (theState->m_type == state::token_type::TOK_OPEN) { next_token(theState); - if (theState->m_type != te_parser::state::token_type::TOK_CLOSE) + if (theState->m_type != state::token_type::TOK_CLOSE) { - theState->m_type = te_parser::state::token_type::TOK_ERROR; + theState->m_type = state::token_type::TOK_ERROR; } else { @@ -1986,9 +1985,9 @@ te_expr* te_parser::base(te_parser::state* theState) } next_token(theState); - if (theState->m_type != te_parser::state::token_type::TOK_OPEN) + if (theState->m_type != state::token_type::TOK_OPEN) { - theState->m_type = te_parser::state::token_type::TOK_ERROR; + theState->m_type = state::token_type::TOK_ERROR; } else { @@ -2002,20 +2001,19 @@ te_expr* te_parser::base(te_parser::state* theState) { next_token(theState); ret->m_parameters[i] = expr_level1(theState); - if (theState->m_type != te_parser::state::token_type::TOK_SEP) + if (theState->m_type != state::token_type::TOK_SEP) { break; } } - if (theState->m_type == te_parser::state::token_type::TOK_CLOSE && (i != arity - 1) && - varValid && is_variadic(openingVar->m_type)) + if (theState->m_type == state::token_type::TOK_CLOSE && (i != arity - 1) && varValid && + is_variadic(openingVar->m_type)) { next_token(theState); } - else if (theState->m_type != te_parser::state::token_type::TOK_CLOSE || - (i != arity - 1)) + else if (theState->m_type != state::token_type::TOK_CLOSE || (i != arity - 1)) { - theState->m_type = te_parser::state::token_type::TOK_ERROR; + theState->m_type = state::token_type::TOK_ERROR; } else { @@ -2028,12 +2026,12 @@ te_expr* te_parser::base(te_parser::state* theState) } //-------------------------------------------------- -te_expr* te_parser::list(te_parser::state* theState) +te_expr* te_parser::list(state* theState) { /* = {"," } */ te_expr* ret = expr_level1(theState); - while (theState->m_type == te_parser::state::token_type::TOK_SEP) + while (theState->m_type == state::token_type::TOK_SEP) { next_token(theState); ret = new_expr(TE_PURE, te_variant_type(te_builtins::te_comma), @@ -2045,15 +2043,14 @@ te_expr* te_parser::list(te_parser::state* theState) // Operator precedence, lowest to highest: //-------------------------------------------------- -te_expr* te_parser::expr_level1(te_parser::state* theState) +te_expr* te_parser::expr_level1(state* theState) { /* = {(logic operations) } */ // These are the lowest of operator precedence // (once we have split tokens into arguments) te_expr* ret = expr_level2(theState); - while (theState->m_type == te_parser::state::token_type::TOK_INFIX && - is_function2(theState->m_value) && + while (theState->m_type == state::token_type::TOK_INFIX && is_function2(theState->m_value) && get_function2(theState->m_value) == te_builtins::te_or) { const te_fun2 func = get_function2(theState->m_value); @@ -2065,14 +2062,13 @@ te_expr* te_parser::expr_level1(te_parser::state* theState) } //-------------------------------------------------- -te_expr* te_parser::expr_level2(te_parser::state* theState) +te_expr* te_parser::expr_level2(state* theState) { /* = {(logic operations) } */ // next to lowest in precedence... te_expr* ret = expr_level3(theState); - while (theState->m_type == te_parser::state::token_type::TOK_INFIX && - is_function2(theState->m_value) && + while (theState->m_type == state::token_type::TOK_INFIX && is_function2(theState->m_value) && get_function2(theState->m_value) == te_builtins::te_and) { const te_fun2 func = get_function2(theState->m_value); @@ -2084,14 +2080,13 @@ te_expr* te_parser::expr_level2(te_parser::state* theState) } //-------------------------------------------------- -te_expr* te_parser::expr_level3(te_parser::state* theState) +te_expr* te_parser::expr_level3(state* theState) { /* = {(logic operations) } */ // next to lowest in precedence... te_expr* ret = expr_level4(theState); - while (theState->m_type == te_parser::state::token_type::TOK_INFIX && - is_function2(theState->m_value) && + while (theState->m_type == state::token_type::TOK_INFIX && is_function2(theState->m_value) && get_function2(theState->m_value) == te_builtins::te_bitwise_or) { const te_fun2 func = get_function2(theState->m_value); @@ -2103,14 +2098,13 @@ te_expr* te_parser::expr_level3(te_parser::state* theState) } //-------------------------------------------------- -te_expr* te_parser::expr_level4(te_parser::state* theState) +te_expr* te_parser::expr_level4(state* theState) { /* = {(logic operations) } */ // next to lowest in precedence... te_expr* ret = expr_level5(theState); - while (theState->m_type == te_parser::state::token_type::TOK_INFIX && - is_function2(theState->m_value) && + while (theState->m_type == state::token_type::TOK_INFIX && is_function2(theState->m_value) && get_function2(theState->m_value) == te_builtins::te_bitwise_xor) { const te_fun2 func = get_function2(theState->m_value); @@ -2122,14 +2116,13 @@ te_expr* te_parser::expr_level4(te_parser::state* theState) } //-------------------------------------------------- -te_expr* te_parser::expr_level5(te_parser::state* theState) +te_expr* te_parser::expr_level5(state* theState) { /* = {(logic operations) } */ // next to lowest in precedence... te_expr* ret = expr_level6(theState); - while (theState->m_type == te_parser::state::token_type::TOK_INFIX && - is_function2(theState->m_value) && + while (theState->m_type == state::token_type::TOK_INFIX && is_function2(theState->m_value) && get_function2(theState->m_value) == te_builtins::te_bitwise_and) { const te_fun2 func = get_function2(theState->m_value); @@ -2141,14 +2134,13 @@ te_expr* te_parser::expr_level5(te_parser::state* theState) } //-------------------------------------------------- -te_expr* te_parser::expr_level6(te_parser::state* theState) +te_expr* te_parser::expr_level6(state* theState) { /* = {(logic operations) } */ // next to lowest in precedence... te_expr* ret = expr_level7(theState); - while (theState->m_type == te_parser::state::token_type::TOK_INFIX && - is_function2(theState->m_value) && + while (theState->m_type == state::token_type::TOK_INFIX && is_function2(theState->m_value) && (get_function2(theState->m_value) == te_builtins::te_equal || get_function2(theState->m_value) == te_builtins::te_not_equal)) { @@ -2161,13 +2153,12 @@ te_expr* te_parser::expr_level6(te_parser::state* theState) } //-------------------------------------------------- -te_expr* te_parser::expr_level7(te_parser::state* theState) +te_expr* te_parser::expr_level7(state* theState) { /* = {(comparison operators) } */ te_expr* ret = expr_level8(theState); - while (theState->m_type == te_parser::state::token_type::TOK_INFIX && - is_function2(theState->m_value) && + while (theState->m_type == state::token_type::TOK_INFIX && is_function2(theState->m_value) && (get_function2(theState->m_value) == te_builtins::te_less_than || get_function2(theState->m_value) == te_builtins::te_less_than_equal_to || get_function2(theState->m_value) == te_builtins::te_greater_than || @@ -2182,13 +2173,12 @@ te_expr* te_parser::expr_level7(te_parser::state* theState) } //-------------------------------------------------- -te_expr* te_parser::expr_level8(te_parser::state* theState) +te_expr* te_parser::expr_level8(state* theState) { /* = {("<<" | ">>") } */ te_expr* ret = expr_level9(theState); - while (theState->m_type == te_parser::state::token_type::TOK_INFIX && - is_function2(theState->m_value) && + while (theState->m_type == state::token_type::TOK_INFIX && is_function2(theState->m_value) && (get_function2(theState->m_value) == te_builtins::te_left_shift || get_function2(theState->m_value) == te_builtins::te_right_shift #if __cplusplus >= 202002L && !defined(TE_FLOAT) @@ -2212,13 +2202,12 @@ te_expr* te_parser::expr_level8(te_parser::state* theState) } //-------------------------------------------------- -te_expr* te_parser::expr_level9(te_parser::state* theState) +te_expr* te_parser::expr_level9(state* theState) { /* = {("+" | "-") } */ te_expr* ret = term(theState); - while (theState->m_type == te_parser::state::token_type::TOK_INFIX && - is_function2(theState->m_value) && + while (theState->m_type == state::token_type::TOK_INFIX && is_function2(theState->m_value) && (get_function2(theState->m_value) == te_builtins::te_add || get_function2(theState->m_value) == te_builtins::te_sub)) { @@ -2232,14 +2221,13 @@ te_expr* te_parser::expr_level9(te_parser::state* theState) // Higher levels of operator precedence: //-------------------------------------------------- -te_expr* te_parser::term(te_parser::state* theState) +te_expr* te_parser::term(state* theState) { /* = {("*" | "/" | "%") } */ - // third from highest level of operator precedence + // third from the highest level of operator precedence te_expr* ret = factor(theState); - while (theState->m_type == te_parser::state::token_type::TOK_INFIX && - is_function2(theState->m_value) && + while (theState->m_type == state::token_type::TOK_INFIX && is_function2(theState->m_value) && (get_function2(theState->m_value) == te_builtins::te_mul || get_function2(theState->m_value) == te_builtins::te_divide || get_function2(theState->m_value) == te_builtins::te_modulus)) @@ -2257,7 +2245,7 @@ te_expr* te_parser::term(te_parser::state* theState) te_expr* te_parser::factor(te_parser::state* theState) { /* = {"^" } */ - // second from highest level of operator precedence + // second from the highest level of operator precedence te_expr* ret = power(theState); int neg{ 0 }; @@ -2272,8 +2260,7 @@ te_expr* te_parser::factor(te_parser::state* theState) } te_expr* insertion{ nullptr }; - while (theState->m_type == te_parser::state::token_type::TOK_INFIX && - is_function2(theState->m_value) && + while (theState->m_type == state::token_type::TOK_INFIX && is_function2(theState->m_value) && (get_function2(theState->m_value) == static_cast(te_builtins::te_pow))) { const te_fun2 t = get_function2(theState->m_value); @@ -2301,14 +2288,13 @@ te_expr* te_parser::factor(te_parser::state* theState) return ret; } #else -te_expr* te_parser::factor(te_parser::state* theState) +te_expr* te_parser::factor(state* theState) { /* = {"^" } */ - // second from highest level of operator precedence + // second from the highest level of operator precedence te_expr* ret = power(theState); - while (theState->m_type == te_parser::state::token_type::TOK_INFIX && - is_function2(theState->m_value) && + while (theState->m_type == state::token_type::TOK_INFIX && is_function2(theState->m_value) && (get_function2(theState->m_value) == static_cast(te_builtins::te_pow))) { const te_fun2 func = get_function2(theState->m_value); @@ -2321,13 +2307,13 @@ te_expr* te_parser::factor(te_parser::state* theState) #endif //-------------------------------------------------- -te_expr* te_parser::power(te_parser::state* theState) +te_expr* te_parser::power(state* theState) { /* = {("-" | "+")} */ // highest level of operator precedence int theSign{ 1 }; bool bitwiseNot{ false }; - while (theState->m_type == te_parser::state::token_type::TOK_INFIX && + while (theState->m_type == state::token_type::TOK_INFIX && ((is_function2(theState->m_value) && (get_function2(theState->m_value) == te_builtins::te_add || get_function2(theState->m_value) == te_builtins::te_sub)) @@ -2371,15 +2357,15 @@ te_expr* te_parser::power(te_parser::state* theState) //-------------------------------------------------- // tuple-list-maker template -auto make_closure_arg_list(const F& fn, te_expr* ctx, std::index_sequence) +auto make_closure_arg_list(const F& func, te_expr* ctx, std::index_sequence) { - return std::make_tuple(ctx, fn(Indices)...); + return std::make_tuple(ctx, func(Indices)...); } template -auto make_function_arg_list(const F& fn, std::index_sequence) +auto make_function_arg_list(const F& func, std::index_sequence) { - return std::make_tuple(fn(Indices)...); + return std::make_tuple(func(Indices)...); } te_type te_parser::te_eval(const te_expr* texp) @@ -2395,26 +2381,26 @@ te_type te_parser::te_eval(const te_expr* texp) { return (e < texp->m_parameters.size()) ? te_eval(texp->m_parameters[e]) : te_nan; }; return std::visit( - [&, texp](const auto& var) -> te_type + [&, texp](const T0& var) -> te_type { - using T = std::decay_t; + using T = std::decay_t; if constexpr (te_is_constant_v) { return var; } - else if constexpr (te_is_variable_v) + if constexpr (te_is_variable_v) { return *var; } - else if constexpr (std::is_same_v) + if constexpr (std::is_same_v) { return var(); } - else if constexpr (std::is_same_v) + if constexpr (std::is_same_v) { return var(texp->m_parameters[0]); } - else if constexpr (te_is_closure_v) + if constexpr (te_is_closure_v) { constexpr size_t n_args = te_function_arity; static_assert(n_args > 0); @@ -2422,16 +2408,13 @@ te_type te_parser::te_eval(const te_expr* texp) make_closure_arg_list(M, texp->m_parameters[n_args - 1], std::make_index_sequence{})); } - else if constexpr (te_is_function_v) + if constexpr (te_is_function_v) { constexpr size_t n_args = te_function_arity; return std::apply(var, make_function_arg_list(M, std::make_index_sequence{})); } - else - { - return te_nan; - } + return te_nan; }, texp->m_value); // NOLINTEND @@ -2455,7 +2438,7 @@ void te_parser::optimize(te_expr* texp) { const auto arity = get_arity(texp->m_value); bool known{ true }; - for (std::decay::type i = 0; i < arity; ++i) + for (std::decay_t i = 0; i < arity; ++i) { if (texp->m_parameters[i] == nullptr) { @@ -2485,7 +2468,7 @@ te_expr* te_parser::te_compile(const std::string_view expression, std::set(commentStart); @@ -2548,7 +2531,7 @@ bool te_parser::compile(const std::string_view expression) // remove single-line comments else if (m_expression[commentStart + 1] == '/') { - auto commentEnd = m_expression.find_first_of("\n\r", commentStart); + const auto commentEnd = m_expression.find_first_of("\n\r", commentStart); if (commentEnd == std::string::npos) { m_expression.erase(commentStart); @@ -2693,3 +2676,5 @@ std::string te_parser::info() #endif return sysInfo; } + +// NOLINTEND(readability-redundant-casting,readability-avoid-nested-conditional-operator) diff --git a/tinyexpr.h b/tinyexpr.h index c489d2e..e90ce0f 100644 --- a/tinyexpr.h +++ b/tinyexpr.h @@ -82,8 +82,9 @@ constexpr int TINYEXPR_CPP_MAJOR_VERSION = 1; constexpr int TINYEXPR_CPP_MINOR_VERSION = 0; constexpr int TINYEXPR_CPP_PATCH_VERSION = 0; constexpr int TINYEXPR_CPP_TWEAK_VERSION = 1; -constexpr wchar_t TINYEXPR_CPP_COPYRIGHT[] = L"TinyExpr: Copyright (c) 2015-2020 Lewis Van Winkle\n" - "TinyExpr++: Copyright (c) 2020-2025 Blake Madden"; +#define TINYEXPR_CPP_COPYRIGHT \ + "TinyExpr: Copyright (c) 2015-2020 Lewis Van Winkle\n" \ + "TinyExpr++: Copyright (c) 2020-2025 Blake Madden" class te_parser; @@ -229,7 +230,7 @@ using te_usr_fun1 = std::function; using te_usr_variant_type = std::variant; // do not change the ordering of these, the indices are used to determine -// the value type of a te_variable +// the value type of te_variable using te_variant_type = std::variant= 'A' && ch <= 'Z')); + return chr + (32 * (chr >= 'A' && chr <= 'Z')); } }; @@ -299,7 +300,7 @@ class te_expr explicit te_expr(const te_variable_flags type) noexcept : m_type(type) {} /// @private - te_expr() noexcept {} + te_expr() noexcept = default; /// @private te_expr(const te_expr&) = delete; @@ -307,7 +308,7 @@ class te_expr te_expr& operator=(const te_expr&) = delete; /// @private - virtual ~te_expr() {} + virtual ~te_expr() = default; /// @brief The type that m_value represents. te_variable_flags m_type{ TE_DEFAULT }; @@ -457,12 +458,12 @@ class te_parser /** @brief Parses the input @c expression. @param expression The formula to compile. @returns Whether the expression compiled or not. (This can be checked - by calling success() afterwards as well.) + by calling success() afterward as well.) @sa success(). @note Returns NaN if division or modulus by zero occurs. @throws std::runtime_error Throws an exception in the case of arithmetic overflows (e.g., `1 << 64` would cause an overflow).*/ - bool compile(const std::string_view expression); + bool compile(std::string_view expression); /** @brief Evaluates expression passed to compile() previously and returns its result. @returns The result, or NaN on error. @throws std::runtime_error Throws an exception in the case of arithmetic overflows @@ -476,7 +477,7 @@ class te_parser @throws std::runtime_error Throws an exception in the case of arithmetic overflows (e.g., `1 << 64` would cause an overflow).*/ [[nodiscard]] - te_type evaluate(const std::string_view expression); + te_type evaluate(std::string_view expression); /// @returns The last call to evaluate()'s result (which will be NaN on error). [[nodiscard]] @@ -547,7 +548,7 @@ class te_parser /// @brief Adds a custom variable or function. /// @param var The variable/function to add. /// @note Prefer using set_variables_and_functions() as it will be more optimal - /// (less sorts will need to be performed). + /// (fewer sorts will need to be performed). /// @throws std::runtime_error Throws an exception if an illegal character is found /// in the variable name. void add_variable_or_function(te_variable var) @@ -560,7 +561,7 @@ class te_parser /// @param var The variable/function to remove (by name). void remove_variable_or_function(te_variable::name_type var) { - auto foundVar = m_customFuncsAndVars.find( + const auto foundVar = m_customFuncsAndVars.find( te_variable{ std::move(var), static_cast(0.0), TE_DEFAULT, nullptr }); if (foundVar != m_customFuncsAndVars.cend()) { @@ -570,7 +571,7 @@ class te_parser #ifndef TE_NO_BOOKKEEPING /// @brief Removes any custom variables and functions that weren't used in the last compilation. - /// @details This can be useful if the parser is pre-loaded with a large number of + /// @details This can be useful if the parser is preloaded with a large number of /// variables and functions that needs to be pruned after the first expression is parsed. /// @warning After calling this, any custom variables and functions that weren't found /// in the previously parsed expression will no longer be available. @@ -598,7 +599,7 @@ class te_parser void set_unknown_symbol_resolver(te_usr_variant_type usr, const bool keepResolvedVariables = true) { - m_unknownSymbolResolve = usr; + m_unknownSymbolResolve = std::move(usr); m_keepResolvedVariables = keepResolvedVariables; } @@ -673,7 +674,7 @@ class te_parser get_variables_and_functions().insert(std::move(nh)); // if previously compiled, then re-compile since this // constant would have been optimized - if (m_expression.length()) + if (!m_expression.empty()) { compile(m_expression); } @@ -691,14 +692,12 @@ class te_parser { return te_nan; } - if (const auto val = std::get_if(&cvar->m_value); val != nullptr) + if (const auto* const val = std::get_if(&cvar->m_value); val != nullptr) { return *val; } - else - { - return te_nan; - } + + return te_nan; } /// @returns The separator used between function arguments. @@ -743,7 +742,7 @@ class te_parser [[nodiscard]] bool is_function_used(const std::string_view name) const { - return m_usedFunctions.find(te_variable::name_type{ name }) != m_usedFunctions.cend(); + return m_usedFunctions.contains(te_variable::name_type{ name }); } /// @returns @c true if @c name is a variable that had been used in the last parsed formula. @@ -752,7 +751,7 @@ class te_parser [[nodiscard]] bool is_variable_used(const std::string_view name) const { - return m_usedVars.find(te_variable::name_type{ name }) != m_usedVars.cend(); + return m_usedVars.contains(te_variable::name_type{ name }); } #endif /// @returns A report of all available functions and variables. @@ -805,7 +804,7 @@ class te_parser /// @brief Resets any resolved variables from USR if not being cached. void reset_usr_resolved_if_necessary() { - if (!m_keepResolvedVariables && m_resolvedVariables.size()) + if (!m_keepResolvedVariables && !m_resolvedVariables.empty()) { for (const auto& resolvedVar : m_resolvedVariables) { @@ -835,7 +834,7 @@ class te_parser /// (and has a valid length). /// @param var The variable to validate. /// @throws std::runtime_error Throws an exception if an illegal character is found. - void validate_name(const te_variable& var) const + static void validate_name(const te_variable& var) { if (var.m_name.empty()) { @@ -848,7 +847,7 @@ class te_parser } const auto varCharPos = std::find_if(var.m_name.cbegin(), var.m_name.cend(), - [](const auto ch) noexcept { return !is_name_char_valid(ch); }); + [](const auto chr) noexcept { return !is_name_char_valid(chr); }); if (varCharPos != var.m_name.cend()) { throw std::runtime_error(std::string("Invalid character in variable name: ") + @@ -857,11 +856,11 @@ class te_parser } /// @returns @c true if character is valid for a function or variable name. - /// @param ch The character to review. + /// @param chr The character to review. [[nodiscard]] - constexpr static bool is_name_char_valid(const char ch) noexcept + constexpr static bool is_name_char_valid(const char chr) noexcept { - return (is_letter(ch) || (ch >= '0' && ch <= '9') || (ch == '_') || (ch == '.')); + return (is_letter(chr) || (chr >= '0' && chr <= '9') || (chr == '_') || (chr == '.')); } /// @returns An iterator to the custom variable or function with the given @c name, @@ -909,12 +908,12 @@ class te_parser /// @returns Number of parameters that a function/variable takes. [[nodiscard]] - inline static auto get_arity(const te_variant_type& var) noexcept + static auto get_arity(const te_variant_type& var) { return std::visit( - [](const auto& var_) -> size_t + [](const auto& var) -> size_t { - using T = std::decay_t; + using T = std::decay_t; if constexpr (te_is_constant_v || te_is_variable_v) { return 0; @@ -958,12 +957,12 @@ class te_parser } [[nodiscard]] - constexpr static bool is_function(const te_variant_type& var) noexcept + constexpr static bool is_function(const te_variant_type& var) { return std::visit( - [](const auto& var_) -> bool + [](const auto& var) -> bool { - using T = std::decay_t; + using T = std::decay_t; return te_is_function_v; }, var); @@ -987,12 +986,12 @@ class te_parser #undef TE_DEF_FUNCTION [[nodiscard]] - constexpr static bool is_closure(const te_variant_type& var) noexcept + constexpr static bool is_closure(const te_variant_type& var) { return std::visit( - [](const auto& var_) -> bool + [](const auto& var) -> bool { - using T = std::decay_t; + using T = std::decay_t; return te_is_closure::value; }, var); @@ -1047,34 +1046,34 @@ class te_parser }; [[nodiscard]] - inline static te_expr* new_expr(const te_variable_flags type, te_variant_type value, - const std::initializer_list& parameters) + static te_expr* new_expr(const te_variable_flags type, te_variant_type value, + const std::initializer_list& parameters) { - te_expr* ret = new te_expr{ type, std::move(value) }; + auto* ret = new te_expr{ type, value }; ret->m_parameters.resize( std::max(std::max(parameters.size(), get_arity(ret->m_value)) + (is_closure(ret->m_value) ? 1 : 0), 0)); - if (parameters.size()) + if (parameters.size() != 0U) { - std::copy(parameters.begin(), parameters.end(), ret->m_parameters.begin()); + std::ranges::copy(parameters, ret->m_parameters.begin()); } return ret; } [[nodiscard]] - inline static te_expr* new_expr(const te_variable_flags type, te_variant_type value) + static te_expr* new_expr(const te_variable_flags type, te_variant_type value) { - te_expr* ret = new te_expr{ type, std::move(value) }; + auto* ret = new te_expr{ type, value }; ret->m_parameters.resize(static_cast(get_arity(ret->m_value)) + (is_closure(ret->m_value) ? 1 : 0)); return ret; } [[nodiscard]] - constexpr static bool is_letter(const char ch) noexcept + constexpr static bool is_letter(const char chr) noexcept { - return (ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z'); + return (chr >= 'a' && chr <= 'z') || (chr >= 'A' && chr <= 'Z'); } /** @brief Parses the input expression and binds variables. @@ -1083,14 +1082,14 @@ class te_parser variables to add to the parser. @returns null on error.*/ [[nodiscard]] - te_expr* te_compile(const std::string_view expression, std::set& variables); + te_expr* te_compile(std::string_view expression, std::set& variables); /* Evaluates the expression. */ [[nodiscard]] static te_type te_eval(const te_expr* texp); /* Frees the expression. */ /* This is safe to call on null pointers. */ - inline static void te_free(te_expr* texp) + static void te_free(te_expr* texp) { if (texp == nullptr) { @@ -1111,10 +1110,10 @@ class te_parser } [[nodiscard]] - static auto find_lookup(state* s, const std::string_view name) + static auto find_lookup(state* ste, const std::string_view name) { - return s->m_lookup.find(te_variable{ te_variable::name_type{ name }, - static_cast(0.0), TE_DEFAULT, nullptr }); + return ste->m_lookup.find(te_variable{ te_variable::name_type{ name }, + static_cast(0.0), TE_DEFAULT, nullptr }); } void next_token(state* theState); From 6fa7631802f808aae52b823e51a331c38bbf5839 Mon Sep 17 00:00:00 2001 From: Blake-Madden <66873089+Blake-Madden@users.noreply.github.com> Date: Sun, 23 Nov 2025 06:20:57 -0500 Subject: [PATCH 02/17] Suppress cppcheck false positive --- tinyexpr.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tinyexpr.cpp b/tinyexpr.cpp index 70e3f6e..a4e7877 100644 --- a/tinyexpr.cpp +++ b/tinyexpr.cpp @@ -2384,6 +2384,7 @@ te_type te_parser::te_eval(const te_expr* texp) [&, texp](const T0& var) -> te_type { using T = std::decay_t; + // cppcheck-suppress-begin internalAstError if constexpr (te_is_constant_v) { return var; @@ -2414,6 +2415,7 @@ te_type te_parser::te_eval(const te_expr* texp) return std::apply(var, make_function_arg_list(M, std::make_index_sequence{})); } + // cppcheck-suppress-end internalAstError return te_nan; }, texp->m_value); From f7c7a9741c2226a36a270cd8b2e1a9eb1d43c3bd Mon Sep 17 00:00:00 2001 From: Blake-Madden <66873089+Blake-Madden@users.noreply.github.com> Date: Sun, 23 Nov 2025 06:23:31 -0500 Subject: [PATCH 03/17] Demo requires C++20 now --- tests/demo/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/demo/CMakeLists.txt b/tests/demo/CMakeLists.txt index e9c0dd2..495bb4f 100644 --- a/tests/demo/CMakeLists.txt +++ b/tests/demo/CMakeLists.txt @@ -10,7 +10,7 @@ project(TEDemo) cmake_minimum_required(VERSION 3.12) -set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD 20) set(CMAKE_CXX_STANDARD_REQUIRED True) set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_SOURCE_DIR}/bin) From a37bd02f47143f765501ccabdaba1ddf7870bca8 Mon Sep 17 00:00:00 2001 From: Blake-Madden <66873089+Blake-Madden@users.noreply.github.com> Date: Sun, 23 Nov 2025 06:26:35 -0500 Subject: [PATCH 04/17] Requires C++20 now --- README.md | 2 +- docs/manual/building.qmd | 2 +- docs/manual/index.qmd | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 66f57a9..32ad692 100644 --- a/README.md +++ b/README.md @@ -40,7 +40,7 @@ Note: for current users of *TinyExpr++*, please see the [compatibility advisory] ## Features -- **C++17 with no dependencies**. +- **C++20 with no dependencies**. - Single source file and header file. - Simple and fast. - Implements standard operator precedence. diff --git a/docs/manual/building.qmd b/docs/manual/building.qmd index 563c91b..acf88f5 100644 --- a/docs/manual/building.qmd +++ b/docs/manual/building.qmd @@ -11,6 +11,6 @@ doxygen docs/Doxyfile ## Requirements {-} -*TinyExpr++* must be compiled as C++17.\index{compiling!requirements} (Some additional features require C++20.) +*TinyExpr++* must be compiled as C++20.\index{compiling!requirements} MSVC, GCC, and Clang compilers are supported. diff --git a/docs/manual/index.qmd b/docs/manual/index.qmd index b4ce3e1..836b9cd 100644 --- a/docs/manual/index.qmd +++ b/docs/manual/index.qmd @@ -31,7 +31,7 @@ It's open-source, free, easy-to-use, and self-contained in a single source and h ## Features {-} -- C++17 with no dependencies. +- C++20 with no dependencies. - Single source file and header file. - Simple and fast. - Implements standard operator precedence. From e4a1d5fb34c0f8196a001ff8b8097b06e909a78c Mon Sep 17 00:00:00 2001 From: Blake-Madden <66873089+Blake-Madden@users.noreply.github.com> Date: Sun, 23 Nov 2025 06:29:34 -0500 Subject: [PATCH 05/17] Suppress waring about unnamed vars in template syntax --- tinyexpr.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tinyexpr.cpp b/tinyexpr.cpp index a4e7877..6c5c2be 100644 --- a/tinyexpr.cpp +++ b/tinyexpr.cpp @@ -49,7 +49,7 @@ #include "tinyexpr.h" -// NOLINTBEGIN(readability-redundant-casting,readability-avoid-nested-conditional-operator) +// NOLINTBEGIN(readability-redundant-casting,readability-avoid-nested-conditional-operator,hicpp-named-parameter,readability-named-parameter) // builtin functions namespace te_builtins @@ -2679,4 +2679,4 @@ std::string te_parser::info() return sysInfo; } -// NOLINTEND(readability-redundant-casting,readability-avoid-nested-conditional-operator) +// NOLINTEND(readability-redundant-casting,readability-avoid-nested-conditional-operator,hicpp-named-parameter,readability-named-parameter) From 7154276e7ac66d9bcac5fa5e1ac4d7a454121b99 Mon Sep 17 00:00:00 2001 From: Blake-Madden <66873089+Blake-Madden@users.noreply.github.com> Date: Sun, 23 Nov 2025 06:33:42 -0500 Subject: [PATCH 06/17] Workaround cppcheck AST parser bug --- tinyexpr.cpp | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/tinyexpr.cpp b/tinyexpr.cpp index 6c5c2be..9e9acda 100644 --- a/tinyexpr.cpp +++ b/tinyexpr.cpp @@ -2384,24 +2384,23 @@ te_type te_parser::te_eval(const te_expr* texp) [&, texp](const T0& var) -> te_type { using T = std::decay_t; - // cppcheck-suppress-begin internalAstError if constexpr (te_is_constant_v) { return var; } - if constexpr (te_is_variable_v) + else if constexpr (te_is_variable_v) { return *var; } - if constexpr (std::is_same_v) + else if constexpr (std::is_same_v) { return var(); } - if constexpr (std::is_same_v) + else if constexpr (std::is_same_v) { return var(texp->m_parameters[0]); } - if constexpr (te_is_closure_v) + else if constexpr (te_is_closure_v) { constexpr size_t n_args = te_function_arity; static_assert(n_args > 0); @@ -2409,13 +2408,12 @@ te_type te_parser::te_eval(const te_expr* texp) make_closure_arg_list(M, texp->m_parameters[n_args - 1], std::make_index_sequence{})); } - if constexpr (te_is_function_v) + else if constexpr (te_is_function_v) { constexpr size_t n_args = te_function_arity; return std::apply(var, make_function_arg_list(M, std::make_index_sequence{})); } - // cppcheck-suppress-end internalAstError return te_nan; }, texp->m_value); From d01e283f79029051139008ebad971f38dffea067 Mon Sep 17 00:00:00 2001 From: Blake-Madden <66873089+Blake-Madden@users.noreply.github.com> Date: Sun, 23 Nov 2025 06:41:00 -0500 Subject: [PATCH 07/17] Prevent narrowing warning --- tinyexpr.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tinyexpr.cpp b/tinyexpr.cpp index 9e9acda..b187fe7 100644 --- a/tinyexpr.cpp +++ b/tinyexpr.cpp @@ -938,7 +938,7 @@ namespace te_builtins { throw std::runtime_error("Bitwise NOT value must be positive."); } - if (val > std::numeric_limits::max()) + if (val > static_cast(std::numeric_limits::max())) { throw std::runtime_error("Value is too large for bitwise NOT."); } From 7230f5c48202c212fce586c4e741164c68dcdfa7 Mon Sep 17 00:00:00 2001 From: Blake-Madden <66873089+Blake-Madden@users.noreply.github.com> Date: Sun, 23 Nov 2025 06:49:37 -0500 Subject: [PATCH 08/17] Suppress cppcheck's broken AST parser warnings --- tinyexpr.cpp | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/tinyexpr.cpp b/tinyexpr.cpp index b187fe7..c465241 100644 --- a/tinyexpr.cpp +++ b/tinyexpr.cpp @@ -2368,6 +2368,7 @@ auto make_function_arg_list(const F& func, std::index_sequence) return std::make_tuple(func(Indices)...); } +// cppcheck-suppress-begin all te_type te_parser::te_eval(const te_expr* texp) { if (texp == nullptr) @@ -2376,7 +2377,6 @@ te_type te_parser::te_eval(const te_expr* texp) } // NOLINTBEGIN - // cppcheck-suppress unreadVariable const auto M = [&texp = std::as_const(texp)](const size_t e) { return (e < texp->m_parameters.size()) ? te_eval(texp->m_parameters[e]) : te_nan; }; @@ -2388,19 +2388,19 @@ te_type te_parser::te_eval(const te_expr* texp) { return var; } - else if constexpr (te_is_variable_v) + if constexpr (te_is_variable_v) { return *var; } - else if constexpr (std::is_same_v) + if constexpr (std::is_same_v) { return var(); } - else if constexpr (std::is_same_v) + if constexpr (std::is_same_v) { return var(texp->m_parameters[0]); } - else if constexpr (te_is_closure_v) + if constexpr (te_is_closure_v) { constexpr size_t n_args = te_function_arity; static_assert(n_args > 0); @@ -2408,7 +2408,7 @@ te_type te_parser::te_eval(const te_expr* texp) make_closure_arg_list(M, texp->m_parameters[n_args - 1], std::make_index_sequence{})); } - else if constexpr (te_is_function_v) + if constexpr (te_is_function_v) { constexpr size_t n_args = te_function_arity; return std::apply(var, @@ -2420,6 +2420,8 @@ te_type te_parser::te_eval(const te_expr* texp) // NOLINTEND } +// cppcheck-suppress-end all + //-------------------------------------------------- void te_parser::optimize(te_expr* texp) { From 08884c6663d74805a75fd464b06db0eb53946b2f Mon Sep 17 00:00:00 2001 From: Blake-Madden <66873089+Blake-Madden@users.noreply.github.com> Date: Sun, 23 Nov 2025 06:53:50 -0500 Subject: [PATCH 09/17] Remove cppcheck GH action template parser is broken with C++20 constructs --- .github/workflows/cppcheck.yml | 47 ---------------------------------- README.md | 1 - tinyexpr.cpp | 3 --- 3 files changed, 51 deletions(-) delete mode 100644 .github/workflows/cppcheck.yml diff --git a/.github/workflows/cppcheck.yml b/.github/workflows/cppcheck.yml deleted file mode 100644 index 636fb65..0000000 --- a/.github/workflows/cppcheck.yml +++ /dev/null @@ -1,47 +0,0 @@ -name: cppcheck -on: [push] - -concurrency: - group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} - cancel-in-progress: true - -jobs: - build: - name: cppcheck-test - runs-on: ubuntu-latest - permissions: - contents: read - steps: - - uses: actions/checkout@v4 - - - name: cppcheck - uses: deep5050/cppcheck-action@main - with: - std: c++20 - inline_suppression: enable - exclude_check: ./tests - output_file: cppcheck_report.txt - - - name: print output - run: | - REPORTFILE=./cppcheck_report.txt - WARNINGSFILE=./warnings.txt - if test -f "$REPORTFILE"; then - # Filter innocuous warnings and write the remaining ones to another file. - # Note that you can add more warnings by adding it in the parenthesis, - # with "\|" in front of it. For example, "(missingIncludeSystem\|useStlAlgorithm\)" - sed 's/\[\(missingIncludeSystem\|unmatchedSuppression\|useStlAlgorithm\)\]//g' "$REPORTFILE" > "$WARNINGSFILE" - # are there any remaining warnings? - if grep -qP '\[[a-zA-Z0-9]+\]' "$WARNINGSFILE"; then - # print the remaining warnings - echo Warnings detected: - echo ================== - cat "$WARNINGSFILE" | grep -P '\[[a-zA-Z0-9]+\]' - # fail the job - exit 1 - else - echo No warnings detected - fi - else - echo "$REPORTFILE" not found - fi diff --git a/README.md b/README.md index 32ad692..112d758 100644 --- a/README.md +++ b/README.md @@ -22,7 +22,6 @@ for a full list of features. | Code Analyses | Result | | ------------- | ------------- | -| cppcheck | [![cppcheck](https://github.com/Blake-Madden/tinyexpr-plusplus/actions/workflows/cppcheck.yml/badge.svg)](https://github.com/Blake-Madden/tinyexpr-plusplus/actions/workflows/cppcheck.yml) | | MS PREfast | [![Microsoft C++ Code Analysis](https://github.com/Blake-Madden/tinyexpr-plusplus/actions/workflows/msvc.yml/badge.svg)](https://github.com/Blake-Madden/tinyexpr-plusplus/actions/workflows/msvc.yml) | | CodeQL | [![CodeQL](https://github.com/Blake-Madden/tinyexpr-plusplus/actions/workflows/codeql.yml/badge.svg)](https://github.com/Blake-Madden/tinyexpr-plusplus/actions/workflows/codeql.yml) | | Quneiform | [![i18n-check](https://github.com/Blake-Madden/tinyexpr-plusplus/actions/workflows/i18n-check.yml/badge.svg)](https://github.com/Blake-Madden/tinyexpr-plusplus/actions/workflows/i18n-check.yml) | diff --git a/tinyexpr.cpp b/tinyexpr.cpp index c465241..91d55b9 100644 --- a/tinyexpr.cpp +++ b/tinyexpr.cpp @@ -2368,7 +2368,6 @@ auto make_function_arg_list(const F& func, std::index_sequence) return std::make_tuple(func(Indices)...); } -// cppcheck-suppress-begin all te_type te_parser::te_eval(const te_expr* texp) { if (texp == nullptr) @@ -2420,8 +2419,6 @@ te_type te_parser::te_eval(const te_expr* texp) // NOLINTEND } -// cppcheck-suppress-end all - //-------------------------------------------------- void te_parser::optimize(te_expr* texp) { From 08fabafd6a896681532439daeef1d332d3913c39 Mon Sep 17 00:00:00 2001 From: Blake-Madden <66873089+Blake-Madden@users.noreply.github.com> Date: Sun, 23 Nov 2025 11:51:02 -0500 Subject: [PATCH 10/17] Fix shadowed variables --- tinyexpr.h | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/tinyexpr.h b/tinyexpr.h index e90ce0f..c1a59b1 100644 --- a/tinyexpr.h +++ b/tinyexpr.h @@ -911,9 +911,9 @@ class te_parser static auto get_arity(const te_variant_type& var) { return std::visit( - [](const auto& var) -> size_t + [](const auto& var0) -> size_t { - using T = std::decay_t; + using T = std::decay_t; if constexpr (te_is_constant_v || te_is_variable_v) { return 0; @@ -960,9 +960,9 @@ class te_parser constexpr static bool is_function(const te_variant_type& var) { return std::visit( - [](const auto& var) -> bool + [](const auto& var0) -> bool { - using T = std::decay_t; + using T = std::decay_t; return te_is_function_v; }, var); @@ -989,9 +989,9 @@ class te_parser constexpr static bool is_closure(const te_variant_type& var) { return std::visit( - [](const auto& var) -> bool + [](const auto& var0) -> bool { - using T = std::decay_t; + using T = std::decay_t; return te_is_closure::value; }, var); From 2fbd0cfac91e90420faf02d8edf2faec99b3c1e3 Mon Sep 17 00:00:00 2001 From: Blake-Madden <66873089+Blake-Madden@users.noreply.github.com> Date: Sun, 23 Nov 2025 11:54:45 -0500 Subject: [PATCH 11/17] AddGCC/Clang build warnings to test script --- tests/CMakeLists.txt | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 04685ab..6d3cdae 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -111,6 +111,16 @@ if(TE_BUILD_TEST_RUNNER) target_compile_options(${CMAKE_PROJECT_NAME} PUBLIC /Zc:__cplusplus /MP /W3 /WX /wd4554 $<$:/Od> $<$:/O2>) endif() + elseif(CMAKE_CXX_COMPILER_ID MATCHES "Clang|GNU") + target_compile_options(${CMAKE_PROJECT_NAME} PUBLIC + -Wall -Wextra -Wpedantic -Wshadow -Werror + $<$:-O0 -g> + $<$:-O3>) + if(USE_ADDRESS_SANITIZE) + target_compile_options(${CMAKE_PROJECT_NAME} PUBLIC + -fsanitize=address -fno-omit-frame-pointer) + target_link_options(${CMAKE_PROJECT_NAME} PUBLIC -fsanitize=address) + endif() endif() target_link_libraries(${CMAKE_PROJECT_NAME} PRIVATE Catch2::Catch2) From 8272df310ff6b2d80a154558aa3a2fa4ae876f8c Mon Sep 17 00:00:00 2001 From: Blake-Madden <66873089+Blake-Madden@users.noreply.github.com> Date: Sun, 23 Nov 2025 12:02:40 -0500 Subject: [PATCH 12/17] Fix shadow var in test runner --- tests/tetests.cpp | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/tests/tetests.cpp b/tests/tetests.cpp index fc17097..b467164 100644 --- a/tests/tetests.cpp +++ b/tests/tetests.cpp @@ -3402,18 +3402,18 @@ TEST_CASE("Bitwise operators", "[bitwise]") te_parser::supports_32bit()) { val = std::numeric_limits::max(); - decltype(val) res = ~val; - CHECK(tep.evaluate("~" + std::to_string(val)) == res); + decltype(val) res2 = ~val; + CHECK(tep.evaluate("~" + std::to_string(val)) == res2); val /= 2; - res = ~val; - CHECK(tep.evaluate("~" + std::to_string(val)) == res); + res2 = ~val; + CHECK(tep.evaluate("~" + std::to_string(val)) == res2); val = 1986; - res = ~val; - CHECK(tep.evaluate("~" + std::to_string(val)) == res); - CHECK(tep.evaluate("+~1986") == res); - CHECK(tep.evaluate("~+1986") == res); + res2 = ~val; + CHECK(tep.evaluate("~" + std::to_string(val)) == res2); + CHECK(tep.evaluate("+~1986") == res2); + CHECK(tep.evaluate("~+1986") == res2); } } From 1d5577c3d546c9e359fdd650373cc1ab09907d24 Mon Sep 17 00:00:00 2001 From: Blake-Madden <66873089+Blake-Madden@users.noreply.github.com> Date: Sun, 23 Nov 2025 12:10:21 -0500 Subject: [PATCH 13/17] Suppress warnings on test script --- tests/CMakeLists.txt | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 6d3cdae..fa1d3d2 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -98,6 +98,15 @@ if(TE_BUILD_TEST_RUNNER) set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_SOURCE_DIR}/bin) add_executable(${CMAKE_PROJECT_NAME} ../tinyexpr.cpp tetests.cpp testingmain.cpp) + # Disable specific -Werror warnings only for tetests.cpp + if (CMAKE_CXX_COMPILER_ID MATCHES "Clang|GNU") + set_source_files_properties( + tetests.cpp + PROPERTIES + COMPILE_OPTIONS + "-Wno-error=parentheses" + "-Wno-error=unused-but-set-variable") + endif() if(MSVC) target_compile_definitions(${CMAKE_PROJECT_NAME} PUBLIC _DISABLE_VECTOR_ANNOTATION _DISABLE_STRING_ANNOTATION $<$:NDEBUG>) From 693582aa52c8161034ec8e582077185b315b81d7 Mon Sep 17 00:00:00 2001 From: Blake-Madden <66873089+Blake-Madden@users.noreply.github.com> Date: Sun, 23 Nov 2025 12:12:00 -0500 Subject: [PATCH 14/17] Fix test build script --- tests/CMakeLists.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index fa1d3d2..ec8b254 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -104,8 +104,8 @@ if(TE_BUILD_TEST_RUNNER) tetests.cpp PROPERTIES COMPILE_OPTIONS - "-Wno-error=parentheses" - "-Wno-error=unused-but-set-variable") + -Wno-error=parentheses + -Wno-error=unused-but-set-variable) endif() if(MSVC) target_compile_definitions(${CMAKE_PROJECT_NAME} PUBLIC _DISABLE_VECTOR_ANNOTATION _DISABLE_STRING_ANNOTATION From 2ed842aac273e299031b948cab5b14b51a166063 Mon Sep 17 00:00:00 2001 From: Blake-Madden <66873089+Blake-Madden@users.noreply.github.com> Date: Sun, 23 Nov 2025 12:17:08 -0500 Subject: [PATCH 15/17] Test script fix --- tests/CMakeLists.txt | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index ec8b254..6c80af5 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -101,11 +101,7 @@ if(TE_BUILD_TEST_RUNNER) # Disable specific -Werror warnings only for tetests.cpp if (CMAKE_CXX_COMPILER_ID MATCHES "Clang|GNU") set_source_files_properties( - tetests.cpp - PROPERTIES - COMPILE_OPTIONS - -Wno-error=parentheses - -Wno-error=unused-but-set-variable) + tetests.cpp PROPERTIES COMPILE_FLAGS "-Wno-error=parentheses -Wno-error=unused-but-set-variable") endif() if(MSVC) target_compile_definitions(${CMAKE_PROJECT_NAME} PUBLIC _DISABLE_VECTOR_ANNOTATION _DISABLE_STRING_ANNOTATION From 59d2de560c05c42b33052f127596628c00fc695f Mon Sep 17 00:00:00 2001 From: Blake-Madden <66873089+Blake-Madden@users.noreply.github.com> Date: Sun, 23 Nov 2025 12:21:14 -0500 Subject: [PATCH 16/17] More warning suppression in test script --- tests/CMakeLists.txt | 2 +- tests/tetests.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 6c80af5..89f3d6b 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -101,7 +101,7 @@ if(TE_BUILD_TEST_RUNNER) # Disable specific -Werror warnings only for tetests.cpp if (CMAKE_CXX_COMPILER_ID MATCHES "Clang|GNU") set_source_files_properties( - tetests.cpp PROPERTIES COMPILE_FLAGS "-Wno-error=parentheses -Wno-error=unused-but-set-variable") + tetests.cpp PROPERTIES COMPILE_FLAGS "-Wno-parentheses -Wno-unused-but-set-variable") endif() if(MSVC) target_compile_definitions(${CMAKE_PROJECT_NAME} PUBLIC _DISABLE_VECTOR_ANNOTATION _DISABLE_STRING_ANNOTATION diff --git a/tests/tetests.cpp b/tests/tetests.cpp index b467164..f69c1b0 100644 --- a/tests/tetests.cpp +++ b/tests/tetests.cpp @@ -3770,7 +3770,7 @@ TEST_CASE("Unknown symbol resolve lambda with capture", "[usr]") std::string str("id.temperature < 51"); te_type temperature = 49.0; te_parser parser; - parser.set_unknown_symbol_resolver([&](std::string_view symbol) + parser.set_unknown_symbol_resolver([&]([[maybe_unused]] std::string_view symbol) { return temperature += 1.0; }, false); From 348546ca914cddca0b94d0a4d10354579eb3c65b Mon Sep 17 00:00:00 2001 From: Blake-Madden <66873089+Blake-Madden@users.noreply.github.com> Date: Sun, 23 Nov 2025 12:23:00 -0500 Subject: [PATCH 17/17] More test script warning suppression --- tests/tetests.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/tetests.cpp b/tests/tetests.cpp index f69c1b0..e7a045f 100644 --- a/tests/tetests.cpp +++ b/tests/tetests.cpp @@ -3754,7 +3754,7 @@ TEST_CASE("Unknown symbol resolve funct pointer purge resolved 2", "[usr]") { std::string str("id.temperature < 51"); te_parser parser; - parser.set_unknown_symbol_resolver([](std::string_view symbol) + parser.set_unknown_symbol_resolver([]([[maybe_unused]] std::string_view symbol) { static te_type temperature = 49.0; return temperature += 1.0;