From eb4cfe7fa4bb4ce79851bf56cd7190a93548faaa Mon Sep 17 00:00:00 2001 From: Blake Madden Date: Wed, 28 Feb 2024 11:24:26 -0500 Subject: [PATCH] Add bitwise rotation functions Provides 8, 16, 32, and 64-bit functions. #13 --- TinyExprChanges.md | 12 +- docs/manual/compile-time-options.qmd | 15 +++ docs/manual/functions.qmd | 10 +- docs/manual/operators.qmd | 4 +- tests/CMakeLists.txt | 8 +- tests/tetests.cpp | 116 ++++++++++++++++++ tinyexpr.cpp | 171 ++++++++++++++++++++++++++- tinyexpr.h | 3 + 8 files changed, 328 insertions(+), 11 deletions(-) diff --git a/TinyExprChanges.md b/TinyExprChanges.md index 3dd4f84..ba33e50 100644 --- a/TinyExprChanges.md +++ b/TinyExprChanges.md @@ -34,11 +34,13 @@ The following are changes from the original TinyExpr C library: - `and`: returns true (i.e., non-zero) if all conditions are true (accepts 1-7 arguments). - `average`: returns the mean for a range of values (accepts 1-7 arguments). - `bitand`: bitwise AND. - - `bitlshift`: left shift operator. - Negative shift amount arguments (similar to *Excel*) is supported. + - `bitlrotate64`: bitwise (`uint64_t`) left rotate. (Only available if compiled as C++20.) + - `bitlshift`: left shift. + Negative shift amount arguments (similar to *Excel*) are supported. - `bitor`: bitwise OR. - - `bitrshift`: right shift operator. - Negative shift amount arguments (similar to *Excel*) is supported. + - `bitrrotate64`: bitwise (`uint64_t`) right rotate. (Only available if compiled as C++20.) + - `bitrshift`: right shift. + Negative shift amount arguments (similar to *Excel*) are supported. - `bitxor`: bitwise XOR. - `cot`: returns the cotangent of an angle. - `combin`: alias for `ncr()`, like the *Excel* function. @@ -81,6 +83,8 @@ The following are changes from the original TinyExpr C library: - `>=` greater than or equal to. - `<<` left shift operator. - `>>` right shift operator. + - `<<<` left (`uint64_t`) rotation operator. + - `>>>` right (`uint64_t`) rotation operator. - `**` exponentiation (alias for `^`). - `round` now supports negative number of digit arguments, similar to *Excel*. For example, `ROUND(-50.55,-2)` will yield `-100`. diff --git a/docs/manual/compile-time-options.qmd b/docs/manual/compile-time-options.qmd index 6fe678c..f547229 100644 --- a/docs/manual/compile-time-options.qmd +++ b/docs/manual/compile-time-options.qmd @@ -40,3 +40,18 @@ That will match how many scripting languages do it (e.g., Python, Ruby). Note that symbols can be defined by passing them to your compiler's command line (or in a Cmake configuration) as such: `-DTE_POW_FROM_RIGHT` + +## C++20 Features + +If compiling as C++20, then the following functions and operators will be available: + +- `BITLROTATE8` +- `BITLROTATE16` +- `BITLROTATE32` +- `BITLROTATE64` +- `BITRROTATE8` +- `BITRROTATE16` +- `BITRROTATE32` +- `BITRROTATE64` +- `<<<` (unsigned 64-bit left rotation) +- `>>>` (unsigned 64-bit right rotation) diff --git a/docs/manual/functions.qmd b/docs/manual/functions.qmd index 0e57f39..fbb74d5 100644 --- a/docs/manual/functions.qmd +++ b/docs/manual/functions.qmd @@ -11,9 +11,17 @@ The following built-in functions are available: | ATAN(x) | Returns the principal value of the arc tangent of *x*, expressed in radians.. | | ATAN2(y, x) | Returns the principal value of the arc tangent of *y*,*x*, expressed in radians. | | BITAND(Number1, Number2) | Returns a bitwise 'AND' of two (integral) numbers. (Both numbers must be positive.) | +| BITLROTATE8(Number, RotateAmount) | Returns *Number* left rotated left to the most significant bit by the specified number (*RotateAmount*) of bits.
\linebreak *Number* is rotated as an unsigned 8-bit integer.
\linebreak (Only available if compiled as C++20.) | +| BITLROTATE16(Number, RotateAmount) | Returns *Number* left rotated left to the most significant bit by the specified number (*RotateAmount*) of bits.
\linebreak *Number* is rotated as an unsigned 16-bit integer.
\linebreak (Only available if compiled as C++20.) | +| BITLROTATE32(Number, RotateAmount) | Returns *Number* left rotated left to the most significant bit by the specified number (*RotateAmount*) of bits.
\linebreak *Number* is rotated as an unsigned 32-bit integer.
\linebreak (Only available if compiled as C++20.) | +| BITLROTATE64(Number, RotateAmount) | Returns *Number* left rotated left to the most significant bit by the specified number (*RotateAmount*) of bits.
\linebreak *Number* is rotated as an unsigned 64-bit integer.
\linebreak Note, however, that values beyond the range of `double` should not be used as they will wrap around.
\linebreak (Only available if compiled as C++20.) | | BITLSHIFT(Number, ShiftAmount) | Returns *Number* left shifted by the specified number (*ShiftAmount*) of bits. | -| BITRSHIFT(Number, ShiftAmount) | Returns *Number* right shifted by the specified number (*ShiftAmount*) of bits. | | BITOR(Number1, Number2) | Returns a bitwise 'OR' of two (integral) numbers. (Both numbers must be positive.) | +| BITRROTATE8(Number, RotateAmount) | Returns *Number* right rotated right to the least significant bit by the specified number (*RotateAmount*) of bits.
\linebreak *Number* is rotated as an unsigned 8-bit integer.
\linebreak (Only available if compiled as C++20.) | +| BITRROTATE16(Number, RotateAmount) | Returns *Number* right rotated right to the least significant bit by the specified number (*RotateAmount*) of bits.
\linebreak *Number* is rotated as an unsigned 16-bit integer.
\linebreak (Only available if compiled as C++20.) | +| BITRROTATE32(Number, RotateAmount) | Returns *Number* right rotated right to the least significant bit by the specified number (*RotateAmount*) of bits.
\linebreak *Number* is rotated as an unsigned 32-bit integer.
\linebreak (Only available if compiled as C++20.) | +| BITRROTATE64(Number, RotateAmount) | Returns *Number* right rotated right to the least significant bit by the specified number (*RotateAmount*) of bits.
\linebreak *Number* is rotated as an unsigned 64-bit integer.
\linebreak Note, however, that values beyond the range of `double` should not be used as they will wrap around.
\linebreak (Only available if compiled as C++20.) | +| BITRSHIFT(Number, ShiftAmount) | Returns *Number* right shifted by the specified number (*ShiftAmount*) of bits. | | BITXOR(Number1, Number2) | Returns a bitwise 'XOR' of two (integral) numbers. (Both numbers must be positive.) | | CEIL(Number) | Smallest integer not less than *Number*.
\linebreak `CEIL(-3.2)` = -3
\linebreak `CEIL(3.2)` = 4 | | CLAMP(Number, Start, End) | Constrains *Number* within the range of *Start* and *End*. | diff --git a/docs/manual/operators.qmd b/docs/manual/operators.qmd index 9cbc1de..5c4fa75 100644 --- a/docs/manual/operators.qmd +++ b/docs/manual/operators.qmd @@ -27,6 +27,8 @@ The following operators\index{operators} are supported within math expressions: | ( ) | Groups sub-expressions, overriding the order of operations. | | << | Bitwise left shift. | | >> | Bitwise right shift. | +| <<< | Bitwise left rotate. (Only available if compiled as C++20.) | +| >>> | Bitwise right rotate. (Only available if compiled as C++20.) | Table: Operators ::: @@ -41,7 +43,7 @@ For operators, the order of precedence is: | ^ | Exponentiation. | | \*, /, and % | Multiplication, division, and modulus. | | \+ and - | Addition and subtraction. | -| << and >> | Bitwise left and right shift. | +| <<, >>, <<<, >>> | Bitwise left and right shift and rotation. | | <, \>, \>=, <= | Relational comparisons. | | \= and \!\= | Equality comparisons. | | && | Logical conjunction (AND). | diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index b3a2255..6a5077a 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -8,14 +8,14 @@ ############################################################################# cmake_minimum_required(VERSION 3.12) -set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD 20) set(CMAKE_CXX_STANDARD_REQUIRED True) # Options: # - USE_ADDRESS_SANITIZE to use ASAN # - USE_CLANG_TIDY to run clang-tidy # - TE_FLOAT and TE_POW_FROM_RIGHT to enable the respective preprocessors -# when compiling +# - TE_BITWISE_OPERATIONS to parse ^, &, and | as bitwise operators project(TETestRunner) @@ -68,10 +68,10 @@ if(MSVC) target_compile_definitions(${CMAKE_PROJECT_NAME} PUBLIC _DISABLE_VECTOR_ANNOTATION _DISABLE_STRING_ANNOTATION $<$:NDEBUG>) if(USE_ADDRESS_SANITIZE) - target_compile_options(${CMAKE_PROJECT_NAME} PUBLIC /MP /W3 /WX /wd4554 + target_compile_options(${CMAKE_PROJECT_NAME} PUBLIC /Zc:__cplusplus /MP /W3 /WX /wd4554 $<$:/Od /fsanitize=address> $<$:/O2>) else() - target_compile_options(${CMAKE_PROJECT_NAME} PUBLIC /MP /W3 /WX /wd4554 + target_compile_options(${CMAKE_PROJECT_NAME} PUBLIC /Zc:__cplusplus /MP /W3 /WX /wd4554 $<$:/Od> $<$:/O2>) endif() endif() diff --git a/tests/tetests.cpp b/tests/tetests.cpp index e2e6364..4d0b57d 100644 --- a/tests/tetests.cpp +++ b/tests/tetests.cpp @@ -2176,6 +2176,122 @@ TEST_CASE("Bitwise operators", "[bitwise]") } } +#if __cplusplus >= 202002L + +TEST_CASE("Rotate operators", "[rotate]") + { + te_parser tep; + + SECTION("BITLROTATE8") + { + constexpr uint8_t i = std::numeric_limits::max(); + CHECK(tep.evaluate("BITLROTATE8(0, 0)") == std::rotl((uint8_t)0, 0)); + CHECK(tep.evaluate("BITLROTATE8(255, 0)") == std::rotl(i, 0)); + CHECK(tep.evaluate("BITLROTATE8(255, 1)") == std::rotl(i, 1)); + CHECK(tep.evaluate("BITLROTATE8(255, 4)") == std::rotl(i, 4)); + CHECK(tep.evaluate("BITLROTATE8(255, 9)") == std::rotl(i, 9)); + CHECK(tep.evaluate("BITLROTATE8(255, -1)") == std::rotl(i, -1)); + } + SECTION("BITLROTATE16") + { + constexpr uint16_t i = std::numeric_limits::max(); + CHECK(tep.evaluate("BITLROTATE16(0, 0)") == std::rotl((uint16_t)0, 0)); + CHECK(tep.evaluate("BITLROTATE16(65535, 0)") == std::rotl(i, 0)); + CHECK(tep.evaluate("BITLROTATE16(65535, 1)") == std::rotl(i, 1)); + CHECK(tep.evaluate("BITLROTATE16(65535, 4)") == std::rotl(i, 4)); + CHECK(tep.evaluate("BITLROTATE16(65535, 9)") == std::rotl(i, 9)); + CHECK(tep.evaluate("BITLROTATE16(65535, -1)") == std::rotl(i, -1)); + } + SECTION("BITLROTATE32") + { + constexpr uint32_t i = std::numeric_limits::max(); + CHECK(tep.evaluate("BITLROTATE32(0, 0)") == std::rotl((uint32_t)0, 0)); + CHECK(tep.evaluate("BITLROTATE32(4294967295, 0)") == std::rotl(i, 0)); + CHECK(tep.evaluate("BITLROTATE32(4294967295, 1)") == std::rotl(i, 1)); + CHECK(tep.evaluate("BITLROTATE32(4294967295, 4)") == std::rotl(i, 4)); + CHECK(tep.evaluate("BITLROTATE32(4294967295, 9)") == std::rotl(i, 9)); + CHECK(tep.evaluate("BITLROTATE32(4294967295, -1)") == std::rotl(i, -1)); + } + SECTION("BITLROTATE64") + { + // malformed + CHECK(std::isnan(tep.evaluate("5 <"))); + CHECK(std::isnan(tep.evaluate("5 <<"))); + CHECK(std::isnan(tep.evaluate("5 <<<"))); + + std::uint64_t i = 4294967295; + CHECK(tep.evaluate("0 <<< 0") == std::rotl((uint64_t)0, 0)); + CHECK(tep.evaluate("4294967295 <<< 0") == std::rotl(i, 0)); + CHECK(tep.evaluate("4294967295 <<< 1") == std::rotl(i, 1)); + CHECK(tep.evaluate("4294967295 <<< 4") == std::rotl(i, 4)); + CHECK(tep.evaluate("4294967295 <<< 9") == std::rotl(i, 9)); + CHECK(tep.evaluate("4294967295 <<< -1") == std::rotl(i, -1)); + + CHECK(tep.evaluate("BITLROTATE64(0, 0)") == std::rotl((uint64_t)0, 0)); + CHECK(tep.evaluate("BITLROTATE64(4294967295, 0)") == std::rotl(i, 0)); + CHECK(tep.evaluate("BITLROTATE64(4294967295, 1)") == std::rotl(i, 1)); + CHECK(tep.evaluate("BITLROTATE64(4294967295, 4)") == std::rotl(i, 4)); + CHECK(tep.evaluate("BITLROTATE64(4294967295, 9)") == std::rotl(i, 9)); + CHECK(tep.evaluate("BITLROTATE64(4294967295, -1)") == std::rotl(i, -1)); + } + + SECTION("BITRROTATE8") + { + constexpr std::uint8_t i = std::numeric_limits::max(); + CHECK(tep.evaluate("BITRROTATE8(0, 0)") == std::rotr((uint8_t)0, 0)); + CHECK(tep.evaluate("BITRROTATE8(255, 0)") == std::rotr(i, 0)); + CHECK(tep.evaluate("BITRROTATE8(255, 1)") == std::rotr(i, 1)); + CHECK(tep.evaluate("BITRROTATE8(255, 4)") == std::rotr(i, 4)); + CHECK(tep.evaluate("BITRROTATE8(255, 9)") == std::rotr(i, 9)); + CHECK(tep.evaluate("BITRROTATE8(255, -1)") == std::rotr(i, -1)); + } + SECTION("BITRROTATE16") + { + constexpr std::uint16_t i = std::numeric_limits::max(); + CHECK(tep.evaluate("BITRROTATE16(0, 0)") == std::rotr((uint16_t)0, 0)); + CHECK(tep.evaluate("BITRROTATE16(65535, 0)") == std::rotr(i, 0)); + CHECK(tep.evaluate("BITRROTATE16(65535, 1)") == std::rotr(i, 1)); + CHECK(tep.evaluate("BITRROTATE16(65535, 4)") == std::rotr(i, 4)); + CHECK(tep.evaluate("BITRROTATE16(65535, 9)") == std::rotr(i, 9)); + CHECK(tep.evaluate("BITRROTATE16(65535, -1)") == std::rotr(i, -1)); + } + SECTION("BITRROTATE32") + { + constexpr std::uint32_t i = std::numeric_limits::max(); + CHECK(tep.evaluate("BITRROTATE32(0, 0)") == std::rotr((uint32_t)0, 0)); + CHECK(tep.evaluate("BITRROTATE32(4294967295, 0)") == std::rotr(i, 0)); + CHECK(tep.evaluate("BITRROTATE32(4294967295, 1)") == std::rotr(i, 1)); + CHECK(tep.evaluate("BITRROTATE32(4294967295, 4)") == std::rotr(i, 4)); + CHECK(tep.evaluate("BITRROTATE32(4294967295, 9)") == std::rotr(i, 9)); + CHECK(tep.evaluate("BITRROTATE32(4294967295, -1)") == std::rotr(i, -1)); + } + SECTION("BITRROTATE64") + { + // malformed + CHECK(std::isnan(tep.evaluate("5 >"))); + CHECK(std::isnan(tep.evaluate("5 >>"))); + CHECK(std::isnan(tep.evaluate("5 >>>"))); + + // TODO limited to 32-bit int until possible long double support + // can be added (at least for some compilers) + constexpr std::uint64_t i = std::numeric_limits::max();; + CHECK(tep.evaluate("0 >>> 0") == std::rotr((uint64_t)0, 0)); + CHECK(tep.evaluate("4294967295 >>> 0") == std::rotr(i, 0)); + CHECK(tep.evaluate("4294967295 >>> 1") == std::rotr(i, 1)); + CHECK(tep.evaluate("4294967295 >>> 4") == std::rotr(i, 4)); + CHECK(tep.evaluate("4294967295 >>> 9") == std::rotr(i, 9)); + CHECK(tep.evaluate("4294967295 >>> -1") == std::rotr(i, -1)); + + CHECK(tep.evaluate("BITRROTATE64(0, 0)") == std::rotr((uint64_t)0, 0)); + CHECK(tep.evaluate("BITRROTATE64(4294967295, 0)") == std::rotr(i, 0)); + CHECK(tep.evaluate("BITRROTATE64(4294967295, 1)") == std::rotr(i, 1)); + CHECK(tep.evaluate("BITRROTATE64(4294967295, 4)") == std::rotr(i, 4)); + CHECK(tep.evaluate("BITRROTATE64(4294967295, 9)") == std::rotr(i, 9)); + CHECK(tep.evaluate("BITRROTATE64(4294967295, -1)") == std::rotr(i, -1)); + } + } +#endif + TEST_CASE("Shift operators", "[shift]") { te_parser tep; diff --git a/tinyexpr.cpp b/tinyexpr.cpp index 8d0f36d..3aeb2e4 100644 --- a/tinyexpr.cpp +++ b/tinyexpr.cpp @@ -433,6 +433,136 @@ namespace te_builtins return val1 * val2; } +#if __cplusplus >= 202002L + //-------------------------------------------------- + [[nodiscard]] + static te_type te_right_rotate8(te_type val1, te_type val2) + { + if (std::floor(val1) != val1 || std::floor(val2) != val2) + { + throw std::runtime_error("Bitwise RIGHT ROTATE operation must use integers."); + } + else if (val1 < 0) + { + throw std::runtime_error("Bitwise RIGHT ROTATE value must be positive."); + } + + return static_cast(std::rotr(static_cast(val1), static_cast(val2))); + } + + //-------------------------------------------------- + [[nodiscard]] + static te_type te_left_rotate8(te_type val1, te_type val2) + { + if (std::floor(val1) != val1 || std::floor(val2) != val2) + { + throw std::runtime_error("Bitwise LEFT ROTATE operation must use integers."); + } + else if (val1 < 0) + { + throw std::runtime_error("Bitwise LEFT ROTATE value must be positive."); + } + + return static_cast(std::rotl(static_cast(val1), static_cast(val2))); + } + + //-------------------------------------------------- + [[nodiscard]] + static te_type te_right_rotate16(te_type val1, te_type val2) + { + if (std::floor(val1) != val1 || std::floor(val2) != val2) + { + throw std::runtime_error("Bitwise RIGHT ROTATE operation must use integers."); + } + else if (val1 < 0) + { + throw std::runtime_error("Bitwise RIGHT ROTATE value must be positive."); + } + + return static_cast(std::rotr(static_cast(val1), static_cast(val2))); + } + + //-------------------------------------------------- + [[nodiscard]] + static te_type te_left_rotate16(te_type val1, te_type val2) + { + if (std::floor(val1) != val1 || std::floor(val2) != val2) + { + throw std::runtime_error("Bitwise LEFT ROTATE operation must use integers."); + } + else if (val1 < 0) + { + throw std::runtime_error("Bitwise LEFT ROTATE value must be positive."); + } + + return static_cast(std::rotl(static_cast(val1), static_cast(val2))); + } + + //-------------------------------------------------- + [[nodiscard]] + static te_type te_right_rotate32(te_type val1, te_type val2) + { + if (std::floor(val1) != val1 || std::floor(val2) != val2) + { + throw std::runtime_error("Bitwise RIGHT ROTATE operation must use integers."); + } + else if (val1 < 0) + { + throw std::runtime_error("Bitwise RIGHT ROTATE value must be positive."); + } + + return static_cast(std::rotr(static_cast(val1), static_cast(val2))); + } + + //-------------------------------------------------- + [[nodiscard]] + static te_type te_left_rotate32(te_type val1, te_type val2) + { + if (std::floor(val1) != val1 || std::floor(val2) != val2) + { + throw std::runtime_error("Bitwise LEFT ROTATE operation must use integers."); + } + else if (val1 < 0) + { + throw std::runtime_error("Bitwise LEFT ROTATE value must be positive."); + } + + return static_cast(std::rotl(static_cast(val1), static_cast(val2))); + } + + //-------------------------------------------------- + [[nodiscard]] + static te_type te_right_rotate64(te_type val1, te_type val2) + { + if (std::floor(val1) != val1 || std::floor(val2) != val2) + { + throw std::runtime_error("Bitwise RIGHT ROTATE operation must use integers."); + } + else if (val1 < 0) + { + throw std::runtime_error("Bitwise RIGHT ROTATE value must be positive."); + } + + return static_cast(std::rotr(static_cast(val1), static_cast(val2))); + } + + //-------------------------------------------------- + [[nodiscard]] + static te_type te_left_rotate64(te_type val1, te_type val2) + { + if (std::floor(val1) != val1 || std::floor(val2) != val2) + { + throw std::runtime_error("Bitwise LEFT ROTATE operation must use integers."); + } + else if (val1 < 0) + { + throw std::runtime_error("Bitwise LEFT ROTATE value must be positive."); + } + + return static_cast(std::rotl(static_cast(val1), static_cast(val2))); + } +#endif + //-------------------------------------------------- [[nodiscard]] static te_type te_bitwise_or(te_type val1, te_type val2) @@ -781,6 +911,16 @@ const std::set te_parser::m_functions = { // NOLINT static_cast(TE_PURE | TE_VARIADIC) }, { "bitand", static_cast(te_builtins::te_bitwise_and), TE_PURE }, { "bitor", static_cast(te_builtins::te_bitwise_or), TE_PURE }, +#if __cplusplus >= 202002L + { "bitlrotate8", static_cast(te_builtins::te_left_rotate8), TE_PURE }, + { "bitrrotate8", static_cast(te_builtins::te_right_rotate8), TE_PURE }, + { "bitlrotate16", static_cast(te_builtins::te_left_rotate16), TE_PURE }, + { "bitrrotate16", static_cast(te_builtins::te_right_rotate16), TE_PURE }, + { "bitlrotate32", static_cast(te_builtins::te_left_rotate32), TE_PURE }, + { "bitrrotate32", static_cast(te_builtins::te_right_rotate32), TE_PURE }, + { "bitlrotate64", static_cast(te_builtins::te_left_rotate64), TE_PURE }, + { "bitrrotate64", static_cast(te_builtins::te_right_rotate64), TE_PURE }, +#endif { "bitlshift", static_cast(te_builtins::te_left_shift_or_right), TE_PURE }, { "bitrshift", static_cast(te_builtins::te_right_shift_or_left), TE_PURE }, { "bitxor", static_cast(te_builtins::te_bitwise_xor), TE_PURE }, @@ -1054,6 +1194,23 @@ void te_parser::next_token(te_parser::state* theState) { theState->m_type = te_parser::state::token_type::TOK_SEP; } +#if __cplusplus >= 202002L + // 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_value = static_cast(te_builtins::te_left_rotate64); + 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_value = static_cast(te_builtins::te_right_rotate64); + std::advance(theState->m_next, 2); + } +#endif // shift operators else if (tok == '<' && (*theState->m_next == '<')) { @@ -1453,7 +1610,19 @@ te_expr* te_parser::expr_level8(te_parser::state* theState) while (theState->m_type == te_parser::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)) + get_function2(theState->m_value) == te_builtins::te_right_shift +#if __cplusplus >= 202002L + || + get_function2(theState->m_value) == te_builtins::te_left_rotate64 || + get_function2(theState->m_value) == te_builtins::te_right_rotate64 || + get_function2(theState->m_value) == te_builtins::te_left_rotate32 || + get_function2(theState->m_value) == te_builtins::te_right_rotate32 || + get_function2(theState->m_value) == te_builtins::te_left_rotate16 || + get_function2(theState->m_value) == te_builtins::te_right_rotate16 || + get_function2(theState->m_value) == te_builtins::te_left_rotate8 || + get_function2(theState->m_value) == te_builtins::te_right_rotate8 +#endif + )) { const te_fun2 func = get_function2(theState->m_value); next_token(theState); diff --git a/tinyexpr.h b/tinyexpr.h index 8afcf5b..a403090 100644 --- a/tinyexpr.h +++ b/tinyexpr.h @@ -68,6 +68,9 @@ #include #include #include +#if __has_include() +#include +#endif class te_parser;