// SPDX-FileCopyrightText: Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen // SPDX-License-Identifier: BSD-3-Clause #include "vtkExprTkFunctionParser.h" #include "vtkAssume.h" #include "vtkObjectFactory.h" #include "vtkStringFormatter.h" #include #include #include #include // exprtk macros #define exprtk_disable_string_capabilities #define exprtk_disable_rtl_io_file #define exprtk_disable_caseinsensitivity #include "vtk_exprtk.h" #include "vtksys/SystemTools.hxx" using ExprTkResultType = exprtk::results_context::type_store_t::store_type; /** * Implementation of vtkExprTkTools */ VTK_ABI_NAMESPACE_BEGIN struct vtkExprTkTools { exprtk::symbol_table SymbolTable; exprtk::expression Expression; exprtk::parser Parser; }; /** * Implementation of the magnitude function */ template class mag : public exprtk::igeneric_function { public: typedef typename exprtk::igeneric_function igfun_t; typedef typename igfun_t::parameter_list_t parameter_list_t; typedef typename igfun_t::generic_type generic_type; typedef typename generic_type::scalar_view scalar_t; typedef typename generic_type::vector_view vector_t; using exprtk::igeneric_function::operator(); mag() : exprtk::igeneric_function("V|VTT") /* Overloads: 0. V - x(vector) 1. VTT - x(vector), r0, r1 */ { } T operator()(const std::size_t& ps_index, parameter_list_t parameters) override { const vector_t x(parameters[0]); std::size_t r0 = 0; std::size_t r1 = x.size() - 1; if ((1 == ps_index) && !exprtk::rtl::vecops::helper::load_vector_range::process(parameters, r0, r1, 2, 3, 0)) { return std::numeric_limits::quiet_NaN(); } if (exprtk::rtl::vecops::helper::invalid_range(x, r0, r1)) { return std::numeric_limits::quiet_NaN(); } T result = T(0); for (std::size_t i = r0; i <= r1; ++i) { result += (x[i] * x[i]); } result = std::sqrt(result); return result; } }; /** * Implementation of the x element of cross product function */ template class crossX : public exprtk::igeneric_function { public: typedef typename exprtk::igeneric_function igfun_t; typedef typename igfun_t::parameter_list_t parameter_list_t; typedef typename igfun_t::generic_type generic_type; typedef typename generic_type::scalar_view scalar_t; typedef typename generic_type::vector_view vector_t; using exprtk::igeneric_function::operator(); crossX() : exprtk::igeneric_function("VV|VVTT") /* Overloads: 0. VV - x(vector), y(vector) 1. VVTT - x(vector), y(vector), r0, r1 */ { } T operator()(const std::size_t& ps_index, parameter_list_t parameters) override { const vector_t x(parameters[0]); const vector_t y(parameters[1]); std::size_t r0 = 0; std::size_t r1 = std::min(x.size(), y.size()) - 1; if ((1 == ps_index) && !exprtk::rtl::vecops::helper::load_vector_range::process(parameters, r0, r1, 2, 3, 0)) { return std::numeric_limits::quiet_NaN(); } if (exprtk::rtl::vecops::helper::invalid_range(y, r0, r1)) { return std::numeric_limits::quiet_NaN(); } if (x.size() != 3 || y.size() != 3) { // cross product is only defined for 3D vectors return std::numeric_limits::quiet_NaN(); } T result = x[1] * y[2] - x[2] * y[1]; return result; } }; /** * Implementation of the y element of cross product function */ template class crossY : public exprtk::igeneric_function { public: typedef typename exprtk::igeneric_function igfun_t; typedef typename igfun_t::parameter_list_t parameter_list_t; typedef typename igfun_t::generic_type generic_type; typedef typename generic_type::scalar_view scalar_t; typedef typename generic_type::vector_view vector_t; using exprtk::igeneric_function::operator(); crossY() : exprtk::igeneric_function("VV|VVTT") /* Overloads: 0. VV - x(vector), y(vector) 1. VVTT - x(vector), y(vector), r0, r1 */ { } T operator()(const std::size_t& ps_index, parameter_list_t parameters) override { const vector_t x(parameters[0]); const vector_t y(parameters[1]); std::size_t r0 = 0; std::size_t r1 = std::min(x.size(), y.size()) - 1; if ((1 == ps_index) && !exprtk::rtl::vecops::helper::load_vector_range::process(parameters, r0, r1, 2, 3, 0)) { return std::numeric_limits::quiet_NaN(); } if (exprtk::rtl::vecops::helper::invalid_range(y, r0, r1)) { return std::numeric_limits::quiet_NaN(); } if (x.size() != 3 || y.size() != 3) { // cross product is only defined for 3D vectors return std::numeric_limits::quiet_NaN(); } T result = x[2] * y[0] - x[0] * y[2]; return result; } }; /** * Implementation of the z element of cross product function */ template class crossZ : public exprtk::igeneric_function { public: typedef typename exprtk::igeneric_function igfun_t; typedef typename igfun_t::parameter_list_t parameter_list_t; typedef typename igfun_t::generic_type generic_type; typedef typename generic_type::scalar_view scalar_t; typedef typename generic_type::vector_view vector_t; using exprtk::igeneric_function::operator(); crossZ() : exprtk::igeneric_function("VV|VVTT") /* Overloads: 0. VV - x(vector), y(vector) 1. VVTT - x(vector), y(vector), r0, r1 */ { } T operator()(const std::size_t& ps_index, parameter_list_t parameters) override { const vector_t x(parameters[0]); const vector_t y(parameters[1]); std::size_t r0 = 0; std::size_t r1 = std::min(x.size(), y.size()) - 1; if ((1 == ps_index) && !exprtk::rtl::vecops::helper::load_vector_range::process(parameters, r0, r1, 2, 3, 0)) { return std::numeric_limits::quiet_NaN(); } if (exprtk::rtl::vecops::helper::invalid_range(y, r0, r1)) { return std::numeric_limits::quiet_NaN(); } if (x.size() != 3 || y.size() != 3) { // cross product is only defined for 3D vectors return std::numeric_limits::quiet_NaN(); } T result = x[0] * y[1] - x[1] * y[0]; return result; } }; namespace { /** * Implementation of sign function. */ inline double sign(double v) { if (v == 0.) { return 0.; } else if (std::signbit(v)) { return -1.; } else { return 1.; } } // compile-time declaration of needed function/variables/vectors/packages // these are useful to minimize the construction cost, especially when // multiple instances of this class are instantiated exprtk::rtl::vecops::package vectorOperationsPackage; std::vector iHat = { 1, 0, 0 }; std::vector jHat = { 0, 1, 0 }; std::vector kHat = { 0, 0, 1 }; mag magnitude; crossX crossXProduct; crossY crossYProduct; crossZ crossZProduct; //------------------------------------------------------------------------------ std::string RemoveSpacesFrom(std::string str) { str.erase(remove_if(str.begin(), str.end(), isspace), str.end()); return str; } //------------------------------------------------------------------------------ bool HasEnding(const std::string& fullString, const std::string& ending) { if (fullString.size() >= ending.size()) { return (fullString.compare(fullString.size() - ending.size(), ending.size(), ending) == 0); } else { return false; } } //------------------------------------------------------------------------------ std::string GenerateRandomAlphabeticString(unsigned int len) { static constexpr auto chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" "abcdefghijklmnopqrstuvwxyz"; auto rng = std::default_random_engine(std::random_device{}()); auto dist = std::uniform_int_distribution(0, static_cast(std::strlen(chars) - 1)); auto result = std::string(len, '\0'); std::generate_n(begin(result), len, [&]() { return chars[dist(rng)]; }); return result; } //------------------------------------------------------------------------------ std::string GenerateUniqueVariableName( const std::vector& variableNames, const std::string& variableName) { std::string sanitizedName = vtkExprTkFunctionParser::SanitizeName(variableName.c_str()); do { sanitizedName += GenerateRandomAlphabeticString(5); } while ( std::find(variableNames.begin(), variableNames.end(), sanitizedName) != variableNames.end()); return sanitizedName; } } vtkStandardNewMacro(vtkExprTkFunctionParser); //------------------------------------------------------------------------------ vtkExprTkFunctionParser::vtkExprTkFunctionParser() { this->ParseMTime.Modified(); this->FunctionMTime.Modified(); this->ReplaceInvalidValues = 0; this->ReplacementValue = 0.0; this->ExprTkTools = new vtkExprTkTools; // add vector support this->ExprTkTools->SymbolTable.add_package(vectorOperationsPackage); // add basic constants (inf, pi) // this->ExprTkTools->SymbolTable.add_epsilon(); this->ExprTkTools->SymbolTable.add_infinity(); this->ExprTkTools->SymbolTable.add_pi(); // add unit vectors this->ExprTkTools->SymbolTable.add_vector("iHat", iHat); this->ExprTkTools->SymbolTable.add_vector("jHat", jHat); this->ExprTkTools->SymbolTable.add_vector("kHat", kHat); // add ln and sign this->ExprTkTools->SymbolTable.add_function("ln", std::log); this->ExprTkTools->SymbolTable.add_function("sign", sign); // add magnitude function this->ExprTkTools->SymbolTable.add_function("mag", magnitude); // add functions which are used to implement cross product this->ExprTkTools->SymbolTable.add_function("crossX", crossXProduct); this->ExprTkTools->SymbolTable.add_function("crossY", crossYProduct); this->ExprTkTools->SymbolTable.add_function("crossZ", crossZProduct); // register symbol table this->ExprTkTools->Expression.register_symbol_table(this->ExprTkTools->SymbolTable); // enable the collection of variables, which will be used in UpdateNeededVariables this->ExprTkTools->Parser.dec().collect_variables() = true; this->SizeMode = SizeModes::AutoDetected; this->ResultType = ExprTkResultType::e_scalar; this->ResultSize = 1; } //------------------------------------------------------------------------------ vtkExprTkFunctionParser::~vtkExprTkFunctionParser() { this->RemoveAllVariables(); delete this->ExprTkTools; } //------------------------------------------------------------------------------ void vtkExprTkFunctionParser::SetFunction(const char* function) { // check if we have already set the same function string if (!this->Function.empty() && function && this->Function == function) { return; } if (function) { this->Function = function; this->FunctionWithUsedVariableNames = this->Function; } else { this->Function = std::string(); this->FunctionWithUsedVariableNames = std::string(); } this->FunctionMTime.Modified(); this->ScalarVariableNeeded.clear(); this->VectorVariableNeeded.clear(); this->Modified(); } //------------------------------------------------------------------------------ vtkExprTkFunctionParser::SizeModes vtkExprTkFunctionParser::DetermineSizeMode( const std::string& function) { // if first charachter is { and last is }, then VectorDefined const std::string funcNoSpaces = RemoveSpacesFrom(function); return funcNoSpaces.front() == '{' && funcNoSpaces.back() == '}' ? SizeModes::VectorDefined : SizeModes::AutoDetected; } //------------------------------------------------------------------------------ int vtkExprTkFunctionParser::Parse(ParseMode mode) { if (this->Function.empty()) { vtkErrorMacro("Parse: no function has been set"); return 0; } // During the parsing of the first mode, perform the necessary changes in the function if (mode == ParseMode::DetectReturnType) { // Before parsing, replace the original variable names in the function // with the valid ones if needed. for (size_t i = 0; i < this->OriginalScalarVariableNames.size(); ++i) { if (this->OriginalScalarVariableNames[i] != this->UsedScalarVariableNames[i]) { vtksys::SystemTools::ReplaceString(this->FunctionWithUsedVariableNames, this->OriginalScalarVariableNames[i], this->UsedScalarVariableNames[i]); } } for (size_t i = 0; i < this->OriginalVectorVariableNames.size(); ++i) { if (this->OriginalVectorVariableNames[i] != this->UsedVectorVariableNames[i]) { vtksys::SystemTools::ReplaceString(this->FunctionWithUsedVariableNames, this->OriginalVectorVariableNames[i], this->UsedVectorVariableNames[i]); } } // remove spaces to perform replacement for norm and cross this->FunctionWithUsedVariableNames = RemoveSpacesFrom(this->FunctionWithUsedVariableNames); // check for usage of old dot format product, e.g. (v1.v2) instead of dot(v1,v2) if (this->CheckOldFormatOfDotProductUsage()) { std::string oldDotUsageError = "Warn: 0000 Type: [Old Usage] Msg: " "Possible usage of old format of dot product v1.v2. Please use dot(v1,v2)." "\tExpression: " + this->Function + '\n'; vtkWarningMacro(<< oldDotUsageError); } // fix cross occurrences with something that ExprTk can understand this->FunctionWithUsedVariableNames = FixVectorReturningFunctionOccurrences(VectorReturningFunction::Cross); // fix norm occurrences with something that ExprTk can understand this->FunctionWithUsedVariableNames = FixVectorReturningFunctionOccurrences(VectorReturningFunction::Norm); } if (mode == ParseMode::DetectReturnType) { // ExprTK, in order to extract vector and scalar results, and identify the result type, // it requires to "return results" instead of just evaluating an expression auto function = this->FunctionWithUsedVariableNames; if (this->SizeMode == SizeModes::VectorDefined) { // remove { and } to extract the number of vector components as the number of results returned vtksys::SystemTools::ReplaceString(function, "{", ""); vtksys::SystemTools::ReplaceString(function, "}", ""); } this->ExpressionString = "return [" + function + "];"; } else { // Since we know now the return type, we can assign the result to a result scalar/vector const std::string resultName = GenerateUniqueVariableName(this->UsedScalarVariableNames, "res_"); assert(this->ExprTkTools->SymbolTable.symbol_exists(resultName) == false); if (this->SizeMode == SizeModes::AutoDetected) { if (this->ResultType == ExprTkResultType::e_scalar) { this->ExprTkTools->SymbolTable.add_variable(resultName, this->Result[0]); } else { this->ExprTkTools->SymbolTable.add_vector(resultName, this->Result); } this->ExpressionString = resultName + " := " + this->FunctionWithUsedVariableNames + ";"; } else // (this->SizeMode == SizeModes::VectorDefined) { const std::string varName = GenerateUniqueVariableName(this->UsedVectorVariableNames, "res_temp_"); assert(this->ExprTkTools->SymbolTable.symbol_exists(varName) == false); if (this->ResultType == ExprTkResultType::e_scalar) { this->ExprTkTools->SymbolTable.add_variable(resultName, this->Result[0]); } else { this->ExprTkTools->SymbolTable.add_vector(resultName, this->Result); } this->ExpressionString = "var " + varName + "[" + vtk::to_string(this->ResultSize) + "] := " + this->FunctionWithUsedVariableNames + "; " + resultName + " := " + varName + ";"; } } this->ExprTkTools->Expression.register_symbol_table(this->ExprTkTools->SymbolTable); bool parsingResult = this->ExprTkTools->Parser.compile(this->ExpressionString, this->ExprTkTools->Expression); // check parsing result if (!parsingResult) { // print error only once if (mode == ParseMode::DetectReturnType) { std::stringstream parsingErrorStream; // save error for (std::size_t i = 0; i < this->ExprTkTools->Parser.error_count(); ++i) { auto error = this->ExprTkTools->Parser.get_error(i); parsingErrorStream << "Err: " << i << " Type: [" << exprtk::parser_error::to_str(error.mode) << "] Msg: " << error.diagnostic << "\tExpression: " << this->Function << "\n"; } vtkErrorMacro(<< parsingErrorStream.str()); } return 0; } if (mode == ParseMode::DetectReturnType) { // Collect meta-data about variables that are needed for evaluation of the // function. this->UpdateNeededVariables(); } this->ParseMTime.Modified(); return 1; } std::string vtkExprTkFunctionParser::FixVectorReturningFunctionOccurrences( VectorReturningFunction vectorReturningFunction) { std::string desiredFunction; std::string functionWithoutParenthesis; if (vectorReturningFunction == VectorReturningFunction::Cross) { desiredFunction = "cross("; functionWithoutParenthesis = "cross"; } else { desiredFunction = "norm("; functionWithoutParenthesis = "norm"; } // collect all the variables that end with the desired function, e.g. mycross, m1cross std::vector variableNamesContainingFunction; for (const auto& scalarVariable : this->UsedScalarVariableNames) { if (HasEnding(scalarVariable, functionWithoutParenthesis)) { variableNamesContainingFunction.push_back(scalarVariable); } } for (const auto& vectorVariable : this->UsedVectorVariableNames) { if (HasEnding(vectorVariable, functionWithoutParenthesis)) { variableNamesContainingFunction.push_back(vectorVariable); } } // sort vector by size to ensure that the largest variables names will be checked first std::sort(variableNamesContainingFunction.begin(), variableNamesContainingFunction.end(), [](const std::string& s1, const std::string& s2) -> bool { return s1.size() > s2.size(); }); static const std::string allowedChars = "01234565789.,()+-*/%^|&=<>!"; std::string::size_type pos = 0; std::string function = this->FunctionWithUsedVariableNames; while ((pos = function.find(desiredFunction, pos)) != std::string::npos) { // if we are not in the beginning if (static_cast(pos) - 1 != -1) { // check the found occurrence if it's part of a variable // this check is required because the previous character could be a number // and that is part of a variable name which includes cross, such m1cross bool foundVariableOccurrence = false; for (const auto& variable : variableNamesContainingFunction) { // check the size of the variable vs the function if (variable.size() >= functionWithoutParenthesis.size()) { const int sizeDifference = static_cast(variable.size() - functionWithoutParenthesis.size()); // check pos to not exceed the beginning if (static_cast(pos) - sizeDifference >= 0) { // check if occurrence match variable if (function.substr(pos - sizeDifference, variable.size()) == variable) { foundVariableOccurrence = true; break; } } } } // skip the found occurrence if it's part of a variable if (foundVariableOccurrence) { pos += desiredFunction.size(); continue; } // check if a character that is allowed is found bool allowedCharFound = false; for (char allowedChar : allowedChars) { if (function[pos - 1] == allowedChar) { allowedCharFound = true; break; } } // skip the found occurrence if no allowed character has been found if (!allowedCharFound) { pos += desiredFunction.size(); continue; } } pos += desiredFunction.size(); // match the number of parenthesis int leftParenthesis = 1; // 1 because we have already detected one int rightParenthesis = 0; std::stringstream interior; for (size_t i = pos; i < function.size(); ++i) { if (function[i] == ')') { ++rightParenthesis; } if (function[i] == '(') { ++leftParenthesis; } if (leftParenthesis == rightParenthesis) { break; } else { interior << function[i]; } } // if the number of left and right parenthesis is equal, then replace appropriately if (rightParenthesis == leftParenthesis) { // go back to replace pos -= desiredFunction.size(); std::string replacement; if (vectorReturningFunction == VectorReturningFunction::Cross) { // (iHat*crossX(v1,v2)+jHat*crossY(v1,v2)+kHat*crossZ(v1,v2)) replacement = "(iHat*crossX(" + interior.str() + ")" + "+jHat*crossY(" + interior.str() + ")" + "+kHat*crossZ(" + interior.str() + "))"; } else { // ((v)/mag(v)) replacement = "((" + interior.str() + ")/mag(" + interior.str() + "))"; } // perform replacement, +1 is for the right parenthesis function.replace(pos, desiredFunction.size() + interior.str().size() + 1, replacement); } else { // ExprTk will catch it the parenthesis mismatch // ExprTk will also catch all the cases that the interior is not valid break; } } return function; } //------------------------------------------------------------------------------ bool vtkExprTkFunctionParser::CheckOldFormatOfDotProductUsage() { const std::string function = this->FunctionWithUsedVariableNames; std::string::size_type pos = 0; while ((pos = function.find('.', pos)) != std::string::npos) { // if we are not in the beginning if (static_cast(pos) - 1 != -1) { // check if left character is digit bool leftCharacterIsDigit = false; if (std::isdigit(function[pos - 1])) { leftCharacterIsDigit = true; } // check if right character is digit bool rightCharacterIsDigit = false; // before that check, check if you can look at the right character if (pos + 1 < function.size()) { if (std::isdigit(function[pos + 1])) { rightCharacterIsDigit = true; } } // both left character and right character are not digits // then this is a possible product usage if (!leftCharacterIsDigit && !rightCharacterIsDigit) { return true; } else { ++pos; } } else { // check if right character is number bool rightCharacterIsNumber = false; // before that check, check if you can look at the right character if (pos + 1 < function.size()) { if (std::isdigit(function[pos + 1])) { rightCharacterIsNumber = true; } } // right character is not a number // then this is a possible product usage if (!rightCharacterIsNumber) { return true; } else { ++pos; } } } return false; } //------------------------------------------------------------------------------ void vtkExprTkFunctionParser::InvalidateFunction() { this->FunctionMTime.Modified(); } //------------------------------------------------------------------------------ bool vtkExprTkFunctionParser::Evaluate() { if (this->FunctionMTime.GetMTime() > this->ParseMTime.GetMTime()) { this->SizeMode = DetermineSizeMode(this->FunctionWithUsedVariableNames); // compile with mode 0 to identify return type if (this->Parse(ParseMode::DetectReturnType) == 0) { return false; } // perform evaluation to identify the return type this->ExprTkTools->Expression.value(); const auto& results = this->ExprTkTools->Expression.results(); if (this->SizeMode == SizeModes::AutoDetected) { auto result = results[0]; this->ResultType = result.type; if (result.size != 0) { this->ResultSize = static_cast(result.size); } else { // if the parser could not identify the size, set it to 3 for vector and 1 for scalar this->ResultSize = this->ResultType == ExprTkResultType::e_vector ? 3 : 1; } } else // (this->SizeMode == SizeModes::VectorDefined) { this->ResultSize = static_cast(results.count()); this->ResultType = this->ResultSize > 1 ? ExprTkResultType::e_vector : ExprTkResultType::e_scalar; // assert that all results have type e_scalar and size 1 for (size_t i = 0; i < results.count(); ++i) { if (results[i].type != ExprTkResultType::e_scalar || results[i].size != 1) { vtkErrorMacro("In VectorDefined size mode, all components must be scalars of size 1."); this->ResultType = ExprTkResultType::e_unknown; } } } this->Result.resize(this->ResultSize); this->ParserErrorResult.resize(this->ResultSize, std::numeric_limits::quiet_NaN()); // compile with mode 1 to save results in the result array if (this->Parse(ParseMode::SaveResultInVariable) == 0) { return false; } } // perform evaluation this->ExprTkTools->Expression.value(); // check for invalid results if (this->ResultType == ExprTkResultType::e_unknown) { return false; } switch (this->ResultType) { case ExprTkResultType::e_scalar: if (std::isnan(this->Result[0]) || std::isinf(this->Result[0])) { if (this->ReplaceInvalidValues) { this->Result[0] = this->ReplacementValue; } else { vtkErrorMacro("Invalid result because of mathematically wrong input."); return false; } } break; case ExprTkResultType::e_vector: for (int i = 0; i < this->ResultSize; i++) { if (std::isnan(this->Result[i]) || std::isinf(this->Result[i])) { if (this->ReplaceInvalidValues) { this->Result[i] = this->ReplacementValue; } else { vtkErrorMacro("Invalid vector element result because of mathematically wrong input."); return false; } } } break; default: vtkErrorMacro("Not supported result type."); return false; } return true; } //------------------------------------------------------------------------------ int vtkExprTkFunctionParser::IsScalarResult() { if (!this->Evaluate()) { return 0; } return (this->ResultType == ExprTkResultType::e_scalar); } //------------------------------------------------------------------------------ double vtkExprTkFunctionParser::GetScalarResult() { if (!(this->IsScalarResult())) { vtkErrorMacro("GetScalarResult: no valid scalar result"); return this->ParserErrorResult[0]; } return this->Result[0]; } //------------------------------------------------------------------------------ int vtkExprTkFunctionParser::IsVectorResult() { if (!this->Evaluate()) { return 0; } return (this->ResultType == ExprTkResultType::e_vector); } //------------------------------------------------------------------------------ double* vtkExprTkFunctionParser::GetVectorResult() { if (!(this->IsVectorResult())) { vtkErrorMacro("GetVectorResult: no valid vector result"); return this->ParserErrorResult.data(); } return this->Result.data(); } //------------------------------------------------------------------------------ std::string vtkExprTkFunctionParser::GetScalarVariableName(int i) { if (i >= 0 && i < this->GetNumberOfScalarVariables()) { return this->OriginalScalarVariableNames[i]; } return std::string(); } //------------------------------------------------------------------------------ std::string vtkExprTkFunctionParser::GetVectorVariableName(int i) { if (i >= 0 && i < this->GetNumberOfVectorVariables()) { return this->OriginalVectorVariableNames[i]; } return std::string(); } //------------------------------------------------------------------------------ void vtkExprTkFunctionParser::SetScalarVariableValue( const std::string& inVariableName, double value) { if (inVariableName.empty()) { vtkErrorMacro("Variable name is empty"); return; } // check if variable name exists in vectors for (size_t i = 0; i < this->OriginalVectorVariableNames.size(); i++) { if (this->OriginalVectorVariableNames[i] == inVariableName) { vtkErrorMacro("Scalar variable name is already registered as a vector variable name"); return; } } // check if variable already exists for (size_t i = 0; i < this->OriginalScalarVariableNames.size(); i++) { if (this->OriginalScalarVariableNames[i] == inVariableName) { if (*this->ScalarVariableValues[i] != value) { *this->ScalarVariableValues[i] = value; this->Modified(); } return; } } double* scalarValue = new double(value); // if variable name is not sanitized, create a unique sanitized string and set it as variable name std::string variableName = vtkExprTkFunctionParser::SanitizeName(inVariableName.c_str()); if (variableName != inVariableName) { variableName = GenerateUniqueVariableName(this->UsedScalarVariableNames, inVariableName); } // check if variable is a registered keyword, e.g. sin(). bool additionResult = this->ExprTkTools->SymbolTable.add_variable(variableName, *scalarValue); if (additionResult) { this->ScalarVariableValues.push_back(scalarValue); this->OriginalScalarVariableNames.push_back(inVariableName); this->UsedScalarVariableNames.push_back(variableName); this->Modified(); } else { delete scalarValue; vtkErrorMacro("Scalar variable `" << inVariableName << "` is a reserved keyword"); } } //------------------------------------------------------------------------------ void vtkExprTkFunctionParser::SetScalarVariableValue(int i, double value) { if (VTK_UNLIKELY(i < 0 || i >= this->GetNumberOfScalarVariables())) { return; } *this->ScalarVariableValues[i] = value; } //------------------------------------------------------------------------------ double vtkExprTkFunctionParser::GetScalarVariableValue(const std::string& inVariableName) { for (size_t i = 0; i < this->OriginalScalarVariableNames.size(); i++) { if (this->OriginalScalarVariableNames[i] == inVariableName) { return *this->ScalarVariableValues[i]; } } vtkErrorMacro( "GetScalarVariableValue: scalar variable name " << inVariableName << " does not exist"); return this->ParserErrorResult[0]; } //------------------------------------------------------------------------------ double vtkExprTkFunctionParser::GetScalarVariableValue(int i) { if (i < 0 || i >= this->GetNumberOfScalarVariables()) { vtkErrorMacro("GetScalarVariableValue: scalar variable number " << i << " does not exist"); return this->ParserErrorResult[0]; } return *this->ScalarVariableValues[i]; } //------------------------------------------------------------------------------ void vtkExprTkFunctionParser::SetVectorVariableValue( const std::string& inVariableName, double* values, int size) { if (inVariableName.empty()) { vtkErrorMacro("Variable name is empty"); return; } // check if variable name exists in vectors for (size_t i = 0; i < this->OriginalScalarVariableNames.size(); i++) { if (this->OriginalScalarVariableNames[i] == inVariableName) { vtkErrorMacro("Vector variable name is already registered as a scalar variable name"); return; } } // check if variable already exists for (size_t i = 0; i < this->OriginalVectorVariableNames.size(); i++) { if (this->OriginalVectorVariableNames[i] == inVariableName) { this->VectorVariableValues[i]->resize(size); bool isEqual = std::equal( this->VectorVariableValues[i]->begin(), this->VectorVariableValues[i]->end(), values); if (!isEqual) { std::copy_n(values, size, this->VectorVariableValues[i]->begin()); this->Modified(); } return; } } std::vector* vector = new std::vector(); vector->resize(size); std::copy_n(values, size, vector->begin()); // if variable name is not sanitized, create a unique sanitized string and set it as variable name std::string variableName = vtkExprTkFunctionParser::SanitizeName(inVariableName.c_str()); if (variableName != inVariableName) { variableName = GenerateUniqueVariableName(this->UsedVectorVariableNames, inVariableName); } // check if variable is a registered keyword, e.g. sin(). bool additionResult = this->ExprTkTools->SymbolTable.add_vector(variableName, *vector); if (additionResult) { this->VectorVariableValues.push_back(vector); this->OriginalVectorVariableNames.push_back(inVariableName); this->UsedVectorVariableNames.push_back(variableName); this->Modified(); } else { delete vector; vtkErrorMacro("Vector variable `" << inVariableName << "` is a reserved keyword"); } } //------------------------------------------------------------------------------ void vtkExprTkFunctionParser::SetVectorVariableValue(int i, double* values, int size) { if (VTK_UNLIKELY(i < 0 || i >= this->GetNumberOfVectorVariables())) { return; } assert(this->VectorVariableValues[i]->size() == static_cast(size)); std::copy_n(values, size, this->VectorVariableValues[i]->begin()); } //------------------------------------------------------------------------------ double* vtkExprTkFunctionParser::GetVectorVariableValue(const std::string& inVariableName) { for (size_t i = 0; i < this->OriginalVectorVariableNames.size(); i++) { if (this->OriginalVectorVariableNames[i] == inVariableName) { return this->VectorVariableValues[i]->data(); } } vtkErrorMacro( "GetVectorVariableValue: vector variable name " << inVariableName << " does not exist"); return this->ParserErrorResult.data(); } //------------------------------------------------------------------------------ double* vtkExprTkFunctionParser::GetVectorVariableValue(int i) { if (i < 0 || i >= this->GetNumberOfVectorVariables()) { vtkErrorMacro("GetVectorVariableValue: vector variable number " << i << " does not exist"); return this->ParserErrorResult.data(); } return this->VectorVariableValues[i]->data(); } //------------------------------------------------------------------------------ void vtkExprTkFunctionParser::RemoveScalarVariables() { this->ExprTkTools->SymbolTable.clear_variables(); this->OriginalScalarVariableNames.clear(); this->UsedScalarVariableNames.clear(); for (size_t i = 0; i < this->ScalarVariableValues.size(); ++i) { delete this->ScalarVariableValues[i]; } this->ScalarVariableValues.clear(); } //------------------------------------------------------------------------------ void vtkExprTkFunctionParser::RemoveVectorVariables() { // we clear vector variables to avoid removing iHat,jHat, kHat for (size_t i = 0; i < this->UsedVectorVariableNames.size(); ++i) { this->ExprTkTools->SymbolTable.remove_vector(this->UsedVectorVariableNames[i]); } this->OriginalVectorVariableNames.clear(); this->UsedVectorVariableNames.clear(); for (size_t i = 0; i < this->VectorVariableValues.size(); ++i) { delete this->VectorVariableValues[i]; } this->VectorVariableValues.clear(); } //------------------------------------------------------------------------------ void vtkExprTkFunctionParser::RemoveAllVariables() { this->ExprTkTools->Expression.release(); this->RemoveScalarVariables(); this->RemoveVectorVariables(); } //------------------------------------------------------------------------------ vtkMTimeType vtkExprTkFunctionParser::GetMTime() { vtkMTimeType mTime = this->Superclass::GetMTime(); mTime = std::max(this->ParseMTime, mTime); mTime = std::max(this->FunctionMTime, mTime); return mTime; } //------------------------------------------------------------------------------ void vtkExprTkFunctionParser::PrintSelf(ostream& os, vtkIndent indent) { this->Superclass::PrintSelf(os, indent); os << indent << "SizeMode: " << (this->GetSizeMode() == SizeModes::AutoDetected ? "AutoDetected" : "UserProvided") << endl; os << indent << "ResultSize: " << this->ResultSize << endl; os << indent << "Function: " << (this->GetFunction() ? this->GetFunction() : "(none)") << endl; os << indent << "FunctionWithUsedVariableNames: " << (!this->FunctionWithUsedVariableNames.empty() ? this->FunctionWithUsedVariableNames : "(none)") << endl; os << indent << "ExpressionString: " << (!this->ExpressionString.empty() ? this->ExpressionString : "(none)") << endl; for (size_t i = 0; i < this->OriginalScalarVariableNames.size(); i++) { os << indent << " " << this->OriginalScalarVariableNames[i] << " / " << this->UsedScalarVariableNames[i] << ": " << (*this->ScalarVariableValues[i]) << endl; } for (size_t i = 0; i < this->OriginalVectorVariableNames.size(); i++) { os << indent << " " << this->OriginalVectorVariableNames[i] << " / " << this->UsedVectorVariableNames[i] << ": ("; for (size_t j = 0; j < this->VectorVariableValues[i]->size(); j++) { if (j > 0) { os << ", "; } os << (*this->VectorVariableValues[i])[j]; } os << ")" << endl; } if (!this->Function.empty() && this->ExprTkTools->Expression.results().count() > 0) { if (this->ResultType == ExprTkResultType::e_scalar) { os << indent << "ScalarResult: " << this->GetScalarResult() << endl; os << indent << "VectorResult: " << "(none)" << endl; } else { os << indent << "ScalarResult: " << "(none)" << endl; os << indent << "VectorResult: ("; for (int i = 0; i < this->ResultSize; i++) { if (i > 0) { os << ", "; } os << this->Result[i]; } os << ")" << endl; } } else { os << indent << "ScalarResult: " << "(none)" << endl; os << indent << "VectorResult: " << "(none)" << endl; } os << indent << "Replace Invalid Values: " << (this->GetReplaceInvalidValues() ? "On" : "Off") << endl; os << indent << "Replacement Value: " << this->GetReplacementValue() << endl; } //------------------------------------------------------------------------------ void vtkExprTkFunctionParser::UpdateNeededVariables() { this->ScalarVariableNeeded.clear(); this->ScalarVariableNeeded.resize(this->UsedScalarVariableNames.size(), false); this->VectorVariableNeeded.clear(); this->VectorVariableNeeded.resize(this->UsedVectorVariableNames.size(), false); // store variables after parsing std::deque::dependent_entity_collector::symbol_t> symbolList; this->ExprTkTools->Parser.dec().symbols(symbolList); // convert them to a set to remove duplicates std::set variables; for (const auto& symbol : symbolList) { variables.insert(symbol.first); } for (auto& variable : variables) { // check if variable exists in scalars for (size_t j = 0; j < this->UsedScalarVariableNames.size(); ++j) { if (variable == this->UsedScalarVariableNames[j]) { this->ScalarVariableNeeded[j] = true; break; } } // check if variable exists in vectors for (size_t j = 0; j < this->UsedVectorVariableNames.size(); ++j) { if (variable == this->UsedVectorVariableNames[j]) { this->VectorVariableNeeded[j] = true; break; } } } } //------------------------------------------------------------------------------ std::string vtkExprTkFunctionParser::SanitizeName(const char* name) { if (!name || name[0] == '\0') { return std::string(); } std::ostringstream cname; for (size_t cc = 0; name[cc]; cc++) { if (isalnum(name[cc]) || name[cc] == '_') { cname << name[cc]; } } // if first character is not an alphabet, add an 'a' to it. if (cname.str().empty() || isalpha(cname.str()[0])) { return cname.str(); } else { return "a" + cname.str(); } } //------------------------------------------------------------------------------ int vtkExprTkFunctionParser::GetScalarVariableIndex(const std::string& inVariableName) { for (size_t i = 0; i < this->OriginalScalarVariableNames.size(); ++i) { if (this->OriginalScalarVariableNames[i] == inVariableName) { return static_cast(i); } } return -1; } //------------------------------------------------------------------------------ bool vtkExprTkFunctionParser::GetScalarVariableNeeded(int i) { if (i < 0 || i >= static_cast(this->ScalarVariableNeeded.size())) { return false; } return this->ScalarVariableNeeded[i]; } //------------------------------------------------------------------------------ bool vtkExprTkFunctionParser::GetScalarVariableNeeded(const std::string& inVariableName) { std::vector::const_iterator iter = std::find(this->OriginalScalarVariableNames.begin(), this->OriginalScalarVariableNames.end(), inVariableName); if (iter != this->OriginalScalarVariableNames.end()) { return this->GetScalarVariableNeeded( static_cast(iter - this->OriginalScalarVariableNames.begin())); } else { vtkErrorMacro( "GetScalarVariableNeeded: scalar variable name " << inVariableName << " does not exist"); return false; } } //------------------------------------------------------------------------------ int vtkExprTkFunctionParser::GetVectorVariableIndex(const std::string& inVariableName) { for (size_t i = 0; i < this->OriginalVectorVariableNames.size(); i++) { if (this->OriginalVectorVariableNames[i] == inVariableName) { return static_cast(i); } } return -1; } //------------------------------------------------------------------------------ bool vtkExprTkFunctionParser::GetVectorVariableNeeded(int i) { if (i < 0 || i >= static_cast(this->VectorVariableNeeded.size())) { return false; } return this->VectorVariableNeeded[i]; } //------------------------------------------------------------------------------ bool vtkExprTkFunctionParser::GetVectorVariableNeeded(const std::string& inVariableName) { std::vector::const_iterator iter = std::find(this->OriginalVectorVariableNames.begin(), this->OriginalVectorVariableNames.end(), inVariableName); if (iter != this->OriginalVectorVariableNames.end()) { return this->GetVectorVariableNeeded( static_cast(iter - this->OriginalVectorVariableNames.begin())); } else { vtkErrorMacro( "GetVectorVariableNeeded: scalar variable name " << inVariableName << " does not exist"); return false; } } VTK_ABI_NAMESPACE_END