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