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
|