LCOV - code coverage report
Current view: top level - frmts/vrt - vrtexpression_muparser.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 86 96 89.6 %
Date: 2025-05-31 00:00:17 Functions: 14 14 100.0 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  Virtual GDAL Datasets
       4             :  * Purpose:  Implementation of GDALExpressionEvaluator.
       5             :  * Author:   Daniel Baston
       6             :  *
       7             :  ******************************************************************************
       8             :  * Copyright (c) 2024, ISciences LLC
       9             :  *
      10             :  * SPDX-License-Identifier: MIT
      11             :  ****************************************************************************/
      12             : 
      13             : #include "vrtexpression.h"
      14             : #include "cpl_string.h"
      15             : 
      16             : #include <limits>
      17             : #include <map>
      18             : #include <optional>
      19             : #include "muparser_header.h"
      20             : 
      21             : namespace gdal
      22             : {
      23             : 
      24             : /*! @cond Doxygen_Suppress */
      25             : 
      26         177 : static std::optional<std::string> Sanitize(const std::string &osVariable)
      27             : {
      28             :     // muparser does not allow characters '[' or ']' which we use to emulate
      29             :     // vectors. Replace these with a combination of underscores
      30         177 :     auto from = osVariable.find('[');
      31         177 :     if (from != std::string::npos)
      32             :     {
      33          80 :         auto to = osVariable.find(']');
      34          80 :         if (to != std::string::npos)
      35             :         {
      36         160 :             auto sanitized = std::string("__") + osVariable.substr(0, from) +
      37         160 :                              +"__" +
      38         240 :                              osVariable.substr(from + 1, to - from - 1) + "__";
      39          80 :             return sanitized;
      40             :         }
      41             :     }
      42             : 
      43          97 :     return std::nullopt;
      44             : }
      45             : 
      46          64 : static void ReplaceVariable(std::string &expression,
      47             :                             const std::string &variable,
      48             :                             const std::string &sanitized)
      49             : {
      50          64 :     std::string::size_type seekPos = 0;
      51          64 :     auto pos = expression.find(variable, seekPos);
      52         183 :     while (pos != std::string::npos)
      53             :     {
      54         119 :         auto end = pos + variable.size();
      55             : 
      56         219 :         if (pos == 0 ||
      57         100 :             (!std::isalnum(expression[pos - 1]) && expression[pos - 1] != '_'))
      58             :         {
      59             :             expression =
      60          76 :                 expression.substr(0, pos) + sanitized + expression.substr(end);
      61             :         }
      62             : 
      63         119 :         seekPos = end;
      64         119 :         pos = expression.find(variable, seekPos);
      65             :     }
      66          64 : }
      67             : 
      68             : class MuParserExpression::Impl
      69             : {
      70             :   public:
      71          53 :     explicit Impl(std::string_view osExpression)
      72          53 :         : m_osExpression(std::string(osExpression))
      73             :     {
      74          53 :     }
      75             : 
      76         148 :     void Register(std::string_view osVariable, double *pdfValue)
      77             :     {
      78             :         try
      79             :         {
      80         164 :             m_oParser.DefineVar(std::string(osVariable), pdfValue);
      81             :         }
      82           8 :         catch (const mu::Parser::exception_type &)
      83             :         {
      84           8 :             CPLError(CE_Failure, CPLE_AppDefined, "Invalid variable name: %s",
      85          16 :                      std::string(osVariable).c_str());
      86           8 :             m_bCompileFailed = true;
      87             :         }
      88         148 :     }
      89             : 
      90          61 :     CPLErr Compile()
      91             :     {
      92          61 :         if (m_bCompileFailed)
      93             :         {
      94           8 :             return CE_Failure;
      95             :         }
      96             : 
      97             :         // On some platforms muparser does not seem to parse "nan" as a floating
      98             :         // point literal.
      99             :         try
     100             :         {
     101          53 :             m_oParser.DefineConst("nan",
     102             :                                   std::numeric_limits<double>::quiet_NaN());
     103          53 :             m_oParser.DefineConst("NaN",
     104             :                                   std::numeric_limits<double>::quiet_NaN());
     105             :         }
     106           0 :         catch (const mu::Parser::exception_type &)
     107             :         {
     108             :         }
     109             : 
     110             :         try
     111             :         {
     112         106 :             std::string tmpExpression(m_osExpression);
     113             : 
     114         117 :             for (const auto &[osFrom, osTo] : m_oSubstitutions)
     115             :             {
     116          64 :                 ReplaceVariable(tmpExpression, osFrom, osTo);
     117             :             }
     118             : 
     119          53 :             m_oParser.SetExpr(tmpExpression);
     120             :         }
     121           0 :         catch (const mu::Parser::exception_type &e)
     122             :         {
     123           0 :             CPLError(CE_Failure, CPLE_AppDefined, "%s", e.GetMsg().c_str());
     124           0 :             return CE_Failure;
     125             :         }
     126           0 :         catch (const std::exception &e)
     127             :         {
     128           0 :             CPLError(CE_Failure, CPLE_AppDefined, "%s", e.what());
     129           0 :             return CE_Failure;
     130             :         }
     131             : 
     132          53 :         return CE_None;
     133             :     }
     134             : 
     135       18771 :     CPLErr Evaluate()
     136             :     {
     137       18771 :         if (!m_bIsCompiled)
     138             :         {
     139          53 :             if (auto eErr = Compile(); eErr != CE_None)
     140             :             {
     141           8 :                 return eErr;
     142             :             }
     143             : 
     144          45 :             m_bIsCompiled = true;
     145             :         }
     146             : 
     147             :         try
     148             :         {
     149             :             int nResults;
     150       18763 :             const double *dfResults = m_oParser.Eval(nResults);
     151       18762 :             m_adfResults.resize(nResults);
     152       18762 :             std::copy(dfResults, dfResults + nResults, m_adfResults.begin());
     153             :         }
     154           1 :         catch (const mu::Parser::exception_type &e)
     155             :         {
     156           1 :             CPLError(CE_Failure, CPLE_AppDefined, "%s", e.GetMsg().c_str());
     157           1 :             return CE_Failure;
     158             :         }
     159           0 :         catch (const std::exception &e)
     160             :         {
     161           0 :             CPLError(CE_Failure, CPLE_AppDefined, "%s", e.what());
     162           0 :             return CE_Failure;
     163             :         }
     164             : 
     165       18762 :         return CE_None;
     166             :     }
     167             : 
     168             :     const CPLString m_osExpression;
     169             :     std::map<CPLString, CPLString> m_oSubstitutions{};
     170             :     mu::Parser m_oParser{};
     171             :     std::vector<double> m_adfResults{1};
     172             :     bool m_bIsCompiled = false;
     173             :     bool m_bCompileFailed = false;
     174             : };
     175             : 
     176          53 : MuParserExpression::MuParserExpression(std::string_view osExpression)
     177          53 :     : m_pImpl{std::make_unique<Impl>(osExpression)}
     178             : 
     179             : {
     180          53 : }
     181             : 
     182         106 : MuParserExpression::~MuParserExpression()
     183             : {
     184         106 : }
     185             : 
     186           8 : CPLErr MuParserExpression::Compile()
     187             : {
     188           8 :     return m_pImpl->Compile();
     189             : }
     190             : 
     191         148 : void MuParserExpression::RegisterVariable(std::string_view osVariable,
     192             :                                           double *pdfValue)
     193             : {
     194         296 :     auto sanitized = Sanitize(std::string(osVariable));
     195         148 :     if (sanitized.has_value())
     196             :     {
     197          51 :         m_pImpl->m_oSubstitutions[std::string(osVariable)] = sanitized.value();
     198             :     }
     199         148 :     m_pImpl->Register(sanitized.value_or(std::string(osVariable)), pdfValue);
     200         148 : }
     201             : 
     202           7 : void MuParserExpression::RegisterVector(std::string_view osVariable,
     203             :                                         std::vector<double> *padfValues)
     204             : {
     205             :     // muparser does not support vector variables, so we simulate them
     206             :     // by creating a scalar variable for each element, and then replacing
     207             :     // the name of the vector by a list of its elements before compiling
     208             :     // the expression.
     209          14 :     CPLString osElementVarName;
     210          14 :     CPLString osElementsList;
     211           7 :     std::string osVectorVarName(osVariable);
     212             : 
     213             :     int nElementVarNameLength = static_cast<int>(
     214           7 :         4 + osVectorVarName.size() + std::log10(padfValues->size()));
     215           7 :     osElementsList.reserve(padfValues->size() *
     216           7 :                            (1 + nElementVarNameLength));  // +1 for commas
     217             : 
     218          36 :     for (std::size_t i = 0; i < padfValues->size(); i++)
     219             :     {
     220             :         osElementVarName.Printf("%s[%d]", osVectorVarName.c_str(),
     221          29 :                                 static_cast<int>(i));
     222          29 :         osElementVarName = Sanitize(osElementVarName).value();
     223          29 :         RegisterVariable(osElementVarName, padfValues->data() + i);
     224             : 
     225          29 :         if (i > 0)
     226             :         {
     227          22 :             osElementsList += ",";
     228             :         }
     229          29 :         osElementsList += osElementVarName;
     230             :     }
     231             : 
     232           7 :     m_pImpl->m_oSubstitutions[osVectorVarName] = std::move(osElementsList);
     233           7 : }
     234             : 
     235       18771 : CPLErr MuParserExpression::Evaluate()
     236             : {
     237       18771 :     return m_pImpl->Evaluate();
     238             : }
     239             : 
     240       18777 : const std::vector<double> &MuParserExpression::Results() const
     241             : {
     242       18777 :     return m_pImpl->m_adfResults;
     243             : }
     244             : 
     245             : /*! @endcond Doxygen_Suppress */
     246             : 
     247             : }  // namespace gdal

Generated by: LCOV version 1.14