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