Seamly2D
Code documentation
qmuparsertokenreader.cpp
Go to the documentation of this file.
1 /***************************************************************************************************
2  **
3  ** Copyright (C) 2013 Ingo Berg
4  **
5  ** Permission is hereby granted, free of charge, to any person obtaining a copy of this
6  ** software and associated documentation files (the "Software"), to deal in the Software
7  ** without restriction, including without limitation the rights to use, copy, modify,
8  ** merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
9  ** permit persons to whom the Software is furnished to do so, subject to the following conditions:
10  **
11  ** The above copyright notice and this permission notice shall be included in all copies or
12  ** substantial portions of the Software.
13  **
14  ** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT
15  ** NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
16  ** NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
17  ** DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18  ** OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
19  **
20  ******************************************************************************************************/
21 
22 #include "qmuparsertokenreader.h"
23 
24 #include <assert.h>
25 #include <QCharRef>
26 #include <QList>
27 #include <QMessageLogger>
28 #include <QStringList>
29 #include <QtDebug>
30 #include <fstream>
31 #include <iterator>
32 #include <map>
33 #include <memory>
34 #include <string>
35 #include <utility>
36 
37 #include "qmudef.h"
38 #include "qmuparserbase.h"
39 
40 /**
41  * @file
42  * @brief This file contains the parser token reader implementation.
43  */
44 
45 namespace qmu
46 {
47 
48 //---------------------------------------------------------------------------------------------------------------------
49 /**
50  * @brief Copy constructor.
51  *
52  * @sa Assign
53  * @throw nothrow
54  */
56  :m_pParser( a_Reader.m_pParser ), m_strFormula( a_Reader.m_strFormula ), m_iPos( a_Reader.m_iPos ),
57  m_iSynFlags( a_Reader.m_iSynFlags ), m_bIgnoreUndefVar( a_Reader.m_bIgnoreUndefVar ),
58  m_pFunDef( a_Reader.m_pFunDef ), m_pPostOprtDef( a_Reader.m_pPostOprtDef ),
59  m_pInfixOprtDef( a_Reader.m_pInfixOprtDef ), m_pOprtDef( a_Reader.m_pOprtDef),
60  m_pConstDef( a_Reader.m_pConstDef ), m_pStrVarDef( a_Reader.m_pStrVarDef ), m_pVarDef( a_Reader.m_pVarDef ),
61  m_pFactory( a_Reader.m_pFactory ), m_pFactoryData( a_Reader.m_pFactoryData ), m_vIdentFun( a_Reader.m_vIdentFun ),
62  m_UsedVar( a_Reader.m_UsedVar ), m_fZero(0), m_iBrackets( a_Reader.m_iBrackets ), m_lastTok(),
63  m_cArgSep( a_Reader.m_cArgSep )
64 {}
65 
66 //---------------------------------------------------------------------------------------------------------------------
67 /**
68  * @brief Assignement operator.
69  *
70  * Self assignement will be suppressed otherwise #Assign is called.
71  *
72  * @param a_Reader Object to copy to this token reader.
73  * @throw nothrow
74  */
76 {
77  if ( &a_Reader != this )
78  {
79  Assign ( a_Reader );
80  }
81 
82  return *this;
83 }
84 
85 //---------------------------------------------------------------------------------------------------------------------
86 /**
87  * @brief Assign state of a token reader to this token reader.
88  *
89  * @param a_Reader Object from which the state should be copied.
90  * @throw nothrow
91  */
93 {
94  m_pParser = a_Reader.m_pParser;
95  m_strFormula = a_Reader.m_strFormula;
96  m_iPos = a_Reader.m_iPos;
97  m_iSynFlags = a_Reader.m_iSynFlags;
98 
99  m_UsedVar = a_Reader.m_UsedVar;
100  m_pFunDef = a_Reader.m_pFunDef;
101  m_pConstDef = a_Reader.m_pConstDef;
102  m_pVarDef = a_Reader.m_pVarDef;
103  m_pStrVarDef = a_Reader.m_pStrVarDef;
104  m_pPostOprtDef = a_Reader.m_pPostOprtDef;
105  m_pInfixOprtDef = a_Reader.m_pInfixOprtDef;
106  m_pOprtDef = a_Reader.m_pOprtDef;
108  m_vIdentFun = a_Reader.m_vIdentFun;
109  m_pFactory = a_Reader.m_pFactory;
110  m_pFactoryData = a_Reader.m_pFactoryData;
111  m_iBrackets = a_Reader.m_iBrackets;
112  m_cArgSep = a_Reader.m_cArgSep;
113 
114  m_fZero = 0;
115  m_lastTok = token_type();
116 }
117 
118 //---------------------------------------------------------------------------------------------------------------------
119 /**
120  * @brief Constructor.
121  *
122  * Create a Token reader and bind it to a parser object.
123  *
124  * @pre [assert] a_pParser may not be NULL
125  * @post #m_pParser==a_pParser
126  * @param a_pParent Parent parser object of the token reader.
127  */
129  : m_pParser ( a_pParent ), m_strFormula(), m_iPos ( 0 ), m_iSynFlags ( 0 ), m_bIgnoreUndefVar ( false ),
130  m_pFunDef ( nullptr ), m_pPostOprtDef ( nullptr ), m_pInfixOprtDef ( nullptr ), m_pOprtDef ( nullptr ),
131  m_pConstDef ( nullptr ), m_pStrVarDef ( nullptr ), m_pVarDef ( nullptr ), m_pFactory ( nullptr ),
132  m_pFactoryData ( nullptr ), m_vIdentFun(), m_UsedVar(), m_fZero ( 0 ), m_iBrackets ( 0 ), m_lastTok(),
133  m_cArgSep ( ';' )
134 {
135  assert ( m_pParser );
136  SetParent ( m_pParser );
137 }
138 
139 //---------------------------------------------------------------------------------------------------------------------
140 /**
141  * @brief Create instance of a QParserTokenReader identical with this and return its pointer.
142  *
143  * This is a factory method the calling function must take care of the object destruction.
144  *
145  * @return A new QParserTokenReader object.
146  * @throw nothrow
147  */
149 {
150  std::unique_ptr<QmuParserTokenReader> ptr ( new QmuParserTokenReader ( *this ) );
151  ptr->SetParent ( a_pParent );
152  return ptr.release();
153 }
154 
155 //---------------------------------------------------------------------------------------------------------------------
157 {
158  m_lastTok = tok;
159  return m_lastTok;
160 }
161 
162 //---------------------------------------------------------------------------------------------------------------------
164 {
165  // Use push_front is used to give user defined callbacks a higher priority than
166  // the built in ones. Otherwise reading hex numbers would not work
167  // since the "0" in "0xff" would always be read first making parsing of
168  // the rest impossible.
169  // reference:
170  // http://sourceforge.net/projects/muparser/forums/forum/462843/topic/4824956
171  m_vIdentFun.push_front ( a_pCallback );
172 }
173 
174 //---------------------------------------------------------------------------------------------------------------------
175 void QmuParserTokenReader::SetVarCreator ( facfun_type a_pFactory, void *pUserData )
176 {
177  m_pFactory = a_pFactory;
178  m_pFactoryData = pUserData;
179 }
180 
181 //---------------------------------------------------------------------------------------------------------------------
182 /**
183  * @brief Initialize the token Reader.
184  *
185  * Sets the formula position index to zero and set Syntax flags to default for initial formula parsing.
186  * @pre [assert] triggered if a_szFormula==0
187  */
188 void QmuParserTokenReader::SetFormula ( const QString &a_strFormula )
189 {
190  m_strFormula = a_strFormula;
191  ReInit();
192 }
193 
194 //---------------------------------------------------------------------------------------------------------------------
195 /**
196  * @brief Reset the token reader to the start of the formula.
197  *
198  * The syntax flags will be reset to a value appropriate for the start of a formula.
199  * @post #m_iPos==0, #m_iSynFlags = noOPT | noBC | noPOSTOP | noSTR
200  * @throw nothrow
201  * @sa ESynCodes
202  */
204 {
205  m_iPos = 0;
207  m_iBrackets = 0;
208  m_UsedVar.clear();
209  m_lastTok = token_type();
210 }
211 
212 //---------------------------------------------------------------------------------------------------------------------
213 /**
214  * @brief Read the next token from the string.
215  */
216 QmuParserTokenReader::token_type QmuParserTokenReader::ReadNextToken(const QLocale &locale, const QChar &decimal,
217  const QChar &thousand)
218 {
219  assert ( m_pParser );
220 
221  token_type tok;
222 
223  // Ignore all non printable characters when reading the expression
224  while (m_strFormula.size() > m_iPos && m_strFormula.at(m_iPos) <= QChar(0x20))
225  {
226  ++m_iPos;
227  }
228 
229  if ( IsEOF ( tok ) )
230  {
231  return SaveBeforeReturn ( tok ); // Check for end of formula
232  }
233  if ( IsOprt ( tok ) )
234  {
235  return SaveBeforeReturn ( tok ); // Check for user defined binary operator
236  }
237  if ( IsFunTok ( tok ) )
238  {
239  return SaveBeforeReturn ( tok ); // Check for function token
240  }
241  if ( IsBuiltIn ( tok ) )
242  {
243  return SaveBeforeReturn ( tok ); // Check built in operators / tokens
244  }
245  if ( IsArgSep ( tok ) )
246  {
247  return SaveBeforeReturn ( tok ); // Check for function argument separators
248  }
249  if ( IsValTok ( tok, locale, decimal, thousand ) )
250  {
251  return SaveBeforeReturn ( tok ); // Check for values / constant tokens
252  }
253  if ( IsVarTok ( tok ) )
254  {
255  return SaveBeforeReturn ( tok ); // Check for variable tokens
256  }
257  if ( IsStrVarTok ( tok ) )
258  {
259  return SaveBeforeReturn ( tok ); // Check for string variables
260  }
261  if ( IsString ( tok ) )
262  {
263  return SaveBeforeReturn ( tok ); // Check for String tokens
264  }
265  if ( IsInfixOpTok ( tok ) )
266  {
267  return SaveBeforeReturn ( tok ); // Check for unary operators
268  }
269  if ( IsPostOpTok ( tok ) )
270  {
271  return SaveBeforeReturn ( tok ); // Check for unary operators
272  }
273 
274  // Check String for undefined variable token. Done only if a
275  // flag is set indicating to ignore undefined variables.
276  // This is a way to conditionally avoid an error if
277  // undefined variables occur.
278  // (The GetUsedVar function must suppress the error for
279  // undefined variables in order to collect all variable
280  // names including the undefined ones.)
281  if ( ( m_bIgnoreUndefVar || m_pFactory ) && IsUndefVarTok ( tok ) )
282  {
283  return SaveBeforeReturn ( tok );
284  }
285 
286  // Check for unknown token
287  //
288  // !!! From this point on there is no exit without an exception possible...
289  //
290  QString strTok;
291  int iEnd = ExtractToken ( m_pParser->ValidNameChars(), strTok, m_iPos );
292  if ( iEnd != m_iPos )
293  {
294  Error ( ecUNASSIGNABLE_TOKEN, m_iPos, strTok );
295  }
296 
298  return token_type(); // never reached
299 }
300 
301 //---------------------------------------------------------------------------------------------------------------------
303 {
304  m_pParser = a_pParent;
305  m_pFunDef = &a_pParent->m_FunDef;
306  m_pOprtDef = &a_pParent->m_OprtDef;
307  m_pInfixOprtDef = &a_pParent->m_InfixOprtDef;
308  m_pPostOprtDef = &a_pParent->m_PostOprtDef;
309  m_pVarDef = &a_pParent->m_VarDef;
310  m_pStrVarDef = &a_pParent->m_StrVarDef;
311  m_pConstDef = &a_pParent->m_ConstDef;
312 }
313 
314 //---------------------------------------------------------------------------------------------------------------------
315 /**
316  * @brief Extract all characters that belong to a certain charset.
317  *
318  * @param a_szCharSet [in] Const char array of the characters allowed in the token.
319  * @param a_sTok [out] The string that consists entirely of characters listed in a_szCharSet.
320  * @param a_iPos [in] Position in the string from where to start reading.
321  * @return The Position of the first character not listed in a_szCharSet.
322  * @throw nothrow
323  */
324 QT_WARNING_PUSH
325 QT_WARNING_DISABLE_MSVC(4309)
326 int QmuParserTokenReader::ExtractToken ( const QString &a_szCharSet, QString &a_sTok, int a_iPos ) const
327 {
328  const std::wstring m_strFormulaStd = m_strFormula.toStdWString();
329  const std::wstring a_szCharSetStd = a_szCharSet.toStdWString();
330 
331  int iEnd = static_cast<int>(m_strFormulaStd.find_first_not_of ( a_szCharSetStd, static_cast<std::size_t>(a_iPos) ));
332 
333  if ( iEnd == static_cast<int>(string_type::npos) )
334  {
335  iEnd = static_cast<int>(m_strFormulaStd.length());
336  }
337 
338  // Assign token string if there was something found
339  if ( a_iPos != iEnd )
340  {
341  a_sTok = QString().fromStdWString ( std::wstring ( m_strFormulaStd.begin() + a_iPos,
342  m_strFormulaStd.begin() + iEnd ) );
343  }
344 
345  return iEnd;
346 }
347 
348 //---------------------------------------------------------------------------------------------------------------------
349 /**
350  * @brief Check Expression for the presence of a binary operator token.
351  *
352  * Userdefined binary operator "++" gives inconsistent parsing result for the equations "a++b" and "a ++ b" if
353  * alphabetic characters are allowed in operator tokens. To avoid this this function checks specifically
354  * for operator tokens.
355  */
356 int QmuParserTokenReader::ExtractOperatorToken ( QString &a_sTok, int a_iPos ) const
357 {
358  const std::wstring m_strFormulaStd = m_strFormula.toStdWString();
359  // Changed as per Issue 6: https://code.google.com/p/muparser/issues/detail?id=6
360  const std::wstring oprtCharsStd = m_pParser->ValidOprtChars().toStdWString();
361 
362  int iEnd = static_cast<int>( m_strFormulaStd.find_first_not_of ( oprtCharsStd, static_cast<std::size_t>(a_iPos) ) );
363  if ( iEnd == static_cast<int>( string_type::npos ) )
364  {
365  iEnd = static_cast<int>( m_strFormulaStd.length() );
366  }
367 
368  // Assign token string if there was something found
369  if ( a_iPos != iEnd )
370  {
371  a_sTok = QString().fromStdWString ( string_type ( m_strFormulaStd.begin() + a_iPos,
372  m_strFormulaStd.begin() + iEnd ) );
373  return iEnd;
374  }
375  else
376  {
377  // There is still the chance of having to deal with an operator consisting exclusively
378  // of alphabetic characters.
379  return ExtractToken ( QMUP_CHARS, a_sTok, a_iPos );
380  }
381 }
383 
384 //---------------------------------------------------------------------------------------------------------------------
385 /**
386  * @brief Check if a built in operator or other token can be found
387  * @param a_Tok [out] Operator token if one is found. This can either be a binary operator or an infix operator token.
388  * @return true if an operator token has been found.
389  */
391 {
392  const QStringList pOprtDef = QmuParserBase::GetOprtDef();
393 
394  // Compare token with function and operator strings
395  // check string for operator/function
396  for ( int i = 0; i < pOprtDef.size(); ++i )
397  {
398  int len = pOprtDef.at ( i ).length();
399  if ( pOprtDef.at ( i ) == m_strFormula.mid ( m_iPos, len ) )
400  {
401  if (i >= cmLE && i <= cmASSIGN)
402  {
403  //if (len!=sTok.length())
404  // continue;
405 
406  // The assignement operator need special treatment
407  if ( i == cmASSIGN && m_iSynFlags & noASSIGN )
408  {
409  Error ( ecUNEXPECTED_OPERATOR, m_iPos, pOprtDef.at ( i ) );
410  }
411 
412  if ( m_pParser->HasBuiltInOprt() == false)
413  {
414  continue;
415  }
416  if ( m_iSynFlags & noOPT )
417  {
418  // Maybe its an infix operator not an operator
419  // Both operator types can share characters in
420  // their identifiers
421  if ( IsInfixOpTok ( a_Tok ) )
422  {
423  return true;
424  }
425 
426  Error ( ecUNEXPECTED_OPERATOR, m_iPos, pOprtDef.at ( i ) );
427  }
428 
430  m_iSynFlags |= noEND;
431  }
432  else if (i == cmBO)
433  {
434  if ( m_iSynFlags & noBO )
435  {
436  Error ( ecUNEXPECTED_PARENS, m_iPos, pOprtDef.at ( i ) );
437  }
438 
439  if ( m_lastTok.GetCode() == cmFUNC )
440  {
442  }
443  else
444  {
446  }
447 
448  ++m_iBrackets;
449  }
450  else if (i == cmBC)
451  {
452  if ( m_iSynFlags & noBC )
453  {
454  Error ( ecUNEXPECTED_PARENS, m_iPos, pOprtDef.at ( i ) );
455  }
456 
458 
459  if ( --m_iBrackets < 0 )
460  {
461  Error ( ecUNEXPECTED_PARENS, m_iPos, pOprtDef.at ( i ) );
462  }
463  }
464  else if (i == cmELSE)
465  {
466  if ( m_iSynFlags & noELSE )
467  {
468  Error ( ecUNEXPECTED_CONDITIONAL, m_iPos, pOprtDef.at ( i ) );
469  }
470 
472  }
473  else if (i == cmIF)
474  {
475  if ( m_iSynFlags & noIF )
476  {
477  Error ( ecUNEXPECTED_CONDITIONAL, m_iPos, pOprtDef.at ( i ) );
478  }
479 
481  }
482  else // The operator is listed in c_DefaultOprt, but not here. This is a bad thing...
483  {
485  }
486 
487  m_iPos += len;
488  a_Tok.Set ( static_cast<ECmdCode>(i), pOprtDef.at ( i ) );
489  return true;
490  } // if operator string found
491  } // end of for all operator strings
492 
493  return false;
494 }
495 
496 //---------------------------------------------------------------------------------------------------------------------
498 {
499  if ( m_strFormula.at ( m_iPos ) == m_cArgSep )
500  {
501  // copy the separator into null terminated string
502  QString szSep;
503  szSep[0] = m_cArgSep;
504  szSep[1] = 0;
505 
506  if ( m_iSynFlags & noARG_SEP )
507  {
508  Error ( ecUNEXPECTED_ARG_SEP, m_iPos, szSep );
509  }
510 
512  m_iPos++;
513  a_Tok.Set ( cmARG_SEP, szSep );
514  return true;
515  }
516 
517  return false;
518 }
519 
520 //---------------------------------------------------------------------------------------------------------------------
521 /**
522  * @brief Check for End of Formula.
523  *
524  * @return true if an end of formula is found false otherwise.
525  * @param a_Tok [out] If an eof is found the corresponding token will be stored there.
526  * @sa IsOprt, IsFunTok, IsStrFunTok, IsValTok, IsVarTok, IsString, IsInfixOpTok, IsPostOpTok
527  */
529 {
530  // check for EOF
531  if ( m_iPos >= m_strFormula.size())
532  {
533  if ( m_iSynFlags & noEND )
534  {
535  try
536  {
538  }
539  catch (qmu::QmuParserError &e)
540  {
541  qDebug() << " Code:" << e.GetCode() << "(" << e.GetMsg() << ")";
542  throw;
543  }
544  }
545 
546  if ( m_iBrackets > 0 )
547  {
548  Error ( ecMISSING_PARENS, m_iPos, ")" );
549  }
550 
551  m_iSynFlags = 0;
552  a_Tok.Set ( cmEND );
553  return true;
554  }
555 
556  return false;
557 }
558 
559 //---------------------------------------------------------------------------------------------------------------------
560 /**
561  * @brief Check if a string position contains a unary infix operator.
562  * @return true if a function token has been found false otherwise.
563  */
565 {
566  QString sTok;
567  int iEnd = ExtractToken ( m_pParser->ValidInfixOprtChars(), sTok, m_iPos );
568  if ( iEnd == m_iPos )
569  {
570  return false;
571  }
572 
573  // iteraterate over all postfix operator strings
574  auto it = m_pInfixOprtDef->rbegin();
575  for ( ; it != m_pInfixOprtDef->rend(); ++it )
576  {
577  if ( sTok.indexOf ( it->first ) == 0 )
578  {
579  a_Tok.Set ( it->second, it->first );
580  m_iPos += static_cast<int>(it->first.length());
581 
582  if ( m_iSynFlags & noINFIXOP )
583  {
585  }
586 
588  return true;
589  }
590  }
591 
592  return false;
593 }
594 
595 //---------------------------------------------------------------------------------------------------------------------
596 /**
597  * @brief Check whether the token at a given position is a function token.
598  * @param a_Tok [out] If a value token is found it will be placed here.
599  * @throw ParserException if Syntaxflags do not allow a function at a_iPos
600  * @return true if a function token has been found false otherwise.
601  * @pre [assert] m_pParser!=0
602  */
604 {
605  QString strTok;
606  int iEnd = ExtractToken ( m_pParser->ValidNameChars(), strTok, m_iPos );
607  if ( iEnd == m_iPos )
608  {
609  return false;
610  }
611 
612  funmap_type::const_iterator item = m_pFunDef->find ( strTok );
613  if ( item == m_pFunDef->end() )
614  {
615  return false;
616  }
617 
618  // Check if the next sign is an opening bracket
619  if ( m_strFormula.at ( iEnd ) != '(' )
620  {
621  return false;
622  }
623 
624  a_Tok.Set ( item->second, strTok );
625 
626  m_iPos = iEnd;
627  if ( m_iSynFlags & noFUN )
628  {
629  Error ( ecUNEXPECTED_FUN, m_iPos - static_cast<int>(a_Tok.GetAsString().length()), a_Tok.GetAsString() );
630  }
631 
632  m_iSynFlags = noANY ^ noBO;
633  return true;
634 }
635 
636 //---------------------------------------------------------------------------------------------------------------------
637 /**
638  * @brief Check if a string position contains a binary operator.
639  * @param a_Tok [out] Operator token if one is found. This can either be a binary operator or an infix operator token.
640  * @return true if an operator token has been found.
641  */
643 {
644  QString strTok;
645 
646  int iEnd = ExtractOperatorToken ( strTok, m_iPos );
647  if ( iEnd == m_iPos )
648  {
649  return false;
650  }
651 
652  // Check if the operator is a built in operator, if so ignore it here
653  const QStringList &pOprtDef = QmuParserBase::GetOprtDef();
654  QStringList::const_iterator constIterator;
655  for ( constIterator = pOprtDef.constBegin(); m_pParser->HasBuiltInOprt() && constIterator != pOprtDef.constEnd();
656  ++constIterator )
657  {
658  if ( ( *constIterator ) == strTok )
659  {
660  return false;
661  }
662  }
663 
664  // Note:
665  // All tokens in oprt_bin_maptype are have been sorted by their length
666  // Long operators must come first! Otherwise short names (like: "add") that
667  // are part of long token names (like: "add123") will be found instead
668  // of the long ones.
669  // Length sorting is done with ascending length so we use a reverse iterator here.
670  auto it = m_pOprtDef->rbegin();
671  for ( ; it != m_pOprtDef->rend(); ++it )
672  {
673  const QString &sID = it->first;
674  if ( sID == m_strFormula.mid ( m_iPos, sID.length() ) )
675  {
676  a_Tok.Set ( it->second, strTok );
677 
678  // operator was found
679  if ( m_iSynFlags & noOPT )
680  {
681  // An operator was found but is not expected to occur at
682  // this position of the formula, maybe it is an infix
683  // operator, not a binary operator. Both operator types
684  // can share characters in their identifiers.
685  if ( IsInfixOpTok ( a_Tok ) )
686  {
687  return true;
688  }
689  else
690  {
691  // nope, no infix operator
692  return false;
693  //Error(ecUNEXPECTED_OPERATOR, m_iPos, a_Tok.GetAsString());
694  }
695 
696  }
697 
698  m_iPos += sID.length();
700  return true;
701  }
702  }
703 
704  return false;
705 }
706 
707 //---------------------------------------------------------------------------------------------------------------------
708 /**
709  * @brief Check if a string position contains a unary post value operator.
710  */
712 {
713  // <ibg 20110629> Do not check for postfix operators if they are not allowed at
714  // the current expression index.
715  //
716  // This will fix the bug reported here:
717  //
718  // http://sourceforge.net/tracker/index.php?func=detail&aid=3343891&group_id=137191&atid=737979
719  //
720  if ( m_iSynFlags & noPOSTOP )
721  {
722  return false;
723  }
724  // </ibg>
725 
726  // Tricky problem with equations like "3m+5":
727  // m is a postfix operator, + is a valid sign for postfix operators and
728  // for binary operators parser detects "m+" as operator string and
729  // finds no matching postfix operator.
730  //
731  // This is a special case so this routine slightly differs from the other
732  // token readers.
733 
734  // Test if there could be a postfix operator
735  QString sTok;
736  int iEnd = ExtractToken ( m_pParser->ValidOprtChars(), sTok, m_iPos );
737  if ( iEnd == m_iPos )
738  {
739  return false;
740  }
741 
742  // iteraterate over all postfix operator strings
743  auto it = m_pPostOprtDef->rbegin();
744  for ( ; it != m_pPostOprtDef->rend(); ++it )
745  {
746  if ( sTok.indexOf ( it->first ) == 0 )
747  {
748  a_Tok.Set ( it->second, sTok );
749  m_iPos += it->first.length();
750 
752  return true;
753  }
754  }
755 
756  return false;
757 }
758 
759 //---------------------------------------------------------------------------------------------------------------------
760 /**
761  * @brief Check whether the token at a given position is a value token.
762  *
763  * Value tokens are either values or constants.
764  *
765  * @param a_Tok [out] If a value token is found it will be placed here.
766  * @return true if a value token has been found.
767  */
768 bool QmuParserTokenReader::IsValTok ( token_type &a_Tok, const QLocale &locale, const QChar &decimal,
769  const QChar &thousand )
770 {
771  assert ( m_pConstDef );
772  assert ( m_pParser );
773 
774  QString strTok;
775  qreal fVal ( 0 );
776  int iEnd ( 0 );
777 
778  // 2.) Check for user defined constant
779  // Read everything that could be a constant name
780  iEnd = ExtractToken ( m_pParser->ValidNameChars(), strTok, m_iPos );
781  if ( iEnd != m_iPos )
782  {
783  valmap_type::const_iterator item = m_pConstDef->find ( strTok );
784  if ( item != m_pConstDef->end() )
785  {
786  m_iPos = iEnd;
787  a_Tok.SetVal ( item->second, strTok );
788 
789  if ( m_iSynFlags & noVAL )
790  {
791  Error ( ecUNEXPECTED_VAL, m_iPos - strTok.length(), strTok );
792  }
793 
795  return true;
796  }
797  }
798 
799  // 3.call the value recognition functions provided by the user
800  // Call user defined value recognition functions
801  std::list<identfun_type>::const_iterator item = m_vIdentFun.begin();
802  for ( item = m_vIdentFun.begin(); item != m_vIdentFun.end(); ++item )
803  {
804  int iStart = m_iPos;
805  if ( ( *item ) ( m_strFormula.mid ( m_iPos ), &m_iPos, &fVal, locale, decimal, thousand ) == 1 )
806  {
807  // 2013-11-27 Issue 2: https://code.google.com/p/muparser/issues/detail?id=2
808  strTok = m_strFormula.mid ( iStart, m_iPos-iStart );
809  if ( m_iSynFlags & noVAL )
810  {
811  Error ( ecUNEXPECTED_VAL, m_iPos - strTok.length(), strTok );
812  }
813 
814  a_Tok.SetVal ( fVal, strTok );
816  return true;
817  }
818  }
819 
820  return false;
821 }
822 
823 //---------------------------------------------------------------------------------------------------------------------
824 /**
825  * @brief Check wheter a token at a given position is a variable token.
826  * @param a_Tok [out] If a variable token has been found it will be placed here.
827  * @return true if a variable token has been found.
828  */
830 {
831  if ( m_pVarDef->empty())
832  {
833  return false;
834  }
835 
836  QString strTok;
837  int iEnd = ExtractToken ( m_pParser->ValidNameChars(), strTok, m_iPos );
838  if ( iEnd == m_iPos )
839  {
840  return false;
841  }
842 
843  varmap_type::const_iterator item = m_pVarDef->find ( strTok );
844  if ( item == m_pVarDef->end() )
845  {
846  return false;
847  }
848 
849  if ( m_iSynFlags & noVAR )
850  {
851  Error ( ecUNEXPECTED_VAR, m_iPos, strTok );
852  }
853 
855 
856  m_iPos = iEnd;
857  a_Tok.SetVar ( item->second, strTok );
858  m_UsedVar[item->first] = item->second; // Add variable to used-var-list
859 
861 
862 // Zur Info hier die SynFlags von IsVal():
863 // m_iSynFlags = noVAL | noVAR | noFUN | noBO | noINFIXOP | noSTR | noASSIGN;
864  return true;
865 }
866 
867 //---------------------------------------------------------------------------------------------------------------------
869 {
870  if ( m_pStrVarDef == nullptr || m_pStrVarDef->empty() )
871  {
872  return false;
873  }
874 
875  QString strTok;
876  int iEnd = ExtractToken ( m_pParser->ValidNameChars(), strTok, m_iPos );
877  if ( iEnd == m_iPos )
878  {
879  return false;
880  }
881 
882  strmap_type::const_iterator item = m_pStrVarDef->find ( strTok );
883  if ( item == m_pStrVarDef->end() )
884  {
885  return false;
886  }
887 
888  if ( m_iSynFlags & noSTR )
889  {
890  Error ( ecUNEXPECTED_VAR, m_iPos, strTok );
891  }
892 
893  m_iPos = iEnd;
894  if ( m_pParser->m_vStringVarBuf.size() == false)
895  {
897  }
898 
899  a_Tok.SetString ( m_pParser->m_vStringVarBuf[item->second], m_pParser->m_vStringVarBuf.size() );
900 
901  m_iSynFlags = noANY ^ ( noBC | noOPT | noEND | noARG_SEP );
902  return true;
903 }
904 
905 
906 //---------------------------------------------------------------------------------------------------------------------
907 /**
908  * @brief Check wheter a token at a given position is an undefined variable.
909  *
910  * @param a_Tok [out] If a variable tom_pParser->m_vStringBufken has been found it will be placed here.
911  * @return true if a variable token has been found.
912  * @throw nothrow
913  */
915 {
916  QString strTok;
917  int iEnd ( ExtractToken ( m_pParser->ValidNameChars(), strTok, m_iPos ) );
918  if ( iEnd == m_iPos )
919  {
920  return false;
921  }
922 
923  if ( m_iSynFlags & noVAR )
924  {
925  // <ibg/> 20061021 added token string strTok instead of a_Tok.GetAsString() as the
926  // token identifier.
927  // related bug report:
928  // http://sourceforge.net/tracker/index.php?func=detail&aid=1578779&group_id=137191&atid=737979
929  Error ( ecUNEXPECTED_VAR, m_iPos - a_Tok.GetAsString().length(), strTok );
930  }
931 
932  // If a factory is available implicitely create new variables
933  if ( m_pFactory )
934  {
935  qreal *fVar = m_pFactory ( strTok, m_pFactoryData );
936  a_Tok.SetVar ( fVar, strTok );
937 
938  // Do not use m_pParser->DefineVar( strTok, fVar );
939  // in order to define the new variable, it will clear the
940  // m_UsedVar array which will kill previousely defined variables
941  // from the list
942  // This is safe because the new variable can never override an existing one
943  // because they are checked first!
944  ( *m_pVarDef ) [strTok] = fVar;
945  m_UsedVar[strTok] = fVar; // Add variable to used-var-list
946  }
947  else
948  {
949  a_Tok.SetVar ( &m_fZero, strTok );
950  m_UsedVar[strTok] = nullptr; // Add variable to used-var-list
951  }
952 
953  m_iPos = iEnd;
954 
955  // Call the variable factory in order to let it define a new parser variable
957  return true;
958 }
959 
960 
961 //---------------------------------------------------------------------------------------------------------------------
962 /**
963  * @brief Check wheter a token at a given position is a string.
964  * @param a_Tok [out] If a variable token has been found it will be placed here.
965  * @return true if a string token has been found.
966  * @sa IsOprt, IsFunTok, IsStrFunTok, IsValTok, IsVarTok, IsEOF, IsInfixOpTok, IsPostOpTok
967  */
969 {
970  if ( m_strFormula[m_iPos] != '"' )
971  {
972  return false;
973  }
974 
975  QString strBuf (m_strFormula.mid(m_iPos + 1));
976  int iEnd ( 0 ), iSkip ( 0 );
977 
978  // parser over escaped '\"' end replace them with '"'
979  for ( iEnd = strBuf.indexOf ( "\"" ); iEnd != 0 && iEnd != -1; iEnd = strBuf.indexOf ( "\"", iEnd ) )
980  {
981  if ( strBuf[iEnd - 1] != '\\' )
982  {
983  break;
984  }
985  strBuf.replace ( iEnd - 1, 2, "\"" );
986  iSkip++;
987  }
988 
989  if ( iEnd == -1 )
990  {
992  }
993 
994  QString strTok = strBuf.mid ( 0, iEnd );
995 
996  if ( m_iSynFlags & noSTR )
997  {
998  Error ( ecUNEXPECTED_STR, m_iPos, strTok );
999  }
1000 
1001  m_pParser->m_vStringBuf.push_back ( strTok ); // Store string in internal buffer
1002  a_Tok.SetString ( strTok, m_pParser->m_vStringBuf.size() );
1003 
1004  m_iPos += strTok.length() + 2 + iSkip; // +2 wg Anfhrungszeichen; +iSkip fr entfernte escape zeichen
1005  m_iSynFlags = noANY ^ ( noARG_SEP | noBC | noOPT | noEND );
1006 
1007  return true;
1008 }
1009 
1010 //---------------------------------------------------------------------------------------------------------------------
1011 /**
1012  * @brief Create an error containing the parse error position.
1013  *
1014  * This function will create an Parser Exception object containing the error text and its position.
1015  *
1016  * @param a_iErrc [in] The error code of type #EErrorCodes.
1017  * @param a_iPos [in] The position where the error was detected.
1018  * @param a_sTok [in] The token string representation associated with the error.
1019  * @throw ParserException always throws thats the only purpose of this function.
1020  */
1021 void Q_NORETURN QmuParserTokenReader::Error ( EErrorCodes a_iErrc, int a_iPos, const QString &a_sTok ) const
1022 {
1023  m_pParser->Error ( a_iErrc, a_iPos, a_sTok );
1024 }
1025 } // namespace qmu
Mathematical expressions parser (base parser engine).
Definition: qmuparserbase.h:66
funmap_type m_OprtDef
Binary operator callbacks.
stringbuf_type m_vStringVarBuf
valmap_type m_ConstDef
user constants.
strmap_type m_StrVarDef
user defined string constants
funmap_type m_FunDef
Map of function names and pointers.
void Q_NORETURN Error(EErrorCodes a_iErrc, int a_iPos=-1, const QString &a_sTok=QString()) const
Create an error containing the parse error position.
funmap_type m_PostOprtDef
Postfix operator callbacks.
const QString & ValidNameChars() const
Virtual function that defines the characters allowed in name identifiers.
bool HasBuiltInOprt() const
Query status of built in variables.
funmap_type m_InfixOprtDef
unary infix operator.
static const QStringList & GetOprtDef()
Get the default symbols used for the built in operators.
stringbuf_type m_vStringBuf
String buffer, used for storing string function arguments.
virtual void OnDetectVar(const QString &pExpr, int &nStart, int &nEnd)
const QString & ValidInfixOprtChars() const
Virtual function that defines the characters allowed in infix operator definitions.
const QString & ValidOprtChars() const
Virtual function that defines the characters allowed in operator definitions.
varmap_type m_VarDef
user defind variables.
Error class of the parser.
EErrorCodes GetCode() const
Return the error code.
const QString & GetMsg() const
Returns the message string for this error.
Token reader for the ParserBase class.
std::list< identfun_type > m_vIdentFun
Value token identification function.
int ExtractOperatorToken(QString &a_sTok, int a_iPos) const
Check Expression for the presence of a binary operator token.
QChar m_cArgSep
The character used for separating function arguments.
varmap_type * m_pVarDef
The only non const pointer to parser internals.
const funmap_type * m_pPostOprtDef
bool IsBuiltIn(token_type &a_Tok)
Check if a built in operator or other token can be found.
qreal m_fZero
Dummy value of zero, referenced by undefined variables.
int ExtractToken(const QString &a_szCharSet, QString &a_strTok, int a_iPos) const
Extract all characters that belong to a certain charset.
bool IsArgSep(token_type &a_Tok)
bool IsEOF(token_type &a_Tok)
Check for End of Formula.
void ReInit()
Reset the token reader to the start of the formula.
void AddValIdent(identfun_type a_pCallback)
void SetParent(QmuParserBase *a_pParent)
QmuParserTokenReader(QmuParserBase *a_pParent)
Constructor.
const funmap_type * m_pInfixOprtDef
bool IsValTok(token_type &a_Tok, const QLocale &locale, const QChar &decimal, const QChar &thousand)
Check whether the token at a given position is a value token.
bool IsUndefVarTok(token_type &a_Tok)
Check wheter a token at a given position is an undefined variable.
bool IsStrVarTok(token_type &a_Tok)
QmuParserToken< qreal, QString > token_type
bool IsFunTok(token_type &a_Tok)
Check whether the token at a given position is a function token.
int m_iBrackets
Keep count open brackets.
bool IsInfixOpTok(token_type &a_Tok)
Check if a string position contains a unary infix operator.
const valmap_type * m_pConstDef
bool IsString(token_type &a_Tok)
Check wheter a token at a given position is a string.
void SetFormula(const QString &a_strFormula)
Initialize the token Reader.
bool IsOprt(token_type &a_Tok)
Check if a string position contains a binary operator.
token_type ReadNextToken(const QLocale &locale, const QChar &decimal, const QChar &thousand)
Read the next token from the string.
QmuParserTokenReader * Clone(QmuParserBase *a_pParent) const
Create instance of a QParserTokenReader identical with this and return its pointer.
bool IsVarTok(token_type &a_Tok)
Check wheter a token at a given position is a variable token.
const strmap_type * m_pStrVarDef
void Assign(const QmuParserTokenReader &a_Reader)
Assign state of a token reader to this token reader.
const funmap_type * m_pOprtDef
QmuParserTokenReader & operator=(const QmuParserTokenReader &a_Reader)
Assignement operator.
bool IsPostOpTok(token_type &a_Tok)
Check if a string position contains a unary post value operator.
void SetVarCreator(facfun_type a_pFactory, void *pUserData)
@ noOPT
to avoid i.e. "(+)"
@ noBC
to avoid i.e. "sin)" or "()"
@ noANY
All of he above flags set.
@ noSTR
to block numeric arguments on string functions
@ noBO
to avoid i.e. "cos(7)("
@ noINFIXOP
to avoid i.e. "++4" "!!4"
@ noEND
to avoid unexpected end of formula
@ noASSIGN
to block assignement to constant i.e. "4=7"
@ noARG_SEP
to avoid i.e. ",," or "+," ...
@ noVAL
to avoid i.e. "tan 2" or "sin(8)3.14"
@ noPOSTOP
to avoid i.e. "(5!!)" "sin!"
@ noFUN
to avoid i.e. "sqrt cos" or "(1)sin"
@ noVAR
to avoid i.e. "sin a" or "sin(8)a"
token_type & SaveBeforeReturn(const token_type &tok)
void Q_NORETURN Error(EErrorCodes a_iErrc, int a_iPos=-1, const QString &a_sTok=QString()) const
Create an error containing the parse error position.
ECmdCode GetCode() const
Return the token type.
QmuParserToken & SetVal(TBase a_fVal, const TString &a_strTok=TString())
Make this token a value token.
QmuParserToken & SetVar(TBase *a_pVar, const TString &a_strTok)
make this token a variable token.
QmuParserToken & Set(ECmdCode a_iType, const TString &a_strTok=TString())
Assign a token type.
QmuParserToken & SetString(const TString &a_strTok, int a_iSize)
Make this token a variable token.
const TString & GetAsString() const
Return the token identifier.
Namespace for mathematical applications.
EErrorCodes
Error codes.
@ ecMISSING_PARENS
Missing parens. (Example: "3*sin(3")
@ ecUNEXPECTED_OPERATOR
Unexpected binary operator found.
@ ecUNEXPECTED_STR
A string has been found at an inapropriate position.
@ ecUNEXPECTED_EOF
Unexpected end of formula. (Example: "2+sin(")
@ ecUNEXPECTED_FUN
Unexpected function found. (Example: "sin(8)cos(9)")
@ ecUNASSIGNABLE_TOKEN
Token cant be identified.
@ ecUNEXPECTED_ARG_SEP
An unexpected semicolon has been found. (Example: "1;23")
@ ecUNEXPECTED_PARENS
Unexpected Parenthesis, opening or closing.
@ ecINTERNAL_ERROR
Internal error of any kind.
@ ecUNEXPECTED_VAR
An unexpected variable token has been found.
@ ecUNEXPECTED_CONDITIONAL
@ ecUNEXPECTED_VAL
An unexpected value token has been found.
@ ecUNTERMINATED_STRING
unterminated string constant. (Example: "3*valueof("hello)")
qreal *(* facfun_type)(const QString &, void *)
Callback used for variable creation factory functions.
Definition: qmuparserdef.h:306
int(* identfun_type)(const QString &sExpr, int *nPos, qreal *fVal, const QLocale &locale, const QChar &decimal, const QChar &thousand)
Callback used for functions that identify values in a string.
Definition: qmuparserdef.h:302
ECmdCode
Bytecode values.
Definition: qmuparserdef.h:99
@ cmBC
Operator item: closing bracket.
Definition: qmuparserdef.h:118
@ cmLE
Operator item: less or equal.
Definition: qmuparserdef.h:103
@ cmFUNC
Code for a generic function item.
Definition: qmuparserdef.h:134
@ cmEND
end of formula
Definition: qmuparserdef.h:141
@ cmASSIGN
Operator item: Assignment operator.
Definition: qmuparserdef.h:116
@ cmELSE
For use in the ternary if-then-else operator.
Definition: qmuparserdef.h:120
@ cmARG_SEP
function argument separator
Definition: qmuparserdef.h:122
@ cmIF
For use in the ternary if-then-else operator.
Definition: qmuparserdef.h:119
@ cmBO
Operator item: opening bracket.
Definition: qmuparserdef.h:117
std::wstring string_type
The stringtype used by the parser.
Definition: qmuparserdef.h:196
This file contains the class definition of the qmuparser engine.
#define QMUP_CHARS
Definition: qmuparserdef.h:38
This file contains the parser token reader definition.