Refactoring of ScilabException in AST, exec, execstr.
[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 "parser.hxx"
15 #include "funcmanager.hxx"
16 #include "context.hxx"
17 #include "functions_gw.hxx"
18 #include "execvisitor.hxx"
19 #include "mutevisitor.hxx"
20 #include "yaspio.hxx"
21 #include "scilabexception.hxx"
22 #include "configvariable.hxx"
23
24 #include <iostream>
25 #include <fstream>
26 #include <string>
27
28 extern "C"
29 {
30 #include "os_wcsicmp.h"
31 #include "expandPathVariable.h"
32 #include "prompt.h"
33 #include "Scierror.h"
34 #include "localization.h"
35 #include "os_swprintf.h"
36 #include "mopen.h"
37 #include "mclose.h"
38 }
39
40
41 using namespace types;
42 using namespace ast;
43 using namespace std;
44
45 bool checkPrompt(int _iMode, int _iCheck);
46 void printLine(char* _stPrompt, char* _stLine, bool _bLF);
47 void printExp(std::ifstream* _pFile, Exp* _pExp, char* _pstPrompt, int* _piLine /* in/out */, char* _pstPreviousBuffer);
48
49 /*--------------------------------------------------------------------------*/
50 Function::ReturnValue sci_exec(types::typed_list &in, int _iRetCount, types::typed_list &out)
51 {
52     int promptMode  = 1;
53     int iErr        = 0;
54         bool bErrCatch  = false;
55         Exp* pExp               = NULL;
56     int iID         = 0;
57     Parser parser;
58
59
60         if(in.size() < 1 || in.size() > 3)
61         {
62         ScierrorW(999, _W("%ls: Wrong number of input arguments: %d to %d expected.\n"), L"exec" , 1, 3);
63         return Function::Error;
64         }
65
66         if(in.size() > 1)
67         {//errcatch or mode
68         if(in[1]->isString() && in[1]->getAs<types::String>() ->getSize() == 1)
69                 {//errcatch
70                         String* pS = in[1]->getAs<types::String>();
71                         if(os_wcsicmp(pS->get(0), L"errcatch") == 0)
72                         {
73                                 bErrCatch = true;
74                         }
75                         else
76                         {
77                 ScierrorW(999, _W("%ls: Wrong value for input argument #%d: 'errcatch' expected.\n"), L"execstr", 2);
78                                 return Function::Error;
79                         }
80
81                         if(in.size() > 2)
82                         {
83                 if(in[2]->isDouble() == false || in[2]->getAs<Double>()->getSize() != 1)
84                                 {//mode
85                     ScierrorW(999, _W("%ls: Wrong type for input argument #%d: A integer expected.\n"), L"exec", 3);
86                     return Function::Error;
87                                 }
88
89                 promptMode = (int)in[2]->getAs<Double>()->getReal()[0];
90                         }
91                 }
92                 else if(in[1]->isDouble() && in[1]->getAs<Double>()->getSize() == 1)
93         {//mode
94             promptMode = (int)in[1]->getAs<Double>()->getReal()[0];
95                 }
96                 else
97                 {//not managed
98             ScierrorW(999, _W("%ls: Wrong type for input argument #%d: A integer or string expected.\n"), L"exec", 2);
99             return Function::Error;
100                 }
101         }
102
103     if(in[0]->isString() && in[0]->getAs<types::String>()->getSize() == 1)
104         {//1st argument is a path, parse file and execute it
105                 int iParsePathLen               = 0;
106                 String* pS = in[0]->getAs<types::String>();
107
108         wchar_t* pstFile = pS->get(0);
109         wchar_t *expandedPath = expandPathVariableW(pstFile);
110
111         /*fake call to mopen to show file within file()*/
112         if(mopen(expandedPath, L"r", 0, &iID) != MOPEN_NO_ERROR)
113         {
114             ScierrorW(999, _W("%ls: Cannot open file %ls.\n"), L"exec", expandedPath);
115             return Function::Error;
116         }
117
118         parser.parseFile(expandedPath, L"exec");
119         FREE(expandedPath);
120                 if(parser.getExitStatus() !=  Parser::Succeded)
121                 {
122                         YaspWriteW(parser.getErrorMessage());
123                         delete parser.getTree();
124             mclose(iID);
125                         return Function::Error;
126                 }
127
128                 pExp = parser.getTree();
129         }
130         else if(in[0]->isMacro())
131         {//1st argument is a macro name, execute it in the current environnement
132                 pExp = in[0]->getAsMacro()->getBody();
133         }
134         else if(in[0]->isMacroFile())
135         {//1st argument is a macro name, parse and execute it in the current environnement
136                 if(in[0]->getAsMacroFile()->parse() == false)
137                 {
138             ScierrorW(999, _W("%ls: Unable to parse macro '%s'"), "exec", in[0]->getAsMacroFile()->getName().c_str());
139             mclose(iID);
140                         return Function::Error;
141                 }
142                 pExp = in[0]->getAsMacroFile()->getMacro()->getBody();
143         }
144         else
145         {
146         ScierrorW(999, _W("%ls: Wrong type for input argument #%d: A string expected.\n"), L"exec", 1);
147         mclose(iID);
148                 return Function::Error;
149         }
150
151         std::list<Exp *>::iterator j;
152         std::list<Exp *>LExp = ((SeqExp*)pExp)->exps_get();
153
154         char stPrompt[64];
155         //get prompt
156         GetCurrentPrompt(stPrompt);
157 //    MessageBoxA(NULL, stPrompt, "", 0);
158
159     wchar_t* pwstFile =  expandPathVariableW(in[0]->getAs<types::String>()->get(0));
160     char* pstFile = wide_string_to_UTF8(pwstFile);
161         std::ifstream file(pstFile);
162     FREE(pstFile);
163     FREE(pwstFile);
164
165         char str[1024];
166         int iCurrentLine = -1; //no data in str
167
168     //save current prompt mode
169     int oldVal = ConfigVariable::getPromptMode();
170     ConfigVariable::setPromptMode(promptMode);
171
172         for(j = LExp.begin() ; j != LExp.end() ; j++)
173         {
174                 try
175                 {
176                         //if(checkPrompt(iMode, EXEC_MODE_MUTE))
177                         //{
178                         //      //manage mute option
179                         //      (*j)->mute();
180                         //      MuteVisitor mute;
181                         //      (*j)->accept(mute);
182                         //}
183             //exec3 ou normal prompt mode
184             if(ConfigVariable::getPromptMode() == 1 || ConfigVariable::getPromptMode() == 3)
185                         {
186                                 printExp(&file, *j, stPrompt, &iCurrentLine, str);
187                         }
188
189             //excecute script
190             ExecVisitor execMe;
191             (*j)->accept(execMe);
192
193             //to manage call without ()
194             if(execMe.result_get() != NULL && execMe.result_get()->getAsCallable())
195             {
196                 Callable *pCall = execMe.result_get()->getAsCallable();
197                 types::typed_list out;
198                 types::typed_list in;
199
200                 try
201                 {
202                     ExecVisitor execCall;
203                     Function::ReturnValue Ret = pCall->call(in, 1, out, &execCall);
204
205                     if(Ret == Callable::OK)
206                     {
207                         if(out.size() == 0)
208                         {
209                             execMe.result_set(NULL);
210                         }
211                         else if(out.size() == 1)
212                         {
213                             out[0]->DecreaseRef();
214                             execMe.result_set(out[0]);
215                         }
216                         else
217                         {
218                             for(int i = 0 ; i < static_cast<int>(out.size()) ; i++)
219                             {
220                                 out[i]->DecreaseRef();
221                                 execMe.result_set(i, out[i]);
222                             }
223                         }
224                     }
225                     else if(Ret == Callable::Error)
226                     {
227                         if(ConfigVariable::getLastErrorFunction() == L"")
228                         {
229                             ConfigVariable::setLastErrorFunction(pCall->getName());
230                         }
231
232                         if(pCall->isMacro() || pCall->isMacroFile())
233                         {
234                             wchar_t szError[bsiz];
235                             os_swprintf(szError, bsiz, _W("at line % 5d of function %ls called by :\n"), (*j)->location_get().first_line, pCall->getName().c_str());
236                             throw ScilabMessage(szError);
237                         }
238                         else
239                         {
240                             throw ScilabMessage();
241                         }
242                     }
243                 }
244                 catch(ScilabMessage sm)
245                 {
246                     wostringstream os;
247                     PrintVisitor printMe(os);
248                     (*j)->accept(printMe);
249                     os << std::endl << std::endl;
250                     if(ConfigVariable::getLastErrorFunction() == L"")
251                     {
252                         ConfigVariable::setLastErrorFunction(pCall->getName());
253                     }
254
255                     if(pCall->isMacro() || pCall->isMacroFile())
256                     {
257                         wstring szAllError;
258                         wchar_t szError[bsiz];
259                         os_swprintf(szError, bsiz, _W("at line % 5d of function %ls called by :\n"), sm.GetErrorLocation().first_line, pCall->getName().c_str());
260                         szAllError = szError + os.str();
261                         os_swprintf(szError, bsiz, _W("at line % 5d of exec file called by :\n"), (*j)->location_get().first_line);
262                         szAllError += szError;
263                         throw ScilabMessage(szAllError);
264                     }
265                     else
266                     {
267                         sm.SetErrorMessage(sm.GetErrorMessage() + os.str());
268                         throw sm;
269                     }
270                 }
271             }
272
273             //update ans variable.
274                         if(execMe.result_get() != NULL && execMe.result_get()->isDeletable())
275                         {
276                                 symbol::Context::getInstance()->put(symbol::Symbol(L"ans"), *execMe.result_get());
277                                 if( (*j)->is_verbose() && 
278                     bErrCatch == false)
279                                 {
280                                         std::wostringstream ostr;
281                                         ostr << L"ans = " << std::endl;
282                                         ostr << std::endl;
283                                         ostr << execMe.result_get()->toString(ConfigVariable::getFormat(), ConfigVariable::getConsoleWidth()) << std::endl;
284                                         YaspWriteW(ostr.str().c_str());
285                                 }
286                         }
287
288                         //if( !checkPrompt(iMode, EXEC_MODE_MUTE) &&
289    //             bErrCatch == false)
290                         //{
291                         //      YaspWriteW(L"\n");
292                         //}
293                 }
294         catch(ScilabMessage sm)
295         {
296             YaspErrorW(sm.GetErrorMessage().c_str());
297
298             CallExp* pCall = dynamic_cast<CallExp*>(*j);
299             if(pCall != NULL)
300             {//to print call expression only of it is a macro
301                 ExecVisitor execFunc;
302                 pCall->name_get().accept(execFunc);
303
304                 if(execFunc.result_get() != NULL &&
305                     (execFunc.result_get()->isMacro() || execFunc.result_get()->isMacroFile()))
306                 {
307                     wostringstream os;
308
309                     //add function failed
310                     PrintVisitor printMe(os);
311                     pCall->accept(printMe);
312                     os << std::endl;
313
314                     //add info on file failed
315                     wchar_t szError[bsiz];
316                     os_swprintf(szError, bsiz, _W("at line % 5d of exec file called by :\n"), (*j)->location_get().first_line);
317                     os << szError;
318
319                     if(ConfigVariable::getLastErrorFunction() == L"")
320                     {
321                         ConfigVariable::setLastErrorFunction(execFunc.result_get()->getAsCallable()->getName());
322                     }
323
324
325                     mclose(iID);
326                     //restore previous prompt mode
327                     ConfigVariable::setPromptMode(oldVal);
328                     throw ScilabMessage(os.str(), 0, (*j)->location_get());
329                 }
330             }
331
332             mclose(iID);
333             throw ScilabMessage((*j)->location_get());
334         }
335                 catch(ScilabError se)
336                 {
337             if(ConfigVariable::getLastErrorMessage() == L"")
338             {
339                 ConfigVariable::setLastErrorMessage(se.GetErrorMessage());
340                 ConfigVariable::setLastErrorNumber(se.GetErrorNumber());
341                 ConfigVariable::setLastErrorLine(se.GetErrorLocation().first_line);
342                 ConfigVariable::setLastErrorFunction(wstring(L""));
343             }
344
345             //store message
346             iErr = ConfigVariable::getLastErrorNumber();
347             if(bErrCatch == false)
348             {
349                 //write error
350                 YaspErrorW(se.GetErrorMessage().c_str());
351                 YaspErrorW(L"\n");
352
353                 //write positino
354                 wchar_t szError[bsiz];
355                 os_swprintf(szError, bsiz, _W("at line % 5d of exec file called by :\n"), (*j)->location_get().first_line);
356                 mclose(iID);
357                 //restore previous prompt mode
358                 ConfigVariable::setPromptMode(oldVal);
359                 throw ScilabMessage(szError, 1, (*j)->location_get());
360             }
361             break;
362                 }
363         }
364
365     //restore previous prompt mode
366     ConfigVariable::setPromptMode(oldVal);
367
368     if(bErrCatch)
369     {
370         out.push_back(new Double(iErr));
371         //to lock last error information
372         ConfigVariable::setLastErrorCall();
373     }
374
375         delete parser.getTree();
376     mclose(iID);
377         file.close();
378         return Function::OK;
379 }
380
381 bool checkPrompt(int _iMode, int _iCheck)
382 {
383         return ((_iMode & _iCheck) == _iCheck);
384 }
385
386 void printExp(std::ifstream* _pFile, Exp* _pExp, char* _pstPrompt, int* _piLine /* in/out */, char* _pstPreviousBuffer)
387 {
388         char strLastLine[1024];
389         //case 1, exp is on 1 line and take the entire line
390
391         //case 2, exp is multiline
392
393         //case 3, exp is part of a line.
394         //ex : 3 exp on the same line a = 1; b = 2; c = 3;
395
396         //case 4, exp is multiline but start and/or finish in the middle of a line
397         //ex :
398         //a = 10;for i = 1 : a
399         //      a
400         //end, b = 1;
401
402         Location loc = _pExp->location_get();
403
404         //positionning file curser at loc.first_line
405         {
406                 //strange case, current position is after the wanted position
407                 if(*_piLine > loc.first_line)
408                 {
409                         //reset line counter and restart reading at the start of the file.
410                         *_piLine = -1;
411                         _pFile->seekg( 0, ios_base::beg );
412                 }
413
414                 //bypass previous lines
415                 for(int i = *_piLine ; i < loc.first_line - 1; i++)
416                 {
417                         (*_piLine)++;
418                         _pFile->getline(_pstPreviousBuffer, 1024);
419                 }
420         }
421
422         if(loc.first_line == loc.last_line)
423         {//1 line
424                 strncpy(strLastLine, _pstPreviousBuffer + (loc.first_column - 1), loc.last_column - (loc.first_column - 1));
425                 strLastLine[loc.last_column - (loc.first_column - 1)] = 0;
426         int iExpLen = (int)strlen(strLastLine);
427         int iLineLen = (int)strlen(_pstPreviousBuffer) - (loc.first_column - 1);
428         if(iExpLen == iLineLen)
429         {
430             if(loc.first_column == 1)
431             {
432                 printLine(_pstPrompt, strLastLine, true);
433             }
434             else
435             {
436                 printLine("", strLastLine, true);
437             }
438         }
439         else
440         {
441             if(loc.first_column == 1)
442             {
443                 printLine(_pstPrompt, strLastLine, false);
444             }
445             else
446             {
447                 printLine("", strLastLine, false);
448             }
449         }
450         }
451         else
452         {//multiline
453                 printLine(_pstPrompt, _pstPreviousBuffer + (loc.first_column - 1), true);
454
455                 //print other full lines
456                 for(int i = loc.first_line; i < (loc.last_line - 1) ; i++)
457                 {
458                         (*_piLine)++;
459                         _pFile->getline(_pstPreviousBuffer, 1024);
460                         printLine(_pstPrompt, _pstPreviousBuffer, true);
461                 }
462
463                 //last line
464                 _pFile->getline(_pstPreviousBuffer, 1024);
465                 (*_piLine)++;
466
467                 strncpy(strLastLine, _pstPreviousBuffer, loc.last_column);
468                 strLastLine[loc.last_column] = 0;
469                 printLine(_pstPrompt, strLastLine, true);
470         }
471 }
472
473 void printLine(char* _stPrompt, char* _stLine, bool _bLF)
474 {
475     char* sz = (char*)MALLOC(sizeof(char) * (strlen(_stPrompt) + strlen(_stLine) + 2));
476     memset(sz, 0x00, sizeof(char) * (strlen(_stPrompt) + strlen(_stLine) + 2));
477     if(strlen(_stPrompt) != 0)
478     {
479         strcat(sz, _stPrompt);
480     }
481
482     strcat(sz, _stLine);
483     if(_bLF)
484     {
485         strcat(sz, "\n");
486     }
487
488     YaspWrite(sz);
489     FREE(sz);
490 }
491 /*--------------------------------------------------------------------------*/