NumeRe v1.1.4
NumeRe: Framework für Numerische Rechnungen
procedure.cpp
Go to the documentation of this file.
1/*****************************************************************************
2 NumeRe: Framework fuer Numerische Rechnungen
3 Copyright (C) 2014 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
20#include "procedure.hpp"
21#include "../../kernel.hpp"
23#include <memory>
24#define MAX_PROCEDURE_STACK_SIZE 2000000
25
26extern value_type vAns;
27
28using namespace std;
29
30
35{
36 // init the object
37 init();
38}
39
40
49Procedure::Procedure(const Procedure& _procedure) : FlowCtrl(), PackageManager(_procedure), _localDef(true)
50{
51 // Init the object
52 init();
53
54 // Copy the relevant data
55 sPath = _procedure.sPath;
58 sProcNames = _procedure.sProcNames;
59
62
64
65 for (unsigned int i = 0; i < 6; i++)
66 {
67 sTokens[i][0] = _procedure.sTokens[i][0];
68 sTokens[i][1] = _procedure.sTokens[i][1];
69 }
70}
71
72
79{
80 if (fProcedure.is_open())
81 fProcedure.close();
82
83 if (_varFactory)
84 {
85 delete _varFactory;
86 }
87}
88
89
99{
100 sCallingNameSpace = "main";
101 bProcSupressAnswer = false;
102 bWritingTofile = false;
103 nCurrentLine = 0;
104 nthBlock = 0;
105 nFlags = 0;
106
107 _varFactory = nullptr;
108}
109
110
130Returnvalue Procedure::ProcCalc(string sLine, string sCurrentCommand, int& nByteCode, Parser& _parser, FunctionDefinitionManager& _functions, MemoryManager& _data, Settings& _option, Output& _out, PlotData& _pData, Script& _script)
131{
132 Returnvalue thisReturnVal;
133 int nNum = 0;
134 int nCurrentByteCode = nByteCode;
135 value_type* v = nullptr;
136
137 // Do not clear the vector variables, if we are currently part of a
138 // loop, because the loop uses the cached vector variables for
139 // speeding up the whole calculation process
140 if (!_parser.ActiveLoopMode() || (!_parser.IsLockedPause() && !(nFlags & ProcedureCommandLine::FLAG_INLINE)))
141 _parser.ClearVectorVars(true);
142
143 // Check, whether the user pressed "ESC"
145 {
147 }
148
149 // Remove obsolete whitespaces
150 StripSpaces(sLine);
151
152 // Ignore empty lines
153 if (!sLine.length() || sLine[0] == '@')
154 {
155 thisReturnVal.vNumVal.push_back(NAN);
156 return thisReturnVal;
157 }
158
159 // Check for the "assert" command
160 if (nCurrentByteCode == ProcedureCommandLine::BYTECODE_NOT_PARSED
161 || (nCurrentByteCode & ProcedureCommandLine::BYTECODE_ASSERT
162 && !(nCurrentByteCode & ProcedureCommandLine::BYTECODE_FLOWCTRLSTATEMENT)))
163 {
164 if (findCommand(sLine, "assert").sString == "assert")
165 {
167 sLine.erase(findCommand(sLine, "assert").nPos, 6);
168 StripSpaces(sLine);
169
170 if (nCurrentByteCode == ProcedureCommandLine::BYTECODE_NOT_PARSED)
172 }
173 }
174
175 // Handle the "to_cmd()" function, which is quite slow
176 // Only handle this function, if we're not inside of a loop
177 if (nCurrentByteCode == ProcedureCommandLine::BYTECODE_NOT_PARSED
178 || (nCurrentByteCode & ProcedureCommandLine::BYTECODE_TOCOMMAND
179 && !(nCurrentByteCode & ProcedureCommandLine::BYTECODE_FLOWCTRLSTATEMENT)))
180 {
181 if (sLine.find("to_cmd(") != string::npos)
182 {
183 unsigned int nPos = 0;
184
185 // As long as the "to_cmd()" function is found
186 while (sLine.find("to_cmd(", nPos) != string::npos)
187 {
188 nPos = sLine.find("to_cmd(", nPos) + 6;
189
190 if (isInQuotes(sLine, nPos))
191 continue;
192
193 unsigned int nParPos = getMatchingParenthesis(sLine.substr(nPos));
194
195 if (nParPos == string::npos)
197
198 string sCmdString = sLine.substr(nPos + 1, nParPos - 1);
199 StripSpaces(sCmdString);
200
201 // Parse strings, if the argument contains some
202 if (NumeReKernel::getInstance()->getStringParser().isStringExpression(sCmdString))
203 {
204 sCmdString += " -nq";
205 std::string sDummy;
206 NumeReKernel::getInstance()->getStringParser().evalAndFormat(sCmdString, sDummy, true);
207 }
208
209 // Replace the current command line
210 sLine = sLine.substr(0, nPos - 6) + sCmdString + sLine.substr(nPos + nParPos + 1);
211 nPos -= 5;
212
213 if (nCurrentByteCode == ProcedureCommandLine::BYTECODE_NOT_PARSED)
215 }
216
217 replaceLocalVars(sLine);
218 sCurrentCommand = findCommand(sLine).sString;
219 }
220 }
221
222 // Handle the "throw" command
223 if (nCurrentByteCode == ProcedureCommandLine::BYTECODE_NOT_PARSED
225 && !(nCurrentByteCode & ProcedureCommandLine::BYTECODE_FLOWCTRLSTATEMENT)))
226 {
227 if (sCurrentCommand == "throw")
228 {
229 string sErrorToken;
230
231 if (sLine.length() > 6 && NumeReKernel::getInstance()->getStringParser().isStringExpression(sLine))
232 {
233 if (NumeReKernel::getInstance()->getStringParser().containsStringVars(sLine))
235
237 sErrorToken += " -nq";
238 std::string sDummy;
240 }
241
242 if (nCurrentByteCode == ProcedureCommandLine::BYTECODE_NOT_PARSED)
244
246 }
247 }
248
249 // Call the user prompt routine
250 if (nCurrentByteCode == ProcedureCommandLine::BYTECODE_NOT_PARSED
251 || (nCurrentByteCode & ProcedureCommandLine::BYTECODE_PROMPT
252 && !(nCurrentByteCode & ProcedureCommandLine::BYTECODE_FLOWCTRLSTATEMENT)))
253 {
254 if (sLine.find("??") != string::npos && sCurrentCommand != "help")
255 {
256 sLine = promptForUserInput(sLine);
257
258 if (nCurrentByteCode == ProcedureCommandLine::BYTECODE_NOT_PARSED)
260 }
261 }
262
263 /* --> Die Keyword-Suche soll nur funktionieren, wenn keine Schleife eingegeben wird, oder wenn eine
264 * eine Schleife eingegeben wird, dann nur in den wenigen Spezialfaellen, die zum Nachschlagen
265 * eines Keywords noetig sind ("list", "help", "find", etc.) <--
266 */
267 if (nCurrentByteCode == ProcedureCommandLine::BYTECODE_NOT_PARSED
268 || (nCurrentByteCode & ProcedureCommandLine::BYTECODE_COMMAND
269 && !(nCurrentByteCode & ProcedureCommandLine::BYTECODE_FLOWCTRLSTATEMENT)))
270 {
271 if (!FlowCtrl::isFlowCtrlStatement(sCurrentCommand)
272 || sCurrentCommand == "help"
273 || sCurrentCommand == "man"
274 || sCurrentCommand == "quit"
275 || sCurrentCommand == "list"
276 || sCurrentCommand == "find"
277 || sCurrentCommand == "search"
278 || sCurrentCommand == "mode"
279 || sCurrentCommand == "menue")
280 {
282 string sCommandBack = sLine;
283 switch (commandHandler(sLine))
284 {
286 case NO_COMMAND:
287 if (nCurrentByteCode == ProcedureCommandLine::BYTECODE_NOT_PARSED)
288 {
289 StripSpaces(sCommandBack);
290 string sCurrentLine = sLine;
291 StripSpaces(sCurrentLine);
292 if (sCommandBack != sCurrentLine)
294 }
295 break; // Kein Keywort: Mit dem Parser auswerten
296 case COMMAND_PROCESSED: // Keywort: Naechster Schleifendurchlauf!
297 if (nCurrentByteCode == ProcedureCommandLine::BYTECODE_NOT_PARSED)
299
300 thisReturnVal.vNumVal.push_back(NAN);
301 return thisReturnVal;
302 default:
303 if (nCurrentByteCode == ProcedureCommandLine::BYTECODE_NOT_PARSED)
305
306 thisReturnVal.vNumVal.push_back(NAN);
307 return thisReturnVal; // Keywort "mode"
308 }
309
310 // It may be possible that the user entered "??" during some
311 // command (i.e. "readline"). We'll handle this string at this
312 // place
313 if (sLine.find("??") != string::npos)
314 sLine = promptForUserInput(sLine);
315
316 // It may also be possible that some procedure occures at this
317 // position. Handle them here
318 if (sLine.find('$') != std::string::npos)
319 procedureInterface(sLine, _parser, _functions, _data, _out, _pData, _script, _option, 0);
320 }
321
322 }
323
324 // Call functions if we're not in a loop
325 if (nCurrentByteCode == ProcedureCommandLine::BYTECODE_NOT_PARSED
327 {
328 if (sCurrentCommand != "for" && sCurrentCommand != "if" && sCurrentCommand != "while" && sCurrentCommand != "switch")
329 {
330 if (!_functions.call(sLine))
332
333 // Reduce surrounding white spaces
334 StripSpaces(sLine);
335 }
336 }
337
338 // Handle recursive expressions
339 if (nCurrentByteCode == ProcedureCommandLine::BYTECODE_NOT_PARSED
341 && !(nCurrentByteCode & ProcedureCommandLine::BYTECODE_FLOWCTRLSTATEMENT)))
342 {
344 {
345 // Keep breakpoints and remove the token from the command line
346 bool bBreakPoint = (sLine.substr(0, 2) == "|>");
347
348 if (bBreakPoint)
349 {
350 sLine.erase(0, 2);
351 StripSpaces(sLine);
352 }
353
354 // evaluate the recursive expression
356
357 if (nCurrentByteCode == ProcedureCommandLine::BYTECODE_NOT_PARSED)
359 }
360 }
361
362 // If we're already in a flow control statement
363 // or the current command starts with a flow control
364 // statement, then we pass the current command line
365 // to the FlowCtrl class
366 if (nCurrentByteCode == ProcedureCommandLine::BYTECODE_NOT_PARSED
368 {
369 if (getCurrentBlockDepth() || FlowCtrl::isFlowCtrlStatement(sCurrentCommand))
370 {
371 if (nCurrentByteCode == ProcedureCommandLine::BYTECODE_NOT_PARSED)
373 // Add the suppression semicolon, if necessary
375 sLine += ";";
376
377 // Pass the command line to the FlowCtrl class
378 setCommand(sLine, nCurrentLine);
379
380 // Return now to the calling function
381 thisReturnVal.vNumVal.push_back(NAN);
382 return thisReturnVal;
383 }
384 }
385
386 // Declare all variables, which are needed in the
387 // following sections
388 std::string sCache;
389 bool bWriteToCache = false;
390 bool bWriteToCluster = false;
391
392 // Get elements from data access
393 if (nCurrentByteCode == ProcedureCommandLine::BYTECODE_NOT_PARSED
396 {
397 if (!NumeReKernel::getInstance()->getStringParser().isStringExpression(sLine)
398 && _data.containsTablesOrClusters(sLine))
399 {
400 if (nCurrentByteCode == ProcedureCommandLine::BYTECODE_NOT_PARSED)
402
403 sCache = getDataElements(sLine, _parser, _data, _option);
404
405 if (sCache.length() && sCache.find('#') == string::npos)
406 bWriteToCache = true;
407
408 // Ad-hoc bytecode adaption
409#warning NOTE (numere#1#08/21/21): Might need some adaption, if bytecode issues are experienced
410 if (nCurrentByteCode && NumeReKernel::getInstance()->getStringParser().isStringExpression(sLine))
411 nCurrentByteCode |= ProcedureCommandLine::BYTECODE_STRING;
412 }
413 else if (isClusterCandidate(sLine, sCache))
414 {
415 bWriteToCache = true;
416
417 if (nCurrentByteCode == ProcedureCommandLine::BYTECODE_NOT_PARSED)
419 }
420 }
421
422 // If the current line contains a string value or a string variable,
423 // call the string parser and handle the return value
424 if (nCurrentByteCode == ProcedureCommandLine::BYTECODE_NOT_PARSED
425 || nCurrentByteCode & ProcedureCommandLine::BYTECODE_STRING
427 {
428 if (NumeReKernel::getInstance()->getStringParser().isStringExpression(sLine)
429 || NumeReKernel::getInstance()->getStringParser().isStringExpression(sCache))
430 {
431 auto retVal = NumeReKernel::getInstance()->getStringParser().evalAndFormat(sLine, sCache, bProcSupressAnswer, true);
433
434 if (nCurrentByteCode == ProcedureCommandLine::BYTECODE_NOT_PARSED)
436
437 // Handle the return value
439 {
440 // Only strings
442 return thisReturnVal;
443 }
444
445#warning NOTE (erik.haenel#3#): This is changed due to double writes in combination with c{nlen+1} = VAL
446 // Other: numerical values
447 //if (sCache.length() && _data.containsTablesOrClusters(sCache) && !bWriteToCache)
448 // bWriteToCache = true;
449
450 if (sCache.length())
451 {
452 bWriteToCache = false;
453 sCache.clear();
454 }
455
456 // Ensure that the correct variables are available, because
457 // the user might have used "to_value()" or something similar
458 replaceLocalVars(sLine);
459 }
460 }
461
462 // Create the indices structure
463 Indices _idx;
464
465 // Get the target coordinates of the target cache,
466 // if this is required
467 if (nCurrentByteCode == ProcedureCommandLine::BYTECODE_NOT_PARSED
470 {
471 if (bWriteToCache)
472 {
473 // Get the indices from the corresponding function
474 getIndices(sCache, _idx, _parser, _data, _option);
475
476 if (sCache[sCache.find_first_of("({")] == '{')
477 bWriteToCluster = true;
478
479 if (!isValidIndexSet(_idx))
480 throw SyntaxError(SyntaxError::INVALID_INDEX, sCache, "", _idx.row.to_string() + ", " + _idx.col.to_string());
481
482 if (!bWriteToCluster && _idx.row.isOpenEnd() && _idx.col.isOpenEnd())
483 throw SyntaxError(SyntaxError::NO_MATRIX, sCache, "");
484
485 sCache.erase(sCache.find_first_of("({"));
486 StripSpaces(sCache);
487 }
488 }
489
490 // Set the expression and evaluate it
491 if (!_parser.IsAlreadyParsed(sLine))
492 _parser.SetExpr(sLine);
493
494 v = _parser.Eval(nNum);
496
497 // Copy the return values
498 if (nNum > 1)
499 {
500 for (int i = 0; i < nNum; ++i)
501 thisReturnVal.vNumVal.push_back(v[i]);
502 }
503 else
504 {
505 thisReturnVal.vNumVal.push_back(v[0]);
506 }
507
508 vAns = v[0];
510
511 // Print the output to the console, if it isn't suppressed
514
515 // Write the return values to cache
516 if (bWriteToCache)
517 {
518 // Is it a cluster?
519 if (bWriteToCluster)
520 {
521 NumeRe::Cluster& cluster = _data.getCluster(sCache);
522 cluster.assignResults(_idx, nNum, v);
523 }
524 else
525 _data.writeToTable(_idx, sCache, v, nNum);
526 }
527
528 // Clear the vector variables after the loop returned
529 if (!_parser.ActiveLoopMode() || (!_parser.IsLockedPause() && !(nFlags & ProcedureCommandLine::FLAG_INLINE)))
530 _parser.ClearVectorVars(true);
531
532 return thisReturnVal;
533}
534
535
547bool Procedure::setProcName(const string& sProc, bool bInstallFileName)
548{
549 if (sProc.length())
550 {
551 string _sProc = sProc;
552
553 // Handle the "thisfile" namespace by using the call stack
554 // to obtain the corresponding file name
555 if (sProcNames.length() && !bInstallFileName && _sProc.substr(0, 9) == "thisfile~")
556 {
557 sCurrentProcedureName = sProcNames.substr(sProcNames.rfind(';') + 1);
559 return true;
560 }
561 else if (sLastWrittenProcedureFile.length() && bInstallFileName && _sProc.substr(0, 9) == "thisfile~")
562 {
564 return true;
565 }
566 else if (_sProc.substr(0, 9) == "thisfile~")
567 return false;
568
569 // Replace all tilde characters in the current path
570 // string. Consider the special namespace "main", which
571 // is a reference to the toplevel procedure folder
572 for (size_t i = 0; i < _sProc.length(); i++)
573 {
574 if (_sProc[i] == '~')
575 {
576 if (_sProc.length() > 5 && i >= 4 && _sProc.substr(i - 4, 5) == "main~")
577 _sProc = _sProc.substr(0, i - 4) + _sProc.substr(i + 1);
578 else
579 _sProc[i] = '/';
580 }
581 }
582
583 // Create a valid file name from the procedure name
585
586 // Append the newly obtained procedure file name
587 // to the call stack
589 return true;
590 }
591 else
592 return false;
593}
594
595
614Returnvalue Procedure::execute(string sProc, string sVarList, Parser& _parser, FunctionDefinitionManager& _functions, MemoryManager& _data, Settings& _option, Output& _out, PlotData& _pData, Script& _script, unsigned int nth_procedure)
615{
616 // Measure the current stack size and ensure
617 // that the current call won't exceed the
618 // maximal stack size
619 int stackMeasureVar;
620
621 if ((stackMeasureVar = abs(&stackMeasureVar - NumeReKernel::baseStackPosition)) > MAX_PROCEDURE_STACK_SIZE)
622 {
623 g_logger.info("Stacksize: " + toString(abs(&stackMeasureVar - NumeReKernel::baseStackPosition) / 1024.0, 6) + " KiB.");
624 throw SyntaxError(SyntaxError::PROCEDURE_STACK_OVERFLOW, "$" + sProc + "(" + sVarList + ")", SyntaxError::invalid_position, "\\$" + sProc, nth_procedure);
625 }
626
627 StripSpaces(sProc);
628 NumeReKernel::getInstance()->getDebugger().pushStackItem(sProc + "(" + sVarList + ")", this);
629
630 // Set the file name for the currently selected procedure
631 if (!setProcName(sProc))
632 throw SyntaxError(SyntaxError::INVALID_PROCEDURE_NAME, "$" + sProc + "(" + sVarList + ")", SyntaxError::invalid_position);
633
634 sProcCommandLine.clear();
635 sThisNameSpace.clear();
636 nCurrentLine = 0;
637 nFlags = 0;
638 nReturnType = 1;
639 bReturnSignal = false;
640 nthRecursion = nth_procedure;
641 bool bSupressAnswer_back = NumeReKernel::bSupressAnswer;
642
643 // Prepare the var factory and obtain the current procedure file
644 if (_varFactory)
645 delete _varFactory;
646
647 _varFactory = new ProcedureVarFactory(this, mangleName(sProc), nth_procedure);
649
650 // add spaces in front of and at the end of sVarList
651 sVarList = " " + sVarList + " ";
652
653 // Remove file name extension, if there's one in the procedure name
654 if (sProc.length() > 5 && sProc.substr(sProc.length() - 5) == ".nprc")
655 sProc = sProc.substr(0, sProc.rfind('.'));
656
657 // Get the namespace of this procedure
659
660 // Separate the procedure name from the namespace
661 if (sProc.find('~') != string::npos)
662 sProc = sProc.substr(sProc.rfind('~') + 1);
663
664 if (sProc.find('/') != string::npos)
665 sProc = sProc.substr(sProc.rfind('/') + 1);
666
667 if (sProc.find('\\') != string::npos)
668 sProc = sProc.substr(sProc.rfind('\\') + 1);
669
670 // Prepare the procedure command line elements
671 // and find the current procedure line
672 pair<int, ProcedureCommandLine> currentLine;
673 currentLine.first = ProcElement->gotoProcedure("$" + sProc);
674
676
677 // if the procedure was not found, throw an error
678 if (currentLine.first == -1)
679 {
680 sCallingNameSpace = "main";
681 mVarMap.clear();
682
683 if (_option.useDebugger())
684 _debugger.popStackItem();
685
687 }
688
689 // Get the procedure head line
690 currentLine = ProcElement->getCurrentLine(currentLine.first);
691 nCurrentLine = currentLine.first;
692
693 // verify that this is a procedure headline
694 if (currentLine.second.getType() != ProcedureCommandLine::TYPE_PROCEDURE_HEAD)
695 {
696 sCallingNameSpace = "main";
697 mVarMap.clear();
698
699 if (_option.useDebugger())
700 _debugger.popStackItem();
701
703 }
704
705 // Get the flags
706 nFlags = currentLine.second.getFlags();
707
708 // verify that this was not a private procedure
709 // or that the calling namespace is the same
711 {
712 string sErrorToken;
713
714 if (sCallingNameSpace == "main")
715 sErrorToken = "\"" + sThisNameSpace + "\" (caller namespace: global)";
716 else
717 sErrorToken = "\"" + sThisNameSpace + "\" (caller namespace:" + sCallingNameSpace + ")";
718
719 if (_option.useDebugger())
720 _debugger.popStackItem();
721
723 }
724
726 {
727 if (_option.systemPrints())
728 {
729 // if the print status is true, set it to false
730 _option.enableSystemPrints(false);
731 }
732 else
733 nFlags &= ~ProcedureCommandLine::FLAG_MASK;
734 }
735
736 // Get the argument list and evaluate it
737 string sVarDeclarationList = currentLine.second.getArgumentList();
738
739 // Now the calling namespace is the current namespace
741
742 if (findCommand(sVarList, "var").sString == "var")
743 {
746 }
747
748 if (findCommand(sVarList, "str").sString == "str")
749 {
752 }
753
754 if (findCommand(sVarList, "tab").sString == "tab")
755 {
758 }
759
760 if (findCommand(sVarList, "cst").sString == "cst")
761 {
764 }
765
766 StripSpaces(sVarDeclarationList);
767 StripSpaces(sVarList);
768
769 try
770 {
771 // Evaluate the argument list for the current procedure
772 mVarMap = _varFactory->createProcedureArguments(sVarDeclarationList, sVarList);
773 }
774 catch (...)
775 {
777 _debugger.showError(current_exception());
778
779 resetProcedure(_parser, bSupressAnswer_back);
780 throw;
781 }
782
783 _parser.mVarMapPntr = &mVarMap;
784
786 {
789 }
790
791 std::queue<std::string> commandQueue;
792 std::string sCurrentCommand = "";
793 int nByteCode = 0;
794 int nCurrentByteCode = 0;
795 Returnvalue _ReturnVal;
796
797 // As long as we didn't find the last line,
798 // read the next line from the procedure and execute
799 // this line
800 while (!ProcElement->isLastLine(currentLine.first))
801 {
802 // Set the bytecode from the last calculation
803 ProcElement->setByteCode(nCurrentByteCode | nByteCode, currentLine.first);
804 bProcSupressAnswer = false;
807
808 // Get the next line from one of the current active
809 // command line sources
810 if (!commandQueue.size())
811 {
812 currentLine = ProcElement->getNextLine(currentLine.first);
813 nCurrentLine = currentLine.first;
814 sProcCommandLine = currentLine.second.getCommandLine();
815 nCurrentByteCode = currentLine.second.getByteCode();
816 nByteCode = nCurrentByteCode;
817
818 // Obtain the current command from the command line
819 sCurrentCommand = findCommand(sProcCommandLine).sString;
820
821 if (_option.useDebugger()
823 && sProcCommandLine.substr(0, 2) != "|>")
824 {
825 sProcCommandLine.insert(0, "|> ");
826 }
827
828 // Stop the evaluation if the current procedure,
829 // if we reach the endprocedure command
830 if (currentLine.second.getType() == ProcedureCommandLine::TYPE_PROCEDURE_FOOT)
831 break;
832
833 // Remove the trailing output suppressing semicolon
834 while (sProcCommandLine.back() == ';')
835 {
836 bProcSupressAnswer = true;
837 sProcCommandLine.pop_back();
839 }
840
842
843 // if the current line doesn't contain a namespace command
844 // resolve the local variables
845 if (sCurrentCommand != "namespace" && sProcCommandLine[1] != '@')
846 {
848 }
849 }
850
851 // Handle the command line cache
852 if (commandQueue.size() || sProcCommandLine.find(';') != string::npos)
853 {
854 if (commandQueue.size())
855 {
856 // The command cache is not empty
857 // Get the next task from the command cache
858 sProcCommandLine = commandQueue.front();
859
860 if (sProcCommandLine.back() == ';')
861 {
862 sProcCommandLine.pop_back();
863 bProcSupressAnswer = true;
864 }
865
866 commandQueue.pop();
867 }
868 else if (sProcCommandLine.find(';') == sProcCommandLine.length() - 1)
869 {
870 // Only remove the trailing semicolon -> used to suppress the output of the current line
871 bProcSupressAnswer = true;
872 sProcCommandLine.pop_back();
873 }
874 else
875 {
876 // Use only the first task from the command line and cache the remaining
877 // part in the command cache
879
880 for (const auto& expr : expressions)
881 {
882 commandQueue.push(expr + ";");
883 }
884
885 if (bProcSupressAnswer == false)
886 commandQueue.back().pop_back();
887
888 sProcCommandLine = commandQueue.front();
889
890 if (sProcCommandLine.back() == ';')
891 {
892 sProcCommandLine.pop_back();
893 bProcSupressAnswer = true;
894 }
895
896 commandQueue.pop();
897 }
898 }
899
900 // Handle the defining process and the calling
901 // of local functions
902 if (sCurrentCommand == "lclfunc")
903 {
904 // This is a definition
905 _localDef.defineFunc(sProcCommandLine.substr(sProcCommandLine.find("lclfunc")+7));
906 sProcCommandLine.clear();
907 continue;
908 }
909 else
910 {
911 // This is probably a call to a local function
913 }
914
915 // define the current command to be a flow control statement,
916 // if the procedure was not parsed already
917 if (nCurrentByteCode == ProcedureCommandLine::BYTECODE_NOT_PARSED
918 && (FlowCtrl::isFlowCtrlStatement(sCurrentCommand) || getCurrentBlockDepth()))
919 {
922 }
923
924 // Handle breakpoints and always remove hardcoded ones
925 bool isBreakPoint = sProcCommandLine.substr(sProcCommandLine.find_first_not_of(' '), 2) == "|>";
926
927 if ((_option.useDebugger() || isBreakPoint)
929 {
930 if (isBreakPoint)
931 {
932 sProcCommandLine.erase(sProcCommandLine.find_first_not_of(' '), 2);
934 }
935
936 if ((isBreakPoint || nDebuggerCode == NumeReKernel::DEBUGGER_STEP)
938 {
940 {
942 nDebuggerCode = evalDebuggerBreakPoint(_parser, _option);
943 }
944
945 }
946
947 if (!sProcCommandLine.length())
948 continue;
949
950 sProcCommandLine.insert(0, 1, ' ');
951 }
952
953 // Handle the definition of local variables
954 if (nCurrentByteCode == ProcedureCommandLine::BYTECODE_NOT_PARSED
955 || nCurrentByteCode & ProcedureCommandLine::BYTECODE_VARDEF)
956 {
957 try
958 {
959 if (handleVariableDefinitions(sProcCommandLine, sCurrentCommand))
960 {
961 if (nCurrentByteCode == ProcedureCommandLine::BYTECODE_NOT_PARSED)
963 }
964 }
965 catch (...)
966 {
968 _debugger.showError(current_exception());
969
970 resetProcedure(_parser, bSupressAnswer_back);
971 throw;
972 }
973
974 if (!sProcCommandLine.length())
975 continue;
976 }
977
978 // Handle the definition of namespaces
979 if (nCurrentByteCode == ProcedureCommandLine::BYTECODE_NOT_PARSED
980 || (nCurrentByteCode & ProcedureCommandLine::BYTECODE_NAMESPACE
981 && !(nCurrentByteCode & ProcedureCommandLine::BYTECODE_FLOWCTRLSTATEMENT)))
982 {
983 if (sCurrentCommand == "namespace" && !getCurrentBlockDepth())
984 {
986
987 if (sNameSpace.length())
988 sNameSpace += "~";
989
990 if (nCurrentByteCode == ProcedureCommandLine::BYTECODE_NOT_PARSED)
992
993 sProcCommandLine = "";
994 continue;
995 }
996 }
997
998 // Ensure that inline procedures don't contain flow control statements
1000 {
1001 if (sCurrentCommand == "for"
1002 || sCurrentCommand == "if"
1003 || sCurrentCommand == "switch"
1004 || sCurrentCommand == "try"
1005 || sCurrentCommand == "while")
1006 {
1008
1009 try
1010 {
1012 }
1013 catch (...)
1014 {
1015 resetProcedure(_parser, bSupressAnswer_back);
1016 throw;
1017 }
1018 }
1019 }
1020
1021 // Only try to evaluate a procedure, if there's currently no active flow
1022 // control statement
1023 if (nCurrentByteCode == ProcedureCommandLine::BYTECODE_NOT_PARSED
1025 && !(nCurrentByteCode & ProcedureCommandLine::BYTECODE_FLOWCTRLSTATEMENT)))
1026 {
1027 // Handle procedure calls and plugins in the common
1028 // virtual procedure interface function
1029 try
1030 {
1031 FlowCtrl::ProcedureInterfaceRetVal nRetVal = procedureInterface(sProcCommandLine, _parser, _functions, _data, _out, _pData, _script, _option, 0);
1032 // Only those two return values indicate that this line
1033 // does contain a procedure or a plugin
1034 if ((nRetVal == FlowCtrl::INTERFACE_EMPTY || nRetVal == FlowCtrl::INTERFACE_VALUE)
1035 && nCurrentByteCode == ProcedureCommandLine::BYTECODE_NOT_PARSED)
1036 {
1038 }
1039
1040 // Might be possible that we have s.th. like "return $myProc();", which will
1041 // result in a shortcut
1042 if (getReturnSignal())
1043 {
1044 _ReturnVal = getReturnValue();
1045 // Is it local and can we use it for swapping
1046 // instead of copying it?
1047 if (_ReturnVal.sReturnedTable.length() && !_ReturnVal.delayDelete)
1049
1050 break;
1051 }
1052
1053 if (nRetVal == FlowCtrl::INTERFACE_ERROR || nRetVal == FlowCtrl::INTERFACE_EMPTY)
1054 continue;
1055 }
1056 catch (...)
1057 {
1059 _debugger.showError(current_exception());
1060
1061 nCurrentByteCode = 0;
1062 catchExceptionForTest(current_exception(), bSupressAnswer_back, GetCurrentLine());
1063
1064 // If the error is converted, we have to skip
1065 // the remaining code, otherwise the procedure
1066 // is called again in ProcCalc(). If it's not
1067 // converted, this line won't be reached.
1068 continue;
1069 }
1070 }
1071
1072 // Handle special commands
1073 if (nCurrentByteCode == ProcedureCommandLine::BYTECODE_NOT_PARSED
1074 || nCurrentByteCode & ProcedureCommandLine::BYTECODE_EXPLICIT)
1075 {
1076 if (sCurrentCommand == "explicit")
1077 {
1080
1081 if (nCurrentByteCode == ProcedureCommandLine::BYTECODE_NOT_PARSED)
1083 }
1084 }
1085
1086 if (nCurrentByteCode == ProcedureCommandLine::BYTECODE_NOT_PARSED
1087 || (nCurrentByteCode & ProcedureCommandLine::BYTECODE_THROWCOMMAND
1088 && !(nCurrentByteCode & ProcedureCommandLine::BYTECODE_FLOWCTRLSTATEMENT)))
1089 {
1090 if (sCurrentCommand == "throw")
1091 {
1092 string sErrorToken;
1093
1095 {
1096 if (NumeReKernel::getInstance()->getStringParser().containsStringVars(sProcCommandLine))
1098
1100 sErrorToken += " -nq";
1101 string sDummy = "";
1103 }
1104
1105 if (nCurrentByteCode == ProcedureCommandLine::BYTECODE_NOT_PARSED)
1107
1109
1110 try
1111 {
1113 }
1114 catch (...)
1115 {
1116 resetProcedure(_parser, bSupressAnswer_back);
1117 throw;
1118 }
1119 }
1120 }
1121
1122 if (nCurrentByteCode == ProcedureCommandLine::BYTECODE_NOT_PARSED
1124 && !(nCurrentByteCode & ProcedureCommandLine::BYTECODE_FLOWCTRLSTATEMENT)))
1125 {
1126 if (sCurrentCommand == "return")
1127 {
1128 try
1129 {
1130 if (nCurrentByteCode == ProcedureCommandLine::BYTECODE_NOT_PARSED)
1132
1133 std::string sReturnValue = sProcCommandLine.substr(sProcCommandLine.find("return")+6);
1134
1135 StripSpaces(sReturnValue);
1136
1137 if (sReturnValue.back() == ';')
1138 sReturnValue.pop_back();
1139
1140 StripSpaces(sReturnValue);
1141
1142 if (sReturnValue == "void")
1143 nReturnType = 0;
1144 else if (sReturnValue.find('(') != std::string::npos
1145 && sReturnValue.substr(sReturnValue.find('(')) == "()"
1146 && _data.isTable(sReturnValue.substr(0, sReturnValue.find('('))))
1147 {
1148 _ReturnVal.sReturnedTable = sReturnValue.substr(0, sReturnValue.find('('));
1150 }
1151 else if (sReturnValue.length())
1152 _ReturnVal = ProcCalc(sReturnValue, sCurrentCommand, nCurrentByteCode, _parser, _functions, _data, _option, _out, _pData, _script);
1153
1154 break;
1155 }
1156 catch (...)
1157 {
1159 _debugger.showError(current_exception());
1160
1161 nCurrentByteCode = 0;
1162 catchExceptionForTest(current_exception(), bSupressAnswer_back, GetCurrentLine());
1163 }
1164 }
1165 }
1166
1167 // Evaluate the command line using the ProcCalc
1168 // member function
1169 try
1170 {
1171 ProcCalc(sProcCommandLine, sCurrentCommand, nCurrentByteCode, _parser, _functions, _data, _option, _out, _pData, _script);
1172
1173 if (getReturnSignal())
1174 {
1175 _ReturnVal = getReturnValue();
1176
1177 // Is it local and can we use it for swapping
1178 // instead of copying it?
1179 if (_ReturnVal.sReturnedTable.length())
1181
1182 break;
1183 }
1184 }
1185 catch (...)
1186 {
1188 _debugger.showError(current_exception());
1189
1190 nCurrentByteCode = 0;
1191 catchExceptionForTest(current_exception(), bSupressAnswer_back, GetCurrentLine());
1192 }
1193
1194 sProcCommandLine.clear();
1195 }
1196
1197
1198
1199 // Ensure that all loops are closed now
1201 {
1203
1204 try
1205 {
1207 }
1208 catch (...)
1209 {
1210 resetProcedure(_parser, bSupressAnswer_back);
1211 throw;
1212 }
1213 }
1214
1216 {
1217 // reset the print status
1218 _option.enableSystemPrints();
1219 }
1220
1221 // Reset this procedure
1222 resetProcedure(_parser, bSupressAnswer_back);
1223
1224 // Determine the return value
1225 if (nReturnType && !_ReturnVal.vNumVal.size() && !_ReturnVal.vStringVal.size())
1226 _ReturnVal.vNumVal.push_back(1.0);
1227
1228 return _ReturnVal;
1229}
1230
1231
1250FlowCtrl::ProcedureInterfaceRetVal Procedure::procedureInterface(string& sLine, Parser& _parser, FunctionDefinitionManager& _functions, MemoryManager& _data, Output& _out, PlotData& _pData, Script& _script, Settings& _option, int nth_command)
1251{
1252 // Create a new procedure object on the heap
1253 std::unique_ptr<Procedure> _procedure(new Procedure(*this));
1255
1256 // Handle procedure calls first
1257 if (sLine.find('$') != string::npos && sLine.find('(', sLine.find('$')) != string::npos)
1258 {
1259 // Ensure that the current procedure is no inline procedure
1262
1263 sLine += " ";
1264 unsigned int nPos = 0;
1265 int nProc = 0;
1266
1267 // Handle all procedure calls one after the other
1268 while (sLine.find('$', nPos) != string::npos && sLine.find('(', sLine.find('$', nPos)) != string::npos)
1269 {
1270 nPos = sLine.find('$', nPos) + 1;
1271 string __sName = sLine.substr(nPos, sLine.find('(', nPos) - nPos);
1272 string __sVarList = "";
1273
1274 if (!isInQuotes(sLine, nPos, true))
1275 {
1276 unsigned int nParPos = 0;
1277
1278 // Add namespaces, where necessary
1279 if (__sName.find('~') == string::npos)
1280 __sName = sNameSpace + __sName;
1281
1282 if (__sName.substr(0, 5) == "this~")
1283 __sName.replace(0, 4, sThisNameSpace);
1284
1285
1286 // Handle explicit procedure file names
1287 if (sLine[nPos] == '\'')
1288 {
1289 __sName = sLine.substr(nPos + 1, sLine.find('\'', nPos + 1) - nPos - 1);
1290 nParPos = sLine.find('(', nPos + 1 + __sName.length());
1291 }
1292 else
1293 nParPos = sLine.find('(', nPos);
1294
1295 // Get the variable list
1296 __sVarList = sLine.substr(nParPos);
1297
1298 // Ensure that each parenthesis has its counterpart
1299 if (getMatchingParenthesis(sLine.substr(nParPos)) == string::npos)
1300 throw SyntaxError(SyntaxError::UNMATCHED_PARENTHESIS, sLine, nParPos);
1301
1302 nParPos += getMatchingParenthesis(sLine.substr(nParPos));
1303 __sVarList = __sVarList.substr(1, getMatchingParenthesis(__sVarList) - 1);
1304 unsigned int nVarPos = 0;
1305
1306 // Try to find other procedure calls in the argument
1307 // list and prepend the current namespace
1308 while (__sVarList.find('$', nVarPos) != string::npos)
1309 {
1310 // Find the position of the next procedure candidate
1311 // and increment 1
1312 nVarPos = __sVarList.find('$', nVarPos) + 1;
1313
1314 // If this candidate is not part of a string literal,
1315 // prepend the current namespace
1316 if (!isInQuotes(__sVarList, nVarPos-1) && __sVarList.substr(nVarPos, __sVarList.find('(', nVarPos) - nVarPos).find('~') == string::npos)
1317 __sVarList = __sVarList.substr(0, nVarPos) + sNameSpace + __sVarList.substr(nVarPos);
1318 }
1319
1320 // Call the current procedure
1321 Returnvalue tempreturnval = _procedure->execute(__sName, __sVarList, _parser, _functions, _data, _option, _out, _pData, _script, nthRecursion + 1);
1322
1323 // Evaluate the return value of the called procedure
1324 if (!_procedure->nReturnType)
1325 sLine = sLine.substr(0, nPos - 1) + sLine.substr(nParPos + 1);
1326 else
1327 {
1328 nPos += replaceReturnVal(sLine, _parser, tempreturnval, nPos - 1, nParPos + 1,
1329 "_~PROC~[" + mangleName(__sName) + "~"
1330 + toString(nProc) + "_" + toString((int)nthRecursion) + "_"
1331 + toString((int)(nth_command + nthRecursion)) + "]");
1332 nProc++;
1333 }
1334
1335 nReturnType = 1;
1336 }
1337 else
1338 nPos += __sName.length() + 1;
1339 }
1340
1341 nReturn = FlowCtrl::INTERFACE_VALUE;
1343 StripSpaces(sLine);
1344 _parser.mVarMapPntr = &mVarMap;
1345
1347 _option.enableSystemPrints(false);
1348
1349 if (!sLine.length())
1351 }
1352 else if (sLine.find('$') != string::npos)
1353 {
1354 int nQuotes = 0;
1355
1356 // Ensure that this is no "wrong" procedure call
1357 for (size_t i = 0; i < sLine.length(); i++)
1358 {
1359 if (sLine[i] == '"' && (!i || sLine[i - 1] != '\\'))
1360 nQuotes++;
1361
1362 if (sLine[i] == '$' && !(nQuotes % 2))
1363 {
1364 sLine.clear();
1366 }
1367 }
1368
1369 return nReturn;
1370 }
1371
1372 // Handle plugin calls
1373 if (!(nFlags & ProcedureCommandLine::FLAG_EXPLICIT) && _procedure->isPluginCmd(sLine))
1374 {
1375 if (_procedure->evalPluginCmd(sLine))
1376 {
1377 Returnvalue _return;
1378
1379 // Call the plugin routines
1380 if (!_option.systemPrints())
1381 {
1382 _return = _procedure->execute(_procedure->getPluginProcName(), _procedure->getPluginVarList(), _parser, _functions, _data, _option, _out, _pData, _script, nthRecursion + 1);
1383
1385 _option.enableSystemPrints(false);
1386 }
1387 else
1388 {
1389 _option.enableSystemPrints(false);
1390 _return = _procedure->execute(_procedure->getPluginProcName(), _procedure->getPluginVarList(), _parser, _functions, _data, _option, _out, _pData, _script, nthRecursion + 1);
1391 _option.enableSystemPrints(true);
1392 }
1393
1394 _parser.mVarMapPntr = &mVarMap;
1396
1397 // Handle the plugin return values
1398 if (sLine.length())
1399 {
1400 if (sLine.find("<<RETURNVAL>>") != string::npos)
1401 {
1402 // Shortcut for direct returns
1403 Match _cmd = findCommand(sLine);
1404
1405 if (_cmd.sString == "return"
1406 && sLine.find_first_not_of(' ', _cmd.nPos+_cmd.sString.length()) == sLine.find("<<RETURNVAL>>"))
1407 {
1408 ReturnVal = _return;
1409 bReturnSignal = true;
1410 return nReturn;
1411 }
1412
1413 if (_return.vStringVal.size())
1414 {
1415 string sReturn = "{";
1416
1417 for (unsigned int v = 0; v < _return.vStringVal.size(); v++)
1418 sReturn += _return.vStringVal[v] + ",";
1419
1420 sReturn.back() = '}';
1421 sLine.replace(sLine.find("<<RETURNVAL>>"), 13, sReturn);
1422 }
1423 else if (_return.sReturnedTable.length())
1424 {
1425 std::string sTargetTable = sLine.substr(0, sLine.find("<<RETURNVAL>>"));
1426
1427 if (sTargetTable.find('=') != std::string::npos)
1428 sTargetTable.erase(sTargetTable.find_last_not_of(" ="));
1429
1430 StripSpaces(sTargetTable);
1431
1432 if (sTargetTable.find('(') != std::string::npos
1433 && sTargetTable.substr(sTargetTable.find('(')) == "()"
1434 && sLine.substr(sLine.find("<<RETURNVAL>>")+13).find_first_not_of(" ;") == std::string::npos)
1435 {
1436 sTargetTable.erase(sTargetTable.find('('));
1437
1438 // Copy efficient move operations
1439 if (_return.delayDelete)
1440 {
1441 if (!_data.isTable(sTargetTable))
1442 _data.renameTable(_return.sReturnedTable, sTargetTable, true);
1443 else
1444 {
1445 _data.swapTables(sTargetTable, _return.sReturnedTable);
1446 _data.deleteTable(_return.sReturnedTable);
1447 }
1448 }
1449 else
1450 _data.copyTable(_return.sReturnedTable, sTargetTable);
1451
1452 sLine = _parser.CreateTempVectorVar(std::vector<mu::value_type>({_data.getLines(sTargetTable),
1453 _data.getCols(sTargetTable)}))
1454 + sLine.substr(sLine.find("<<RETURNVAL>>")+13);
1455 }
1456 else
1457 {
1458 sLine.replace(sLine.find("<<RETURNVAL>>"), 13, _data.isEmpty(_return.sReturnedTable) ? "false" : "true");
1459
1460 if (_return.delayDelete)
1461 _data.deleteTable(_return.sReturnedTable);
1462 }
1463 }
1464 else
1465 {
1466 _parser.SetVectorVar("_~PLUGIN[" + _procedure->getPluginProcName() + "~" + toString((int)nthRecursion) + "]", _return.vNumVal);
1467 sLine.replace(sLine.find("<<RETURNVAL>>"), 13, "_~PLUGIN[" + _procedure->getPluginProcName() + "~" + toString((int)(nth_command + nthRecursion)) + "]");
1468 }
1469 }
1470 }
1471 else
1473 }
1474 else
1476 }
1477
1478 return nReturn;
1479}
1480
1481
1495{
1496 // Find the current command
1497 string sCommand = findCommand(sLine).sString;
1498
1499 // Try to identify the command
1500 if (sCommand == "var" || sCommand == "str" || sCommand == "tab" || sCommand == "cst")
1501 {
1502 // Only recognized
1503 return 1;
1504 }
1505 else if (sCommand == "namespace")
1506 {
1507 sLine = sLine.substr(sLine.find("namespace") + 9);
1508 StripSpaces(sLine);
1509
1510 // Evaluate the namespace name
1511 if (sLine.length())
1512 {
1513 if (sLine.find(' ') != string::npos)
1514 sLine = sLine.substr(0, sLine.find(' '));
1515
1516 if (sLine.substr(0, 5) == "this~" || sLine == "this")
1517 sLine.replace(0, 4, sThisNameSpace);
1518
1519 if (sLine != "main")
1520 {
1521 sNameSpace = sLine;
1522 if (sNameSpace[sNameSpace.length() - 1] != '~')
1523 sNameSpace += "~";
1524 }
1525 else
1526 sNameSpace = "";
1527 }
1528 else
1529 sNameSpace = "";
1530
1531 return 1;
1532 }
1533 return -1;
1534}
1535
1536
1547bool Procedure::writeProcedure(string sProcedureLine)
1548{
1549 string sAppendedLine = "";
1550
1551 // Check, whether the current line is a procedure head,
1552 // a procedure foot or the actual body of the procedure
1553 if (sProcedureLine.substr(0, 9) == "procedure"
1554 && sProcedureLine.find('$') != string::npos
1555 && sProcedureLine.find('(', sProcedureLine.find('$')) != string::npos)
1556 {
1557 // This is the handling code for the procedure head.
1558 // It will determine the correct file path from the
1559 // procedure name and its namespace
1560 bool bAppend = false;
1561
1562 nthBlock = 0;
1563
1564 // Get the procedure name and its namespace
1565 string sFileName = sProcedureLine.substr(sProcedureLine.find('$') + 1, sProcedureLine.find('(', sProcedureLine.find('$')) - sProcedureLine.find('$') - 1);
1566 StripSpaces(sFileName);
1567
1568 // Try to evaluate it using the setProcName
1569 // member function
1570 if (!setProcName(sFileName))
1571 return false;
1572
1573 // If the procedure belongs to the "thisfile"
1574 // namespace, then it has to be appended to the
1575 // procedure, which was opened lastly
1576 if (sFileName.substr(0, 9) == "thisfile~")
1577 bAppend = true;
1578
1579 // Create a corresponding folder from the
1580 // namespace
1581 if (sCurrentProcedureName.find_last_of("~/") != string::npos)
1582 {
1583 FileSystem _fSys;
1584 _fSys.setPath(sCurrentProcedureName.substr(0, sCurrentProcedureName.find_last_of("~/")), true, sTokens[5][1]);
1585 }
1586
1587 // If the procedure shall be appended, open the
1588 // filestream in read mode, read everything and
1589 // truncate the file afterwards, otherwise truncate
1590 // the whole file in advance
1591 if (bAppend)
1592 {
1593 fProcedure.open(sCurrentProcedureName.c_str(), ios_base::in);
1594
1595 if (!fProcedure.good())
1596 {
1597 fProcedure.close();
1598 return false;
1599 }
1600
1601 string sLineTemp;
1602 vector<string> vProcedureFile;
1603
1604 // Read the whole file
1605 while (!fProcedure.eof())
1606 {
1607 getline(fProcedure, sLineTemp);
1608 vProcedureFile.push_back(sLineTemp);
1609 }
1610
1611 fProcedure.close();
1612
1613 // find the last "endprocedure" command and
1614 // erase everything after it
1615 for (int i = vProcedureFile.size()-1; i >= 0; i--)
1616 {
1617 if (vProcedureFile[i] == "endprocedure")
1618 {
1619 vProcedureFile.erase(vProcedureFile.begin()+i+1, vProcedureFile.end());
1620 break;
1621 }
1622 }
1623
1624 // Open the file in out mode and truncate it
1625 fProcedure.open(sCurrentProcedureName.c_str(), ios_base::out | ios_base::trunc);
1626
1627 // Write the stored contents to the file
1628 for (size_t i = 0; i < vProcedureFile.size(); i++)
1629 {
1630 fProcedure << vProcedureFile[i] << endl;
1631 }
1632
1633 // Append two line breaks to separate the procedures
1634 fProcedure << endl << endl;
1635 }
1636 else
1637 {
1638 fProcedure.open(sCurrentProcedureName.c_str(), ios_base::out | ios_base::trunc);
1639
1640 // Ensure that the file stream could be opened
1641 if (fProcedure.fail())
1642 {
1643 fProcedure.close();
1644 return false;
1645 }
1646
1647 string sProcName = "";
1648
1649 if (sFileName.find('~') != string::npos)
1650 sProcName = sFileName.substr(sFileName.rfind('~') + 1);
1651 else
1652 sProcName = sFileName;
1653
1654 // Write the procedure head comment
1655 unsigned int nLength = _lang.get("PROC_FOOTER").length();
1656 fProcedure << "#**" << std::setfill('*') << std::setw(nLength) << '*' << endl;
1657 fProcedure << " * NUMERE-" << toUpperCase(_lang.get("COMMON_PROCEDURE")) << ": $" << sProcName << "()" << endl;
1658 fProcedure << " * " << std::setfill('=') << std::setw(nLength) << '=' << endl;
1659 fProcedure << " * " << _lang.get("PROC_ADDED_DATE") << ": " << getTimeStamp(false) << " *#" << endl;
1660 fProcedure << endl;
1661 }
1662
1664 bWritingTofile = true;
1665
1666 // Print prefixed documentation strings, which were
1667 // appended, first
1668 if (sProcedureLine.find("##!") != string::npos)
1669 {
1670 // Line comments
1671 fProcedure << sProcedureLine.substr(sProcedureLine.find("##!"));
1672 sProcedureLine.erase(sProcedureLine.find("##!"));
1673 }
1674 else if (sProcedureLine.find("#*!") != string::npos)
1675 {
1676 // block comments
1677 fProcedure << sProcedureLine.substr(sProcedureLine.find("#*!"));
1678 sProcedureLine.erase(sProcedureLine.find("#*!"));
1679 }
1680
1681 fProcedure << "procedure $";
1682
1683 // Write the procedure name (without the namespace)
1684 if (sFileName.find('~') != string::npos)
1685 fProcedure << sFileName.substr(sFileName.rfind('~') + 1);
1686 else
1687 fProcedure << sFileName;
1688
1689 // Write the argument list
1690 fProcedure << sProcedureLine.substr(sProcedureLine.find('(')) << endl;
1691 return true;
1692 }
1693 else if (sProcedureLine.substr(0, 12) == "endprocedure")
1694 bWritingTofile = false;
1695 else
1696 {
1697 // This is the handling code for the procedure body
1698 // The first cases try to split multiple flow
1699 // control statements, which are passed as a single
1700 // line into multiple lines. This is done by pushing the
1701 // remaining part of the current line into a string cache.
1702 if (sProcedureLine.find('(') != string::npos
1703 && (sProcedureLine.substr(0, 3) == "for"
1704 || sProcedureLine.substr(0, 3) == "if "
1705 || sProcedureLine.substr(0, 3) == "if("
1706 || sProcedureLine.substr(0, 6) == "elseif"
1707 || sProcedureLine.substr(0, 6) == "switch"
1708 || sProcedureLine.substr(0, 5) == "while"))
1709 {
1710 sAppendedLine = sProcedureLine.substr(getMatchingParenthesis(sProcedureLine) + 1);
1711 sProcedureLine.erase(getMatchingParenthesis(sProcedureLine) + 1);
1712 }
1713 else if (sProcedureLine.find(':', 5) != string::npos
1714 && (sProcedureLine.substr(0, 5) == "case "
1715 || sProcedureLine.substr(0, 6) == "catch "
1716 || sProcedureLine.substr(0, 6) == "catch:"
1717 || sProcedureLine.substr(0, 8) == "default "
1718 || sProcedureLine.substr(0, 8) == "default:")
1719 && sProcedureLine.find_first_not_of(' ', sProcedureLine.find(':', 5)) != string::npos)
1720 {
1721 sAppendedLine = sProcedureLine.substr(sProcedureLine.find(':', 5)+1);
1722 sProcedureLine.erase(sProcedureLine.find(':', 5)+1);
1723 }
1724 else if (sProcedureLine.find(' ', 4) != string::npos
1725 && (sProcedureLine.substr(0, 5) == "else "
1726 || sProcedureLine.substr(0, 6) == "endif "
1727 || sProcedureLine.substr(0, 7) == "endtry "
1728 || sProcedureLine.substr(0, 10) == "endswitch "
1729 || sProcedureLine.substr(0, 7) == "endfor "
1730 || sProcedureLine.substr(0, 9) == "endwhile ")
1731 && sProcedureLine.find_first_not_of(' ', sProcedureLine.find(' ', 4)) != string::npos
1732 && sProcedureLine[sProcedureLine.find_first_not_of(' ', sProcedureLine.find(' ', 4))] != '-')
1733 {
1734 sAppendedLine = sProcedureLine.substr(sProcedureLine.find(' ', 4));
1735 sProcedureLine.erase(sProcedureLine.find(' ', 4));
1736 }
1737 else if (sProcedureLine.find(" for ") != string::npos
1738 || sProcedureLine.find(" for(") != string::npos
1739 || sProcedureLine.find(" endfor") != string::npos
1740 || sProcedureLine.find(" if ") != string::npos
1741 || sProcedureLine.find(" if(") != string::npos
1742 || sProcedureLine.find(" else") != string::npos
1743 || sProcedureLine.find(" elseif ") != string::npos
1744 || sProcedureLine.find(" elseif(") != string::npos
1745 || sProcedureLine.find(" endif") != string::npos
1746 || sProcedureLine.find(" switch ") != string::npos
1747 || sProcedureLine.find(" switch(") != string::npos
1748 || sProcedureLine.find(" case") != string::npos
1749 || sProcedureLine.find(" default") != string::npos
1750 || sProcedureLine.find(" endswitch") != string::npos
1751 || sProcedureLine.find(" try") != string::npos
1752 || sProcedureLine.find(" catch") != string::npos
1753 || sProcedureLine.find(" endtry") != string::npos
1754 || sProcedureLine.find(" while ") != string::npos
1755 || sProcedureLine.find(" while(") != string::npos
1756 || sProcedureLine.find(" endwhile") != string::npos)
1757 {
1758 for (unsigned int n = 0; n < sProcedureLine.length(); n++)
1759 {
1760 if (sProcedureLine[n] == ' ' && !isInQuotes(sProcedureLine, n))
1761 {
1762 if (sProcedureLine.substr(n, 5) == " for "
1763 || sProcedureLine.substr(n, 5) == " for("
1764 || sProcedureLine.substr(n, 7) == " endfor"
1765 || sProcedureLine.substr(n, 4) == " if "
1766 || sProcedureLine.substr(n, 4) == " if("
1767 || sProcedureLine.substr(n, 5) == " else"
1768 || sProcedureLine.substr(n, 8) == " elseif "
1769 || sProcedureLine.substr(n, 8) == " elseif("
1770 || sProcedureLine.substr(n, 6) == " endif"
1771 || sProcedureLine.substr(n, 8) == " switch "
1772 || sProcedureLine.substr(n, 8) == " switch("
1773 || sProcedureLine.substr(n, 6) == " case "
1774 || sProcedureLine.substr(n, 9) == " default "
1775 || sProcedureLine.substr(n, 9) == " default:"
1776 || sProcedureLine.substr(n, 10) == " endswitch"
1777 || sProcedureLine.substr(n, 5) == " try "
1778 || sProcedureLine.substr(n, 7) == " catch "
1779 || sProcedureLine.substr(n, 7) == " catch:"
1780 || sProcedureLine.substr(n, 7) == " endtry"
1781 || sProcedureLine.substr(n, 7) == " while "
1782 || sProcedureLine.substr(n, 7) == " while("
1783 || sProcedureLine.substr(n, 9) == " endwhile")
1784 {
1785 sAppendedLine = sProcedureLine.substr(n + 1);
1786 sProcedureLine.erase(n);
1787 break;
1788 }
1789 }
1790 }
1791 }
1792
1793 // Decrement the block count for every
1794 // endBLOCK command
1795 if (findCommand(sProcedureLine).sString == "endif"
1796 || findCommand(sProcedureLine).sString == "endwhile"
1797 || findCommand(sProcedureLine).sString == "endfor"
1798 || findCommand(sProcedureLine).sString == "endtry"
1799 || findCommand(sProcedureLine).sString == "catch"
1800 || findCommand(sProcedureLine).sString == "endcompose"
1801 || findCommand(sProcedureLine).sString == "endswitch"
1802 || findCommand(sProcedureLine).sString == "case"
1803 || findCommand(sProcedureLine).sString == "default"
1804 || findCommand(sProcedureLine).sString == "elseif"
1805 || findCommand(sProcedureLine).sString == "else")
1806 nthBlock--;
1807
1808 string sTabs = "\t";
1809
1810 for (int i = 0; i < nthBlock; i++)
1811 sTabs += '\t';
1812
1813 // Create the procedure line
1814 sProcedureLine = sTabs + sProcedureLine;
1815
1816 // Increment the block count for every
1817 // BLOCK command
1818 if (findCommand(sProcedureLine).sString == "if"
1819 || findCommand(sProcedureLine).sString == "while"
1820 || findCommand(sProcedureLine).sString == "for"
1821 || findCommand(sProcedureLine).sString == "try"
1822 || findCommand(sProcedureLine).sString == "catch"
1823 || findCommand(sProcedureLine).sString == "compose"
1824 || findCommand(sProcedureLine).sString == "switch"
1825 || findCommand(sProcedureLine).sString == "case"
1826 || findCommand(sProcedureLine).sString == "default"
1827 || findCommand(sProcedureLine).sString == "elseif"
1828 || findCommand(sProcedureLine).sString == "else")
1829 nthBlock++;
1830 }
1831
1832 // Write the actual line to the file
1833 if (fProcedure.is_open())
1834 fProcedure << sProcedureLine << endl;
1835
1836 // If this was the last line, write the final comment lines
1837 // to the procedure file and close it afterwards
1838 if (!bWritingTofile && fProcedure.is_open())
1839 {
1840 fProcedure << endl;
1841 fProcedure << "#**" << _lang.get("PROC_END_OF_PROCEDURE") << endl;
1842 fProcedure << " * " << _lang.get("PROC_FOOTER") << endl;
1843 fProcedure << " * https://www.numere.org/" << endl;
1844 fProcedure << " **" << std::setfill('*') << std::setw(_lang.get("PROC_FOOTER").length() + 1) << "#" << endl;
1845
1846 fProcedure.close();
1847
1848 // This ensures that all blocks were closed
1849 if (nthBlock)
1851
1853 }
1854
1855 StripSpaces(sAppendedLine);
1856
1857 // If there are currently contents cached,
1858 // call this member function recursively.
1859 if (sAppendedLine.length())
1860 return writeProcedure(sAppendedLine);
1861
1862 return true;
1863}
1864
1865
1880void Procedure::extractProcedureInformation(const string& sCmdLine, size_t nPos, string& sProcName, string& sArgList, string& sFileName)
1881{
1882 string __sName = sCmdLine.substr(nPos, sCmdLine.find('(', nPos) - nPos);
1883
1884 // Get the argument list
1885 sArgList = sCmdLine.substr(sCmdLine.find('(', nPos));
1886 sArgList.erase(getMatchingParenthesis(sArgList));
1887 sArgList.erase(0, 1);
1888
1889 if (__sName.find('~') == string::npos)
1890 __sName = sNameSpace + __sName;
1891
1892 if (__sName.substr(0, 5) == "this~")
1893 __sName.replace(0, 4, sThisNameSpace);
1894
1895 // Handle the special case of absolute paths
1896 if (sCmdLine[nPos] == '\'')
1897 {
1898 __sName = sCmdLine.substr(nPos + 1, sCmdLine.find('\'', nPos + 1) - nPos - 1);
1899 }
1900
1901 // Create a valid filename first
1902 sFileName = __sName;
1903
1904 // Now remove the namespace stuff
1905 if (__sName.find('~') != string::npos)
1906 sProcName = __sName.substr(__sName.rfind('~')+1);
1907 else
1908 sProcName = __sName;
1909
1910 // Handle namespaces
1911 if (sFileName.find('~') != string::npos)
1912 {
1913 if (sFileName.substr(0, 9) == "thisfile~")
1914 {
1915 if (sProcNames.length())
1916 sFileName = sProcNames.substr(sProcNames.rfind(';') + 1);
1917 else
1918 {
1920 }
1921 }
1922 else
1923 {
1924 for (unsigned int i = 0; i < sFileName.length(); i++)
1925 {
1926 if (sFileName[i] == '~')
1927 {
1928 if (sFileName.length() > 5 && i >= 4 && sFileName.substr(i - 4, 5) == "main~")
1929 sFileName = sFileName.substr(0, i - 4) + sFileName.substr(i + 1);
1930 else
1931 sFileName[i] = '/';
1932 }
1933 }
1934 }
1935 }
1936
1937 // Use the filesystem to determine a valid file name
1938 sFileName = ValidFileName(sFileName, ".nprc");
1939
1940 if (sFileName[1] != ':')
1941 {
1942 sFileName = "<procpath>/" + sFileName;
1943 sFileName = ValidFileName(sFileName, ".nprc");
1944 }
1945}
1946
1947
1958int Procedure::isInline(const string& sProc)
1959{
1960 // No procedures?
1961 if (sProc.find('$') == string::npos)
1963
1964 size_t nProcedures = 0;
1966
1967 if (sProc.find('$') != string::npos && sProc.find('(', sProc.find('$')) != string::npos)
1968 {
1969 size_t nPos = 0;
1970
1971 // Examine all procedures, which may be found in the
1972 // current command string
1973 while (sProc.find('$', nPos) != string::npos && sProc.find('(', sProc.find('$', nPos)) != string::npos)
1974 {
1975 // Extract the name of the procedure
1976 nPos = sProc.find('$', nPos) + 1;
1977
1978 // Only evaluate the current match, if it is not part of a string
1979 if (!isInQuotes(sProc, nPos, true))
1980 {
1981 string __sFileName;
1982 string __sProcName;
1983 string __sArgList;
1984
1985 // Obtain procedure name, argument list and the corresponding
1986 // file name of the procedure
1987 extractProcedureInformation(sProc, nPos, __sProcName, __sArgList, __sFileName);
1988
1989 int nInlineFlag = 0;
1990
1991 // Here happens the hard work: get a procedure element from the library, find
1992 // the procedure definition line, obtain it and examine the already parsed
1993 // flags of this procedure. Additionally, determine, whether the current procedure
1994 // is inlinable.
1995 nInlineable = max(isInlineable(__sProcName, __sFileName, &nInlineFlag), nInlineable);
1996 nProcedures++;
1997
1998 // If the current procedure is not flagged as inline, return the corresponding
1999 // value - we do not need to inspect the current command line further
2000 if (!nInlineFlag)
2002 }
2003 }
2004 }
2005 else
2007
2008 // All procedures were declared as inline
2009 return nInlineable;
2010}
2011
2012
2026vector<string> Procedure::expandInlineProcedures(string& sProc)
2027{
2028 vector<string> vExpandedProcedures;
2029
2030 // No procedures?
2031 if (sProc.find('$') == string::npos)
2032 return vExpandedProcedures;
2033
2034 size_t nProcedures = countProceduresInLine(sProc);
2035
2036 if (sProc.find('$') != string::npos && sProc.find('(', sProc.find('$')) != string::npos)
2037 {
2038 size_t nPos = 0;
2039
2040 // Examine all procedures, which may be found in the
2041 // current command string
2042 while (sProc.find('$', nPos) != string::npos && sProc.find('(', sProc.find('$', nPos)) != string::npos && nProcedures)
2043 {
2044 // Extract the name of the procedure
2045 nPos = sProc.find('$', nPos) + 1;
2046
2047 // Only evaluate the current match, if it is not part of a string
2048 if (!isInQuotes(sProc, nPos, true))
2049 {
2050 string __sFileName;
2051 string __sProcName;
2052 string __sArgList;
2053
2054 // Obtain procedure name, argument list and the corresponding
2055 // file name of the procedure
2056 extractProcedureInformation(sProc, nPos, __sProcName, __sArgList, __sFileName);
2057
2058 // Pre-parse procedures, which are part of the current
2059 // argument list
2060 if (__sArgList.find('$') != string::npos)
2061 {
2062 // Call member function recursively
2063 vector<string> vExpandedArgList = expandInlineProcedures(__sArgList);
2064
2065 // Insert the returned list, if it is non-empty
2066 if (vExpandedArgList.size())
2067 {
2068 vExpandedProcedures.insert(vExpandedProcedures.end(), vExpandedArgList.begin(), vExpandedArgList.end());
2069 }
2070 }
2071
2072 // Ensure that the current procedure is inlinable
2073 // (won't be re-evaluated here, because the result
2074 // of the last evaluation is cached)
2075 if (isInlineable(__sProcName, __sFileName))
2076 {
2077 // Get the inlined representation as a vector
2078 vector<string> vInlinedRepresentation = getInlined(__sProcName, __sArgList, __sFileName, nProcedures);
2079
2080 // Replace the return value and insert the
2081 // stuff before the return value in the overall
2082 // expansion
2083 sProc.replace(nPos-1, getMatchingParenthesis(sProc.substr(nPos-1))+1, vInlinedRepresentation.back());
2084 vExpandedProcedures.insert(vExpandedProcedures.end(), vInlinedRepresentation.begin(), vInlinedRepresentation.end()-1);
2085 }
2086
2087 nProcedures--;
2088 }
2089 }
2090 }
2091
2092 // All procedures were expanded
2093 return vExpandedProcedures;
2094}
2095
2096
2109int Procedure::isInlineable(const string& sProc, const string& sFileName, int* nInlineFlag)
2110{
2111 // Get procedure element and goto to the corresponding line
2113 int nProcedureLine = element->gotoProcedure("$" + sProc);
2115 string sArgumentList;
2116
2117 const int nMAX_INLINING_LINES = 7;
2118
2119 if (nProcedureLine < 0)
2121
2122 // Get the procedure head
2123 pair<int, ProcedureCommandLine> currentline = element->getCurrentLine(nProcedureLine);
2124
2125 // If the inline flag pointer was passed, store the inline flag
2126 // value here
2127 if (nInlineFlag)
2128 {
2129 *nInlineFlag = currentline.second.getFlags() & ProcedureCommandLine::FLAG_INLINE;
2130 }
2131
2132 // Extract information about inlinability and the argument list
2133 nInlineable = currentline.second.isInlineable();
2134 sArgumentList = currentline.second.getArgumentList();
2135
2136 if (nInlineable == ProcedureCommandLine::INLINING_UNKNOWN)
2137 {
2138 size_t nLines = 0;
2139
2140 // Apply the rules and update the procedure definition
2141 while (!element->isLastLine(currentline.first))
2142 {
2143 currentline = element->getNextLine(currentline.first);
2144
2145 // Apply the internal inlining rule set
2146 nInlineable = applyInliningRuleset(currentline.second.getCommandLine(), sArgumentList);
2147
2148 // If the procedure either is not inlinable or we've reached
2149 // the end of the procedure, break the loop
2150 if (!nInlineable || currentline.second.getType() == ProcedureCommandLine::TYPE_PROCEDURE_FOOT || findCommand(currentline.second.getCommandLine()).sString == "return")
2151 {
2152 break;
2153 }
2154
2155 nLines++;
2156 }
2157
2158 // Ensure that we don't have too many lines
2159 if (nLines > nMAX_INLINING_LINES)
2161
2162 // Go to the procedure head again and update the
2163 // inlinability information
2164 currentline = element->getCurrentLine(nProcedureLine);
2165 currentline.second.setInlineable(nInlineable);
2166
2167 return nInlineable;
2168 }
2169
2170 return nInlineable;
2171}
2172
2173
2185int Procedure::applyInliningRuleset(const string& sCommandLine, const string& sArgumentList)
2186{
2187 static const string sINVALID_INLINING_COMMANDS = " cst tab namespace for if while switch ifndef ifndefined def define lclfunc redef redefine undef undefine ";
2188 string command = findCommand(sCommandLine).sString;
2189
2190 // Check for invalid inlining commands
2191 if (sINVALID_INLINING_COMMANDS.find(" " + command + " ") != string::npos)
2193
2194 // Check for procedures in the current line
2195 if (sCommandLine.find("$") != string::npos)
2196 {
2197 size_t nQuotes = 0;
2198
2199 // Go through the line and search for dollars,
2200 // while considering the quotation marks
2201 for (size_t i = 0; i < sCommandLine.length(); i++)
2202 {
2203 if (sCommandLine[i] == '"' && (!i || sCommandLine[i-1] != '\\'))
2204 nQuotes++;
2205
2206 if (sCommandLine[i] == '$' && !(nQuotes % 2))
2208 }
2209 }
2210
2212}
2213
2214
2224size_t Procedure::countProceduresInLine(const string& sCommandLine)
2225{
2226 size_t nProcedures = 0;
2227 size_t nPos = 0;
2228
2229 // Only do something, if there are candidates for procedures
2230 if (sCommandLine.find('$') != string::npos && sCommandLine.find('(', sCommandLine.find('$')) != string::npos)
2231 {
2232
2233 // Examine all procedures candidates, which may be found in the
2234 // current command string
2235 while (sCommandLine.find('$', nPos) != string::npos && sCommandLine.find('(', sCommandLine.find('$', nPos)) != string::npos)
2236 {
2237 nPos = sCommandLine.find('$', nPos) + 1;
2238
2239 // Only count the current match, if it is not part of a string
2240 if (!isInQuotes(sCommandLine, nPos, true))
2241 {
2242 nProcedures++;
2243 }
2244 }
2245 }
2246
2247 // Return the number of strings
2248 return nProcedures;
2249}
2250
2251
2265vector<string> Procedure::getInlined(const string& sProc, const string& sArgumentList, const string& sFileName, size_t nProcedures)
2266{
2267 // Prepare a variable factory and get the procedure
2268 // element
2269 ProcedureVarFactory varFactory(this, sProc, nthRecursion, true);
2271
2272 // Goto to the corresponding procedure head
2273 int nProcedureLine = element->gotoProcedure("$" + sProc);
2274 vector<string> vProcCommandLines;
2275 string sCommandLine;
2276 static const string sSPECIALRETURNVALS = " true false nan inf -inf ";
2277
2278 // Get a reference to the debugger. This is needed to insert
2279 // the applied breakpoints at the correct location
2282
2283 // Ensure that the procedure was found in the file
2284 if (nProcedureLine < 0)
2286
2287 // Get the procedure head
2288 pair<int, ProcedureCommandLine> currentline = element->getCurrentLine(nProcedureLine);
2289
2290 // Create the filled argument list in the variable factory
2291 varFactory.createProcedureArguments(currentline.second.getArgumentList(), sArgumentList);
2292
2293 // If there are some argument copies needed, we'll
2294 // insert them now
2295 if (varFactory.vInlineArgDef.size())
2296 {
2297 vProcCommandLines.insert(vProcCommandLines.end(), varFactory.vInlineArgDef.begin(), varFactory.vInlineArgDef.end());
2298
2299 for (const auto& sArgDef : varFactory.vInlineArgDef)
2300 {
2301 if (sArgDef.substr(0, 6) == "_~~TC_")
2302 inlineClusters.insert(sArgDef.substr(0, sArgDef.find('{')));
2303 }
2304 }
2305
2306 // Read each line, replace the arguments with their
2307 // values and push the result in the vector
2308 while (!element->isLastLine(currentline.first))
2309 {
2310 // Get next line and replace the argument occurences
2311 currentline = element->getNextLine(currentline.first);
2312 sCommandLine = varFactory.resolveVariables(" " + currentline.second.getCommandLine() + " ");
2313
2314 // Local variables and strings are allowed and will be redirected
2315 // into temporary cluster elements
2316 if (findCommand(sCommandLine).sString == "var")
2317 {
2318 varFactory.createLocalVars(sCommandLine.substr(findCommand(sCommandLine).nPos + 4));
2319 sCommandLine = varFactory.sInlineVarDef + ";";
2320 inlineClusters.insert(sCommandLine.substr(0, sCommandLine.find('{')));
2321 }
2322 else if (findCommand(sCommandLine).sString == "str")
2323 {
2324 varFactory.createLocalStrings(sCommandLine.substr(findCommand(sCommandLine).nPos + 4));
2325 sCommandLine = varFactory.sInlineStringDef + ";";
2326 inlineClusters.insert(sCommandLine.substr(0, sCommandLine.find('{')));
2327 }
2328
2329 // Insert a breakpoint, if the breakpoint manager
2330 // contains a reference to this line
2331 if (_debugger.getBreakpointManager().isBreakpoint(sFileName, currentline.first))
2332 sCommandLine = "|> " + sCommandLine;
2333
2334 // If the current line is the last procedure line,
2335 // simply push a "true" into the vector
2336 if (currentline.second.getType() == ProcedureCommandLine::TYPE_PROCEDURE_FOOT)
2337 {
2338 vProcCommandLines.push_back("true");
2339 break;
2340 }
2341
2342 // If the current line is a "return" statement,
2343 // remove the statement and inspect the return
2344 // value
2345 if (findCommand(sCommandLine).sString == "return")
2346 {
2347 // Get the return value
2348 size_t pos = findCommand(sCommandLine).nPos;
2349 sCommandLine.erase(0, pos+7);
2350
2351 if (sCommandLine.find(';') != string::npos)
2352 sCommandLine.erase(sCommandLine.rfind(';'));
2353
2354 // Strip all spaces from the return value
2355 StripSpaces(sCommandLine);
2356
2357 // Push a or the return value depending on the
2358 // type into the vector. We try to exclude all
2359 // constant cases, which will increase the speed
2360 // of the inlined procedure even more
2361 if (sCommandLine == "void")
2362 vProcCommandLines.push_back("");
2363 else if (!sCommandLine.length())
2364 vProcCommandLines.push_back("true");
2365 else if (sSPECIALRETURNVALS.find(" " + sCommandLine + " ") != string::npos || _parser.GetConst().find(sCommandLine) != _parser.GetConst().end())
2366 vProcCommandLines.push_back(sCommandLine);
2367 else if (isMultiValue(sCommandLine))
2368 {
2369 // Multi value return value
2370 if (nProcedures > 1)
2371 {
2372 // Save the return value in a cluster
2373 string sTempCluster = NumeReKernel::getInstance()->getMemoryManager().createTemporaryCluster("ret");
2374 vProcCommandLines.push_back(sTempCluster + " = " + sCommandLine + ";");
2375 vProcCommandLines.push_back(sTempCluster);
2376 inlineClusters.insert(sTempCluster.substr(0, sTempCluster.find('{')));
2377 }
2378 else
2379 vProcCommandLines.push_back("{" + sCommandLine + "}");
2380 }
2381 else
2382 {
2383 // Single value return value
2384 if (nProcedures > 1)
2385 {
2386 // Save the return value in a cluster
2387 string sTempCluster = NumeReKernel::getInstance()->getMemoryManager().createTemporaryCluster("ret");
2388 vProcCommandLines.push_back(sTempCluster + " = " + sCommandLine + ";");
2389 vProcCommandLines.push_back(sTempCluster);
2390 inlineClusters.insert(sTempCluster.substr(0, sTempCluster.find('{')));
2391 }
2392 else
2393 vProcCommandLines.push_back("(" + sCommandLine + ")");
2394 }
2395
2396 break;
2397 }
2398 else
2399 vProcCommandLines.push_back(sCommandLine);
2400 }
2401
2402 return vProcCommandLines;
2403}
2404
2405
2414std::string Procedure::mangleName(std::string sProcedureName)
2415{
2416 for (size_t i = 0; i < sProcedureName.length(); i++)
2417 {
2418 if (!isalnum(sProcedureName[i]) && sProcedureName[i] != '_' && sProcedureName[i] != '~')
2419 sProcedureName[i] = '_';
2420 }
2421
2422 return sProcedureName;
2423}
2424
2425
2438{
2439 // if the stack is empty, it has to be a breakpoint from a script
2440 // This is only valid, if the script contained flow control statements
2441 if (!NumeReKernel::getInstance()->getDebugger().getStackSize())
2443
2444 // Get a reference to the debugger object
2446
2447 // Gather all information needed by the debugger
2449
2450 // Let the kernel display the debugger window and jump to the
2451 // corresponding line in the procedure file
2452 return _debugger.showBreakPoint();
2453}
2454
2455
2465{
2466 // if the stack is empty, it has to be a breakpoint from a script
2467 // This is only valid, if the script contained flow control statements
2468 if (!NumeReKernel::getInstance()->getDebugger().getStackSize())
2469 return 0;
2470
2471 // Get a reference to the debugger object
2473
2474 // Gather all information needed by the debugger
2476
2477 return 1;
2478}
2479
2480
2495int Procedure::catchExceptionForTest(exception_ptr e_ptr, bool bSupressAnswer_back, int nLine)
2496{
2497 // Assure that the procedure is flagges as "test"
2499 {
2500 try
2501 {
2502 // Rethrow to determine the exception type
2503 rethrow_exception(e_ptr);
2504 }
2506 {
2507 // Catch and convert parser errors
2509 NumeReKernel::failMessage("@ " + toString(nLine+1) + " | FAILED EXPRESSION: '" + e.GetExpr() + "'");
2510 }
2511 catch (SyntaxError& e)
2512 {
2513 // Catch and convert syntax errors with the exception
2514 // of a user abort request
2515 if (e.errorcode == SyntaxError::PROCESS_ABORTED_BY_USER)
2516 {
2517 // Rethrow the abort request
2518 resetProcedure(NumeReKernel::getInstance()->getParser(), bSupressAnswer_back);
2519 throw;
2520 }
2521 else if (e.getToken().length() && (e.errorcode == SyntaxError::PROCEDURE_THROW || e.errorcode == SyntaxError::LOOP_THROW))
2522 {
2523 // Display custom errors with their message
2525 NumeReKernel::failMessage("@ " + toString(nLine+1) + " | ERROR CAUGHT: " + e.getToken());
2526 }
2527 else
2528 {
2529 // Mark default errors only with the failing expression
2531 NumeReKernel::failMessage("@ " + toString(nLine+1) + " | FAILED EXPRESSION: '" + e.getExpr() + "'");
2532 }
2533 }
2534 catch (...)
2535 {
2536 // All other exceptions are not catchable, because they refer
2537 // to internal issues, for which it might not possible to
2538 // handle them here
2539 resetProcedure(NumeReKernel::getInstance()->getParser(), bSupressAnswer_back);
2540 throw;
2541 }
2542 }
2543 else
2544 {
2545 // If not a test, then simply reset the current procedure
2546 // and rethrow the error
2547 resetProcedure(NumeReKernel::getInstance()->getParser(), bSupressAnswer_back);
2548 rethrow_exception(e_ptr);
2549 }
2550
2551 return 0;
2552}
2553
2554
2563unsigned int Procedure::GetCurrentLine() const
2564{
2565 // Get the line number from FlowCtrl
2567 return getCurrentLineNumber();
2568
2569 // Use the internal line number
2570 return std::max(0, nCurrentLine);
2571}
2572
2573
2591size_t Procedure::replaceReturnVal(string& sLine, Parser& _parser, const Returnvalue& _return, unsigned int nPos, unsigned int nPos2, const string& sReplaceName)
2592{
2593 // If the return value is passed to the "return" command, then
2594 // simply use the current value as the next return value and
2595 // inform the procedure handler
2596 Match _cmd = findCommand(sLine.substr(0, nPos));
2597
2598 if (_cmd.sString == "return"
2599 && sLine.substr(0, nPos).find_last_not_of(' ') == _cmd.nPos + _cmd.sString.length()-1
2600 && sLine.substr(nPos2).find_first_not_of("; ") == std::string::npos)
2601 {
2602 ReturnVal = _return;
2603 bReturnSignal = true;
2604 return nPos2;
2605 }
2606
2607 // Replace depending on the type
2608 if (_return.isString())
2609 {
2610 // String value, transform the return value
2611 // into a string vector
2613 sLine = sLine.substr(0, nPos) + sReturn + sLine.substr(nPos2);
2614 return sReturn.length();
2615 }
2616 else if (_return.sReturnedTable.length())
2617 {
2618 std::string sTargetTable = sLine.substr(0, nPos);
2620
2621 if (sTargetTable.find('=') != std::string::npos)
2622 sTargetTable.erase(sTargetTable.find_last_not_of(" =")+1);
2623
2624 StripSpaces(sTargetTable);
2625
2626 if (sTargetTable.find('(') != std::string::npos
2627 && sTargetTable.substr(sTargetTable.find('(')) == "()"
2628 && sLine.substr(nPos2).find_first_not_of(" ;") == std::string::npos)
2629 {
2630 sTargetTable.erase(sTargetTable.find('('));
2631
2632 // Copy efficient move operations
2633 if (_return.delayDelete)
2634 {
2635 if (!_data.isTable(sTargetTable))
2636 _data.renameTable(_return.sReturnedTable, sTargetTable, true);
2637 else
2638 {
2639 _data.swapTables(sTargetTable, _return.sReturnedTable);
2640 _data.deleteTable(_return.sReturnedTable);
2641 }
2642 }
2643 else
2644 _data.copyTable(_return.sReturnedTable, sTargetTable);
2645
2646 std::string sTempVar = _parser.CreateTempVectorVar(std::vector<mu::value_type>({_data.getLines(sTargetTable),
2647 _data.getCols(sTargetTable)}));
2648 sLine = sTempVar + sLine.substr(nPos2);
2649 return sTempVar.length();
2650 }
2651
2652 sLine.replace(nPos, nPos2-nPos, _data.isEmpty(_return.sReturnedTable) ? "false" : "true");
2653
2654 if (_return.delayDelete)
2655 _data.deleteTable(_return.sReturnedTable);
2656
2657 return _data.isEmpty(_return.sReturnedTable) ? 5 : 4;
2658 }
2659 else if (_return.isNumeric())
2660 {
2661 // Numerical value, use the procedure name
2662 // to derive a vector name and declare the
2663 // corresponding vector
2664 _parser.SetVectorVar(sReplaceName, _return.vNumVal);
2665 sLine = sLine.substr(0, nPos) + sReplaceName + sLine.substr(nPos2);
2666
2667 return sReplaceName.length();
2668 }
2669
2670 sLine = sLine.substr(0, nPos) + "nan" + sLine.substr(nPos2);
2671 return 3;
2672}
2673
2674
2686void Procedure::resetProcedure(Parser& _parser, bool bSupressAnswer)
2687{
2688 // Remove the current procedure from the call stack
2690
2691 sCallingNameSpace = "main";
2692 sNameSpace.clear();
2693 sThisNameSpace.clear();
2694 mVarMap.clear();
2695 NumeReKernel::bSupressAnswer = bSupressAnswer;
2696 _parser.mVarMapPntr = 0;
2697 _localDef.reset();
2698 nDebuggerCode = 0;
2699 nFlags = 0;
2700 sTestClusterName.clear();
2701 nthRecursion = 0;
2702
2703 // Delete the variable factory for the current procedure
2704 if (_varFactory)
2705 {
2706 delete _varFactory;
2707 _varFactory = nullptr;
2708 }
2709
2710 // Remove the last procedure in the current stack
2711 if (sProcNames.length())
2712 {
2713 sProcNames.erase(sProcNames.rfind(';'));
2714 }
2715
2716 return;
2717}
2718
2719
2728void Procedure::extractCurrentNamespace(const string& sProc)
2729{
2730 for (unsigned int i = sProc.length() - 1; i >= 0; i--)
2731 {
2732 if (sProc[i] == '\\' || sProc[i] == '/' || sProc[i] == '~')
2733 {
2734 sThisNameSpace = sProc.substr(0, i);
2735
2736 // If the namespace doesn't contain a colon
2737 // replace all path separators with a tilde
2738 // character
2739 if (sThisNameSpace.find(':') == string::npos)
2740 {
2741 for (unsigned int j = 0; j < sThisNameSpace.length(); j++)
2742 {
2743 if (sThisNameSpace[j] == '\\' || sThisNameSpace[j] == '/')
2744 sThisNameSpace[j] = '~';
2745 }
2746 }
2747
2748 break;
2749 }
2750
2751 if (!i)
2752 {
2753 sThisNameSpace = "main";
2754 break;
2755 }
2756 }
2757
2758 // If the current namespace is "thisfile", use the calling namespace
2759 if (sThisNameSpace == "thisfile")
2761}
2762
2763
2776bool Procedure::handleVariableDefinitions(string& sProcCommandLine, const string& sCommand)
2777{
2778 // Is it a variable declaration?
2779 if (sCommand == "var" && sProcCommandLine.length() > 6)
2780 {
2782
2783 sProcCommandLine = "";
2784 return true;
2785 }
2786
2787 // Is it a string declaration?
2788 if (sCommand == "str" && sProcCommandLine.length() > 6)
2789 {
2791
2792 sProcCommandLine = "";
2793 return true;
2794 }
2795
2796 // Is it a table declaration?
2797 if (sCommand == "tab" && sProcCommandLine.length() > 6)
2798 {
2800
2801 sProcCommandLine = "";
2802 return true;
2803 }
2804
2805 // Is it a cluster declaration?
2806 if (sCommand == "cst" && sProcCommandLine.length() > 6)
2807 {
2809
2810 sProcCommandLine = "";
2811 return true;
2812 }
2813
2814 // No local variable declaration in this command line
2815 return false;
2816}
2817
2818
2834int Procedure::handleIncludeSyntax(string& sProcCommandLine, ifstream& fInclude, bool bReadingFromInclude)
2835{
2836 int nIncludeType = 0;
2837
2838 // Try to find the include syntax in the current procedure command line
2839 if (!bReadingFromInclude && sProcCommandLine[1] == '@' && sProcCommandLine[2] != ' ')
2840 {
2842 string sIncludeFileName = "";
2843
2844 // Extract the include file name
2845 if (sProcCommandLine[1] == '"')
2846 sIncludeFileName = sProcCommandLine.substr(2, sProcCommandLine.find('"', 2) - 2);
2847 else
2848 sIncludeFileName = sProcCommandLine.substr(1, sProcCommandLine.find(' ') - 1);
2849
2850 // Extract the include type
2851 if (sProcCommandLine.find(':') != string::npos)
2852 {
2853 if (sProcCommandLine.find("defines", sProcCommandLine.find(':') + 1) != string::npos)
2854 {
2855 nIncludeType = 1;
2856 }
2857 else if (sProcCommandLine.find("globals", sProcCommandLine.find(':') + 1) != string::npos)
2858 {
2859 nIncludeType = 2;
2860 }
2861 else if (sProcCommandLine.find("procedures", sProcCommandLine.find(':') + 1) != string::npos)
2862 {
2863 nIncludeType = 3;
2864 }
2865 }
2866
2867 // Remove everything after the last colon,
2868 // if it is not the colon after the drive
2869 // letter
2870 if (sIncludeFileName.find(':') != string::npos)
2871 {
2872 for (int __i = sIncludeFileName.length() - 1; __i >= 0; __i--)
2873 {
2874 if (sIncludeFileName[__i] == ':'
2875 && (__i > 1
2876 || (__i == 1 && sIncludeFileName.length() > (unsigned int)__i + 1 && sIncludeFileName[__i + 1] != '/')))
2877 {
2878 sIncludeFileName.erase(sIncludeFileName.find(':'));
2879 break;
2880 }
2881 }
2882 }
2883
2884 // Create a valid file name from the extracted file name
2885 if (sIncludeFileName.length())
2886 sIncludeFileName = ValidFileName(sIncludeFileName, ".nscr");
2887 else
2888 {
2889 sProcCommandLine.clear();
2890 return 0;
2891 }
2892
2893 // Open the file stream
2894 fInclude.clear();
2895 fInclude.open(sIncludeFileName.c_str());
2896
2897 if (fInclude.fail())
2898 {
2899 fInclude.close();
2901 }
2902
2903 sProcCommandLine = "";
2904 }
2905 else if (sProcCommandLine[1] == '@')
2906 {
2907 sProcCommandLine = "";
2908 }
2909
2910 // Return the obtained include type
2911 return nIncludeType;
2912}
2913
2914
CommandReturnValues commandHandler(string &sCmd)
This function is the main command handling function.
Definition: built-in.cpp:42
@ COMMAND_PROCESSED
Definition: built-in.hpp:53
@ COMMAND_HAS_RETURNVALUE
Definition: built-in.hpp:54
@ NO_COMMAND
Definition: built-in.hpp:52
void enable(const std::string &sExpr)
Enables the assertion handler using the passed expression.
Definition: error.cpp:228
void reset()
Resets the assertion handler.
Definition: error.cpp:196
AssertionStats getStats() const
Returns the current tests stats.
Definition: error.cpp:328
void checkAssertion(mu::value_type *v, int nNum)
Checks the return value of a muParser evaluated result.
Definition: error.cpp:244
bool isBreakpoint(const std::string &_sFilename, size_t nLine)
void info(const std::string &sMessage)
Convenience member function.
Definition: logger.hpp:106
This class extends the std::vector for endlessness.
Definition: structures.hpp:838
This class implements the basic input/ output file system and provides functionalities to work with f...
Definition: filesystem.hpp:92
std::string sPath
Definition: filesystem.hpp:98
std::string sExecutablePath
Definition: filesystem.hpp:99
std::string ValidFileName(std::string _sFileName, const std::string sExtension=".dat", bool checkExtension=true, bool doCleanPath=true) const
This member function evaluates, whether the passed filename is a valid filename. One may supply a pre...
Definition: filesystem.cpp:280
std::string sTokens[7][2]
Definition: filesystem.hpp:100
int setPath(std::string _sPath, bool bMkDir, std::string _sExePath)
This member function may be used to set the preferred file path of the current FileSystem instance.
Definition: filesystem.cpp:443
void replaceLocalVars(std::string &sLine)
This member function is used to replace variable occurences with their (auto-determined) internal nam...
Definition: flowctrl.cpp:3969
int getCurrentBlockDepth() const
Returns the current block depth while reading a flow control statement to memory.
Definition: flowctrl.cpp:119
bool getReturnSignal() const
Definition: flowctrl.hpp:186
bool bReturnSignal
Definition: flowctrl.hpp:131
static bool isFlowCtrlStatement(const std::string &sCmd)
This static member function returns whether the passed command is a flow control statement.
Definition: flowctrl.cpp:4726
Returnvalue ReturnVal
Definition: flowctrl.hpp:113
std::map< std::string, std::string > mVarMap
Definition: flowctrl.hpp:118
std::set< std::string > inlineClusters
Definition: flowctrl.hpp:119
std::string sTestClusterName
Definition: flowctrl.hpp:127
void setCommand(std::string &__sCmd, int nCurrentLine)
This member function is used to set a command line from the outside into the flow control statement c...
Definition: flowctrl.cpp:1948
int nReturnType
Definition: flowctrl.hpp:130
int nthRecursion
Definition: flowctrl.hpp:128
AssertionStats baseline
Definition: flowctrl.hpp:126
int getCurrentLineNumber() const
This member function returns the current line number as enumerated during passing the commands via "s...
Definition: flowctrl.cpp:4691
int nDebuggerCode
Definition: flowctrl.hpp:125
Returnvalue getReturnValue() const
Definition: flowctrl.hpp:182
void updateTestStats()
Updates the test statistics with the total test statistics.
Definition: flowctrl.cpp:4669
ProcedureInterfaceRetVal
Definition: flowctrl.hpp:85
@ INTERFACE_VALUE
Definition: flowctrl.hpp:89
@ INTERFACE_NONE
Definition: flowctrl.hpp:88
@ INTERFACE_EMPTY
Definition: flowctrl.hpp:86
@ INTERFACE_ERROR
Definition: flowctrl.hpp:87
bool bEvaluatingFlowControlStatements
Definition: flowctrl.hpp:123
This class implements the function definition managing instance.
Definition: define.hpp:74
bool reset()
This member function resets the FunctionDefinitionManager object to a state before any function was d...
Definition: define.cpp:1040
bool defineFunc(const std::string &sExpr, bool bRedefine=false, bool bFallback=false)
This function defines a custom function, by passing it to a new FunctionDefinition class instance.
Definition: define.cpp:655
void setPredefinedFuncs(const std::string &sPredefined)
This member function updates the internal list of predefined functions. If the list is whitespace-sep...
Definition: define.cpp:1158
std::string getPredefinedFuncs() const
Return a list of the internal defined default functions.
Definition: define.hpp:162
bool call(std::string &sExpr, int nRecursion=0)
This function searches for known custom definitions in the passed expression and replaces them with t...
Definition: define.cpp:801
std::string get(const std::string &sMessage, const std::vector< std::string > &vTokens) const
This member function returns the language string for the passed language identifier and replaces all ...
Definition: language.cpp:292
This class represents the central memory managing instance. It will handle all tables and clusters,...
void copyTable(const std::string &source, const std::string &target)
Copy one table to another one (and create the missing table automatically, if needed).
int getLines(StringView sTable, bool _bFull=false) const
bool isEmpty(const std::string &sTable) const
bool isTable(const std::string &sTable) const
This member function returns, whether the passed table name corresponds to a known table.
void swapTables(std::string sTable1, std::string sTable2)
bool deleteTable(const std::string &sCache)
This member function removes the selected table.
void renameTable(const std::string &sCache, const std::string &sNewName, bool bForceRenaming=false)
bool containsTablesOrClusters(const std::string &sCmdLine)
This member function evaluates, whether the passed command line contains tables or clusters.
void writeToTable(int _nLine, int _nCol, const std::string &_sCache, const mu::value_type &_dData)
int getCols(StringView sTable, bool _bFull=false) const
This class represents a whole cluster. The single items are stored as pointers to the abstract cluste...
Definition: cluster.hpp:325
void assignResults(Indices _idx, int nNum, mu::value_type *data)
This member function assigns calculation results as data for the cluster items in memory,...
Definition: cluster.cpp:720
std::vector< std::string > to_string() const
Converts all contents of this cluster to a vector of strings. Intended to be used for data transfer.
Definition: cluster.cpp:903
void setDoubleArray(const std::vector< mu::value_type > &vVals)
This member function assigns values as data for the all cluster items in memory. The type of the clus...
Definition: cluster.cpp:648
Cluster & getCluster(StringView sCluster)
This member function returns a reference to the cluster indicated by the passed cluster identifier.
Definition: cluster.cpp:2077
std::string createTemporaryCluster(const std::string &suffix="")
This member function creates a temporary cluster with a unique name and returns this name to the call...
Definition: cluster.cpp:2191
virtual bool isStringExpression(const std::string &sExpression) override
Returns true, if the passed expression is an expression containing strings, string variables or strin...
StringParserRetVal evalAndFormat(std::string &sLine, std::string &sCache, bool bSilent=false, bool bCheckAssertions=false)
This public member function evaluates the passed string expression and formats the results for the co...
void getStringValues(std::string &sLine)
This public member function resolves all string variable occurences and replaces them with their valu...
void removeTempStringVectorVars()
This member function removes all temporary string vector variables.
std::string createTempStringVectorVar(const std::vector< std::string > &vStringVector)
This member function is used to create a temporary string vector variable.
void throwException(SyntaxError error)
This member function shows the debugger with the corresponding error message obtained by the passed S...
Definition: debugger.cpp:120
void gatherInformations(ProcedureVarFactory *_varFactory, const std::string &_sErraticCommand, const std::string &_sErraticModule, unsigned int _nLineNumber)
This member function gathers all information from the current workspace and stores them internally to...
Definition: debugger.cpp:516
int showBreakPoint()
This member function shows the debugger for the current breakpoint and returns the debugger code (i....
Definition: debugger.cpp:158
void finalizeCatched()
Definition: debugger.hpp:76
void popStackItem()
This member function removes the last item from the stack.
Definition: debugger.cpp:489
void showError(const std::string &sTitle)
This member function shows the debugger with the passed error message.
Definition: debugger.cpp:96
BreakpointManager & getBreakpointManager()
Definition: debugger.hpp:110
void pushStackItem(const std::string &sStackItem, Procedure *_currentProcedure)
This member function adds a new stack item to the monitored stack. Additionally, it cleanes the proce...
Definition: debugger.cpp:457
static NumeReKernel * getInstance()
This static member function returns a a pointer to the singleton instance of the kernel.
Definition: kernel.hpp:221
static ProcedureLibrary ProcLibrary
Definition: kernel.hpp:202
static int evalDebuggerBreakPoint(const std::string &sCurrentCommand="")
This member function handles the creation of the debugger information for a script debugger breakpoin...
Definition: kernel.cpp:3475
NumeRe::StringParser & getStringParser()
Definition: kernel.hpp:286
static bool bSupressAnswer
Definition: kernel.hpp:199
mu::Parser & getParser()
Definition: kernel.hpp:281
NumeReDebugger & getDebugger()
Definition: kernel.hpp:326
static void failMessage(std::string sFailMessage)
This static function may be used to print a test failure message in the terminal.
Definition: kernel.cpp:2865
MemoryManager & getMemoryManager()
Definition: kernel.hpp:263
static void print(const std::string &__sLine, bool printingEnabled=true)
This member function appends the passed string as a new output line to the buffer and informs the ter...
Definition: kernel.cpp:2636
@ DEBUGGER_LEAVE
Definition: kernel.hpp:134
@ DEBUGGER_STEPOVER
Definition: kernel.hpp:133
NumeRe::Cluster & getAns()
Definition: kernel.hpp:268
static bool GetAsyncCancelState()
This function is used by the kernel to get informed, when the user pressed ESC or used other means of...
Definition: kernel.cpp:3703
static std::string formatResultOutput(int nNum, mu::value_type *v)
This static function is used to format the result output in the terminal for numerical-only results.
Definition: kernel.cpp:2730
static int * baseStackPosition
Definition: kernel.hpp:188
This class implements the procedure plugin system. It will be a parent class of the procedure class.
Definition: plugin.hpp:115
This class contains all the plot settings usable by the plotting algorithm.
Definition: plotdata.hpp:42
std::string getArgumentList() const
This class contains the pre-parsed contents of a single procedure file.
std::pair< int, ProcedureCommandLine > getCurrentLine(int currentLine)
This function returns the selected line of the stored file. This member function will be used in comb...
bool isLastLine(int currentline)
This member function determines, whether the current line is the last line of the stored procedure fi...
void setByteCode(int _nByteCode, int nCurrentLine)
This member function can be used to store the created byte code in the current procedure command line...
int gotoProcedure(const std::string &sProcedureName)
This member function returns the line of the stored file, where the desired procedure may be found or...
std::pair< int, ProcedureCommandLine > getNextLine(int currentline)
This member function returns the line after the current selected line. This is probably not the same ...
This class implements the logic to evaluate complex procedures, which may be called recursively.
Definition: procedure.hpp:56
~Procedure()
Destructor ensuring that the procedure output file stream will be closed, if it is still open.
Definition: procedure.cpp:78
virtual int catchExceptionForTest(std::exception_ptr e_ptr, bool bSupressAnswer_back, int nLine) override
This virtual member function is inserted in some automatically catchable locations to convert an erro...
Definition: procedure.cpp:2495
bool bWritingTofile
Definition: procedure.hpp:71
FunctionDefinitionManager _localDef
Definition: procedure.hpp:76
std::string sLastWrittenProcedureFile
Definition: procedure.hpp:68
Returnvalue execute(std::string sProc, std::string sVarList, mu::Parser &_parser, FunctionDefinitionManager &_functions, MemoryManager &_data, Settings &_option, Output &_out, PlotData &_pData, Script &_script, unsigned int nth_procedure=0)
This member function is central in the execution of the currently selected procedure as it handles al...
Definition: procedure.cpp:614
std::string sProcNames
Definition: procedure.hpp:62
std::string sCallingNameSpace
Definition: procedure.hpp:66
virtual int evalDebuggerBreakPoint(mu::Parser &_parser, Settings &_option) override
This virtual member function handles the gathering of all relevant information for the debugger for t...
Definition: procedure.cpp:2437
int applyInliningRuleset(const std::string &sCommandLine, const std::string &sArgumentList)
This private member function applies the internal inlining rule set for a single procedure command li...
Definition: procedure.cpp:2185
std::vector< std::string > getInlined(const std::string &sProc, const std::string &sArgumentList, const std::string &sFileName, size_t nProcedures)
This virtual private member function returns the inlined representation of the selected procedure as ...
Definition: procedure.cpp:2265
ProcedureVarFactory * _varFactory
Definition: procedure.hpp:74
int isInlineable(const std::string &sProc, const std::string &sFileName, int *nInlineFlag=nullptr)
This private member function evaluates, whether the current procedure is inlineable,...
Definition: procedure.cpp:2109
virtual FlowCtrl::ProcedureInterfaceRetVal procedureInterface(std::string &sLine, mu::Parser &_parser, FunctionDefinitionManager &_functions, MemoryManager &_data, Output &_out, PlotData &_pData, Script &_script, Settings &_option, int nth_command=0) override
This member function handles the calls for procedures and plugins, resolves them and executes the cal...
Definition: procedure.cpp:1250
std::string sProcCommandLine
Definition: procedure.hpp:69
void init()
Private initializing member function. Sets all variables to a reasonable default value.
Definition: procedure.cpp:98
virtual int isInline(const std::string &sProc) override
This virtual member function checks, whether the procedures in the current line are declared as inlin...
Definition: procedure.cpp:1958
std::string sNameSpace
Definition: procedure.hpp:65
int handleIncludeSyntax(std::string &sProcCommandLine, std::ifstream &fInclude, bool bReadingFromInclude)
This member function handles the script include syntax, which one may use in other procedures.
Definition: procedure.cpp:2834
void extractProcedureInformation(const std::string &sCmdLine, size_t nPos, std::string &sProcName, std::string &sArgList, std::string &sFileName)
This private member function extracts procedure name, argument list and the corresponding file name f...
Definition: procedure.cpp:1880
std::fstream fProcedure
Definition: procedure.hpp:61
bool bProcSupressAnswer
Definition: procedure.hpp:70
Returnvalue ProcCalc(std::string sLine, std::string sCurrentCommand, int &nByteCode, mu::Parser &_parser, FunctionDefinitionManager &_functions, MemoryManager &_data, Settings &_option, Output &_out, PlotData &_pData, Script &_script)
This member function does the evaluation stuff regarding strings and numerical expressions for the cu...
Definition: procedure.cpp:130
int nthBlock
Definition: procedure.hpp:73
size_t replaceReturnVal(std::string &sLine, mu::Parser &_parser, const Returnvalue &_return, unsigned int nPos, unsigned int nPos2, const std::string &sReplaceName)
This member function replaces the procedure occurence between the both passed positions using akronym...
Definition: procedure.cpp:2591
bool setProcName(const std::string &sProc, bool bInstallFileName=false)
This member function is used to obtain the procedure file name from the selected procedure....
Definition: procedure.cpp:547
friend class ProcedureVarFactory
Definition: procedure.hpp:59
int nFlags
Definition: procedure.hpp:72
virtual int getErrorInformationForDebugger() override
This virtual member function handles the gathering of all relevant information for the debugger for t...
Definition: procedure.cpp:2464
void resetProcedure(mu::Parser &_parser, bool bSupressAnswer)
This member function sets the current procedure object to its original state. It will be called at th...
Definition: procedure.cpp:2686
unsigned int GetCurrentLine() const
This member function will return the current line number depending on whether a flow control statemen...
Definition: procedure.cpp:2563
std::string sCurrentProcedureName
Definition: procedure.hpp:63
size_t countProceduresInLine(const std::string &sCommandLine)
This private member function simply counts the number of procedures, which may be found in the curren...
Definition: procedure.cpp:2224
virtual int procedureCmdInterface(std::string &sLine) override
Virtual member function allowing to identify and evaluate some special procedure commands....
Definition: procedure.cpp:1494
bool handleVariableDefinitions(std::string &sProcCommandLine, const std::string &sCommand)
This method handles the definitions of local variables.
Definition: procedure.cpp:2776
void extractCurrentNamespace(const std::string &sProc)
This member function extracts the namespace of the currently executed procedure.
Definition: procedure.cpp:2728
bool writeProcedure(std::string sProcedureLine)
This member function handles the procedure installation process by governing the file stream and pass...
Definition: procedure.cpp:1547
int nCurrentLine
Definition: procedure.hpp:64
static std::string mangleName(std::string sProcedureName)
Mangles a procedure name to be used as a usual variable.
Definition: procedure.cpp:2414
virtual std::vector< std::string > expandInlineProcedures(std::string &sLine) override
This virtual private member function expands all procedures in the current command line,...
Definition: procedure.cpp:2026
std::string sThisNameSpace
Definition: procedure.hpp:67
Procedure()
Default constructor.
Definition: procedure.cpp:34
ProcedureElement * getProcedureContents(const std::string &sProcedureFileName)
Returns the ProcedureElement pointer to the desired procedure file. It also creates the element,...
This class is the variable factory used by procedure instances to create their local variables and re...
std::string resolveVariables(const std::string &sProcedureCommandLine)
std::map< std::string, std::string > createProcedureArguments(std::string sArgumentList, std::string sArgumentValues)
This member function will create the procedure arguments for the current procedure.
void createLocalTables(std::string sTableList)
This member function will create the local tables for the current procedure.
void createLocalStrings(std::string sStringList)
This member function will create the local string variables for the current procedure.
bool delayDeletionOfReturnedTable(const std::string &sTableName)
Searches for a local table or a local table in the arguments with the corresponding identifier,...
std::vector< std::string > vInlineArgDef
void createLocalClusters(std::string sClusterList)
This member function will create the local clusters for the current procedure.
std::string createTestStatsCluster()
Creates a special cluster containing the test statistics.
void createLocalVars(std::string sVarList)
This member function will create the local numerical variables for the current procedure.
This class manages the setting values of the internal (kernel) settings of this application.
Definition: settings.hpp:663
bool systemPrints() const
Returns, whether system messages shall be printed to the terminal.
Definition: settings.hpp:1140
void enableSystemPrints(bool _bSystemPrints=true)
Enables or disables the system printing functionality. This is a convenience wrapper for the direct m...
Definition: settings.hpp:829
bool useDebugger() const
Returns, whether the debugger is currently active.
Definition: settings.hpp:1150
Common exception class for all exceptions thrown in NumeRe.
Definition: error.hpp:32
@ LOOP_THROW
Definition: error.hpp:152
@ INLINE_PROCEDURE_IS_NOT_INLINE
Definition: error.hpp:119
@ PROCEDURE_STACK_OVERFLOW
Definition: error.hpp:201
@ IF_OR_LOOP_SEEMS_NOT_TO_BE_CLOSED
Definition: error.hpp:117
@ INVALID_PROCEDURE_NAME
Definition: error.hpp:133
@ FUNCTION_ERROR
Definition: error.hpp:110
@ PRIVATE_PROCEDURE_CALLED
INSERT HERE.
Definition: error.hpp:196
@ UNMATCHED_PARENTHESIS
Definition: error.hpp:224
@ SCRIPT_NOT_EXIST
Definition: error.hpp:204
@ WRONG_ARG_NAME
Definition: error.hpp:226
@ PROCEDURE_THROW
Definition: error.hpp:199
@ INVALID_INDEX
Definition: error.hpp:129
@ PROCEDURE_NOT_FOUND
Definition: error.hpp:198
@ PROCESS_ABORTED_BY_USER
Definition: error.hpp:202
static size_t invalid_position
Definition: error.hpp:235
bool isOpenEnd() const
This member function determines, whether the internal index set has an open end.
Definition: structures.hpp:614
std::string to_string() const
This member function converts the vector indexes contents into a human-readable string representation...
Definition: structures.hpp:770
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.
bool IsLockedPause() const
Check, whether the pause mode is locked.
const valmap_type & GetConst() const
Return a map containing all parser constants.
void ClearVectorVars(bool bIgnoreProcedureVects=false)
This member function cleares the internal vector storage.
bool ActiveLoopMode() const
Check, whether the loop mode is active. This function returns true even if the loop mode is paused.
bool IsAlreadyParsed(StringView sNewEquation)
This member function checks, whether the passed expression is already parsed, so that the parsing ste...
string_type CreateTempVectorVar(const std::vector< mu::value_type > &vVar)
This member function copies the passed vector into the internal storage referencing it with a auto-ge...
std::map< std::string, std::string > * mVarMapPntr
Definition: muParserBase.h:98
void SetVectorVar(const std::string &sVarName, const std::vector< mu::value_type > &vVar, bool bAddVectorType=false)
This member function copies the passed vector into the internal storage referencing it with the passe...
Error class of the parser.
const string_type & GetExpr() const
gets the expression related tp this error.
Mathematical expressions parser.
Definition: muParser.h:51
string getDataElements(string &sLine, Parser &_parser, MemoryManager &_data, const Settings &_option, int options)
Searches the passed string for calls to any table or cluster and replaces them with internal vectors ...
Definition: dataaccess.cpp:276
bool isClusterCandidate(string &sLine, string &sCluster, bool doCut)
This function checks, whether the passed command line contains the syntax for a cluster candidate,...
Indices getIndices(StringView sCmd, mu::Parser &_parser, MemoryManager &_data, const Settings &_option)
Wrapper for the new getIndices function interface.
Definition: indices.cpp:49
bool isValidIndexSet(const Indices &_idx)
Definition: dataaccess.hpp:81
Language _lang
Definition: kernel.cpp:39
Assertion _assertionHandler
Definition: error.cpp:27
string sErrorToken
DetachedLogger g_logger
Definition: logger.cpp:23
unsigned int getMatchingParenthesis(const StringView &)
Returns the position of the closing parenthesis.
Definition: tools.cpp:414
CONSTCD11 std::enable_if<!std::chrono::treat_as_floating_point< T >::value, T >::type trunc(T t) NOEXCEPT
Definition: date.h:1113
CONSTCD11 std::chrono::duration< Rep, Period > abs(std::chrono::duration< Rep, Period > d)
Definition: date.h:1317
MUP_BASETYPE value_type
The numeric datatype used by the parser.
Definition: muParserDef.h:251
string promptForUserInput(const string &__sCommand)
This function is invoked, if a prompt operator ("??") was found in a string.
#define MAX_PROCEDURE_STACK_SIZE
Definition: procedure.cpp:24
value_type vAns
#define max(a, b)
Definition: resampler.cpp:30
void StripSpaces(std::string &)
Removes leading and trailing white spaces and tabulator characters.
std::string toUpperCase(const std::string &sLowerCase)
Converts lowercase letters to uppercase ones.
std::string getTimeStamp(bool bGetStamp)
This function simple returns the current time as a default timestamp.
This structure is central for managing the indices of a table or cluster read or write data access....
VectorIndex col
VectorIndex row
Structure for the findCommand function.
unsigned int nPos
std::string sString
Structure as wrapper for the return value of procedures (which may be numerical or string values or a...
std::vector< std::string > vStringVal
bool isString() const
bool isNumeric() const
std::string sReturnedTable
std::vector< mu::value_type > vNumVal
std::string toString(int)
Converts an integer to a string without the Settings bloat.
Match findCommand(StringView sCmd, const std::string &sCommand)
This function is very important for the command handler.
Definition: tools.cpp:1275
bool getStringArgument(const string &sCmd, string &sArgument)
This function searches for the first string value in the passed expression and writes that into sArgu...
Definition: tools.cpp:250
string decodeNameSpace(string sCommandLine, const string &sThisNameSpace)
Definition: tools.cpp:3476
EndlessVector< string > getAllSemiColonSeparatedTokens(string sArgList)
Splits up the complete index list and returns them as an EndlessVector.
Definition: tools.cpp:2403
void evalRecursiveExpressions(string &sExpr)
This function converts the recursive assignments like VAR += VAL into VAR = VAR + (VAL)
Definition: tools.cpp:3346
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
bool isMultiValue(const string &sExpr, bool bIgnoreClosingParenthesis)
Determines, if a string contains multiple comma-separated expressions.
Definition: tools.cpp:484