f845a3d6c625ebc5abc2727a5325215c0b3fe62a
[scilab.git] / scilab / modules / ast / src / cpp / types / macro.cpp
1 /*
2 *  Scilab ( http://www.scilab.org/ ) - This file is part of Scilab
3 *  Copyright (C) 2009-2009 - DIGITEO - Bruno JOFRET
4 *
5  * Copyright (C) 2012 - 2016 - Scilab Enterprises
6  *
7  * This file is hereby licensed under the terms of the GNU GPL v2.0,
8  * pursuant to article 5.3.4 of the CeCILL v.2.1.
9  * This file was originally licensed under the terms of the CeCILL v2.1,
10  * and continues to be available under such terms.
11  * For more information, see the COPYING file which you should have received
12  * along with this program.
13 *
14 */
15
16 #include <memory>
17 #include <sstream>
18 #include <cstdio>
19
20 #include "macro.hxx"
21 #include "list.hxx"
22 #include "void.hxx"
23 #include "listinsert.hxx"
24 #include "string.hxx"
25 #include "context.hxx"
26 #include "symbol.hxx"
27 #include "scilabWrite.hxx"
28 #include "configvariable.hxx"
29 #include "serializervisitor.hxx"
30 #include "filemanager.hxx"
31
32 extern "C"
33 {
34 #include "localization.h"
35 #include "Scierror.h"
36 #include "sci_malloc.h"
37 #include "os_string.h"
38 }
39
40 namespace types
41 {
42 Macro::Macro(const std::wstring& _stName, std::list<symbol::Variable*>& _inputArgs, std::list<symbol::Variable*>& _outputArgs, ast::SeqExp &_body, const std::wstring& _stModule):
43     Callable(),
44     m_inputArgs(&_inputArgs), m_outputArgs(&_outputArgs), m_body(_body.clone()),
45     m_Nargin(symbol::Context::getInstance()->getOrCreate(symbol::Symbol(L"nargin"))),
46     m_Nargout(symbol::Context::getInstance()->getOrCreate(symbol::Symbol(L"nargout"))),
47     m_Varargin(symbol::Context::getInstance()->getOrCreate(symbol::Symbol(L"varargin"))),
48     m_Varargout(symbol::Context::getInstance()->getOrCreate(symbol::Symbol(L"varargout")))
49 {
50     setName(_stName);
51     setModule(_stModule);
52     bAutoAlloc = false;
53     m_pDblArgIn = new Double(1);
54     m_pDblArgIn->IncreaseRef(); //never delete
55     m_pDblArgOut = new Double(1);
56     m_pDblArgOut->IncreaseRef(); //never delete
57
58     m_body->setReturnable();
59     m_stPath = L"";
60 }
61
62 Macro::~Macro()
63 {
64     delete m_body;
65     m_pDblArgIn->DecreaseRef();
66     m_pDblArgIn->killMe();
67     m_pDblArgOut->DecreaseRef();
68     m_pDblArgOut->killMe();
69
70     if (m_inputArgs)
71     {
72         delete m_inputArgs;
73     }
74
75     if (m_outputArgs)
76     {
77         delete m_outputArgs;
78     }
79
80     for (const auto & sub : m_submacro)
81     {
82         sub.second->DecreaseRef();
83         sub.second->killMe();
84     }
85
86     m_submacro.clear();
87 }
88
89 void Macro::cleanCall(symbol::Context * pContext, int oldPromptMode)
90 {
91     //restore previous prompt mode
92     ConfigVariable::setPromptMode(oldPromptMode);
93     //close the current scope
94     pContext->scope_end();
95     ConfigVariable::macroFirstLine_end();
96 }
97
98 Macro* Macro::clone()
99 {
100     IncreaseRef();
101     return this;
102 }
103
104 void Macro::whoAmI()
105 {
106     std::cout << "types::Macro";
107 }
108
109 ast::SeqExp* Macro::getBody(void)
110 {
111     return m_body;
112 }
113
114 bool Macro::toString(std::wostringstream& ostr)
115 {
116     // get macro name
117     wchar_t* wcsVarName = NULL;
118     if (ostr.str() == SPACES_LIST)
119     {
120         wcsVarName = os_wcsdup(getName().c_str());
121     }
122     else
123     {
124         wcsVarName = os_wcsdup(ostr.str().c_str());
125     }
126
127     ostr.str(L"");
128     ostr << L"[";
129
130     // output arguments [a,b,c] = ....
131     if (m_outputArgs->empty() == false)
132     {
133         std::list<symbol::Variable*>::iterator OutArg = m_outputArgs->begin();
134         std::list<symbol::Variable*>::iterator OutArgfter = OutArg;
135         OutArgfter++;
136
137         for (; OutArgfter != m_outputArgs->end(); OutArgfter++)
138         {
139             ostr << (*OutArg)->getSymbol().getName();
140             ostr << ",";
141             OutArg++;
142         }
143
144         ostr << (*OutArg)->getSymbol().getName();
145     }
146
147     ostr << L"]";
148
149     // function name
150     ostr << L"=" << wcsVarName << L"(";
151
152     // input arguments function(a,b,c)
153     if (m_inputArgs->empty() == false)
154     {
155         std::list<symbol::Variable*>::iterator inArg = m_inputArgs->begin();
156         std::list<symbol::Variable*>::iterator inRagAfter = inArg;
157         inRagAfter++;
158
159         for (; inRagAfter != m_inputArgs->end(); inRagAfter++)
160         {
161             ostr << (*inArg)->getSymbol().getName();
162             ostr << ",";
163             inArg++;
164         }
165
166         ostr << (*inArg)->getSymbol().getName();
167     }
168
169     ostr << L")" << std::endl;
170
171     FREE(wcsVarName);
172     return true;
173 }
174
175 Callable::ReturnValue Macro::call(typed_list &in, optional_list &opt, int _iRetCount, typed_list &out)
176 {
177     int rhs = (int)in.size();
178     bool bVarargout = false;
179
180     int  iRetCount = std::max(0, _iRetCount);
181
182     ReturnValue RetVal = Callable::OK;
183     symbol::Context *pContext = symbol::Context::getInstance();
184
185     //open a new scope
186     pContext->scope_begin();
187     //store the line number where is stored this macro in file.
188     ConfigVariable::macroFirstLine_begin(getFirstLine());
189
190     //check excepted and input/output parameters numbers
191     // Scilab Macro can be called with less than prototyped arguments,
192     // but not more execpts with varargin
193
194     // varargin management
195     if (m_inputArgs->size() > 0 && m_inputArgs->back()->getSymbol().getName() == L"varargin")
196     {
197         List* pL = new List();;
198         int iVarPos = rhs;
199         if (iVarPos > static_cast<int>(m_inputArgs->size()) - 1)
200         {
201             iVarPos = static_cast<int>(m_inputArgs->size()) - 1;
202         }
203
204         //add variables in context or varargin list
205         std::list<symbol::Variable*>::iterator itName = m_inputArgs->begin();
206         for (int i = 0; i < rhs; ++i)
207         {
208             if (in[i]->isListInsert())
209             {
210                 //named
211                 std::wstring var(in[i]->getAs<ListInsert>()->getInsert()->getAs<String>()->get()[0]);
212                 if (i < iVarPos)
213                 {
214                     pContext->put(symbol::Symbol(var), opt[var]);
215                     ++itName;
216                 }
217                 else
218                 {
219                     //varargin
220                     pL->append(opt[var]);
221                 }
222             }
223             else
224             {
225                 //context
226                 if (i < iVarPos)
227                 {
228                     pContext->put(*itName, in[i]);
229                     ++itName;
230                 }
231                 else
232                 {
233                     //varargin
234                     pL->append(in[i]);
235                 }
236             }
237         }
238
239         //add varargin to macro scope
240         pContext->put(m_Varargin, pL);
241     }
242     else if (rhs > m_inputArgs->size())
243     {
244         if (m_inputArgs->size() == 0)
245         {
246             Scierror(999, _("Wrong number of input arguments: This function has no input argument.\n"));
247         }
248         else
249         {
250             Scierror(999, _("Wrong number of input arguments.\n"));
251         }
252
253         pContext->scope_end();
254         ConfigVariable::macroFirstLine_end();
255         return Callable::Error;
256     }
257     else
258     {
259         //assign value to variable in the new context
260         std::list<symbol::Variable*>::iterator i;
261         typed_list::const_iterator j;
262
263         for (i = m_inputArgs->begin(), j = in.begin(); j != in.end(); ++j, ++i)
264         {
265             if (*j && (*j)->isListInsert() == false)
266             {
267                 //prevent assignation of NULL value
268                 pContext->put(*i, *j);
269             }
270         }
271
272         //add optional parameters in current scope
273         for (const auto& it : opt)
274         {
275             if (it.second)
276             {
277                 pContext->put(symbol::Symbol(it.first), it.second);
278             }
279         }
280     }
281
282     // varargout management
283     //rules :
284     // varargout is a list
285     // varargout can containt more items than caller need
286     // varargout must containt at leat caller needs
287
288     if (m_outputArgs->size() >= 1 && m_outputArgs->back()->getSymbol().getName() == L"varargout")
289     {
290         bVarargout = true;
291         List* pL = new List();
292         pContext->put(m_Varargout, pL);
293     }
294
295     // iRetCount = 0 is granted to the macro (as argn(0))
296     // when there is no formal output argument
297     // or if varargout is the only formal output argument.
298     if ( m_outputArgs->size() - (bVarargout ? 1 : 0) >= 1 )
299     {
300         iRetCount = std::max(1, iRetCount);
301     }
302
303     //common part with or without varargin/varargout
304
305     // Declare nargin & nargout in function context.
306     if (m_pDblArgIn->getRef() > 1)
307     {
308         m_pDblArgIn->DecreaseRef();
309         m_pDblArgIn = m_pDblArgIn->clone();
310         m_pDblArgIn->IncreaseRef();
311     }
312     m_pDblArgIn->set(0, static_cast<double>(rhs));
313
314     if (m_pDblArgOut->getRef() > 1)
315     {
316         m_pDblArgOut->DecreaseRef();
317         m_pDblArgOut = m_pDblArgOut->clone();
318         m_pDblArgOut->IncreaseRef();
319     }
320
321     m_pDblArgOut->set(0, iRetCount);
322
323     pContext->put(m_Nargin, m_pDblArgIn);
324     pContext->put(m_Nargout, m_pDblArgOut);
325
326
327     //add sub macro in current context
328     for (const auto & sub : m_submacro)
329     {
330         pContext->put(sub.first, sub.second);
331     }
332
333     //save current prompt mode
334     int oldVal = ConfigVariable::getPromptMode();
335     std::wstring iExecFile = ConfigVariable::getExecutedFile();
336     std::unique_ptr<ast::ConstVisitor> exec (ConfigVariable::getDefaultVisitor());
337     try
338     {
339         ConfigVariable::setExecutedFile(m_stPath);
340         ConfigVariable::setPromptMode(-1);
341         m_body->accept(*exec);
342         //restore previous prompt mode
343         ConfigVariable::setPromptMode(oldVal);
344         ConfigVariable::setExecutedFile(iExecFile);
345     }
346     catch (const ast::InternalError& ie)
347     {
348         ConfigVariable::setExecutedFile(iExecFile);
349         cleanCall(pContext, oldVal);
350         throw ie;
351     }
352     catch (const ast::InternalAbort& ia)
353     {
354         ConfigVariable::setExecutedFile(iExecFile);
355         cleanCall(pContext, oldVal);
356         throw ia;
357     }
358
359     //nb excepted output without varargout
360     int iRet = std::min((int)m_outputArgs->size() - (bVarargout ? 1 : 0), std::max(1, iRetCount));
361
362     //normal output management
363     //for (std::list<symbol::Variable*>::iterator i = m_outputArgs->begin(); i != m_outputArgs->end() && _iRetCount; ++i, --_iRetCount)
364     for (auto arg : *m_outputArgs)
365     {
366         iRet--;
367         if (iRet < 0)
368         {
369             break;
370         }
371
372         InternalType * pIT = pContext->get(arg);
373         if (pIT)
374         {
375             out.push_back(pIT);
376             pIT->IncreaseRef();
377         }
378         else
379         {
380             const int size = (const int)out.size();
381             for (int j = 0; j < size; ++j)
382             {
383                 out[j]->DecreaseRef();
384                 out[j]->killMe();
385             }
386             out.clear();
387             cleanCall(pContext, oldVal);
388
389             char* pstArgName = wide_string_to_UTF8(arg->getSymbol().getName().c_str());
390             char* pstMacroName = wide_string_to_UTF8(getName().c_str());
391             Scierror(999, _("Undefined variable '%s' in function '%s'.\n"), pstArgName, pstMacroName);
392             FREE(pstArgName);
393             FREE(pstMacroName);
394             return Callable::Error;
395         }
396     }
397
398     //varargout management
399     if (bVarargout)
400     {
401         InternalType* pOut = pContext->get(m_Varargout);
402         if (pOut == NULL)
403         {
404             cleanCall(pContext, oldVal);
405             Scierror(999, _("Invalid index.\n"));
406             return Callable::Error;
407         }
408
409         if (pOut->isList() == false)
410         {
411             cleanCall(pContext, oldVal);
412             char* pstMacroName = wide_string_to_UTF8(getName().c_str());
413             Scierror(999, _("%s: Wrong type for %s: A list expected.\n"), pstMacroName, "Varargout");
414             FREE(pstMacroName);
415             return Callable::Error;
416         }
417
418         List* pVarOut = pOut->getAs<List>();
419         const int size = std::min(pVarOut->getSize(), std::max(1,iRetCount) - (int)out.size());
420         for (int i = 0 ; i < size ; ++i)
421         {
422             InternalType* pIT = pVarOut->get(i);
423             if (pIT->isVoid())
424             {
425                 for (int j = 0; j < i; ++j)
426                 {
427                     out[j]->DecreaseRef();
428                     out[j]->killMe();
429                 }
430                 out.clear();
431                 cleanCall(pContext, oldVal);
432
433                 Scierror(999, _("List element number %d is Undefined.\n"), i + 1);
434                 return Callable::Error;
435             }
436
437             pIT->IncreaseRef();
438             out.push_back(pIT);
439         }
440     }
441
442     //close the current scope
443     cleanCall(pContext, oldVal);
444
445     for (typed_list::iterator i = out.begin(), end = out.end(); i != end; ++i)
446     {
447         (*i)->DecreaseRef();
448     }
449
450     return RetVal;
451 }
452
453 std::list<symbol::Variable*>* Macro::getInputs()
454 {
455     return m_inputArgs;
456 }
457
458 std::list<symbol::Variable*>* Macro::getOutputs()
459 {
460     return m_outputArgs;
461 }
462
463 int Macro::getNbInputArgument(void)
464 {
465     return (int)m_inputArgs->size();
466 }
467
468 int Macro::getNbOutputArgument(void)
469 {
470     if (m_outputArgs->size() >= 1 && m_outputArgs->back()->getSymbol().getName() == L"varargout")
471     {
472         return -1;
473     }
474
475     return (int)m_outputArgs->size();
476 }
477
478 bool Macro::getMemory(long long* _piSize, long long* _piSizePlusType)
479 {
480     ast::SerializeVisitor serialMacro(m_body);
481     unsigned char* macroSerial = serialMacro.serialize(false, false);
482     unsigned int macroSize = *((unsigned int*)macroSerial);
483
484     *_piSize = macroSize;
485     *_piSizePlusType = *_piSize + sizeof(Macro);
486     return true;
487 }
488
489 bool Macro::operator==(const InternalType& it)
490 {
491     if (const_cast<InternalType &>(it).isMacro() == false)
492     {
493         return false;
494     }
495
496     std::list<symbol::Variable*>* pInput = NULL;
497     std::list<symbol::Variable*>* pOutput = NULL;
498     types::Macro* pRight = const_cast<InternalType &>(it).getAs<types::Macro>();
499
500     //check inputs
501     pInput = pRight->getInputs();
502     if (pInput->size() != m_inputArgs->size())
503     {
504         return false;
505     }
506
507     std::list<symbol::Variable*>::iterator itOld = pInput->begin();
508     std::list<symbol::Variable*>::iterator itEndOld = pInput->end();
509     std::list<symbol::Variable*>::iterator itMacro = m_inputArgs->begin();
510
511     for (; itOld != itEndOld ; ++itOld, ++itMacro)
512     {
513         if ((*itOld)->getSymbol() != (*itMacro)->getSymbol())
514         {
515             return false;
516         }
517     }
518
519     //check outputs
520     pOutput = pRight->getOutputs();
521     if (pOutput->size() != m_outputArgs->size())
522     {
523         return false;
524     }
525
526     itOld = pOutput->begin();
527     itEndOld = pOutput->end();
528     itMacro = m_outputArgs->begin();
529
530     for (; itOld != itEndOld ; ++itOld, ++itMacro)
531     {
532         if ((*itOld)->getSymbol() != (*itMacro)->getSymbol())
533         {
534             return false;
535         }
536     }
537
538     ast::Exp* pExp = pRight->getBody();
539     ast::SerializeVisitor serialOld(pExp);
540     unsigned char* oldSerial = serialOld.serialize(false, false);
541     ast::SerializeVisitor serialMacro(m_body);
542     unsigned char* macroSerial = serialMacro.serialize(false, false);
543
544     //check buffer length
545     unsigned int oldSize = *((unsigned int*)oldSerial);
546     unsigned int macroSize = *((unsigned int*)macroSerial);
547     if (oldSize != macroSize)
548     {
549         return false;
550     }
551
552     bool ret = (memcmp(oldSerial, macroSerial, oldSize) == 0);
553     return ret;
554 }
555
556 void Macro::add_submacro(const symbol::Symbol& s, Macro* macro)
557 {
558     macro->IncreaseRef();
559     symbol::Context* ctx = symbol::Context::getInstance();
560     symbol::Variable* var = ctx->getOrCreate(s);
561     m_submacro[var] = macro;
562 }
563 }