From dbf65df693072a254e00e933b68d631476e51567 Mon Sep 17 00:00:00 2001 From: Blake-Madden Date: Wed, 1 Nov 2023 11:23:09 -0400 Subject: [PATCH] Fix compile not flushing resolved vars via USR when not keeping them Explain that evaluate() will not cause a USR to be called a again due to the vars in the expression being optimized away #10 --- docs/manual/24-UnknownSymbolResolution.Rmd | 7 ++++++- tests/tetests.cpp | 22 ++++++++++++++++------ tinyexpr.cpp | 10 +++++----- tinyexpr.h | 10 ++++++++++ 4 files changed, 37 insertions(+), 12 deletions(-) diff --git a/docs/manual/24-UnknownSymbolResolution.Rmd b/docs/manual/24-UnknownSymbolResolution.Rmd index 6f22c70..566e5b7 100644 --- a/docs/manual/24-UnknownSymbolResolution.Rmd +++ b/docs/manual/24-UnknownSymbolResolution.Rmd @@ -134,7 +134,8 @@ With subsequent evaluations, the previously unrecognized symbols you resolved wi This means that it will not to be resolved again and will result in the value that your USR returned from before. If you prefer to not have resolved symbols added to the parser, then pass `false` to the second parameter to `set_unknown_symbol_resolver()`. -This will force the same unknown symbols to be resolved again with every evaluation. +This will force the same unknown symbols to be resolved again with every call to `compile(expression)` or `evaluate(expression)`. +(The version of `evaluate()` which does not take any parameters will not force calling the USR again, see below.) This can be useful for when your USR's symbol resolutions are dynamic and may change with each call. ::: {.minipage data-latex="{\textwidth}"} @@ -166,3 +167,7 @@ tep.evaluate("STRESS_LEVEL"); // 5 tep.evaluate("STRESS_LEVEL"); // 6 ``` ::: + +As a final note, if you are not keeping resolved variables, your USR will only be called again if you call `evaluate` or `compile` with an expression. +Because the original expression gets optimized, `evaluate()` it will not re-evaluate any variables. Calling `evaluate` with the original expression +will force a re-compilation and in turn call the USR again. diff --git a/tests/tetests.cpp b/tests/tetests.cpp index 64e9843..b71a827 100644 --- a/tests/tetests.cpp +++ b/tests/tetests.cpp @@ -2022,9 +2022,6 @@ TEST_CASE("Unknown symbol resolve funct pointer keep resolved", "[usr]") te_parser tep; tep.set_unknown_symbol_resolver(ResolveResolutionSymbols); - CHECK(tep.evaluate("RES * 3") == 3 * 96); - // case sensitive, won't recognize it - CHECK(std::isnan(tep.evaluate("resolution * 5"))); CHECK(tep.evaluate("RESOLUTION * 3") == 3 * 96); // is in the parser now and can be recognized case sensitively CHECK(tep.evaluate("resolution * 5") == 480); @@ -2035,14 +2032,27 @@ TEST_CASE("Unknown symbol resolve funct pointer purge resolved", "[usr]") te_parser tep; tep.set_unknown_symbol_resolver(ResolveResolutionSymbols, false); - CHECK(tep.evaluate("RES * 3") == 3 * 96); - // case sensitive, won't recognize it - CHECK(std::isnan(tep.evaluate("resolution * 5"))); CHECK(tep.evaluate("RESOLUTION * 3") == 3 * 96); // not in the parser, so case sensitive look up will fail CHECK(std::isnan(tep.evaluate("resolution * 5"))); } +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) + { + static double temperature = 49.0; + return temperature += 1.0; + }, false); + CHECK(parser.compile(str)); // 50 + CHECK_FALSE(parser.evaluate(str)); // 51 + CHECK_FALSE(parser.evaluate(str)); // 52 + CHECK(parser.evaluate("id.temperature") == 53); + } + + TEST_CASE("Unknown symbol resolve 1 param purged", "[usr]") { te_parser tep; diff --git a/tinyexpr.cpp b/tinyexpr.cpp index f2d442e..37bbf5d 100644 --- a/tinyexpr.cpp +++ b/tinyexpr.cpp @@ -1349,6 +1349,9 @@ bool te_parser::compile(const std::string_view expression) m_result = te_nan; m_lastErrorMessage = expt.what(); } + + reset_usr_resolved_if_necessary(); + return m_parseSuccess; } @@ -1368,11 +1371,8 @@ double te_parser::evaluate() m_lastErrorMessage = expt.what(); } - if (!m_keepResolvedVarialbes && resolvedVariables.size()) - { - for (const auto& resolvedVar : resolvedVariables) - { remove_variable_or_function(resolvedVar); } - } + reset_usr_resolved_if_necessary(); + return m_result; } diff --git a/tinyexpr.h b/tinyexpr.h index 7789175..2bb5fe2 100644 --- a/tinyexpr.h +++ b/tinyexpr.h @@ -437,6 +437,16 @@ class te_parser const std::string& get_expression() const noexcept { return m_expression; }; private: + /// @brief Resets any resolved variables from USR if not being cached. + void reset_usr_resolved_if_necessary() + { + if (!m_keepResolvedVarialbes && resolvedVariables.size()) + { + for (const auto& resolvedVar : resolvedVariables) + { remove_variable_or_function(resolvedVar); } + resolvedVariables.clear(); + } + } /// @brief Gets the compiled expression, which will the optimized version /// of the original expression. /// @returns The compiled expression.