add checkNamedArguments function to get unexpected named arguments.
[scilab.git] / scilab / modules / ast / src / cpp / symbol / context.cpp
1 /*
2 *  Scilab ( http://www.scilab.org/ ) - This file is part of Scilab
3 *  Copyright (C) 2008-2008 - DIGITEO - Antoine ELIAS
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 #include <iomanip>
16 #include <cctype>
17
18 #include "context.hxx"
19 #include "internal.hxx"
20 #include "function.hxx"
21 #include "macro.hxx"
22 #include "macrofile.hxx"
23 #include "variables.hxx"
24 #include "configvariable.hxx"
25
26 extern "C"
27 {
28 #include "getmemory.h"
29 #include "os_string.h"
30 }
31
32 namespace symbol
33 {
34 Context* Context::me = nullptr;
35
36 Context::Context()
37 {
38     m_iLevel = SCOPE_ALL;
39     globals = new std::list<Symbol>();
40     console = nullptr;
41 }
42
43 Context::~Context()
44 {
45     while (!varStack.empty())
46     {
47         VarList * pVL = varStack.top();
48         pVL->clear();
49         delete pVL;
50         varStack.pop();
51     }
52     if (globals)
53     {
54         delete globals;
55     }
56 }
57
58 Context* Context::getInstance(void)
59 {
60     if (me == nullptr)
61     {
62         me = new Context();
63     }
64     return me;
65 }
66
67 void Context::destroyInstance(void)
68 {
69     if (me)
70     {
71         delete me;
72         me = nullptr;
73     }
74 }
75
76 void Context::scope_begin()
77 {
78     m_iLevel++;
79     if (m_iLevel == SCOPE_CONSOLE)
80     {
81         console = new VarList();
82         varStack.push(console);
83     }
84     else
85     {
86         varStack.push(new VarList());
87     }
88 }
89
90 void Context::clearAll()
91 {
92     libraries.clearAll();
93     variables.clearAll();
94 }
95
96 void Context::scope_end()
97 {
98     //clear varList of current scope
99     if (varStack.empty() == false)
100     {
101         clearCurrentScope(true);
102     }
103
104     m_iLevel--;
105
106     if (m_iLevel < SCOPE_CONSOLE)
107     {
108         console = nullptr;
109     }
110 }
111
112 bool Context::clearCurrentScope(bool _bClose)
113 {
114     if (varStack.empty())
115     {
116         return true;
117     }
118
119     VarList* varList = varStack.top();
120     std::list<Symbol> toremove;
121     for (auto var : *varList)
122     {
123         if (var.second->empty() == false)
124         {
125             ScopedVariable* pSV = var.second->top();
126             if (pSV->m_iLevel == m_iLevel && (_bClose || pSV->protect == false))
127             {
128                 types::InternalType * pIT = pSV->m_pIT;
129                 if (pIT->isLibrary())
130                 {
131                     // at scilab exit, pIT have a ref == 2 because
132                     // it is contains in libraries and variables.
133                     // call remove will set ref to 1 then the next
134                     // pIT->DecreaseRef(); pIT->killMe(); will delete it.
135                     libraries.remove(var.first, m_iLevel);
136                 }
137
138                 pIT->DecreaseRef();
139                 pIT->killMe();
140
141                 var.second->pop();
142                 delete pSV;
143                 toremove.push_back(var.first);
144             }
145         }
146     }
147
148     if (_bClose)
149     {
150         delete varList;
151         varStack.pop();
152     }
153     else
154     {
155         for (auto var : toremove)
156         {
157             varList->erase(var);
158         }
159     }
160     return true;
161 }
162
163 Variable* Context::getOrCreate(const Symbol& _key)
164 {
165     return variables.getOrCreate(_key);
166 }
167
168 int Context::getLevel(const Symbol & _key) const
169 {
170     VarList::iterator it = varStack.top()->find(_key);
171     if (it != varStack.top()->end())
172     {
173         if (!it->second->empty())
174         {
175             return it->second->top()->m_iLevel;
176         }
177     }
178     else
179     {
180         const int ret = variables.getLevel(_key);
181         if (ret == SCOPE_ALL)
182         {
183             return libraries.getLevel(_key);
184         }
185         else
186         {
187             return ret;
188         }
189     }
190
191     return SCOPE_ALL;
192 }
193
194 types::InternalType* Context::get(const Symbol& _key)
195 {
196     return get(_key, SCOPE_ALL);
197 }
198
199 types::InternalType* Context::get(const Variable* _var)
200 {
201     types::InternalType* pIT = _var->get();
202     if (pIT == NULL)
203     {
204         //look in libraries
205         pIT = libraries.get(_var->getSymbol(), SCOPE_ALL);
206         if (pIT && pIT->isLibrary() == false)
207         {
208             put((Variable*)_var, pIT);
209         }
210     }
211
212     return pIT;
213 }
214
215 types::InternalType* Context::get(const Symbol& _key, int _iLevel)
216 {
217     types::InternalType* pIT = NULL;
218     if (_iLevel == m_iLevel || _iLevel == SCOPE_ALL)
219     {
220         //look for in current VarList
221         VarList::iterator it = varStack.top()->find(_key);
222         if (it != varStack.top()->end())
223         {
224             if (it->second->empty() == false)
225             {
226                 pIT = it->second->get();
227             }
228         }
229     }
230
231     if (pIT == NULL)
232     {
233         pIT = variables.get(_key, _iLevel);
234         if (pIT == NULL)
235         {
236             //find in libraries
237             pIT = libraries.get(_key, _iLevel);
238         }
239     }
240     return pIT;
241 }
242
243 types::InternalType* Context::getCurrentLevel(const Symbol& _key)
244 {
245     return variables.get(_key, m_iLevel);
246 }
247
248 types::InternalType* Context::getCurrentLevel(Variable* _var)
249 {
250     return variables.get(_var, m_iLevel);
251 }
252
253 types::InternalType* Context::getAllButCurrentLevel(const Symbol& _key)
254 {
255     return variables.getAllButCurrentLevel(_key, m_iLevel);
256 }
257
258 types::InternalType* Context::getAtLevel(const Symbol& _key, int level)
259 {
260     return variables.getAllButCurrentLevel(_key, level == SCOPE_ALL ? m_iLevel : level + 1);
261 }
262
263 types::InternalType* Context::getFunction(const Symbol& _key)
264 {
265     return get(_key);
266 }
267
268 int Context::getFunctionList(std::list<Symbol>& lst, const std::wstring& _stModuleName)
269 {
270     return variables.getFunctionList(lst, _stModuleName, m_iLevel);
271 }
272
273 int Context::getFunctionList(std::list<types::Callable *> & lst, std::wstring _stModuleName)
274 {
275     return variables.getFunctionList(lst, _stModuleName, m_iLevel);
276 }
277
278 int Context::getConsoleVarsName(std::list<std::wstring>& lst)
279 {
280     if (console)
281     {
282         for (const auto & var : *console)
283         {
284             lst.push_back(var.first.getName());
285         }
286     }
287
288     return static_cast<int>(lst.size());
289 }
290
291 int Context::getVarsName(std::list<std::wstring>& lst)
292 {
293     variables.getVarsName(lst);
294     libraries.getVarsName(lst);
295
296     return static_cast<int>(lst.size());
297 }
298
299 int Context::getMacrosName(std::list<std::wstring>& lst)
300 {
301     variables.getMacrosName(lst);
302     libraries.getMacrosName(lst);
303     return static_cast<int>(lst.size());
304 }
305
306 int Context::getFunctionsName(std::list<std::wstring>& lst)
307 {
308     return variables.getFunctionsName(lst);
309 }
310
311 int Context::getVarsNameForWho(std::list<std::wstring>& lst, bool bSorted)
312 {
313     int iZero = 0;
314     variables.getVarsNameForWho(lst, &iZero, bSorted);
315     return static_cast<int>(lst.size());
316 }
317
318 int Context::getGlobalNameForWho(std::list<std::wstring>& lst, bool bSorted)
319 {
320     int iZero = 0;
321     variables.getGlobalNameForWho(lst, &iZero, bSorted);
322     return static_cast<int>(lst.size());
323 }
324
325 int Context::getWhereIs(std::list<std::wstring>& lst, const std::wstring& _str)
326 {
327     return libraries.whereis(lst, Symbol(_str));
328 }
329
330 int Context::getLibrariesList(std::list<std::wstring>& lst)
331 {
332     return libraries.librarieslist(lst);
333 }
334
335 bool Context::put(Variable* _var, types::InternalType* _pIT)
336 {
337     if (_pIT->isLibrary())
338     {
339         Library* lib = libraries.getOrCreate(_var->getSymbol());
340         lib->put((types::Library*)_pIT, m_iLevel);
341     }
342
343     if (_var->put(_pIT, m_iLevel) == false)
344     {
345         return false;
346     }
347
348     if (varStack.empty() == false)
349     {
350         (*varStack.top())[_var->getSymbol()] = _var;
351     }
352
353     return true;
354 }
355
356 bool Context::put(const Symbol& _key, types::InternalType* _pIT)
357 {
358     Variable* var = variables.getOrCreate(_key);
359
360     if (var->empty())
361     {
362         //box is empty, check if a macro from a library have this name.
363         //in this case, add it to context before set new value.
364         types::InternalType* pIT = get(_key);
365         if (pIT && (pIT->isMacroFile() || pIT->isMacro()))
366         {
367             put(var, pIT);
368             return put(var, _pIT);
369         }
370     }
371
372     return put(var, _pIT);
373 }
374
375 bool Context::remove(const Symbol& _key)
376 {
377     bool ret = variables.remove(_key, m_iLevel);
378
379     if (ret)
380     {
381         varStack.top()->erase(_key);
382     }
383
384     ret = ret | libraries.remove(_key, m_iLevel);
385     return ret;
386 }
387
388 bool Context::removeAll()
389 {
390     return clearCurrentScope(false);
391 }
392
393 bool Context::putInPreviousScope(Variable* _var, types::InternalType* _pIT)
394 {
395     //add variable in previous scope
396     if (variables.putInPreviousScope(_var, _pIT, m_iLevel - 1) == false)
397     {
398         return false;
399     }
400
401     //add variable in stack of using variables
402     if (varStack.empty() == false)
403     {
404         VarList * list = varStack.top();
405         varStack.pop();
406         if (varStack.empty() == false)
407         {
408             (*varStack.top())[_var->getSymbol()] = _var;
409         }
410         varStack.push(list);
411     }
412
413     if (_pIT->isLibrary())
414     {
415         libraries.putInPreviousScope(_var->getSymbol(), _pIT->getAs<types::Library>(), m_iLevel - 1);
416     }
417     return true;
418 }
419
420 bool Context::addFunction(types::Function *_info)
421 {
422     Variable* var = variables.getOrCreate(Symbol(_info->getName()));
423     variables.putInPreviousScope(var, _info, SCOPE_GATEWAY);
424     return true;
425 }
426
427 bool Context::addMacro(types::Macro *_info)
428 {
429     return put(Symbol(_info->getName()), _info);
430 }
431
432 bool Context::addMacroFile(types::MacroFile *_info)
433 {
434     return put(Symbol(_info->getName()), _info);
435 }
436
437 bool Context::isGlobalVisible(const Symbol& _key)
438 {
439     return variables.isGlobalVisible(_key, m_iLevel);
440 }
441
442 /*return global variable existance status*/
443 bool Context::isGlobal(const Symbol& _key)
444 {
445     return variables.isGlobal(_key, m_iLevel);
446 }
447
448 types::InternalType* Context::getGlobalValue(const Symbol& _key)
449 {
450     return variables.getGlobalValue(_key);
451 }
452
453 void Context::setGlobalVisible(const Symbol& _key, bool bVisible)
454 {
455     variables.setGlobalVisible(_key, bVisible, m_iLevel);
456 }
457
458 void Context::setGlobal(const Symbol& _key)
459 {
460     variables.setGlobal(_key);
461     globals->push_back(_key);
462 }
463
464 bool Context::removeGlobal(const Symbol& _key)
465 {
466     // skip permanant variables : %modalWarning, %toolboxes, %toolboxes_dir
467     if (_key.getName() == L"%modalWarning"  ||
468             _key.getName() == L"%toolboxes"     ||
469             _key.getName() == L"%toolboxes_dir")
470     {
471         return false;
472     }
473
474     variables.removeGlobal(_key, m_iLevel);
475     globals->remove(_key);
476     return true;
477 }
478
479 void Context::removeGlobalAll()
480 {
481     std::list<Symbol>::iterator it = globals->begin();
482
483     while (it != globals->end())
484     {
485         if (removeGlobal(*it) == false)
486         {
487             globals->remove(*it);
488         }
489
490         it = globals->begin();
491     }
492
493     globals->clear();
494
495     globals->emplace_back(L"%modalWarning");
496     globals->emplace_back(L"%toolboxes");
497     globals->emplace_back(L"%toolboxes_dir");
498 }
499
500 void Context::print(std::wostream& ostr, bool sorted) const
501 {
502     std::list<std::wstring> lstVar;
503     std::list<std::wstring> lstGlobal;
504     int iVarLenMax = 10; // initialise to the minimal value of padding
505     int iGlobalLenMax = 10; // initialise to the minimal value of padding
506     variables.getVarsNameForWho(lstVar, &iVarLenMax);
507     variables.getGlobalNameForWho(lstGlobal, &iGlobalLenMax);
508     libraries.getVarsNameForWho(&lstVar, &iVarLenMax);
509
510     if (sorted)
511     {
512         lstVar.sort();
513         lstGlobal.sort();
514     }
515
516 #define strSize 64
517     wchar_t wcsVarElem[strSize];
518     wchar_t wcsVarVariable[strSize];
519     wchar_t wcsGlobalElem[strSize];
520     wchar_t wcsGlobalVariable[strSize];
521
522     int iMemTotal = 0;
523     int iMemUsed = 0;
524     int nbMaxVar = 0;
525
526 #ifdef _MSC_VER
527     MEMORYSTATUSEX statex;
528     statex.dwLength = sizeof(statex);
529     GlobalMemoryStatusEx(&statex);
530     iMemTotal = (int)(statex.ullTotalPhys / (1024 * 1024));
531 #else
532     iMemTotal = getmemorysize();
533 #endif
534
535     ostr << _W("Your variables are:") << std::endl << std::endl;
536     std::list<std::wstring>::const_iterator it = lstVar.begin();
537     int iWidth = ConfigVariable::getConsoleWidth();
538     int iCurrentWidth = 0;
539     for (int i = 1; it != lstVar.end(); ++it, i++)
540     {
541         if (iCurrentWidth + iVarLenMax + 1 > iWidth)
542         {
543             ostr << std::endl;
544             iCurrentWidth = 0;
545         }
546         ostr << std::setw(iVarLenMax + 1) << *it;
547         iCurrentWidth += iVarLenMax + 1;
548     }
549
550     os_swprintf(wcsVarElem, strSize, _W(" using %10d elements out of  %10d.\n").c_str(), iMemUsed, iMemTotal);
551     ostr << std::endl << wcsVarElem;
552
553     os_swprintf(wcsVarVariable, strSize, _W(" and   %10d variables out of %10d.\n").c_str(), lstVar.size(), nbMaxVar);
554     ostr << wcsVarVariable << std::endl;
555
556     ostr << std::endl << _W("Your global variables are:") << std::endl << std::endl;
557     it = lstGlobal.begin();
558     for (int i = 1; it != lstGlobal.end(); ++it, i++)
559     {
560         ostr << std::setw(iGlobalLenMax + 1) << *it;
561         if (i % 4 == 0)
562         {
563             ostr << std::endl;
564         }
565     }
566
567     ostr << std::endl;
568
569     os_swprintf(wcsGlobalElem, strSize, _W(" using %10d elements out of  %10d.\n").c_str(), iMemUsed, iMemTotal);
570     ostr << std::endl << wcsGlobalElem;
571
572     os_swprintf(wcsGlobalVariable, strSize, _W(" and   %10d variables out of %10d.\n").c_str(), lstGlobal.size(), nbMaxVar);
573     ostr << wcsGlobalVariable;
574 }
575
576 int Context::getScopeLevel()
577 {
578     return m_iLevel;
579 }
580
581 bool Context::isValidVariableName(const wchar_t* wcsVarName)
582 {
583     static const wchar_t FORBIDDEN_CHARS[] = L" */\\.,;:^@><=+-&|()~\n\t'\"";
584     if (wcslen(wcsVarName) == 0 || std::wcspbrk(wcsVarName, FORBIDDEN_CHARS) || isdigit(wcsVarName[0]))
585     {
586         return false;
587     }
588     return true;
589 }
590
591 bool Context::isValidVariableName(const char* name)
592 {
593     bool isValid = false;
594     wchar_t* wcsname = to_wide_string(name);
595     if (wcsname)
596     {
597         isValid = isValidVariableName(wcsname);
598         FREE(wcsname);
599     }
600
601     return isValid;
602 }
603
604 int Context::getLibsToVariableBrowser(std::list<Library*>& lst)
605 {
606     libraries.getVarsToVariableBrowser(lst);
607
608     std::list<Library*> toremove;
609     //list lib that have a variable with the same name
610     for (auto lib : lst)
611     {
612         Variable* var = getOrCreate(lib->getSymbol());
613         if (var->empty() == false)
614         {
615             toremove.push_back(lib);
616         }
617     }
618
619     //remove
620     for (auto lib : toremove)
621     {
622         lst.remove(lib);
623     }
624
625     return static_cast<int>(lst.size());
626 }
627
628 int Context::getVarsToVariableBrowser(std::list<Variable*>& lst)
629 {
630     variables.getVarsToVariableBrowser(lst);
631     return static_cast<int>(lst.size());
632 }
633
634 int Context::getCurrentScope(std::list<std::wstring>& lst, bool sorted)
635 {
636     return variables.getCurrentScope(lst, m_iLevel, sorted);
637 }
638
639 void Context::updateProtection(bool protect)
640 {
641     if (varStack.empty() == false)
642     {
643         VarList* lst = varStack.top();
644         for (auto var : *lst)
645         {
646             if (var.second->empty() == false)
647             {
648                 ScopedVariable* pSV = var.second->top();
649                 //only for current scope but normally vars in VarStack are in the current scope
650                 if (pSV->m_iLevel == m_iLevel)
651                 {
652                     pSV->protect = protect;
653                 }
654                 else
655                 {
656                     std::wcerr << L"heu ... " << var.first.getName() << std::endl;
657                 }
658             }
659         }
660     }
661 }
662
663 void Context::protect()
664 {
665     updateProtection(true);
666 }
667
668 void Context::unprotect()
669 {
670     updateProtection(false);
671 }
672
673 bool Context::isprotected(const Symbol& key)
674 {
675     return isprotected(getOrCreate(key));
676 }
677
678 bool Context::isprotected(Variable* _var)
679 {
680     //don't check protection on "ans"
681     if (_var->getSymbol().getName() == L"ans")
682     {
683         return false;
684     }
685
686     if (_var->empty() == false)
687     {
688         ScopedVariable* pSV = _var->top();
689         if (pSV->m_iLevel == m_iLevel && pSV->protect)
690         {
691             return true;
692         }
693     }
694     return false;
695 }
696
697 int Context::protectedVars(std::list<std::wstring>& vars)
698 {
699     return variables.getProtectedVarsName(vars);
700 }
701 }