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