/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 |