Coverage Report

Created: 2026-02-09 06:25

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/muparser/src/muParser.cpp
Line
Count
Source
1
/*
2
3
   _____  __ _____________ _______  ______ ___________
4
  /     \|  |  \____ \__  \\_  __ \/  ___// __ \_  __ \
5
   |  Y Y  \  |  /  |_> > __ \|  | \/\___ \\  ___/|  | \/
6
   |__|_|  /____/|   __(____  /__|  /____  >\___  >__|
7
     \/      |__|       \/           \/     \/
8
   Copyright (C) 2004 - 2022 Ingo Berg
9
10
  Redistribution and use in source and binary forms, with or without modification, are permitted
11
  provided that the following conditions are met:
12
13
    * Redistributions of source code must retain the above copyright notice, this list of
14
    conditions and the following disclaimer.
15
    * Redistributions in binary form must reproduce the above copyright notice, this list of
16
    conditions and the following disclaimer in the documentation and/or other materials provided
17
    with the distribution.
18
19
  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
20
  IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
21
  FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
22
  CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23
  DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24
  DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
25
  IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
26
  OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27
*/
28
29
#include "muParser.h"
30
#include "muParserTemplateMagic.h"
31
32
//--- Standard includes ------------------------------------------------------------------------
33
#include <cmath>
34
#include <algorithm>
35
#include <numeric>
36
#include <cstdlib>
37
38
39
using namespace std;
40
41
/** \file
42
  \brief Implementation of the standard floating point parser.
43
*/
44
45
46
47
/** \brief Namespace for mathematical applications. */
48
namespace mu
49
{
50
  //---------------------------------------------------------------------------
51
  /** \brief Default value recognition callback.
52
    \param [in] a_szExpr Pointer to the expression
53
    \param [in, out] a_iPos Pointer to an index storing the current position within the expression
54
    \param [out] a_fVal Pointer where the value should be stored in case one is found.
55
    \return 1 if a value was found 0 otherwise.
56
  */
57
  int Parser::IsVal(const char_type* a_szExpr, int* a_iPos, value_type* a_fVal)
58
571k
  {
59
    // There is an issue with libc++ where it creates an error if a double value is followed by a character which
60
    // is the case when using postfix operators.
61
    //
62
    // http://cplusplus.github.io/LWG/lwg-defects.html#2381
63
    //
64
    // This happens only with libc++, not with libstdc++ (Gnu C++ standard library)
65
    // It seems that Macs are using libc++. This is causing #123. The fix below will fix #123
66
    // but is will break localization support and cause #136.
67
68
    // I'm disabling this fix. For systems using libc++ you must put a space between floating point numbers and postfix operators.
69
#if defined(__APPLE__) && defined(NEVERTRUE)
70
    try
71
    {
72
      std::size_t charsProcessed;
73
      value_type fVal = static_cast<value_type>(std::stod(string_type(a_szExpr), &charsProcessed));
74
      if (charsProcessed == 0)
75
        return 0;
76
77
      *a_iPos += (int)charsProcessed;
78
      *a_fVal = fVal;
79
      return 1;
80
    }
81
    catch (...)
82
    {
83
      return 0;
84
    }
85
#else
86
571k
    value_type fVal(0);
87
88
571k
    stringstream_type stream(a_szExpr);
89
571k
    stream.imbue(Parser::s_locale);
90
571k
    stream >> fVal;
91
571k
    stringstream_type::pos_type iEnd = stream.tellg(); // Position after reading
92
93
571k
    if (iEnd == (stringstream_type::pos_type) - 1)
94
7.34k
      return 0;
95
96
564k
    *a_iPos += (int)iEnd;
97
564k
    *a_fVal = fVal;
98
564k
    return 1;
99
571k
#endif    
100
571k
  }
101
102
103
  //---------------------------------------------------------------------------
104
  /** \brief Constructor.
105
106
    Call ParserBase class constructor and trigger Function, Operator and Constant initialization.
107
  */
108
  Parser::Parser()
109
3.51k
    :ParserBase()
110
3.51k
  {
111
3.51k
    AddValIdent(IsVal);
112
113
3.51k
    InitCharSets();
114
3.51k
    InitFun();
115
3.51k
    InitConst();
116
3.51k
    InitOprt();
117
3.51k
  }
118
119
  //---------------------------------------------------------------------------
120
  /** \brief Define the character sets.
121
    \sa DefineNameChars, DefineOprtChars, DefineInfixOprtChars
122
123
    This function is used for initializing the default character sets that define
124
    the characters to be useable in function and variable names and operators.
125
  */
126
  void Parser::InitCharSets()
127
3.51k
  {
128
3.51k
    DefineNameChars(_T("0123456789_abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"));
129
3.51k
    DefineOprtChars(_T("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ+-*^/?<>=#!$%&|~'_{}"));
130
3.51k
    DefineInfixOprtChars(_T("/+-*^?<>=#!$%&|~'_"));
131
3.51k
  }
132
133
  //---------------------------------------------------------------------------
134
  /** \brief Initialize the default functions. */
135
  void Parser::InitFun()
136
3.51k
  {
137
3.51k
    if (mu::TypeInfo<mu::value_type>::IsInteger())
138
0
    {
139
      // When setting MUP_BASETYPE to an integer type
140
      // Place functions for dealing with integer values here
141
      // ...
142
      // ...
143
      // ...
144
0
    }
145
3.51k
    else
146
3.51k
    {
147
      // trigonometric functions
148
3.51k
      DefineFun(_T("sin"), MathImpl<value_type>::Sin);
149
3.51k
      DefineFun(_T("cos"), MathImpl<value_type>::Cos);
150
3.51k
      DefineFun(_T("tan"), MathImpl<value_type>::Tan);
151
3.51k
      DefineFun(_T("cot"), MathImpl<value_type>::Cot);
152
      // arcus functions
153
3.51k
      DefineFun(_T("asin"), MathImpl<value_type>::ASin);
154
3.51k
      DefineFun(_T("acos"), MathImpl<value_type>::ACos);
155
3.51k
      DefineFun(_T("atan"), MathImpl<value_type>::ATan);
156
3.51k
      DefineFun(_T("atan2"), MathImpl<value_type>::ATan2);
157
      // hyperbolic functions
158
3.51k
      DefineFun(_T("sinh"), MathImpl<value_type>::Sinh);
159
3.51k
      DefineFun(_T("cosh"), MathImpl<value_type>::Cosh);
160
3.51k
      DefineFun(_T("tanh"), MathImpl<value_type>::Tanh);
161
3.51k
      DefineFun(_T("coth"), MathImpl<value_type>::Coth);
162
      // arcus hyperbolic functions
163
3.51k
      DefineFun(_T("asinh"), MathImpl<value_type>::ASinh);
164
3.51k
      DefineFun(_T("acosh"), MathImpl<value_type>::ACosh);
165
3.51k
      DefineFun(_T("atanh"), MathImpl<value_type>::ATanh);
166
      // Logarithm functions
167
3.51k
      DefineFun(_T("log2"), MathImpl<value_type>::Log2);
168
3.51k
      DefineFun(_T("log10"), MathImpl<value_type>::Log10);
169
3.51k
      DefineFun(_T("log"), MathImpl<value_type>::Log);
170
3.51k
      DefineFun(_T("ln"), MathImpl<value_type>::Log);
171
      // misc
172
3.51k
      DefineFun(_T("exp"), MathImpl<value_type>::Exp);
173
3.51k
      DefineFun(_T("sqrt"), MathImpl<value_type>::Sqrt);
174
3.51k
      DefineFun(_T("sign"), MathImpl<value_type>::Sign);
175
3.51k
      DefineFun(_T("rint"), MathImpl<value_type>::Rint);
176
3.51k
      DefineFun(_T("abs"), MathImpl<value_type>::Abs);
177
      // Functions with variable number of arguments
178
3.51k
      DefineFun(_T("sum"), MathImpl<value_type>::Sum);
179
3.51k
      DefineFun(_T("avg"), MathImpl<value_type>::Avg);
180
3.51k
      DefineFun(_T("min"), MathImpl<value_type>::Min);
181
3.51k
      DefineFun(_T("max"), MathImpl<value_type>::Max);
182
      // Random number
183
3.51k
      DefineFun(_T("rnd"), MathImpl<value_type>::Rnd, false);
184
3.51k
    }
185
3.51k
  }
186
187
  //---------------------------------------------------------------------------
188
  /** \brief Initialize constants.
189
190
    By default the parser recognizes two constants. Pi ("pi") and the Eulerian
191
    number ("_e").
192
  */
193
  void Parser::InitConst()
194
3.51k
  {
195
3.51k
    DefineConst(_T("_pi"), MathImpl<value_type>::CONST_PI);
196
3.51k
    DefineConst(_T("_e"), MathImpl<value_type>::CONST_E);
197
3.51k
  }
198
199
  //---------------------------------------------------------------------------
200
  /** \brief Initialize operators.
201
202
    By default only the unary minus operator is added.
203
  */
204
  void Parser::InitOprt()
205
3.51k
  {
206
3.51k
    DefineInfixOprt(_T("-"), MathImpl<value_type>::UnaryMinus);
207
3.51k
    DefineInfixOprt(_T("+"), MathImpl<value_type>::UnaryPlus);
208
3.51k
  }
209
210
  //---------------------------------------------------------------------------
211
  void Parser::OnDetectVar(string_type* /*pExpr*/, int& /*nStart*/, int& /*nEnd*/)
212
0
  {
213
    // this is just sample code to illustrate modifying variable names on the fly.
214
    // I'm not sure anyone really needs such a feature...
215
    /*
216
217
218
    string sVar(pExpr->begin()+nStart, pExpr->begin()+nEnd);
219
    string sRepl = std::string("_") + sVar + "_";
220
221
    int nOrigVarEnd = nEnd;
222
    cout << "variable detected!\n";
223
    cout << "  Expr: " << *pExpr << "\n";
224
    cout << "  Start: " << nStart << "\n";
225
    cout << "  End: " << nEnd << "\n";
226
    cout << "  Var: \"" << sVar << "\"\n";
227
    cout << "  Repl: \"" << sRepl << "\"\n";
228
    nEnd = nStart + sRepl.length();
229
    cout << "  End: " << nEnd << "\n";
230
    pExpr->replace(pExpr->begin()+nStart, pExpr->begin()+nOrigVarEnd, sRepl);
231
    cout << "  New expr: " << *pExpr << "\n";
232
    */
233
0
  }
234
235
  //---------------------------------------------------------------------------
236
  /** \brief Numerically differentiate with regard to a variable.
237
    \param [in] a_Var Pointer to the differentiation variable.
238
    \param [in] a_fPos Position at which the differentiation should take place.
239
    \param [in] a_fEpsilon Epsilon used for the numerical differentiation.
240
241
    Numerical differentiation uses a 5 point operator yielding a 4th order
242
    formula. The default value for epsilon is 0.00074 which is
243
    numeric_limits<double>::epsilon() ^ (1/5).
244
  */
245
  value_type Parser::Diff(value_type* a_Var, value_type  a_fPos, value_type  a_fEpsilon) const
246
0
  {
247
0
    value_type fRes(0);
248
0
    value_type fBuf(*a_Var);
249
0
    value_type f[4] = { 0,0,0,0 };
250
0
    value_type fEpsilon(a_fEpsilon);
251
252
    // Backwards compatible calculation of epsilon inc case the user doesn't provide
253
    // his own epsilon
254
0
    if (fEpsilon == 0)
255
0
      fEpsilon = (a_fPos == 0) ? (value_type)1e-10 : (value_type)1e-7 * a_fPos;
256
257
0
    *a_Var = a_fPos + 2 * fEpsilon;  f[0] = Eval();
258
0
    *a_Var = a_fPos + 1 * fEpsilon;  f[1] = Eval();
259
0
    *a_Var = a_fPos - 1 * fEpsilon;  f[2] = Eval();
260
0
    *a_Var = a_fPos - 2 * fEpsilon;  f[3] = Eval();
261
0
    *a_Var = fBuf; // restore variable
262
263
0
    fRes = (-f[0] + 8 * f[1] - 8 * f[2] + f[3]) / (12 * fEpsilon);
264
0
    return fRes;
265
0
  }
266
} // namespace mu