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