Coverage Report

Created: 2026-03-15 06:13

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/muparser/src/muParserTokenReader.cpp
Line
Count
Source
1
/*
2
3
   _____  __ _____________ _______  ______ ___________
4
  /     \|  |  \____ \__  \\_  __ \/  ___// __ \_  __ \
5
   |  Y Y  \  |  /  |_> > __ \|  | \/\___ \\  ___/|  | \/
6
   |__|_|  /____/|   __(____  /__|  /____  >\___  >__|
7
     \/      |__|       \/           \/     \/
8
   Copyright (C) 2026 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 <cstdio>
30
#include <cstring>
31
#include <map>
32
#include <stack>
33
#include <string>
34
35
#include "muParserTokenReader.h"
36
#include "muParserTemplateMagic.h"
37
#include "muParserBase.h"
38
39
#if defined(_MSC_VER)
40
  #pragma warning(push)
41
  #pragma warning(disable : 26812) 
42
#endif
43
44
/** \file
45
  \brief This file contains the parser token reader implementation.
46
*/
47
48
49
namespace mu
50
{
51
52
  // Forward declaration
53
  class ParserBase;
54
55
  /** \brief Copy constructor.
56
57
    \sa Assign
58
    \throw nothrow
59
  */
60
  ParserTokenReader::ParserTokenReader(const ParserTokenReader& a_Reader)
61
0
  {
62
0
    Assign(a_Reader);
63
0
  }
64
65
66
  /** \brief Assignment operator.
67
68
    Self assignment will be suppressed otherwise #Assign is called.
69
70
    \param a_Reader Object to copy to this token reader.
71
    \throw nothrow
72
  */
73
  ParserTokenReader& ParserTokenReader::operator=(const ParserTokenReader& a_Reader)
74
0
  {
75
0
    if (&a_Reader != this)
76
0
      Assign(a_Reader);
77
78
0
    return *this;
79
0
  }
80
81
82
  /** \brief Assign state of a token reader to this token reader.
83
84
    \param a_Reader Object from which the state should be copied.
85
    \throw nothrow
86
  */
87
  void ParserTokenReader::Assign(const ParserTokenReader& a_Reader)
88
0
  {
89
0
    m_pParser = a_Reader.m_pParser;
90
0
    m_strFormula = a_Reader.m_strFormula;
91
0
    m_iPos = a_Reader.m_iPos;
92
0
    m_iSynFlags = a_Reader.m_iSynFlags;
93
94
0
    m_UsedVar = a_Reader.m_UsedVar;
95
0
    m_pFunDef = a_Reader.m_pFunDef;
96
0
    m_pConstDef = a_Reader.m_pConstDef;
97
0
    m_pVarDef = a_Reader.m_pVarDef;
98
0
    m_pStrVarDef = a_Reader.m_pStrVarDef;
99
0
    m_pPostOprtDef = a_Reader.m_pPostOprtDef;
100
0
    m_pInfixOprtDef = a_Reader.m_pInfixOprtDef;
101
0
    m_pOprtDef = a_Reader.m_pOprtDef;
102
0
    m_bIgnoreUndefVar = a_Reader.m_bIgnoreUndefVar;
103
0
    m_vIdentFun = a_Reader.m_vIdentFun;
104
0
    m_pFactory = a_Reader.m_pFactory;
105
0
    m_pFactoryData = a_Reader.m_pFactoryData;
106
0
    m_bracketStack = a_Reader.m_bracketStack;
107
0
    m_cArgSep = a_Reader.m_cArgSep;
108
0
    m_fZero = a_Reader.m_fZero;
109
0
    m_lastTok = a_Reader.m_lastTok;
110
0
  }
111
112
113
  /** \brief Constructor.
114
115
    Create a Token reader and bind it to a parser object.
116
117
    \pre [assert] a_pParser may not be NULL
118
    \post #m_pParser==a_pParser
119
    \param a_pParent Parent parser object of the token reader.
120
  */
121
  ParserTokenReader::ParserTokenReader(ParserBase* a_pParent)
122
3.82k
    :m_pParser(a_pParent)
123
3.82k
    , m_strFormula()
124
3.82k
    , m_iPos(0)
125
3.82k
    , m_iSynFlags(0)
126
3.82k
    , m_bIgnoreUndefVar(false)
127
3.82k
    , m_pFunDef(nullptr)
128
3.82k
    , m_pPostOprtDef(nullptr)
129
3.82k
    , m_pInfixOprtDef(nullptr)
130
3.82k
    , m_pOprtDef(nullptr)
131
3.82k
    , m_pConstDef(nullptr)
132
3.82k
    , m_pStrVarDef(nullptr)
133
3.82k
    , m_pVarDef(nullptr)
134
3.82k
    , m_pFactory(nullptr)
135
3.82k
    , m_pFactoryData(nullptr)
136
3.82k
    , m_vIdentFun()
137
3.82k
    , m_UsedVar()
138
3.82k
    , m_fZero(0)
139
3.82k
    , m_bracketStack()
140
3.82k
    , m_lastTok()
141
3.82k
    , m_cArgSep(',')
142
3.82k
  {
143
3.82k
    MUP_ASSERT(m_pParser != nullptr);
144
3.82k
    SetParent(m_pParser);
145
3.82k
  }
146
147
148
  /** \brief Create instance of a ParserTokenReader identical with this
149
        and return its pointer.
150
151
    This is a factory method the calling function must take care of the object destruction.
152
153
    \return A new ParserTokenReader object.
154
    \throw nothrow
155
  */
156
  ParserTokenReader* ParserTokenReader::Clone(ParserBase* a_pParent) const
157
0
  {
158
0
    std::unique_ptr<ParserTokenReader> ptr(new ParserTokenReader(*this));
159
0
    ptr->SetParent(a_pParent);
160
0
    return ptr.release();
161
0
  }
162
163
164
  ParserTokenReader::token_type& ParserTokenReader::SaveBeforeReturn(const token_type& tok)
165
2.20M
  {
166
2.20M
    m_lastTok = tok;
167
2.20M
    return m_lastTok;
168
2.20M
  }
169
170
171
  void ParserTokenReader::AddValIdent(identfun_type a_pCallback)
172
3.82k
  {
173
    // Use push_front is used to give user defined callbacks a higher priority than
174
    // the built in ones. Otherwise reading hex numbers would not work
175
    // since the "0" in "0xff" would always be read first making parsing of 
176
    // the rest impossible.
177
    // reference:
178
    // http://sourceforge.net/projects/muparser/forums/forum/462843/topic/4824956
179
3.82k
    m_vIdentFun.push_front(a_pCallback);
180
3.82k
  }
181
182
183
  void ParserTokenReader::SetVarCreator(facfun_type a_pFactory, void* pUserData)
184
0
  {
185
0
    m_pFactory = a_pFactory;
186
0
    m_pFactoryData = pUserData;
187
0
  }
188
189
190
  /** \brief Return the current position of the token reader in the formula string.
191
192
    \return #m_iPos
193
    \throw nothrow
194
  */
195
  int ParserTokenReader::GetPos() const
196
239
  {
197
239
    return m_iPos;
198
239
  }
199
200
201
  /** \brief Return a reference to the formula.
202
203
    \return #m_strFormula
204
    \throw nothrow
205
  */
206
  const string_type& ParserTokenReader::GetExpr() const
207
9.71k
  {
208
9.71k
    return m_strFormula;
209
9.71k
  }
210
211
212
  /** \brief Return a map containing the used variables only. */
213
  varmap_type& ParserTokenReader::GetUsedVar()
214
0
  {
215
0
    return m_UsedVar;
216
0
  }
217
218
219
  /** \brief Initialize the token Reader.
220
221
    Sets the formula position index to zero and set Syntax flags to default for initial formula parsing.
222
    \pre [assert] triggered if a_szFormula==0
223
  */
224
  void ParserTokenReader::SetFormula(const string_type& a_strFormula)
225
3.79k
  {
226
3.79k
    m_strFormula = a_strFormula;
227
3.79k
    ReInit();
228
3.79k
  }
229
230
231
  /** \brief Set Flag that controls behaviour in case of undefined variables being found.
232
233
    If true, the parser does not throw an exception if an undefined variable is found.
234
    otherwise it does. This variable is used internally only!
235
    It suppresses a "undefined variable" exception in GetUsedVar().
236
    Those function should return a complete list of variables including
237
    those the are not defined by the time of it's call.
238
  */
239
  void ParserTokenReader::IgnoreUndefVar(bool bIgnore)
240
0
  {
241
0
    m_bIgnoreUndefVar = bIgnore;
242
0
  }
243
244
245
  /** \brief Reset the token reader to the start of the formula.
246
247
    The syntax flags will be reset to a value appropriate for the
248
    start of a formula.
249
    \post #m_iPos==0, #m_iSynFlags = noOPT | noBC | noPOSTOP | noSTR
250
    \throw nothrow
251
    \sa ESynCodes
252
  */
253
  void ParserTokenReader::ReInit()
254
137k
  {
255
137k
    m_iPos = 0;
256
137k
    m_iSynFlags = sfSTART_OF_LINE;
257
137k
    m_bracketStack = std::stack<int>();
258
137k
    m_UsedVar.clear();
259
137k
    m_lastTok = token_type();
260
137k
  }
261
262
263
  /** \brief Read the next token from the string. */
264
  ParserTokenReader::token_type ParserTokenReader::ReadNextToken()
265
2.20M
  {
266
2.20M
    MUP_ASSERT(m_pParser != nullptr);
267
268
2.20M
    const char_type* szExpr = m_strFormula.c_str();
269
2.20M
    token_type tok;
270
271
    // Ignore all non printable characters when reading the expression
272
2.20M
    while (szExpr[m_iPos] > 0 && szExpr[m_iPos] <= 0x20)
273
4.34k
    {
274
      // 14-31 are control characters. I donÄt want to have to deal with such strings at all!
275
      // (see https://en.cppreference.com/w/cpp/string/byte/isprint)
276
4.34k
      if (szExpr[m_iPos] >= 14 && szExpr[m_iPos] <= 31)
277
13
        Error(ecINVALID_CHARACTERS_FOUND, m_iPos);
278
279
4.34k
      ++m_iPos;
280
4.34k
    }
281
282
    // Check for end of formula
283
2.20M
    if (IsEOF(tok))
284
1.90k
      return SaveBeforeReturn(tok);
285
286
    // Check for user defined binary operator
287
2.20M
    if (IsOprt(tok))
288
0
      return SaveBeforeReturn(tok);
289
290
    // Check for function token
291
2.20M
    if (IsFunTok(tok))
292
12.7k
      return SaveBeforeReturn(tok);
293
294
    // Check built in operators / tokens
295
2.19M
    if (IsBuiltIn(tok))
296
1.31M
      return SaveBeforeReturn(tok);
297
298
    // Check for function argument separators
299
876k
    if (IsArgSep(tok))
300
284k
      return SaveBeforeReturn(tok);
301
302
    // Check for values / constant tokens
303
592k
    if (IsValTok(tok))
304
578k
      return SaveBeforeReturn(tok);
305
306
    // Check for variable tokens
307
13.5k
    if (IsVarTok(tok))
308
0
      return SaveBeforeReturn(tok);
309
310
    // Check for string variables
311
13.5k
    if (IsStrVarTok(tok))
312
0
      return SaveBeforeReturn(tok);
313
314
    // Check for String tokens
315
13.5k
    if (IsString(tok))
316
11.8k
      return SaveBeforeReturn(tok);
317
318
    // Check for unary operators
319
1.67k
    if (IsInfixOpTok(tok))
320
0
      return SaveBeforeReturn(tok);
321
322
    // Check for unary operators
323
1.67k
    if (IsPostOpTok(tok))
324
0
      return SaveBeforeReturn(tok);
325
326
    // Check String for undefined variable token. Done only if a 
327
    // flag is set indicating to ignore undefined variables.
328
    // This is a way to conditionally avoid an error if 
329
    // undefined variables occur. 
330
    // (The GetUsedVar function must suppress the error for
331
    // undefined variables in order to collect all variable 
332
    // names including the undefined ones.)
333
1.67k
    if ((m_bIgnoreUndefVar || m_pFactory) && IsUndefVarTok(tok))
334
0
      return SaveBeforeReturn(tok);
335
336
    // Check for unknown token
337
    // 
338
    // !!! From this point on there is no exit without an exception possible...
339
    // 
340
1.67k
    string_type strTok;
341
1.67k
    auto iEnd = ExtractToken(m_pParser->ValidNameChars(), strTok, (std::size_t)m_iPos);
342
1.67k
    if (iEnd != m_iPos)
343
323
      Error(ecUNASSIGNABLE_TOKEN, m_iPos, strTok);
344
345
1.67k
    Error(ecUNASSIGNABLE_TOKEN, m_iPos, m_strFormula.substr(m_iPos));
346
1.67k
    return token_type(); // never reached
347
1.67k
  }
348
349
350
  void ParserTokenReader::SetParent(ParserBase* a_pParent)
351
3.82k
  {
352
3.82k
    m_pParser = a_pParent;
353
3.82k
    m_pFunDef = &a_pParent->m_FunDef;
354
3.82k
    m_pOprtDef = &a_pParent->m_OprtDef;
355
3.82k
    m_pInfixOprtDef = &a_pParent->m_InfixOprtDef;
356
3.82k
    m_pPostOprtDef = &a_pParent->m_PostOprtDef;
357
3.82k
    m_pVarDef = &a_pParent->m_VarDef;
358
3.82k
    m_pStrVarDef = &a_pParent->m_StrVarDef;
359
3.82k
    m_pConstDef = &a_pParent->m_ConstDef;
360
3.82k
  }
361
362
363
  /** \brief Extract all characters that belong to a certain charset.
364
365
    \param a_szCharSet [in] Const char array of the characters allowed in the token.
366
    \param a_strTok [out]  The string that consists entirely of characters listed in a_szCharSet.
367
    \param a_iPos [in] Position in the string from where to start reading.
368
    \return The Position of the first character not listed in a_szCharSet.
369
    \throw nothrow
370
  */
371
  int ParserTokenReader::ExtractToken(const char_type* a_szCharSet, string_type& a_sTok, std::size_t a_iPos) const
372
4.71M
  {
373
4.71M
    auto iEnd = m_strFormula.find_first_not_of(a_szCharSet, a_iPos);
374
375
4.71M
    if (iEnd == string_type::npos)
376
0
      iEnd = m_strFormula.length();
377
378
    // Assign token string if there was something found
379
4.71M
    if (a_iPos != iEnd)
380
1.18M
      a_sTok = string_type(m_strFormula.begin() + a_iPos, m_strFormula.begin() + iEnd);
381
382
4.71M
    return static_cast<int>(iEnd);
383
4.71M
  }
384
385
386
  /** \brief Check Expression for the presence of a binary operator token.
387
388
    Userdefined binary operator "++" gives inconsistent parsing result for
389
    the equations "a++b" and "a ++ b" if alphabetic characters are allowed
390
    in operator tokens. To avoid this this function checks specifically
391
    for operator tokens.
392
  */
393
  int ParserTokenReader::ExtractOperatorToken(string_type& a_sTok, std::size_t a_iPos) const
394
2.20M
  {
395
    // Changed as per Issue 6: https://code.google.com/p/muparser/issues/detail?id=6
396
2.20M
    auto iEnd = m_strFormula.find_first_not_of(m_pParser->ValidOprtChars(), a_iPos);
397
2.20M
    if (iEnd == string_type::npos)
398
0
      iEnd = m_strFormula.length();
399
400
    // Assign token string if there was something found
401
2.20M
    if (a_iPos != iEnd)
402
287k
    {
403
287k
      a_sTok = string_type(m_strFormula.begin() + a_iPos, m_strFormula.begin() + iEnd);
404
287k
      return static_cast<int>(iEnd);
405
287k
    }
406
1.91M
    else
407
1.91M
    {
408
      // There is still the chance of having to deal with an operator consisting exclusively
409
      // of alphabetic characters.
410
1.91M
      return ExtractToken(_T("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"), a_sTok, (std::size_t)a_iPos);
411
1.91M
    }
412
2.20M
  }
413
414
415
  /** \brief Check if a built in operator or other token can be found
416
    \param a_Tok  [out] Operator token if one is found. This can either be a binary operator or an infix operator token.
417
    \return true if an operator token has been found.
418
  */
419
  bool ParserTokenReader::IsBuiltIn(token_type& a_Tok)
420
2.18M
  {
421
2.18M
    const char_type** const pOprtDef = m_pParser->GetOprtDef(),
422
2.18M
      * const szFormula = m_strFormula.c_str();
423
424
    // Compare token with function and operator strings
425
    // check string for operator/function
426
36.1M
    for (int i = 0; pOprtDef[i]; i++)
427
35.2M
    {
428
35.2M
      std::size_t len(std::char_traits<char_type>::length(pOprtDef[i]));
429
35.2M
      if (string_type(pOprtDef[i]) == string_type(szFormula + m_iPos, szFormula + m_iPos + len))
430
1.31M
      {
431
1.31M
        switch (i)
432
1.31M
        {
433
2.91k
        case cmLAND:
434
5.93k
        case cmLOR:
435
11.8k
        case cmLT:
436
14.3k
        case cmGT:
437
15.4k
        case cmLE:
438
17.0k
        case cmGE:
439
18.1k
        case cmNEQ:
440
19.9k
        case cmEQ:
441
30.7k
        case cmADD:
442
38.6k
        case cmSUB:
443
43.5k
        case cmMUL:
444
45.6k
        case cmDIV:
445
122k
        case cmPOW:
446
122k
        case cmASSIGN:
447
          // The assignment operator need special treatment
448
122k
          if (i == cmASSIGN && m_iSynFlags & noASSIGN)
449
6
            Error(ecUNEXPECTED_OPERATOR, m_iPos, pOprtDef[i]);
450
451
122k
          if (!m_pParser->HasBuiltInOprt()) continue;
452
122k
          if (m_iSynFlags & noOPT)
453
9.53k
          {
454
            // Maybe its an infix operator not an operator
455
            // Both operator types can share characters in 
456
            // their identifiers
457
9.53k
            if (IsInfixOpTok(a_Tok))
458
9.41k
              return true;
459
460
118
            Error(ecUNEXPECTED_OPERATOR, m_iPos, pOprtDef[i]);
461
118
          }
462
463
113k
          m_iSynFlags = noBC | noOPT | noARG_SEP | noPOSTOP | noASSIGN | noIF | noELSE | noEND;
464
113k
          break;
465
466
957k
        case cmBO:
467
957k
          if (m_iSynFlags & noBO)
468
15
            Error(ecUNEXPECTED_PARENS, m_iPos, pOprtDef[i]);
469
470
957k
          if (m_lastTok.GetCode() == cmFUNC)
471
12.7k
            m_iSynFlags = noOPT | noEND | noARG_SEP | noPOSTOP | noASSIGN | noIF | noELSE;
472
945k
          else
473
945k
            m_iSynFlags = noBC | noOPT | noEND | noARG_SEP | noPOSTOP | noASSIGN | noIF | noELSE;
474
475
957k
          m_bracketStack.push(cmBO);
476
957k
          break;
477
478
41.0k
        case cmBC:
479
41.0k
          if (m_iSynFlags & noBC)
480
7
            Error(ecUNEXPECTED_PARENS, m_iPos, pOprtDef[i]);
481
482
41.0k
          m_iSynFlags = noBO | noVAR | noVAL | noFUN | noINFIXOP | noSTR | noASSIGN;
483
484
41.0k
          if (!m_bracketStack.empty())
485
41.0k
            m_bracketStack.pop();
486
21
          else
487
21
            Error(ecUNEXPECTED_PARENS, m_iPos, pOprtDef[i]);
488
41.0k
          break;
489
490
43.4k
        case cmELSE:
491
43.4k
          if (m_iSynFlags & noELSE)
492
12
            Error(ecUNEXPECTED_CONDITIONAL, m_iPos, pOprtDef[i]);
493
494
43.4k
          m_iSynFlags = noBC | noPOSTOP | noEND | noOPT | noIF | noELSE | noSTR;
495
43.4k
          break;
496
497
148k
        case cmIF:
498
148k
          if (m_iSynFlags & noIF)
499
23
            Error(ecUNEXPECTED_CONDITIONAL, m_iPos, pOprtDef[i]);
500
501
148k
          m_iSynFlags = noBC | noPOSTOP | noEND | noOPT | noIF | noELSE | noSTR;
502
148k
          break;
503
504
0
        default:      // The operator is listed in c_DefaultOprt, but not here. This is a bad thing...
505
0
          Error(ecINTERNAL_ERROR);
506
1.31M
        } // switch operator id
507
508
1.30M
        m_iPos += (int)len;
509
1.30M
        a_Tok.Set((ECmdCode)i, pOprtDef[i]);
510
1.30M
        return true;
511
1.31M
      } // if operator string found
512
35.2M
    } // end of for all operator strings
513
514
875k
    return false;
515
2.18M
  }
516
517
518
  bool ParserTokenReader::IsArgSep(token_type& a_Tok)
519
875k
  {
520
875k
    const char_type* szFormula = m_strFormula.c_str();
521
522
875k
    if (szFormula[m_iPos] == m_cArgSep)
523
284k
    {
524
      // copy the separator into null terminated string
525
284k
      char_type szSep[2];
526
284k
      szSep[0] = m_cArgSep;
527
284k
      szSep[1] = 0;
528
529
284k
      if (m_iSynFlags & noARG_SEP)
530
6
        Error(ecUNEXPECTED_ARG_SEP, m_iPos, szSep);
531
532
284k
      m_iSynFlags = noBC | noOPT | noEND | noARG_SEP | noPOSTOP | noASSIGN;
533
284k
      m_iPos++;
534
284k
      a_Tok.Set(cmARG_SEP, szSep);
535
284k
      return true;
536
284k
    }
537
538
591k
    return false;
539
875k
  }
540
541
542
  /** \brief Check for End of Formula.
543
544
    \return true if an end of formula is found false otherwise.
545
    \param a_Tok [out] If an eof is found the corresponding token will be stored there.
546
    \throw nothrow
547
    \sa IsOprt, IsFunTok, IsStrFunTok, IsValTok, IsVarTok, IsString, IsInfixOpTok, IsPostOpTok
548
  */
549
  bool ParserTokenReader::IsEOF(token_type& a_Tok)
550
2.20M
  {
551
2.20M
    const char_type* szFormula = m_strFormula.c_str();
552
553
    // check for EOF
554
2.20M
    if (!szFormula[m_iPos] /*|| szFormula[m_iPos] == '\n'*/)
555
2.71k
    {
556
2.71k
      if (m_iSynFlags & noEND)
557
642
        Error(ecUNEXPECTED_EOF, m_iPos);
558
559
2.71k
      if (!m_bracketStack.empty())
560
170
        Error(ecMISSING_PARENS, m_iPos, _T(")"));
561
562
2.71k
      m_iSynFlags = 0;
563
2.71k
      a_Tok.Set(cmEND);
564
2.71k
      return true;
565
2.71k
    }
566
567
2.20M
    return false;
568
2.20M
  }
569
570
571
  /** \brief Check if a string position contains a unary infix operator.
572
    \return true if a function token has been found false otherwise.
573
  */
574
  bool ParserTokenReader::IsInfixOpTok(token_type& a_Tok)
575
10.0k
  {
576
10.0k
    string_type sTok;
577
10.0k
    auto iEnd = ExtractToken(m_pParser->ValidInfixOprtChars(), sTok, (std::size_t)m_iPos);
578
10.0k
    if (iEnd == m_iPos)
579
347
      return false;
580
581
    // iterate over all postfix operator strings
582
9.72k
    funmap_type::const_reverse_iterator it = m_pInfixOprtDef->rbegin();
583
15.4k
    for (; it != m_pInfixOprtDef->rend(); ++it)
584
15.2k
    {
585
15.2k
      if (sTok.find(it->first) != 0)
586
5.75k
        continue;
587
588
9.44k
      a_Tok.Set(it->second, it->first);
589
9.44k
      m_iPos += (int)it->first.length();
590
591
9.44k
      if (m_iSynFlags & noINFIXOP)
592
30
        Error(ecUNEXPECTED_OPERATOR, m_iPos, a_Tok.GetAsString());
593
594
9.44k
      m_iSynFlags |= noPOSTOP | noINFIXOP | noOPT | noBC | noSTR | noASSIGN | noARG_SEP;
595
9.44k
      return true;
596
15.2k
    }
597
598
276
    return false;
599
9.72k
  }
600
601
602
  /** \brief Check whether the token at a given position is a function token.
603
    \param a_Tok [out] If a value token is found it will be placed here.
604
    \throw ParserException if Syntaxflags do not allow a function at a_iPos
605
    \return true if a function token has been found false otherwise.
606
    \pre [assert] m_pParser!=0
607
  */
608
  bool ParserTokenReader::IsFunTok(token_type& a_Tok)
609
2.20M
  {
610
2.20M
    string_type strTok;
611
2.20M
    auto iEnd = ExtractToken(m_pParser->ValidNameChars(), strTok, (std::size_t)m_iPos);
612
2.20M
    if (iEnd == m_iPos)
613
1.61M
      return false;
614
615
591k
    funmap_type::const_iterator item = m_pFunDef->find(strTok);
616
591k
    if (item == m_pFunDef->end())
617
578k
      return false;
618
619
    // Check if the next sign is an opening bracket
620
12.7k
    const char_type* szFormula = m_strFormula.c_str();
621
12.7k
    if (szFormula[iEnd] != '(')
622
24
      return false;
623
624
    // fix for #164: https://github.com/beltoforion/muparser/issues/164
625
12.7k
    if (m_lastTok.GetFuncAddr() == generic_callable_type{ (erased_fun_type)&MathImpl<value_type>::UnaryPlus, nullptr })
626
3
    {
627
3
      Error(ecUNARY_PLUS_IN_FRONT_OF_FUNCTION, m_iPos - (int)a_Tok.GetAsString().length(), a_Tok.GetAsString());
628
3
    }
629
630
12.7k
    a_Tok.Set(item->second, strTok);
631
632
12.7k
    m_iPos = (int)iEnd;
633
12.7k
    if (m_iSynFlags & noFUN)
634
2
      Error(ecUNEXPECTED_FUN, m_iPos - (int)a_Tok.GetAsString().length(), a_Tok.GetAsString());
635
636
12.7k
    m_iSynFlags = noANY ^ noBO;
637
12.7k
    return true;
638
12.7k
  }
639
640
641
  /** \brief Check if a string position contains a binary operator.
642
    \param a_Tok  [out] Operator token if one is found. This can either be a binary operator or an infix operator token.
643
    \return true if an operator token has been found.
644
  */
645
  bool ParserTokenReader::IsOprt(token_type& a_Tok)
646
2.20M
  {
647
2.20M
    const char_type* const szExpr = m_strFormula.c_str();
648
2.20M
    string_type strTok;
649
650
2.20M
    auto iEnd = ExtractOperatorToken(strTok, (std::size_t)m_iPos);
651
2.20M
    if (iEnd == m_iPos)
652
1.91M
      return false;
653
654
    // Check if the operator is a built in operator, if so ignore it here
655
287k
    const char_type** const pOprtDef = m_pParser->GetOprtDef();
656
4.09M
    for (int i = 0; m_pParser->HasBuiltInOprt() && pOprtDef[i]; ++i)
657
4.07M
    {
658
4.07M
      if (string_type(pOprtDef[i]) == strTok)
659
261k
        return false;
660
4.07M
    }
661
662
    // Note:
663
    // All tokens in oprt_bin_maptype are have been sorted by their length
664
    // Long operators must come first! Otherwise short names (like: "add") that
665
    // are part of long token names (like: "add123") will be found instead 
666
    // of the long ones.
667
    // Length sorting is done with ascending length so we use a reverse iterator here.
668
25.5k
    funmap_type::const_reverse_iterator it = m_pOprtDef->rbegin();
669
25.5k
    for (; it != m_pOprtDef->rend(); ++it)
670
0
    {
671
0
      const string_type& sID = it->first;
672
0
      if (sID == string_type(szExpr + m_iPos, szExpr + m_iPos + sID.length()))
673
0
      {
674
0
        a_Tok.Set(it->second, strTok);
675
676
        // operator was found
677
0
        if (m_iSynFlags & noOPT)
678
0
        {
679
          // An operator was found but is not expected to occur at
680
          // this position of the formula, maybe it is an infix 
681
          // operator, not a binary operator. Both operator types
682
          // can share characters in their identifiers.
683
0
          if (IsInfixOpTok(a_Tok))
684
0
            return true;
685
0
          else
686
0
          {
687
            // nope, no infix operator
688
0
            return false;
689
            //Error(ecUNEXPECTED_OPERATOR, m_iPos, a_Tok.GetAsString()); 
690
0
          }
691
692
0
        }
693
694
0
        m_iPos += (int)sID.length();
695
0
        m_iSynFlags = noBC | noOPT | noARG_SEP | noPOSTOP | noEND | noASSIGN;
696
0
        return true;
697
0
      }
698
0
    }
699
700
25.5k
    return false;
701
25.5k
  }
702
703
704
  /** \brief Check if a string position contains a unary post value operator. */
705
  bool ParserTokenReader::IsPostOpTok(token_type& a_Tok)
706
535
  {
707
    // <ibg 20110629> Do not check for postfix operators if they are not allowed at
708
    //                the current expression index.
709
    //
710
    //  This will fix the bug reported here:  
711
    //
712
    //  http://sourceforge.net/tracker/index.php?func=detail&aid=3343891&group_id=137191&atid=737979
713
    //
714
535
    if (m_iSynFlags & noPOSTOP)
715
350
      return false;
716
    // </ibg>
717
718
    // Tricky problem with equations like "3m+5":
719
    //     m is a postfix operator, + is a valid sign for postfix operators and 
720
    //     for binary operators parser detects "m+" as operator string and 
721
    //     finds no matching postfix operator.
722
    // 
723
    // This is a special case so this routine slightly differs from the other
724
    // token readers.
725
726
    // Test if there could be a postfix operator
727
185
    string_type sTok;
728
185
    auto iEnd = ExtractToken(m_pParser->ValidOprtChars(), sTok, (std::size_t)m_iPos);
729
185
    if (iEnd == m_iPos)
730
43
      return false;
731
732
    // iterate over all postfix operator strings
733
142
    funmap_type::const_reverse_iterator it = m_pPostOprtDef->rbegin();
734
142
    for (; it != m_pPostOprtDef->rend(); ++it)
735
0
    {
736
0
      if (sTok.find(it->first) != 0)
737
0
        continue;
738
739
0
      a_Tok.Set(it->second, sTok);
740
0
      m_iPos += (int)it->first.length();
741
742
0
      m_iSynFlags = noVAL | noVAR | noFUN | noBO | noPOSTOP | noSTR | noASSIGN;
743
0
      return true;
744
0
    }
745
746
142
    return false;
747
142
  }
748
749
750
  /** \brief Check whether the token at a given position is a value token.
751
752
    Value tokens are either values or constants.
753
754
    \param a_Tok [out] If a value token is found it will be placed here.
755
    \return true if a value token has been found.
756
  */
757
  bool ParserTokenReader::IsValTok(token_type& a_Tok)
758
591k
  {
759
591k
    MUP_ASSERT(m_pConstDef != nullptr);
760
591k
    MUP_ASSERT(m_pParser != nullptr);
761
762
591k
    string_type strTok;
763
591k
    value_type fVal(0);
764
765
    // 2.) Check for user defined constant
766
    // Read everything that could be a constant name
767
591k
    auto iEnd = ExtractToken(m_pParser->ValidNameChars(), strTok, (std::size_t)m_iPos);
768
591k
    if (iEnd != m_iPos)
769
578k
    {
770
578k
      valmap_type::const_iterator item = m_pConstDef->find(strTok);
771
578k
      if (item != m_pConstDef->end())
772
2.46k
      {
773
2.46k
        m_iPos = iEnd;
774
2.46k
        a_Tok.SetVal(item->second, strTok);
775
776
2.46k
        if (m_iSynFlags & noVAL)
777
1
          Error(ecUNEXPECTED_VAL, m_iPos - (int)strTok.length(), strTok);
778
779
2.46k
        m_iSynFlags = noVAL | noVAR | noFUN | noBO | noINFIXOP | noSTR | noASSIGN;
780
2.46k
        return true;
781
2.46k
      }
782
578k
    }
783
784
    // 3.call the value recognition functions provided by the user
785
    // Call user defined value recognition functions
786
588k
    std::list<identfun_type>::const_iterator item = m_vIdentFun.begin();
787
601k
    for (item = m_vIdentFun.begin(); item != m_vIdentFun.end(); ++item)
788
588k
    {
789
588k
      int iStart = m_iPos;
790
588k
      if ((*item)(m_strFormula.c_str() + m_iPos, &m_iPos, &fVal) == 1)
791
576k
      {
792
        // 2013-11-27 Issue 2:  https://code.google.com/p/muparser/issues/detail?id=2
793
576k
        strTok.assign(m_strFormula.c_str(), iStart, (std::size_t)m_iPos - iStart);
794
795
576k
        if (m_iSynFlags & noVAL)
796
16
          Error(ecUNEXPECTED_VAL, m_iPos - (int)strTok.length(), strTok);
797
798
576k
        a_Tok.SetVal(fVal, strTok);
799
576k
        m_iSynFlags = noVAL | noVAR | noFUN | noBO | noINFIXOP | noSTR | noASSIGN;
800
576k
        return true;
801
576k
      }
802
588k
    }
803
804
12.4k
    return false;
805
588k
  }
806
807
808
  /** \brief Check wheter a token at a given position is a variable token.
809
    \param a_Tok [out] If a variable token has been found it will be placed here.
810
      \return true if a variable token has been found.
811
  */
812
  bool ParserTokenReader::IsVarTok(token_type& a_Tok)
813
12.4k
  {
814
12.4k
    if (m_pVarDef->empty())
815
12.4k
      return false;
816
817
0
    string_type strTok;
818
0
    auto iEnd = ExtractToken(m_pParser->ValidNameChars(), strTok, (std::size_t)m_iPos);
819
0
    if (iEnd == m_iPos)
820
0
      return false;
821
822
0
    varmap_type::const_iterator item = m_pVarDef->find(strTok);
823
0
    if (item == m_pVarDef->end())
824
0
      return false;
825
826
0
    if (m_iSynFlags & noVAR)
827
0
      Error(ecUNEXPECTED_VAR, m_iPos, strTok);
828
829
0
    m_pParser->OnDetectVar(&m_strFormula, m_iPos, iEnd);
830
831
0
    m_iPos = iEnd;
832
0
    a_Tok.SetVar(item->second, strTok);
833
0
    m_UsedVar[item->first] = item->second;  // Add variable to used-var-list
834
835
0
    m_iSynFlags = noVAL | noVAR | noFUN | noBO | noINFIXOP | noSTR;
836
837
    //  Zur Info hier die SynFlags von IsVal():
838
    //    m_iSynFlags = noVAL | noVAR | noFUN | noBO | noINFIXOP | noSTR | noASSIGN; 
839
0
    return true;
840
0
  }
841
842
843
  bool ParserTokenReader::IsStrVarTok(token_type& a_Tok)
844
12.4k
  {
845
12.4k
    if (!m_pStrVarDef || m_pStrVarDef->empty())
846
12.4k
      return false;
847
848
0
    string_type strTok;
849
0
    auto iEnd = ExtractToken(m_pParser->ValidNameChars(), strTok, (std::size_t)m_iPos);
850
0
    if (iEnd == m_iPos)
851
0
      return false;
852
853
0
    strmap_type::const_iterator item = m_pStrVarDef->find(strTok);
854
0
    if (item == m_pStrVarDef->end())
855
0
      return false;
856
857
0
    if (m_iSynFlags & noSTR)
858
0
      Error(ecUNEXPECTED_VAR, m_iPos, strTok);
859
860
0
    m_iPos = iEnd;
861
0
    if (!m_pParser->m_vStringVarBuf.size())
862
0
      Error(ecINTERNAL_ERROR);
863
    
864
0
    auto strVal = m_pParser->m_vStringVarBuf[item->second];
865
0
    m_pParser->m_vStringBuf.push_back(strVal);
866
0
    a_Tok.SetString(strVal, m_pParser->m_vStringBuf.size()-1);
867
868
0
    m_iSynFlags = noANY ^ (noBC | noOPT | noEND | noARG_SEP);
869
0
    return true;
870
0
  }
871
872
873
874
  /** \brief Check wheter a token at a given position is an undefined variable.
875
876
    \param a_Tok [out] If a variable token has been found it will be placed here.
877
      \return true if a variable token has been found.
878
    \throw nothrow
879
  */
880
  bool ParserTokenReader::IsUndefVarTok(token_type& a_Tok)
881
0
  {
882
0
    string_type strTok;
883
0
    auto iEnd(ExtractToken(m_pParser->ValidNameChars(), strTok, (std::size_t)m_iPos));
884
0
    if (iEnd == m_iPos)
885
0
      return false;
886
887
0
    if (m_iSynFlags & noVAR)
888
0
    {
889
      // <ibg/> 20061021 added token string strTok instead of a_Tok.GetAsString() as the 
890
      //                 token identifier. 
891
      // related bug report:
892
      // http://sourceforge.net/tracker/index.php?func=detail&aid=1578779&group_id=137191&atid=737979
893
0
      Error(ecUNEXPECTED_VAR, m_iPos - (int)a_Tok.GetAsString().length(), strTok);
894
0
    }
895
896
    // If a factory is available implicitely create new variables
897
0
    if (m_pFactory)
898
0
    {
899
0
      value_type* fVar = m_pFactory(strTok.c_str(), m_pFactoryData);
900
0
      a_Tok.SetVar(fVar, strTok);
901
902
      // Do not use m_pParser->DefineVar( strTok, fVar );
903
      // in order to define the new variable, it will clear the
904
      // m_UsedVar array which will kill previously defined variables
905
      // from the list
906
      // This is safe because the new variable can never override an existing one
907
      // because they are checked first!
908
0
      (*m_pVarDef)[strTok] = fVar;
909
0
      m_UsedVar[strTok] = fVar;  // Add variable to used-var-list
910
0
    }
911
0
    else
912
0
    {
913
0
      a_Tok.SetVar((value_type*)&m_fZero, strTok);
914
0
      m_UsedVar[strTok] = 0;  // Add variable to used-var-list
915
0
    }
916
917
0
    m_iPos = iEnd;
918
919
    // Call the variable factory in order to let it define a new parser variable
920
0
    m_iSynFlags = noVAL | noVAR | noFUN | noBO | noPOSTOP | noINFIXOP | noSTR;
921
0
    return true;
922
0
  }
923
924
925
926
  /** \brief Check wheter a token at a given position is a string.
927
    \param a_Tok [out] If a variable token has been found it will be placed here.
928
    \return true if a string token has been found.
929
    \sa IsOprt, IsFunTok, IsStrFunTok, IsValTok, IsVarTok, IsEOF, IsInfixOpTok, IsPostOpTok
930
    \throw nothrow
931
  */
932
  bool ParserTokenReader::IsString(token_type& a_Tok)
933
12.4k
  {
934
12.4k
    if (m_strFormula[m_iPos] != '"')
935
535
      return false;
936
937
11.9k
    string_type strBuf(&m_strFormula[(std::size_t)m_iPos + 1]);
938
11.9k
    std::size_t iEnd(0), iSkip(0);
939
940
    // parser over escaped '\"' end replace them with '"'
941
12.3k
    for (iEnd = strBuf.find(_T('\"')); iEnd != string_type::npos; iEnd = strBuf.find(_T('\"'), iEnd))
942
12.3k
    {
943
12.3k
      if (iEnd==0 || strBuf[iEnd - 1] != '\\') 
944
11.9k
        break;
945
946
424
      strBuf.replace(iEnd - 1, 2, _T("\""));
947
424
      iSkip++;
948
424
    }
949
950
11.9k
    if (iEnd == string_type::npos)
951
51
      Error(ecUNTERMINATED_STRING, m_iPos, _T("\""));
952
953
11.9k
    string_type strTok(strBuf.begin(), strBuf.begin() + iEnd);
954
955
11.9k
    if (m_iSynFlags & noSTR)
956
45
      Error(ecUNEXPECTED_STR, m_iPos, strTok);
957
958
11.9k
    m_pParser->m_vStringBuf.push_back(strTok); // Store string in internal buffer
959
11.9k
    a_Tok.SetString(strTok, m_pParser->m_vStringBuf.size()-1);
960
961
11.9k
    m_iPos += (int)strTok.length() + 2 + (int)iSkip;  // +2 for quotes; +iSkip for escape characters 
962
11.9k
    m_iSynFlags = noANY ^ (noARG_SEP | noBC | noOPT | noEND);
963
964
11.9k
    return true;
965
12.4k
  }
966
967
968
  /** \brief Create an error containing the parse error position.
969
970
    This function will create an Parser Exception object containing the error text and its position.
971
972
    \param a_iErrc [in] The error code of type #EErrorCodes.
973
    \param a_iPos [in] The position where the error was detected.
974
    \param a_strTok [in] The token string representation associated with the error.
975
    \throw ParserException always throws thats the only purpose of this function.
976
  */
977
  void  ParserTokenReader::Error(EErrorCodes a_iErrc, int a_iPos, const string_type& a_sTok) const
978
1.67k
  {
979
1.67k
    m_pParser->Error(a_iErrc, a_iPos, a_sTok);
980
1.67k
  }
981
982
983
  void ParserTokenReader::SetArgSep(char_type cArgSep)
984
0
  {
985
0
    m_cArgSep = cArgSep;
986
0
  }
987
988
989
  char_type ParserTokenReader::GetArgSep() const
990
3.82k
  {
991
3.82k
    return m_cArgSep;
992
3.82k
  }
993
} // namespace mu
994
995
#if defined(_MSC_VER)
996
  #pragma warning(pop)
997
#endif