clone varargout varaible before close scope
[scilab.git] / scilab / modules / types / src / cpp / 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 "localization.h"
21 #include "scilabWrite.hxx"
22 #include "scilabexception.hxx"
23 #include "configvariable.hxx"
24 #include "mutevisitor.hxx"
25
26 extern "C"
27 {
28 #include "Scierror.h"
29 #include "MALLOC.h"
30 #include "os_swprintf.h"
31 }
32
33 namespace types
34 {
35 Macro::Macro(const std::wstring& _stName, std::list<symbol::Symbol> &_inputArgs, std::list<symbol::Symbol> &_outputArgs, ast::SeqExp &_body, const wstring& _stModule):
36     Callable(), m_inputArgs(&_inputArgs), m_outputArgs(&_outputArgs), m_body(&_body),
37     m_ArgInSymb(symbol::Symbol(L"nargin")), m_ArgOutSymb(symbol::Symbol(L"nargout"))
38 {
39     setName(_stName);
40     setModule(_stModule);
41     bAutoAlloc = false;
42     m_pDblArgIn = new Double(1);
43     m_pDblArgIn->IncreaseRef(); //never delete
44     m_pDblArgOut = new Double(1);
45     m_pDblArgOut->IncreaseRef(); //never delete
46 }
47
48 Macro::~Macro()
49 {
50     delete m_body;
51 }
52
53 InternalType* Macro::clone()
54 {
55     IncreaseRef();
56     return this;
57 }
58
59 void Macro::whoAmI()
60 {
61     std::cout << "types::Macro";
62 }
63
64 InternalType::RealType Macro::getType(void)
65 {
66     return RealMacro;
67 }
68
69 ast::SeqExp* Macro::getBody(void)
70 {
71     return m_body;
72 }
73
74 bool Macro::toString(std::wostringstream& ostr)
75 {
76     ostr << L"FIXME : Implement Macro::toString" << std::endl;
77     return true;
78 }
79
80 Callable::ReturnValue Macro::call(typed_list &in, optional_list &opt, int _iRetCount, typed_list &out, ast::ConstVisitor* execFunc)
81 {
82     bool bVarargout = false;
83     ReturnValue RetVal = Callable::OK;
84     symbol::Context *pContext = symbol::Context::getInstance();
85
86     //open a new scope
87     pContext->scope_begin();
88
89     //add optional paramter in current scope
90     optional_list::const_iterator it;
91     for (it = opt.begin() ; it != opt.end() ; it++)
92     {
93         pContext->put(symbol::Symbol(it->first), *it->second);
94     }
95     //check excepted and input/output parameters numbers
96     // Scilab Macro can be called with less than prototyped arguments,
97     // but not more execpts with varargin
98
99     // varargin management
100     if (m_inputArgs->size() > 0 && m_inputArgs->back().name_get() == L"varargin")
101     {
102         int iVarPos = static_cast<int>(in.size());
103         if (iVarPos > static_cast<int>(m_inputArgs->size()) - 1)
104         {
105             iVarPos = static_cast<int>(m_inputArgs->size()) - 1;
106         }
107
108         //add all standard variable in function context but not varargin
109         std::list<symbol::Symbol>::const_iterator itName = m_inputArgs->begin();
110         typed_list::const_iterator itValue = in.begin();
111         while (iVarPos > 0)
112         {
113             pContext->put((*itName), **itValue);
114             iVarPos--;
115             itName++;
116             itValue++;
117         }
118
119         //create varargin only if previous variable are assigned
120         if (in.size() >= m_inputArgs->size() - 1)
121         {
122             //create and fill varargin
123             List* pL = new List();
124             while (itValue != in.end())
125             {
126                 pL->append(*itValue);
127                 itValue++;
128             }
129             pContext->put(symbol::Symbol(L"varargin"), *pL);
130         }
131     }
132     else if (in.size() > m_inputArgs->size())
133     {
134         wostringstream ostr;
135         ostr << _W("Wrong number of input arguments:") << std::endl << std::endl;
136         ostr << _W("Excepted: ") << m_inputArgs->size() << std::endl;
137
138         if (m_inputArgs->size() > 0)
139         {
140             ostr << _W("Arguments are:") << std::endl << std::endl;
141             ostr << " ";
142             for (std::list<symbol::Symbol>::iterator it =  m_inputArgs->begin() ; it != m_inputArgs->end() ; ++it)
143             {
144                 ostr << (*it).name_get() << L"    ";
145             }
146             ostr << std::endl;
147         }
148
149         char* pst = wide_string_to_UTF8(ostr.str().c_str());
150         Scierror(58, "%s", pst);
151         pContext->scope_end();
152         return Callable::Error;
153     }
154     else
155     {
156         //assign value to variable in the new context
157         std::list<symbol::Symbol>::const_iterator i;
158         typed_list::const_iterator j;
159
160         for (i = m_inputArgs->begin(), j = in.begin(); j != in.end (); ++j, ++i)
161         {
162             pContext->put((*i), **j);
163         }
164     }
165
166     // varargout management
167     //rules :
168     // varargout must be alone
169     // varargout is a list
170     // varargout can containt more items than caller need
171     // varargout must containt at leat caller needs
172     if (m_outputArgs->size() == 1 && m_outputArgs->back().name_get() == L"varargout")
173     {
174         bVarargout = true;
175         List* pL = new List();
176         pContext->put(symbol::Symbol(L"varargout"), *pL);
177     }
178
179     //common part with or without varargin/varargout
180
181     // Declare nargin & nargout in function context.
182     m_pDblArgIn->set(0, static_cast<double>(in.size()));
183     m_pDblArgOut->set(0, _iRetCount);
184     pContext->put(m_ArgInSymb, *m_pDblArgIn);
185     pContext->put(m_ArgOutSymb, *m_pDblArgOut);
186
187     //save current prompt mode
188     int oldVal = ConfigVariable::getPromptMode();
189     try
190     {
191         //m_body->mute();
192         //MuteVisitor mute;
193         //m_body->accept(mute);
194
195         ConfigVariable::setPromptMode(-1);
196         m_body->returnable_set();
197         m_body->accept(*execFunc);
198         //restore previous prompt mode
199         ConfigVariable::setPromptMode(oldVal);
200         if (m_body->is_return())
201         {
202             m_body->returnable_set();
203         }
204
205         //varargout management
206         if (bVarargout)
207         {
208             InternalType* pOut = pContext->get(symbol::Symbol(L"varargout"));
209             if (pOut == NULL)
210             {
211                 Scierror(999, _("Invalid index.\n"));
212                 return Callable::Error;
213             }
214
215             if (pOut->isList() == false || pOut->getAs<List>()->getSize() == 0)
216             {
217                 Scierror(999, _("Invalid index.\n"));
218                 return Callable::Error;
219             }
220
221             List* pVarOut = pOut->getAs<List>();
222             for (int i = 0 ; i < Min(pVarOut->getSize(), _iRetCount) ; i++)
223             {
224                 InternalType* pIT = pVarOut->get(i)->clone();
225                 if (pIT->isListUndefined())
226                 {
227                     Scierror(999, _("List element number %d is Undefined.\n"), i + 1);
228                     return Callable::Error;
229                 }
230
231                 out.push_back(pIT);
232             }
233         }
234         else
235         {
236             //normal output management
237             std::list<symbol::Symbol>::const_iterator i;
238             for (i = m_outputArgs->begin(); i != m_outputArgs->end() && _iRetCount; ++i, --_iRetCount)
239             {
240                 InternalType *pIT = pContext->get((*i));
241                 if (pIT != NULL)
242                 {
243                     out.push_back(pIT);
244                     pIT->IncreaseRef();
245                 }
246                 else
247                 {
248                     char* pst = wide_string_to_UTF8((*i).name_get().c_str());
249                     Scierror(999, _("Undefined variable %s.\n"), pst);
250                     FREE(pst);
251                     return Callable::Error;
252                 }
253             }
254         }
255     }
256     catch (ast::ScilabMessage se)
257     {
258         //restore previous prompt mode
259         ConfigVariable::setPromptMode(oldVal);
260         //close the current scope
261         pContext->scope_end();
262         for (size_t j = 0; j < out.size(); ++j)
263         {
264             out[j]->DecreaseRef();
265         }
266         throw se;
267     }
268     catch (ast::InternalAbort sa)
269     {
270         //restore previous prompt mode
271         ConfigVariable::setPromptMode(oldVal);
272         //close the current scope
273         pContext->scope_end();
274         for (size_t j = 0; j < out.size(); ++j)
275         {
276             out[j]->DecreaseRef();
277         }
278         throw sa;
279     }
280
281     //close the current scope
282     pContext->scope_end();
283
284     for (size_t j = 0; j < out.size(); ++j)
285     {
286         out[j]->DecreaseRef();
287     }
288     return RetVal;
289 }
290
291 std::list<symbol::Symbol>* Macro::inputs_get()
292 {
293     return m_inputArgs;
294 }
295
296 std::list<symbol::Symbol>* Macro::outputs_get()
297 {
298     return m_outputArgs;
299 }
300 }