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