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

Generated by: LCOV version 1.14