exec : management of macro and macrofile.
[scilab.git] / scilab / modules / functions / sci_gateway / cpp / sci_exec.cpp
1 /*
2 * Scilab ( http://www.scilab.org/ ) - This file is part of Scilab
3 * Copyright (C) 2006 - INRIA - Antoine ELIAS
4 *
5 * This file must be used under the terms of the CeCILL.
6 * This source file is licensed as described in the file COPYING, which
7 * you should have received as part of this distribution.  The terms
8 * are also available at
9 * http://www.cecill.info/licences/Licence_CeCILL_V2-en.txt
10 *
11 */
12
13 #include <string.h>
14 #include "functions_gw.hxx"
15
16 #include "parser.hxx"
17 #include "funcmanager.hxx"
18 #include "context.hxx"
19 #include "execvisitor.hxx"
20 #include "mutevisitor.hxx"
21 #include "printvisitor.hxx"
22 #include "visitor_common.hxx"
23 #include "scilabWrite.hxx"
24 #include "scilabexception.hxx"
25 #include "configvariable.hxx"
26 #include "types_tools.hxx"
27
28 #include <iostream>
29 #include <fstream>
30 #include <string>
31
32 extern "C"
33 {
34 #include "os_wcsicmp.h"
35 #include "expandPathVariable.h"
36 #include "prompt.h"
37 #include "Scierror.h"
38 #include "localization.h"
39 #include "os_swprintf.h"
40 #include "mopen.h"
41 #include "mclose.h"
42 #include "fullpath.h"
43 #include "PATH_MAX.h"
44 }
45
46 using namespace ast;
47 bool checkPrompt(int _iMode, int _iCheck);
48 void printLine(const std::string& _stPrompt, const std::string& _stLine, bool _bLF);
49 std::string printExp(std::ifstream& _File, Exp* _pExp, const std::string& _stPrompt, int* _piLine /* in/out */, int* _piCol /* in/out */, std::string& _stPreviousBuffer);
50 std::string getExpression(const std::string& _stFile, Exp* _pExp);
51
52 /*--------------------------------------------------------------------------*/
53 types::Function::ReturnValue sci_exec(types::typed_list &in, int _iRetCount, types::typed_list &out)
54 {
55     int promptMode      = 0;//default value at startup, overthise 3 or verbose ";"
56     bool bPromptMode    = false;
57     int iErr            = 0;
58     bool bErrCatch      = false;
59     Exp* pExp           = NULL;
60     int iID             = 0;
61     Parser parser;
62
63     wchar_t* pwstFile = NULL;
64     char* pstFile = NULL;
65
66     std::string stFile;
67     std::ifstream* file = NULL;
68
69     if (ConfigVariable::getStartProcessing() == false)
70     {
71         if (ConfigVariable::getVerbose())
72         {
73             promptMode = 3;
74         }
75         else
76         {
77             promptMode = -1;
78         }
79     }
80
81     if (in.size() < 1 || in.size() > 3)
82     {
83         Scierror(999, _("%s: Wrong number of input arguments: %d to %d expected.\n"), "exec" , 1, 3);
84         return Function::Error;
85     }
86
87     if (in[0]->isString() && in[0]->getAs<types::String>()->isScalar())
88     {
89         //1st argument is a path, parse file and execute it
90         int iParsePathLen = 0;
91         String* pS = in[0]->getAs<types::String>();
92
93         pwstFile = expandPathVariableW(pS->get(0));
94         pstFile = wide_string_to_UTF8(pwstFile);
95         stFile = std::string(pstFile);
96         file = new std::ifstream(pstFile);
97
98         wchar_t* pwstTemp = (wchar_t*)MALLOC(sizeof(wchar_t) * (PATH_MAX * 2));
99         get_full_pathW(pwstTemp, (const wchar_t*)pwstFile, PATH_MAX * 2);
100
101         /*fake call to mopen to show file within file()*/
102         if (mopen(pwstTemp, L"r", 0, &iID) != MOPEN_NO_ERROR)
103         {
104             FREE(pwstTemp);
105             Scierror(999, _("%s: Cannot open file %s.\n"), "exec", pstFile);
106             return Function::Error;
107         }
108
109         parser.parseFile(pwstTemp, L"exec");
110         FREE(pwstTemp);
111         if (parser.getExitStatus() !=  Parser::Succeded)
112         {
113             scilabWriteW(parser.getErrorMessage());
114             delete parser.getTree();
115             mclose(iID);
116             return Function::Error;
117         }
118
119         if (ConfigVariable::getSerialize())
120         {
121             ast::Exp* temp = parser.getTree();
122             if (ConfigVariable::getTimed())
123             {
124                 pExp = callTyper(temp, L"exec");
125             }
126             else
127             {
128                 pExp = callTyper(temp);
129             }
130
131             delete temp;
132         }
133         else
134         {
135             pExp = parser.getTree();
136         }
137     }
138     else if (in[0]->isMacro() || in[0]->isMacroFile())
139     {
140         types::Macro* pMacro = NULL;
141         typed_list input;
142         optional_list optional;
143         typed_list output;
144         ast::ExecVisitor execFunc;
145
146         if (in[0]->isMacroFile())
147         {
148             //1st argument is a macro name, parse and execute it in the current environnement
149             if (in[0]->getAs<MacroFile>()->parse() == false)
150             {
151                 char* pstMacro = wide_string_to_UTF8(in[0]->getAs<MacroFile>()->getName().c_str());
152                 Scierror(999, _("%s: Unable to parse macro '%s'"), "exec", pstMacro);
153                 FREE(pstMacro);
154                 return Function::Error;
155             }
156
157             pMacro = in[0]->getAs<MacroFile>()->getMacro();
158
159         }
160         else //1st argument is a macro name, execute it in the current environnement
161         {
162             pMacro = in[0]->getAs<Macro>();
163         }
164
165         // We dont care about the input and output argument
166         if (pMacro->outputs_get()->empty() == false || pMacro->inputs_get()->empty() == false)
167         {
168             Scierror(999, _("%s: Wrong type for input argument #%d: A macro without input and output argument expected.\n"), "exec", 1);
169             return Function::Error;
170         }
171
172         promptMode = 3;
173         pExp = pMacro->getBody();
174     }
175     else
176     {
177         Scierror(999, _("%s: Wrong type for input argument #%d: A string expected.\n"), "exec", 1);
178         return Function::Error;
179     }
180
181     // get mode and errcatch
182     if (in.size() > 1)
183     {
184         //errcatch or mode
185         if (in[1]->isString() && in[1]->getAs<types::String>()->isScalar())
186         {
187             //errcatch
188             String* pS = in[1]->getAs<types::String>();
189             if (os_wcsicmp(pS->get(0), L"errcatch") == 0)
190             {
191                 bErrCatch = true;
192             }
193             else
194             {
195                 Scierror(999, _("%s: Wrong value for input argument #%d: 'errcatch' expected.\n"), "exec", 2);
196                 return Function::Error;
197             }
198
199             if (in.size() > 2)
200             {
201                 if (in[2]->isDouble() == false || in[2]->getAs<Double>()->isScalar() == false)
202                 {
203                     //mode
204                     Scierror(999, _("%s: Wrong type for input argument #%d: A integer expected.\n"), "exec", 3);
205                     return Function::Error;
206                 }
207
208                 promptMode = (int)in[2]->getAs<Double>()->getReal()[0];
209                 bPromptMode = true;
210             }
211         }
212         else if (in[1]->isDouble() && in[1]->getAs<Double>()->isScalar())
213         {
214             //mode
215             promptMode = (int)in[1]->getAs<Double>()->getReal()[0];
216             bPromptMode = true;
217         }
218         else
219         {
220             //not managed
221             Scierror(999, _("%s: Wrong type for input argument #%d: A integer or string expected.\n"), "exec", 2);
222             return Function::Error;
223         }
224     }
225
226     ast::exps_t LExp = pExp->getAs<SeqExp>()->getExps();
227
228     char pstPrompt[64];
229     //get prompt
230     GetCurrentPrompt(pstPrompt);
231     std::string stPrompt(pstPrompt);
232
233     std::string str;
234     int iCurrentLine = -1; //no data in str
235     int iCurrentCol = 0; //no data in str
236
237     //save current prompt mode
238     int oldVal = ConfigVariable::getPromptMode();
239
240     ConfigVariable::setPromptMode(promptMode);
241
242     for (ast::exps_t::iterator j = LExp.begin(), itEnd = LExp.end() ; j != itEnd ; ++j)
243     {
244         try
245         {
246             ast::exps_t::iterator k = j;
247             //mode == 0, print new variable but not command
248             if (file && ConfigVariable::getPromptMode() != 0 && ConfigVariable::getPromptMode() != 2)
249             {
250                 int iLastLine = (*j)->getLocation().last_line;
251                 do
252                 {
253                     str = printExp(*file, *k, stPrompt, &iCurrentLine, &iCurrentCol, str);
254                     iLastLine = (*k)->getLocation().last_line;
255                     k++;
256                 }
257                 while (k != LExp.end() && (*k)->getLocation().first_line == iLastLine);
258
259                 // In case where the line ends by spaces, iCurrentCol is not reset
260                 // by printExp because we don't know if that's the end of the expression
261                 // before go out of the loop. So we have to reset column count
262                 // and print a new line before manage the next line.
263                 if (iCurrentCol != 0)
264                 {
265                     iCurrentCol = 0;
266                     printLine("", "", true);
267                 }
268             }
269             else
270             {
271                 k++;
272             }
273
274
275             ast::exps_t::iterator p = j;
276             for (; p != k; p++)
277             {
278                 bool bImplicitCall = false;
279                 j = p;
280                 //excecute script
281                 //force -1 to prevent recursive call to exec to write in console
282                 //ConfigVariable::setPromptMode(-1);
283                 ExecVisitor execMe;
284                 (*j)->accept(execMe);
285                 //ConfigVariable::setPromptMode(promptMode);
286
287
288                 //to manage call without ()
289                 if (execMe.getResult() != NULL && execMe.getResult()->isCallable())
290                 {
291                     Callable *pCall = execMe.getResult()->getAs<Callable>();
292                     types::typed_list out;
293                     types::typed_list in;
294                     types::optional_list opt;
295
296                     try
297                     {
298                         //in this case of calling, we can return only one values
299                         ExecVisitor execCall;
300                         execCall.setExpectedSize(1);
301                         Function::ReturnValue Ret = pCall->call(in, opt, 1, out, &execCall);
302
303                         if (Ret == Callable::OK)
304                         {
305                             if (out.size() == 0)
306                             {
307                                 execMe.setResult(NULL);
308                             }
309                             else if (out.size() == 1)
310                             {
311                                 out[0]->DecreaseRef();
312                                 execMe.setResult(out[0]);
313                                 bImplicitCall = true;
314                             }
315                             else
316                             {
317                                 for (int i = 0 ; i < static_cast<int>(out.size()) ; i++)
318                                 {
319                                     out[i]->DecreaseRef();
320                                     execMe.setResult(i, out[i]);
321                                 }
322                             }
323                         }
324                         else if (Ret == Callable::Error)
325                         {
326                             if (ConfigVariable::getLastErrorFunction() == L"")
327                             {
328                                 ConfigVariable::setLastErrorFunction(pCall->getName());
329                             }
330
331                             if (pCall->isMacro() || pCall->isMacroFile())
332                             {
333                                 wchar_t szError[bsiz];
334                                 os_swprintf(szError, bsiz, _W("at line % 5d of function %ls called by :\n").c_str(), (*j)->getLocation().first_line, pCall->getName().c_str());
335                                 throw ast::ScilabMessage(szError);
336                             }
337                             else
338                             {
339                                 throw ast::ScilabMessage();
340                             }
341                         }
342                     }
343                     catch (ScilabMessage sm)
344                     {
345                         wostringstream os;
346                         PrintVisitor printMe(os);
347                         (*j)->accept(printMe);
348                         os << std::endl << std::endl;
349                         if (ConfigVariable::getLastErrorFunction() == L"")
350                         {
351                             ConfigVariable::setLastErrorFunction(pCall->getName());
352                         }
353
354                         if (pCall->isMacro() || pCall->isMacroFile())
355                         {
356                             wstring szAllError;
357                             wchar_t szError[bsiz];
358                             os_swprintf(szError, bsiz, _W("at line % 5d of function %ls called by :\n").c_str(), sm.GetErrorLocation().first_line, pCall->getName().c_str());
359                             szAllError = szError + os.str();
360                             os_swprintf(szError, bsiz, _W("at line % 5d of exec file called by :\n").c_str(), (*j)->getLocation().first_line);
361                             szAllError += szError;
362                             throw ast::ScilabMessage(szAllError);
363                         }
364                         else
365                         {
366                             sm.SetErrorMessage(sm.GetErrorMessage() + os.str());
367                             throw sm;
368                         }
369                     }
370                 }
371
372                 //update ans variable
373                 SimpleVar* pVar = dynamic_cast<SimpleVar*>(*j);
374                 //don't output Simplevar and empty result
375                 if (execMe.getResult() != NULL && (pVar == NULL || bImplicitCall))
376                 {
377                     InternalType* pITAns = execMe.getResult();
378                     symbol::Context::getInstance()->put(symbol::Symbol(L"ans"), pITAns);
379                     if ( (*j)->isVerbose() && bErrCatch == false)
380                     {
381                         //TODO manage multiple returns
382                         scilabWriteW(L" ans  =\n\n");
383                         std::wostringstream ostrName;
384                         ostrName << SPACES_LIST << L"ans";
385                         VariableToString(pITAns, ostrName.str().c_str());
386                     }
387                 }
388             }
389         }
390         catch (const ScilabMessage& sm)
391         {
392             scilabErrorW(sm.GetErrorMessage().c_str());
393
394             CallExp* pCall = dynamic_cast<CallExp*>(*j);
395             if (pCall != NULL)
396             {
397                 //to print call expression only of it is a macro
398                 ExecVisitor execFunc;
399                 pCall->getName().accept(execFunc);
400
401                 if (execFunc.getResult() != NULL &&
402                         (execFunc.getResult()->isMacro() || execFunc.getResult()->isMacroFile()))
403                 {
404                     wostringstream os;
405
406                     //add function failed
407                     PrintVisitor printMe(os);
408                     pCall->accept(printMe);
409                     os << std::endl;
410
411                     //add info on file failed
412                     wchar_t szError[bsiz];
413                     os_swprintf(szError, bsiz, _W("at line % 5d of exec file called by :\n").c_str(), (*j)->getLocation().first_line);
414                     os << szError;
415
416                     if (ConfigVariable::getLastErrorFunction() == L"")
417                     {
418                         ConfigVariable::setLastErrorFunction(execFunc.getResult()->getAs<Callable>()->getName());
419                     }
420
421                     if (file)
422                     {
423                         mclose(iID);
424                     }
425
426                     //restore previous prompt mode
427                     ConfigVariable::setPromptMode(oldVal);
428                     throw ast::ScilabMessage(os.str(), 0, (*j)->getLocation());
429                 }
430             }
431
432             if (file)
433             {
434                 mclose(iID);
435             }
436
437             throw ast::ScilabMessage((*j)->getLocation());
438         }
439         catch (const ast::ScilabError& se)
440         {
441             if (ConfigVariable::getLastErrorNumber() == 0)
442             {
443                 ConfigVariable::setLastErrorMessage(se.GetErrorMessage());
444                 ConfigVariable::setLastErrorNumber(se.GetErrorNumber());
445                 ConfigVariable::setLastErrorLine(se.GetErrorLocation().first_line);
446                 ConfigVariable::setLastErrorFunction(wstring(L""));
447             }
448
449             //store message
450             iErr = ConfigVariable::getLastErrorNumber();
451             if (bErrCatch == false)
452             {
453                 if (file)
454                 {
455                     file->close();
456                     //print failed command
457                     scilabError(getExpression(stFile, *j).c_str());
458                     scilabErrorW(L"\n");
459                 }
460
461                 //write error
462                 scilabErrorW(se.GetErrorMessage().c_str());
463
464                 //write position
465                 wchar_t szError[bsiz];
466                 os_swprintf(szError, bsiz, _W("at line % 5d of exec file called by :\n").c_str(), (*j)->getLocation().first_line);
467                 scilabErrorW(szError);
468                 if (file)
469                 {
470                     mclose(iID);
471                 }
472
473                 //restore previous prompt mode
474                 ConfigVariable::setPromptMode(oldVal);
475                 //throw ast::ScilabMessage(szError, 1, (*j)->getLocation());
476                 //print already done, so just foward exception but with message
477                 throw ast::ScilabError();
478             }
479             break;
480         }
481     }
482
483     //restore previous prompt mode
484     ConfigVariable::setPromptMode(oldVal);
485
486     if (bErrCatch)
487     {
488         out.push_back(new Double(iErr));
489         //to lock last error information
490         ConfigVariable::setLastErrorCall();
491     }
492
493     if (file)
494     {
495         delete pExp;
496         mclose(iID);
497         file->close();
498         delete file;
499         FREE(pstFile);
500         FREE(pwstFile);
501     }
502
503     return Function::OK;
504 }
505
506 std::string getExpression(const std::string& _stFile, Exp* _pExp)
507 {
508     std::string out;
509     std::string stBuffer;
510     int iLine = 0;
511     Location loc = _pExp->getLocation();
512     std::ifstream file(_stFile.c_str());
513
514     //bypass previous lines
515     for (int i = 0 ; i < loc.first_line; i++)
516     {
517         std::getline(file, stBuffer);
518     }
519
520     if (loc.first_line == loc.last_line)
521     {
522         int iStart = loc.first_column - 1;
523         int iEnd = loc.last_column - 1;
524         int iLen = iEnd - iStart;
525         out += string(stBuffer.c_str() + iStart, iLen);
526     }
527     else
528     {
529         //
530
531         //first line, entire or not
532         out += string(stBuffer.c_str() + loc.first_column - 1);
533         out += "\n";
534
535         //print other full lines
536         for (int i = loc.first_line; i < (loc.last_line - 1) ; i++)
537         {
538             std::getline(file, stBuffer);
539             out += stBuffer;
540             out += "\n";
541         }
542
543
544         //last line, entire or not
545         getline(file, stBuffer);
546         out += string(stBuffer.c_str(), loc.last_column - 1);
547         out += "\n";
548     }
549     return out;
550 }
551
552 bool checkPrompt(int _iMode, int _iCheck)
553 {
554     return ((_iMode & _iCheck) == _iCheck);
555 }
556
557 std::string printExp(std::ifstream& _File, Exp* _pExp, const std::string& _stPrompt, int* _piLine /* in/out */, int* _piCol /* in/out */, std::string& _stPreviousBuffer)
558 {
559     //case 1, exp is on 1 line and take the entire line
560
561     //case 2, exp is multiline
562
563     //case 3, exp is part of a line.
564     //ex : 3 exp on the same line a = 1; b = 2; c = 3;
565
566     //case 4, exp is multiline but start and/or finish in the middle of a line
567     //ex :
568     //a = 10;for i = 1 : a
569     //  a
570     //end, b = 1;
571
572     Location loc = _pExp->getLocation();
573
574     //positionning file curser at loc.first_line
575     {
576         //strange case, current position is after the wanted position
577         if (*_piLine > loc.first_line)
578         {
579             //reset line counter and restart reading at the start of the file.
580             *_piLine = -1;
581             _File.seekg( 0, ios_base::beg );
582         }
583
584         //bypass previous lines
585         for (int i = *_piLine ; i < loc.first_line - 1; i++)
586         {
587
588             (*_piLine)++;
589             if ((*_piLine) != (loc.first_line - 1))
590             {
591                 //empty line but not sequential lines
592                 printLine("", "", true);
593             }
594             std::getline(_File, _stPreviousBuffer);
595         }
596     }
597
598     if (loc.first_line == loc.last_line)
599     {
600         //1 line
601         int iStart = loc.first_column - 1;
602         int iEnd = loc.last_column - 1;
603         int iLen = iEnd - iStart;
604         std::string strLastLine(_stPreviousBuffer.c_str() + iStart, iLen);
605         int iExpLen = iLen;
606         int iLineLen = (int)_stPreviousBuffer.size();
607         //printLine(_pstPrompt, strLastLine, true, false);
608
609         if (iStart == 0 && iExpLen == iLineLen)
610         {
611             //entire line
612             if (*_piCol)
613             {
614                 //blank char at the end of previous line
615                 printLine("", "", true);
616             }
617             printLine(_stPrompt, strLastLine, true);
618             *_piCol = 0;
619         }
620         else
621         {
622             if (iStart == 0)
623             {
624                 //begin of line
625                 if (*_piCol)
626                 {
627                     //blank char at the end of previous line
628                     printLine("", "", true);
629                 }
630                 printLine(_stPrompt, strLastLine, false);
631                 *_piCol = loc.last_column;
632             }
633             else
634             {
635                 if (*_piCol == 0)
636                 {
637                     printLine(_stPrompt, "", false);
638                     (*_piCol)++;
639                 }
640
641                 if (*_piCol < loc.first_column)
642                 {
643                     //pickup separator between expressionsfrom file and add to output
644                     int iSize = loc.first_column - *_piCol;
645                     std::string stTemp(_stPreviousBuffer.c_str() +  (*_piCol - 1), iSize);
646                     printLine("", stTemp, false);
647                     *_piCol = loc.first_column;
648                 }
649
650                 if (iEnd == iLineLen)
651                 {
652                     printLine("", strLastLine, true);
653                     *_piCol = 0;
654                 }
655                 else
656                 {
657                     printLine("", strLastLine, false);
658                     *_piCol = loc.last_column;
659                 }
660             }
661         }
662     }
663     else
664     {
665         //multiline
666
667         if (loc.first_column == 1)
668         {
669             if (*_piCol)
670             {
671                 //blank char at the end of previous line
672                 printLine("", "", true);
673             }
674             printLine(_stPrompt, _stPreviousBuffer.c_str() + (loc.first_column - 1), false);
675         }
676         else
677         {
678             if (*_piCol < loc.first_column)
679             {
680                 //pickup separator between expressionsfrom file and add to output
681                 printLine(_stPrompt, _stPreviousBuffer.c_str(), false);
682                 *_piCol = loc.first_column;
683             }
684         }
685
686         //print other full lines
687         for (int i = loc.first_line; i < (loc.last_line - 1) ; i++)
688         {
689             (*_piLine)++;
690             std::getline(_File, _stPreviousBuffer);
691             // dont print empty line of function body
692             if (_stPreviousBuffer.size() != 0)
693             {
694                 printLine(_stPrompt, _stPreviousBuffer.c_str(), false);
695             }
696         }
697
698         //last line
699         std::getline(_File, _stPreviousBuffer);
700         (*_piLine)++;
701
702         int iSize = loc.last_column - 1;
703         std::string stLastLine(_stPreviousBuffer.c_str(), iSize);
704         int iLineLen = (int)_stPreviousBuffer.size();
705         if (iLineLen == iSize)
706         {
707             printLine(_stPrompt, stLastLine, true);
708             *_piCol = 0;
709         }
710         else
711         {
712             printLine(_stPrompt, stLastLine, false);
713             *_piCol = loc.last_column;
714         }
715     }
716
717     return _stPreviousBuffer;
718 }
719
720 void printLine(const std::string& _stPrompt, const std::string& _stLine, bool _bLF)
721 {
722     std::string st;
723
724     if (_stPrompt.size() != 0)
725     {
726         st = "\n" + _stPrompt;
727     }
728
729     st += _stLine;
730     if (_bLF)
731     {
732         st += "\n";
733     }
734
735     scilabWrite(st.c_str());
736 }
737 /*--------------------------------------------------------------------------*/