NumeRe v1.1.4
NumeRe: Framework für Numerische Rechnungen
stringlogicparser.cpp
Go to the documentation of this file.
1/*****************************************************************************
2 NumeRe: Framework fuer Numerische Rechnungen
3 Copyright (C) 2019 Erik Haenel et al.
4
5 This program is free software: you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation, either version 3 of the License, or
8 (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17******************************************************************************/
18
19#include "stringlogicparser.hpp"
20#include "../../kernel.hpp"
21
22using namespace std;
23
24string removeMaskedStrings(const string& sString);
25
26namespace NumeRe
27{
37 {
38 bool bReturningLogicals = false;
39 size_t nPos = 0;
40
41 // As long as there's a question mark in the current string
42 while (sLine.find('?', nPos) != string::npos)
43 {
44 nPos = sLine.find('?', nPos);
45
46 // Ensure, that we're not in a string literal
47 if (!isInQuotes(sLine, nPos))
48 {
49 // Get the current ternary as a vector
50 vector<string> vTernary = getStringTernaryExpression(sLine, nPos);
51 nPos = 0;
52
53 // Evaluate logical string expressions
54 vTernary[0] = evalStringLogic(vTernary[0], bReturningLogicals);
55 bool result = false;
56
57 // Evaluate the condition of the ternary logically
58 if (vTernary[0].find('"') != string::npos)
59 {
60 StripSpaces(vTernary[0]);
61 result = (bool)(vTernary[0].length() - 2);
62 }
63 else
64 {
66 result = NumeReKernel::getInstance()->getParser().Eval() != 0.0;
67 }
68
69 // return the evaluated part of the string
70 if (result)
71 return sLine + evalStringLogic(vTernary[1], bReturningLogicals);
72 else
73 return sLine + evalStringLogic(vTernary[2], bReturningLogicals);
74 }
75 else
76 nPos++;
77 }
78
79 return sLine;
80 }
81
82
94 size_t StringLogicParser::detectPathTokens(const string& sString, size_t nPos)
95 {
96 if (sString.substr(nPos, 2) == "<>")
97 return 1u;
98
99 if (sString.substr(nPos, 4) == "<wp>")
100 return 3u;
101
102 if (sString.substr(nPos, 6) == "<this>")
103 return 5u;
104
105 if (sString.substr(nPos, 10) == "<loadpath>")
106 return 9u;
107
108 if (sString.substr(nPos, 10) == "<savepath>")
109 return 9u;
110
111 if (sString.substr(nPos, 10) == "<procpath>")
112 return 9u;
113
114 if (sString.substr(nPos, 10) == "<plotpath>")
115 return 9u;
116
117 if (sString.substr(nPos, 12) == "<scriptpath>")
118 return 11u;
119
120 return 0u;
121 }
122
123
133 string StringLogicParser::prepareComparisonValues(const std::string& _sLine)
134 {
135 bool bReturningLogicals = false;
136
137 string sLine = evalStringLogic(_sLine, bReturningLogicals);
138 StripSpaces(sLine);
139
140 if (sLine.front() == '"' && sLine.back() == '"')
141 {
142 concatenateStrings(sLine);
143 sLine = sLine.substr(1, sLine.length() - 2);
144 }
145
146 return removeMaskedStrings(sLine);
147 }
148
149
163 vector<string> StringLogicParser::getStringTernaryExpression(string& sLine, size_t& nPos)
164 {
165 vector<string> vTernary;
166 size_t nTernaryStart = 0;
167 size_t nColonPosition = 0;
168
169 string sTernary = sLine.substr(nTernaryStart);
170 sLine.erase(nTernaryStart);
171
172 size_t quotes = 0;
173 int nQuestionMarks = 0;
174
175 // Search for the operators of the ternary
176 // Jump over additional quotation marks
177 for (size_t i = nPos; i < sTernary.length(); i++)
178 {
179 // Jump ver parentheses
180 if (!(quotes % 2) && (sTernary[i] == '(' || sTernary[i] == '[' || sTernary[i] == '{'))
181 i += getMatchingParenthesis(sTernary.substr(i));
182
183 // Increment the question mark counter
184 if (!(quotes % 2) && sTernary[i] == '?')
185 nQuestionMarks++;
186
187 // Increment the quotation mark counter
188 if (sTernary[i] == '"' && sTernary[i - 1] != '\\')
189 quotes++;
190
191 // If there's a colon, decrement the quotation mark
192 // counter
193 if (!(quotes % 2) && sTernary[i] == ':')
194 {
195 nQuestionMarks--;
196 if (!nQuestionMarks)
197 {
198 // this is the correct colon
199 nColonPosition = i;
200 break;
201 }
202 }
203 }
204
205 // This is obviously not a real ternary
206 if (!nColonPosition)
207 throw SyntaxError(SyntaxError::INVALID_INDEX, sLine, nPos, sTernary);
208
209 // Distribute the expression parts of the ternary
210 // across the vector components
211 vTernary.push_back(sTernary.substr(0, nPos));
212 vTernary.push_back(sTernary.substr(nPos + 1, nColonPosition - 1 - nPos));
213 vTernary.push_back(sTernary.substr(nColonPosition + 1));
214
215 nPos = nTernaryStart;
216 return vTernary;
217 }
218
219
229 bool StringLogicParser::detectStringLogicals(const string& sString)
230 {
231 if (!sString.length())
232 return false;
233
234 int nQuotes = 0;
235
236 for (size_t i = 0; i < sString.length(); i++)
237 {
238 // Count quotation marks
239 if (sString[i] == '"' && (!i || sString[i - 1] != '\\'))
240 {
241 nQuotes++;
242 continue;
243 }
244
245 // Detect path tokens
246 if (sString[i] == '<')
247 {
248 // Search for path tokens
249 size_t nAdv = detectPathTokens(sString, i);
250
251 if (nAdv)
252 {
253 i += nAdv;
254 continue;
255 }
256 }
257
258 // Logicals are only possible outside of string literals
259 if (!(nQuotes % 2))
260 {
261 if (sString.substr(i, 2) == "&&"
262 || sString.substr(i, 2) == "||"
263 || sString.substr(i, 2) == "<="
264 || sString.substr(i, 2) == ">="
265 || sString.substr(i, 2) == "!="
266 || sString.substr(i, 2) == "=="
267 || sString[i] == '?'
268 || sString[i] == '<'
269 || sString[i] == '>')
270 return true;
271 }
272 }
273
274 return false;
275 }
276
277
288 string StringLogicParser::evalStringLogic(string sLine, bool& bReturningLogicals)
289 {
291
292 // Exclude border cases
293 if (!sLine.length())
294 return "false";
295
296 if (sLine.find('"') == string::npos)
297 {
298 bReturningLogicals = true;
299 return sLine;
300 }
301
302 sLine += " ";
303
304 // Evaluate ternaries first (will start a recursion)
305 sLine = evalStringTernary(sLine);
306
307 unsigned int nPos;
308
309 // Handle parenthesed expression parts
310 // (will start a recursion)
311 if (sLine.find('(') != string::npos)
312 {
313 nPos = 0;
314
315 while (sLine.find('(', nPos) != string::npos)
316 {
317 nPos = sLine.find('(', nPos) + 1;
318
319 if (!isInQuotes(sLine, nPos - 1))
320 {
321 sLine = sLine.substr(0, nPos - 1)
322 + evalStringLogic(sLine.substr(nPos, getMatchingParenthesis(sLine.substr(nPos - 1)) - 1), bReturningLogicals)
323 + sLine.substr(getMatchingParenthesis(sLine.substr(nPos - 1)) + nPos);
324 nPos = 0;
325 }
326 }
327 }
328
329 // Handle the logical and
330 if (sLine.find("&&") != string::npos)
331 {
332 nPos = 0;
333
334 while (sLine.find("&&", nPos) != string::npos)
335 {
336 nPos = sLine.find("&&", nPos) + 2;
337
338 if (!isInQuotes(sLine, nPos - 2))
339 {
340 string sLeft = prepareComparisonValues(sLine.substr(0, nPos-2));
341 string sRight = prepareComparisonValues(sLine.substr(nPos));
342
343 bReturningLogicals = true;
344
345 if (sLeft == "true" && sRight == "true")
346 return "true";
347 else if (sLeft == "false" || sRight == "false")
348 return "false";
349 else
350 {
351 _parser.SetExpr(sLeft + " && " + sRight);
352 return toString(_parser.Eval() != 0.0);
353 }
354 }
355 }
356 }
357
358 // Handle the exclusive or
359 if (sLine.find("|||") != string::npos)
360 {
361 nPos = 0;
362
363 while (sLine.find("|||", nPos) != string::npos)
364 {
365 nPos = sLine.find("|||", nPos) + 3;
366
367 if (!isInQuotes(sLine, nPos - 3))
368 {
369 string sLeft = prepareComparisonValues(sLine.substr(0, nPos-3));
370 string sRight = prepareComparisonValues(sLine.substr(nPos));
371
372 bReturningLogicals = true;
373
374 if ((sLeft == "true" && sRight == "false") || (sLeft == "false" && sRight == "true"))
375 return "true";
376 else if (sLeft == sRight)
377 return "false";
378 else
379 {
380 _parser.SetExpr(sLeft + " ||| " + sRight);
381 return toString(_parser.Eval() != 0.0);
382 }
383 }
384 }
385 }
386
387 // Handle the logical or
388 if (sLine.find("||") != string::npos)
389 {
390 nPos = 0;
391
392 while (sLine.find("||", nPos) != string::npos)
393 {
394 nPos = sLine.find("||", nPos) + 2;
395
396 if (!isInQuotes(sLine, nPos - 2))
397 {
398 string sLeft = prepareComparisonValues(sLine.substr(0, nPos-2));
399 string sRight = prepareComparisonValues(sLine.substr(nPos));
400
401 bReturningLogicals = true;
402
403 if (sLeft == "true" || sRight == "true")
404 return "true";
405 else if (sLeft == "false" && sRight == "false")
406 return "false";
407 else
408 {
409 _parser.SetExpr(sLeft + " || " + sRight);
410 return toString(_parser.Eval() != 0.0);
411 }
412 }
413 }
414 }
415
416 int nQuotes = 0;
417
418 // Handle logical comparisons
419 for (size_t i = 0; i < sLine.length(); i++)
420 {
421 if (sLine[i] == '"' && (!i || sLine[i - 1] != '\\'))
422 nQuotes++;
423
424 if (!(nQuotes % 2))
425 {
426 if (sLine.substr(i, 2) == "==")
427 {
428 bReturningLogicals = true;
429 return toString(prepareComparisonValues(sLine.substr(0, i)) == prepareComparisonValues(sLine.substr(i + 2)));
430 }
431 else if (sLine.substr(i, 2) == "!=")
432 {
433 bReturningLogicals = true;
434 return toString(prepareComparisonValues(sLine.substr(0, i)) != prepareComparisonValues(sLine.substr(i + 2)));
435 }
436 else if (sLine.substr(i, 2) == "<=")
437 {
438 bReturningLogicals = true;
439 return toString(prepareComparisonValues(sLine.substr(0, i)) <= prepareComparisonValues(sLine.substr(i + 2)));
440 }
441 else if (sLine.substr(i, 2) == ">=")
442 {
443 bReturningLogicals = true;
444 return toString(prepareComparisonValues(sLine.substr(0, i)) >= prepareComparisonValues(sLine.substr(i + 2)));
445 }
446 else if (sLine[i] == '<')
447 {
448 bReturningLogicals = true;
449 return toString(prepareComparisonValues(sLine.substr(0, i)) < prepareComparisonValues(sLine.substr(i + 1)));
450 }
451 else if (sLine[i] == '>')
452 {
453 bReturningLogicals = true;
454 return toString(prepareComparisonValues(sLine.substr(0, i)) > prepareComparisonValues(sLine.substr(i + 1)));
455 }
456
457 }
458 }
459
460 StripSpaces(sLine);
461 return sLine;
462 }
463
464
478 {
479 size_t nQuotes = 0;
480
481 for (unsigned int i = 0; i < sExpr.length(); i++)
482 {
483 if (sExpr[i] == '"' && (!i || sExpr[i-1] != '\\'))
484 nQuotes++;
485
486 // Search for the concatenation operator (aka "+")
487 if (!(nQuotes % 2) && sExpr[i] == '+')
488 {
489 StringView sLeft(sExpr, 0, i);
490 StringView sRight(sExpr, i+1);
491
492 sLeft.strip();
493 sRight.strip();
494
495 // Determine the correct concatenation process
496 if (sLeft == "\"\"" && sRight != "\"\"")
497 {
498 sExpr = " " + sRight.to_string();
499 i = 0;
500 }
501 else if (sLeft != "\"\"" && sRight == "\"\"")
502 {
503 sExpr = sLeft.to_string();
504 break;
505 }
506 else if (sLeft.back() == '"' && sRight.front() == '"')
507 {
508 // We removed some characters
509 i = sLeft.length()-2;
510
511 sExpr = sLeft.subview(0, sLeft.length()-1).to_string() + sRight.subview(1).to_string();
512
513 // We're now part of a string
514 nQuotes++;
515 }
516
517 // Everything not catched here is a strange mixture
518 }
519 else if (!(nQuotes % 2)
520 && (isdigit(sExpr[i]) || sExpr.compare(i, 3, "inf") == 0 || sExpr.compare(i, 3, "nan") == 0)) // Numerical literals
521 {
522 size_t nextPos = sExpr.find_first_not_of("0123456789Ee+-infa.", i);
523
524 if (nextPos == std::string::npos)
525 break;
526
527 i += nextPos;
528 }
529 else if (!(nQuotes % 2) && isalpha(sExpr[i])) // Unexpected variables
530 throw SyntaxError(SyntaxError::STRING_ERROR, sExpr, i, sExpr);
531 }
532
533 StripSpaces(sExpr);
534 }
535}
536
std::string prepareComparisonValues(const std::string &_sLine)
Removes masked strings, strips spaces and removes surrounding quotation marks and concatenates the va...
void concatenateStrings(std::string &sExpr)
This member function performs the actual string concatenation of the passed string expression.
std::string evalStringLogic(std::string sLine, bool &bReturningLogicals)
This member function will evaluate logical string expressions in the passed command line.
size_t detectPathTokens(const std::string &sString, size_t nPos)
This member function is a helper for StringLogicParser::detectStringLogicals().
std::vector< std::string > getStringTernaryExpression(std::string &sLine, size_t &nPos)
This member function is a helper for StringLogicParser::evalStringLogic().
std::string evalStringTernary(std::string sLine)
This member function will evaluate the ternary operator for strings.
bool detectStringLogicals(const std::string &sString)
This member function is may detect logical expressions in the passed string expression.
static NumeReKernel * getInstance()
This static member function returns a a pointer to the singleton instance of the kernel.
Definition: kernel.hpp:221
mu::Parser & getParser()
Definition: kernel.hpp:281
void strip()
This member function shrinks the viewed section to remove all leading or trailing whitespace characte...
const char & back() const
This member function provides a const char reference to the last character in the viewed section.
const char & front() const
This member function provides a const char reference to the first character in the viewed section.
std::string to_string() const
This member function returns a copy of the viewed section of the string (via std::string::substr)....
size_t length() const
This member function simply returns the length of the viewed section.
This class is the immutable (const) version of a string view. It can be constructed from a MutableStr...
StringView subview(size_t pos=0, size_t len=std::string::npos) const
This member function creates a new StringView class instance using the selected position and length a...
Common exception class for all exceptions thrown in NumeRe.
Definition: error.hpp:32
@ INVALID_INDEX
Definition: error.hpp:129
@ STRING_ERROR
Definition: error.hpp:209
void SetExpr(StringView a_sExpr)
Set the expression. Triggers first time calculation thus the creation of the bytecode and scanning of...
value_type Eval()
Single-value wrapper around the vectorized overload of this member function.
Mathematical expressions parser.
Definition: muParser.h:51
unsigned int getMatchingParenthesis(const StringView &)
Returns the position of the closing parenthesis.
Definition: tools.cpp:414
void StripSpaces(std::string &)
Removes leading and trailing white spaces and tabulator characters.
string removeMaskedStrings(const string &sString)
This function removes the escape characters from the passed string.
std::string toString(int)
Converts an integer to a string without the Settings bloat.
bool isInQuotes(StringView sExpr, unsigned int nPos, bool bIgnoreVarParser)
Checks, whether the position in the passed expression is part of a string literal.
Definition: tools.cpp:1672