0b2b4aa0848c0c2b0b546bfe7c185412e97509ae
[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 "AnalysisVisitor.hxx"
21 #include "macro.hxx"
22 #include "list.hxx"
23 #include "context.hxx"
24 #include "symbol.hxx"
25 #include "scilabWrite.hxx"
26 #include "configvariable.hxx"
27 #include "serializervisitor.hxx"
28 #include "FBlockListener.hxx"
29
30 extern "C"
31 {
32 #include "localization.h"
33 #include "Scierror.h"
34 #include "sciprint.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     bool bVarargout = false;
177     ReturnValue RetVal = Callable::OK;
178     symbol::Context *pContext = symbol::Context::getInstance();
179
180     //open a new scope
181     pContext->scope_begin();
182     //store the line number where is stored this macro in file.
183     ConfigVariable::macroFirstLine_begin(getFirstLine());
184
185     //check excepted and input/output parameters numbers
186     // Scilab Macro can be called with less than prototyped arguments,
187     // but not more execpts with varargin
188
189     // varargin management
190     if (m_inputArgs->size() > 0 && m_inputArgs->back()->getSymbol().getName() == L"varargin")
191     {
192         int iVarPos = static_cast<int>(in.size());
193         if (iVarPos > static_cast<int>(m_inputArgs->size()) - 1)
194         {
195             iVarPos = static_cast<int>(m_inputArgs->size()) - 1;
196         }
197
198         //add all standard variable in function context but not varargin
199         std::list<symbol::Variable*>::iterator itName = m_inputArgs->begin();
200         typed_list::const_iterator itValue = in.begin();
201         while (iVarPos > 0)
202         {
203             pContext->put(*itName, *itValue);
204             iVarPos--;
205             ++itName;
206             ++itValue;
207         }
208
209         //create varargin only if previous variable are assigned
210         optional_list::const_iterator it = opt.begin();
211         if (in.size() >= m_inputArgs->size() - 1)
212         {
213             //create and fill varargin
214             List* pL = new List();
215             while (itValue != in.end())
216             {
217                 if (*itValue != NULL)
218                 {
219                     pL->append(*itValue);
220                 }
221                 else
222                 {
223                     pL->append(it->second);
224                     it++;
225                 }
226
227                 itValue++;
228             }
229             pContext->put(m_Varargin, pL);
230         }
231     }
232     else if (in.size() > m_inputArgs->size())
233     {
234         if (m_inputArgs->size() == 0)
235         {
236             Scierror(999, _("Wrong number of input arguments: This function has no input argument.\n"));
237         }
238         else
239         {
240             Scierror(999, _("Wrong number of input arguments.\n"));
241         }
242
243         pContext->scope_end();
244         ConfigVariable::macroFirstLine_end();
245         return Callable::Error;
246     }
247     else
248     {
249         //assign value to variable in the new context
250         std::list<symbol::Variable*>::iterator i;
251         typed_list::const_iterator j;
252
253         for (i = m_inputArgs->begin(), j = in.begin(); j != in.end(); ++j, ++i)
254         {
255             if (*j)
256             {
257                 //prevent assignation of NULL value
258                 pContext->put(*i, *j);
259             }
260         }
261
262         //add optional paramter in current scope
263         optional_list::const_iterator it;
264         for (it = opt.begin() ; it != opt.end() ; it++)
265         {
266             pContext->put(symbol::Symbol(it->first), it->second);
267         }
268
269
270     }
271
272     // varargout management
273     //rules :
274     // varargout must be alone
275     // varargout is a list
276     // varargout can containt more items than caller need
277     // varargout must containt at leat caller needs
278     if (m_outputArgs->size() == 1 && m_outputArgs->back()->getSymbol().getName() == L"varargout")
279     {
280         bVarargout = true;
281         List* pL = new List();
282         pContext->put(m_Varargout, pL);
283     }
284
285     //common part with or without varargin/varargout
286
287     // Declare nargin & nargout in function context.
288     if (m_pDblArgIn->getRef() > 1)
289     {
290         m_pDblArgIn->DecreaseRef();
291         m_pDblArgIn = (Double*)m_pDblArgIn->clone();
292         m_pDblArgIn->IncreaseRef();
293     }
294     m_pDblArgIn->set(0, static_cast<double>(in.size()));
295
296     if (m_pDblArgOut->getRef() > 1)
297     {
298         m_pDblArgOut->DecreaseRef();
299         m_pDblArgOut = (Double*)m_pDblArgOut->clone();
300         m_pDblArgOut->IncreaseRef();
301     }
302     m_pDblArgOut->set(0, _iRetCount);
303
304     pContext->put(m_Nargin, m_pDblArgIn);
305     pContext->put(m_Nargout, m_pDblArgOut);
306
307
308     //add sub macro in current context
309     for (const auto & sub : m_submacro)
310     {
311         pContext->put(sub.first, sub.second);
312     }
313
314     //save current prompt mode
315     int oldVal = ConfigVariable::getPromptMode();
316     std::unique_ptr<ast::ConstVisitor> exec (ConfigVariable::getDefaultVisitor());
317     try
318     {
319         ConfigVariable::setPromptMode(-1);
320
321         //use version from analyze
322         if (m_functionId != -1) 
323         {
324             std::wcerr << "macro functionId: " << m_functionId << std::endl;
325             ast::Exp* exp = FBlockListener::getExp(m_functionId);
326             if (exp)
327             {
328                 ast::PrettyPrintVisitor dv(std::wcerr, true, true);
329                 //m_body->accept(dv);
330                 exp->accept(dv);
331
332                 //std::wcerr << "exec new exp" << std::endl;
333                 exp->accept(*exec);
334             }
335             else
336             {
337                 m_body->accept(*exec);
338             }
339         }
340         else 
341         {
342             m_body->accept(*exec);
343         }
344
345         //restore previous prompt mode
346         ConfigVariable::setPromptMode(oldVal);
347     }
348     catch (const ast::InternalError& ie)
349     {
350         cleanCall(pContext, oldVal);
351         throw ie;
352     }
353     catch (const ast::InternalAbort& ia)
354     {
355         cleanCall(pContext, oldVal);
356         throw ia;
357     }
358     // Normally, seqexp throws only SM so no need to catch SErr
359
360     //varargout management
361     if (bVarargout)
362     {
363         InternalType* pOut = pContext->get(m_Varargout);
364         if (pOut == NULL)
365         {
366             cleanCall(pContext, oldVal);
367             Scierror(999, _("Invalid index.\n"));
368             return Callable::Error;
369         }
370
371         if (pOut->isList() == false || pOut->getAs<List>()->getSize() == 0)
372         {
373             cleanCall(pContext, oldVal);
374             Scierror(999, _("Invalid index.\n"));
375             return Callable::Error;
376         }
377
378         List* pVarOut = pOut->getAs<List>();
379         const int size = std::min(pVarOut->getSize(), _iRetCount);
380         for (int i = 0 ; i < size ; ++i)
381         {
382             InternalType* pIT = pVarOut->get(i);
383             if (pIT->isListUndefined())
384             {
385                 for (int j = 0; j < i; ++j)
386                 {
387                     out[j]->DecreaseRef();
388                     out[j]->killMe();
389                 }
390                 out.clear();
391                 cleanCall(pContext, oldVal);
392
393                 Scierror(999, _("List element number %d is Undefined.\n"), i + 1);
394                 return Callable::Error;
395             }
396
397             pIT->IncreaseRef();
398             out.push_back(pIT);
399         }
400     }
401     else
402     {
403         //normal output management
404         for (std::list<symbol::Variable*>::iterator i = m_outputArgs->begin(); i != m_outputArgs->end() && _iRetCount; ++i, --_iRetCount)
405         {
406             InternalType * pIT = pContext->get(*i);
407             if (pIT)
408             {
409                 out.push_back(pIT);
410                 pIT->IncreaseRef();
411             }
412             else
413             {
414                 const int size = (const int)out.size();
415                 for (int j = 0; j < size; ++j)
416                 {
417                     out[j]->DecreaseRef();
418                     out[j]->killMe();
419                 }
420                 out.clear();
421                 cleanCall(pContext, oldVal);
422
423                 char* pstArgName = wide_string_to_UTF8((*i)->getSymbol().getName().c_str());
424                 char* pstMacroName = wide_string_to_UTF8(getName().c_str());
425                 Scierror(999, _("Undefined variable '%s' in function '%s'.\n"), pstArgName, pstMacroName);
426                 FREE(pstArgName);
427                 FREE(pstMacroName);
428                 return Callable::Error;
429             }
430         }
431     }
432
433     //close the current scope
434     cleanCall(pContext, oldVal);
435
436     for (typed_list::iterator i = out.begin(), end = out.end(); i != end; ++i)
437     {
438         (*i)->DecreaseRef();
439     }
440
441     return RetVal;
442 }
443
444 std::list<symbol::Variable*>* Macro::getInputs()
445 {
446     return m_inputArgs;
447 }
448
449 std::list<symbol::Variable*>* Macro::getOutputs()
450 {
451     return m_outputArgs;
452 }
453
454 int Macro::getNbInputArgument(void)
455 {
456     return (int)m_inputArgs->size();
457 }
458
459 int Macro::getNbOutputArgument(void)
460 {
461     if (m_outputArgs->size() == 1 && m_outputArgs->back()->getSymbol().getName() == L"varargout")
462     {
463         return -1;
464     }
465
466     return (int)m_outputArgs->size();
467 }
468
469 bool Macro::operator==(const InternalType& it)
470 {
471     if (const_cast<InternalType &>(it).isMacro() == false)
472     {
473         return false;
474     }
475
476     std::list<symbol::Variable*>* pInput = NULL;
477     std::list<symbol::Variable*>* pOutput = NULL;
478     types::Macro* pRight = const_cast<InternalType &>(it).getAs<types::Macro>();
479
480     //check inputs
481     pInput = pRight->getInputs();
482     if (pInput->size() != m_inputArgs->size())
483     {
484         return false;
485     }
486
487     std::list<symbol::Variable*>::iterator itOld = pInput->begin();
488     std::list<symbol::Variable*>::iterator itEndOld = pInput->end();
489     std::list<symbol::Variable*>::iterator itMacro = m_inputArgs->begin();
490
491     for (; itOld != itEndOld ; ++itOld, ++itMacro)
492     {
493         if ((*itOld)->getSymbol() != (*itMacro)->getSymbol())
494         {
495             return false;
496         }
497     }
498
499     //check outputs
500     pOutput = pRight->getOutputs();
501     if (pOutput->size() != m_outputArgs->size())
502     {
503         return false;
504     }
505
506     itOld = pOutput->begin();
507     itEndOld = pOutput->end();
508     itMacro = m_outputArgs->begin();
509
510     for (; itOld != itEndOld ; ++itOld, ++itMacro)
511     {
512         if ((*itOld)->getSymbol() != (*itMacro)->getSymbol())
513         {
514             return false;
515         }
516     }
517
518     ast::Exp* pExp = pRight->getBody();
519     ast::SerializeVisitor serialOld(pExp);
520     unsigned char* oldSerial = serialOld.serialize(false, false);
521     ast::SerializeVisitor serialMacro(m_body);
522     unsigned char* macroSerial = serialMacro.serialize(false, false);
523
524     //check buffer length
525     unsigned int oldSize = *((unsigned int*)oldSerial);
526     unsigned int macroSize = *((unsigned int*)macroSerial);
527     if (oldSize != macroSize)
528     {
529         free(oldSerial);
530         free(macroSerial);
531         return false;
532     }
533
534     bool ret = (memcmp(oldSerial, macroSerial, oldSize) == 0);
535
536     free(oldSerial);
537     free(macroSerial);
538
539     return ret;
540 }
541
542 void Macro::add_submacro(const symbol::Symbol& s, Macro* macro)
543 {
544     macro->IncreaseRef();
545     symbol::Context* ctx = symbol::Context::getInstance();
546     symbol::Variable* var = ctx->getOrCreate(s);
547     m_submacro[var] = macro;
548 }
549 }