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