1 
2 #include "yacas/lispenvironment.h"
3 #include "yacas/errors.h"
4 #include "yacas/lispatom.h"
5 #include "yacas/lispeval.h"
6 #include "yacas/lispuserfunc.h"
7 #include "yacas/mathuserfunc.h"
8 #include "yacas/standard.h"
9 
10 // we need this only for digits_to_bits
11 #include "yacas/numbers.h"
12 
LispEnvironment(YacasCoreCommands & aCoreCommands,LispUserFunctions & aUserFunctions,LispGlobal & aGlobals,LispHashTable & aHashTable,std::ostream & aOutput,LispPrinter & aPrinter,LispOperators & aPreFixOperators,LispOperators & aInFixOperators,LispOperators & aPostFixOperators,LispOperators & aBodiedOperators,LispIdentifiers & protected_symbols,LispInput * aCurrentInput)13 LispEnvironment::LispEnvironment(YacasCoreCommands& aCoreCommands,
14                                  LispUserFunctions& aUserFunctions,
15                                  LispGlobal& aGlobals,
16                                  LispHashTable& aHashTable,
17                                  std::ostream& aOutput,
18                                  LispPrinter& aPrinter,
19                                  LispOperators& aPreFixOperators,
20                                  LispOperators& aInFixOperators,
21                                  LispOperators& aPostFixOperators,
22                                  LispOperators& aBodiedOperators,
23                                  LispIdentifiers& protected_symbols,
24                                  LispInput* aCurrentInput) :
25     iPrecision(10),       // default user precision of 10 decimal digits
26     iBinaryPrecision(34), // same as 34 bits
27     iInputDirectories(),
28     // iCleanup(),
29     iEvalDepth(0),
30     iMaxEvalDepth(1000),
31     stop_evaluation(false),
32     iEvaluator(new BasicEvaluator),
33     iInputStatus(),
34     secure(false),
35     iTrue(),
36     iFalse(),
37     iEndOfFile(),
38     iEndStatement(),
39     iProgOpen(),
40     iProgClose(),
41     iNth(),
42     iBracketOpen(),
43     iBracketClose(),
44     iListOpen(),
45     iListClose(),
46     iComma(),
47     iList(),
48     iProg(),
49     iLastUniqueId(1),
50     iDebugger(nullptr),
51     iInitialOutput(&aOutput),
52     iCoreCommands(aCoreCommands),
53     iUserFunctions(aUserFunctions),
54     iHashTable(aHashTable),
55     iDefFiles(),
56     iPrinter(aPrinter),
57     iCurrentOutput(&aOutput),
58     iGlobals(aGlobals),
59     iPreFixOperators(aPreFixOperators),
60     iInFixOperators(aInFixOperators),
61     iPostFixOperators(aPostFixOperators),
62     iBodiedOperators(aBodiedOperators),
63     protected_symbols(protected_symbols),
64     iCurrentInput(aCurrentInput),
65     iPrettyReader(nullptr),
66     iPrettyPrinter(nullptr),
67     iDefaultTokenizer(),
68     iXmlTokenizer(),
69     iCurrentTokenizer(&iDefaultTokenizer)
70 {
71     iTrue = LispAtom::New(*this, "True");
72     iFalse = LispAtom::New(*this, "False");
73 
74     Protect(iTrue->String());
75     Protect(iFalse->String());
76 
77     iEndOfFile = LispAtom::New(*this, "EndOfFile");
78     iEndStatement = LispAtom::New(*this, ";");
79     iProgOpen = LispAtom::New(*this, "[");
80     iProgClose = LispAtom::New(*this, "]");
81     iNth = LispAtom::New(*this, "Nth");
82     iBracketOpen = LispAtom::New(*this, "(");
83     iBracketClose = LispAtom::New(*this, ")");
84     iListOpen = LispAtom::New(*this, "{");
85     iListClose = LispAtom::New(*this, "}");
86     iComma = LispAtom::New(*this, ",");
87     iList = LispAtom::New(*this, "List");
88     iProg = LispAtom::New(*this, "Prog");
89 
90     Protect(iList->String());
91     Protect(iProg->String());
92 
93     Protect(iHashTable.LookUp("Infinity"));
94     Protect(iHashTable.LookUp("Undefined"));
95 
96     PushLocalFrame(true);
97 }
98 
~LispEnvironment()99 LispEnvironment::~LispEnvironment()
100 {
101     delete iEvaluator;
102     delete iDebugger;
103 }
104 
SetPrecision(int aPrecision)105 void LispEnvironment::SetPrecision(int aPrecision)
106 {
107     iPrecision = aPrecision; // precision in decimal digits
108     iBinaryPrecision = digits_to_bits(aPrecision, BASE10); // in bits
109 }
110 
GetUniqueId()111 int LispEnvironment::GetUniqueId()
112 {
113     return iLastUniqueId++;
114 }
115 
FindLocal(const LispString * aVariable)116 LispPtr* LispEnvironment::FindLocal(const LispString* aVariable)
117 {
118     assert(!_local_frames.empty());
119 
120     std::size_t last = _local_vars.size();
121 
122     for (std::vector<LocalVariableFrame>::const_reverse_iterator f =
123              _local_frames.rbegin();
124          f != _local_frames.rend();
125          ++f) {
126         const std::size_t first = f->first;
127         for (std::size_t i = last; i > first; --i)
128             if (_local_vars[i - 1].var == aVariable)
129                 return &_local_vars[i - 1].val;
130 
131         if (f->fenced)
132             break;
133 
134         last = first;
135     }
136     return nullptr;
137 }
138 
SetVariable(const LispString * aVariable,LispPtr & aValue,bool aGlobalLazyVariable)139 void LispEnvironment::SetVariable(const LispString* aVariable,
140                                   LispPtr& aValue,
141                                   bool aGlobalLazyVariable)
142 {
143     if (LispPtr* local = FindLocal(aVariable)) {
144         *local = aValue;
145         return;
146     }
147 
148     // FIXME: or should local variables be protected as well?
149     if (Protected(aVariable))
150         throw LispErrProtectedSymbol(*aVariable);
151 
152     auto i = iGlobals.find(aVariable);
153     if (i != iGlobals.end())
154         i->second = LispGlobalVariable(aValue);
155     else
156         i = iGlobals
157                 .insert(std::make_pair(aVariable, LispGlobalVariable(aValue)))
158                 .first;
159 
160     if (aGlobalLazyVariable)
161         i->second.SetEvalBeforeReturn(true);
162 }
163 
GetVariable(const LispString * aVariable,LispPtr & aResult)164 void LispEnvironment::GetVariable(const LispString* aVariable, LispPtr& aResult)
165 {
166     aResult = nullptr;
167 
168     if (LispPtr* local = FindLocal(aVariable)) {
169         aResult = *local;
170         return;
171     }
172 
173     auto i = iGlobals.find(aVariable);
174 
175     if (i != iGlobals.end()) {
176         LispGlobalVariable* l = &i->second;
177         if (l->iEvalBeforeReturn) {
178             iEvaluator->Eval(*this, aResult, l->iValue);
179             // re-lookup the global variable, as this pointer might now be
180             // invalid due to the evaluation actually changing the global
181             // itself.
182             l = &iGlobals.find(aVariable)->second;
183 
184             l->iValue = aResult;
185             l->iEvalBeforeReturn = false;
186         } else {
187             aResult = l->iValue;
188         }
189     }
190 }
191 
UnsetVariable(const LispString * var)192 void LispEnvironment::UnsetVariable(const LispString* var)
193 {
194     if (LispPtr* local = FindLocal(var))
195         *local = nullptr;
196     else {
197         // FIXME: or should local variables be protected as well?
198         if (Protected(var))
199             throw LispErrProtectedSymbol(*var);
200         iGlobals.erase(var);
201     }
202 }
203 
PushLocalFrame(bool fenced)204 void LispEnvironment::PushLocalFrame(bool fenced)
205 {
206     _local_frames.emplace_back(_local_vars.size(), fenced);
207 }
208 
PopLocalFrame()209 void LispEnvironment::PopLocalFrame()
210 {
211     assert(!_local_frames.empty());
212 
213     _local_vars.erase(_local_vars.begin() + _local_frames.back().first,
214                       _local_vars.end());
215     _local_frames.pop_back();
216 }
217 
NewLocal(const LispString * var,LispObject * val)218 void LispEnvironment::NewLocal(const LispString* var, LispObject* val)
219 {
220     assert(!_local_frames.empty());
221 
222     _local_vars.emplace_back(var, val);
223 }
224 
CurrentLocals(LispPtr & aResult)225 void LispEnvironment::CurrentLocals(LispPtr& aResult)
226 {
227     assert(!_local_frames.empty());
228 
229     LispObject* locals = nullptr;
230 
231     std::size_t last = _local_vars.size();
232 
233     for (std::vector<LocalVariableFrame>::const_reverse_iterator f =
234              _local_frames.rbegin();
235          f != _local_frames.rend();
236          ++f) {
237         const std::size_t first = f->first;
238         for (std::size_t i = last; i > first; --i)
239             locals =
240                 LispObjectAdder(LispAtom::New(*this, *_local_vars[i - 1].var)) +
241                 LispObjectAdder(locals);
242 
243         if (f->fenced)
244             break;
245 
246         last = first;
247     }
248     aResult = LispSubList::New(LispObjectAdder(iList->Copy()) +
249                                LispObjectAdder(locals));
250 }
251 
GlobalVariables(LispPtr & aResult)252 void LispEnvironment::GlobalVariables(LispPtr& aResult)
253 {
254     LispPtr vars(iList->Copy());
255     LispIterator tail(vars);
256     ++tail;
257 
258     for (const auto p: iGlobals) {
259         if (p.first->front() == '$' || p.first->front() == '%')
260             continue;
261         *tail = LispAtom::New(*this, *p.first);
262         ++tail;
263     }
264     aResult = LispSubList::New(vars);
265 }
266 
CurrentPrinter()267 LispPrinter& LispEnvironment::CurrentPrinter()
268 {
269     return iPrinter;
270 }
271 
DefFiles()272 LispDefFiles& LispEnvironment::DefFiles()
273 {
274     return iDefFiles;
275 }
276 
PreFix()277 LispOperators& LispEnvironment::PreFix()
278 {
279     return iPreFixOperators;
280 }
InFix()281 LispOperators& LispEnvironment::InFix()
282 {
283     return iInFixOperators;
284 }
PostFix()285 LispOperators& LispEnvironment::PostFix()
286 {
287     return iPostFixOperators;
288 }
Bodied()289 LispOperators& LispEnvironment::Bodied()
290 {
291     return iBodiedOperators;
292 }
293 
CurrentInput()294 LispInput* LispEnvironment::CurrentInput()
295 {
296     return iCurrentInput;
297 }
298 
SetCurrentInput(LispInput * aInput)299 void LispEnvironment::SetCurrentInput(LispInput* aInput)
300 {
301     iCurrentInput = aInput;
302 }
303 
CurrentOutput()304 std::ostream& LispEnvironment::CurrentOutput()
305 {
306     return *iCurrentOutput;
307 }
308 
SetCurrentOutput(std::ostream & aOutput)309 void LispEnvironment::SetCurrentOutput(std::ostream& aOutput)
310 {
311     iCurrentOutput = &aOutput;
312 }
313 
UserFunction(LispPtr & aArguments)314 LispUserFunction* LispEnvironment::UserFunction(LispPtr& aArguments)
315 {
316     auto i = iUserFunctions.find(aArguments->String());
317     if (i != iUserFunctions.end()) {
318         LispMultiUserFunction* multiUserFunc = &i->second;
319         int arity = InternalListLength(aArguments) - 1;
320         return multiUserFunc->UserFunc(arity);
321     }
322     return nullptr;
323 }
324 
UserFunction(const LispString * aName,int aArity)325 LispUserFunction* LispEnvironment::UserFunction(const LispString* aName,
326                                                 int aArity)
327 {
328     auto i = iUserFunctions.find(aName);
329     if (i != iUserFunctions.end())
330         return i->second.UserFunc(aArity);
331 
332     return nullptr;
333 }
334 
UnFenceRule(const LispString * aOperator,int aArity)335 void LispEnvironment::UnFenceRule(const LispString* aOperator, int aArity)
336 {
337     if (Protected(aOperator))
338         throw LispErrProtectedSymbol(*aOperator);
339 
340     auto i = iUserFunctions.find(aOperator);
341 
342     if (i == iUserFunctions.end())
343         throw LispErrInvalidArg();
344 
345     LispMultiUserFunction* multiUserFunc = &i->second;
346 
347     LispUserFunction* userFunc = multiUserFunc->UserFunc(aArity);
348 
349     if (!userFunc)
350         throw LispErrInvalidArg();
351 
352     userFunc->UnFence();
353 }
354 
Retract(const LispString * aOperator,int aArity)355 void LispEnvironment::Retract(const LispString* aOperator, int aArity)
356 {
357     if (Protected(aOperator))
358         throw LispErrProtectedSymbol(*aOperator);
359 
360     auto i = iUserFunctions.find(aOperator);
361 
362     if (i != iUserFunctions.end())
363         i->second.DeleteBase(aArity);
364 }
365 
DeclareRuleBase(const LispString * aOperator,LispPtr & aParameters,int aListed)366 void LispEnvironment::DeclareRuleBase(const LispString* aOperator,
367                                       LispPtr& aParameters,
368                                       int aListed)
369 {
370     if (Protected(aOperator))
371         throw LispErrProtectedSymbol(*aOperator);
372 
373     LispMultiUserFunction* multiUserFunc = MultiUserFunction(aOperator);
374 
375     /*
376      if (multiUserFunc->iFileToOpen)
377     {
378         LISPASSERT(multiUserFunc->iFileToOpen->iIsLoaded);
379         }
380         */
381 
382     // add an operator with this arity to the multiuserfunc.
383     BranchingUserFunction* newFunc =
384         aListed ? new ListedBranchingUserFunction(aParameters)
385                 : new BranchingUserFunction(aParameters);
386 
387     multiUserFunc->DefineRuleBase(newFunc);
388 }
389 
DeclareMacroRuleBase(const LispString * aOperator,LispPtr & aParameters,int aListed)390 void LispEnvironment::DeclareMacroRuleBase(const LispString* aOperator,
391                                            LispPtr& aParameters,
392                                            int aListed)
393 {
394     if (Protected(aOperator))
395         throw LispErrProtectedSymbol(*aOperator);
396 
397     LispMultiUserFunction* multiUserFunc = MultiUserFunction(aOperator);
398 
399     MacroUserFunction* newFunc = aListed
400                                      ? new ListedMacroUserFunction(aParameters)
401                                      : new MacroUserFunction(aParameters);
402 
403     multiUserFunc->DefineRuleBase(newFunc);
404 }
405 
406 LispMultiUserFunction*
MultiUserFunction(const LispString * aOperator)407 LispEnvironment::MultiUserFunction(const LispString* aOperator)
408 {
409     auto i = iUserFunctions.find(aOperator);
410 
411     if (i != iUserFunctions.end())
412         return &i->second;
413 
414     LispMultiUserFunction newMulti;
415     return &iUserFunctions.insert(std::make_pair(aOperator, newMulti))
416                 .first->second;
417 }
418 
HoldArgument(const LispString * aOperator,const LispString * aVariable)419 void LispEnvironment::HoldArgument(const LispString* aOperator,
420                                    const LispString* aVariable)
421 {
422     auto i = iUserFunctions.find(aOperator);
423 
424     if (i == iUserFunctions.end())
425         throw LispErrInvalidArg();
426 
427     LispMultiUserFunction* multiUserFunc = &i->second;
428 
429     multiUserFunc->HoldArgument(aVariable);
430 }
431 
Protect(const LispString * symbol)432 void LispEnvironment::Protect(const LispString* symbol)
433 {
434     protected_symbols.insert(symbol);
435 }
436 
UnProtect(const LispString * symbol)437 void LispEnvironment::UnProtect(const LispString* symbol)
438 {
439     protected_symbols.erase(symbol);
440 }
441 
Protected(const LispString * symbol) const442 bool LispEnvironment::Protected(const LispString* symbol) const
443 {
444     return protected_symbols.find(symbol) != protected_symbols.end();
445 }
446 
DefineRule(const LispString * aOperator,int aArity,int aPrecedence,LispPtr & aPredicate,LispPtr & aBody)447 void LispEnvironment::DefineRule(const LispString* aOperator,
448                                  int aArity,
449                                  int aPrecedence,
450                                  LispPtr& aPredicate,
451                                  LispPtr& aBody)
452 {
453     if (Protected(aOperator))
454         throw LispErrProtectedSymbol(*aOperator);
455 
456     // Find existing multiuser func.
457     auto i = iUserFunctions.find(aOperator);
458 
459     if (i == iUserFunctions.end())
460         throw LispErrCreatingRule();
461 
462     LispMultiUserFunction* multiUserFunc = &i->second;
463 
464     // Get the specific user function with the right arity
465     LispUserFunction* userFunc = multiUserFunc->UserFunc(aArity);
466 
467     if (!userFunc)
468         throw LispErrCreatingRule();
469 
470     // Declare a new evaluation rule
471 
472     if (IsTrue(*this, aPredicate)) {
473         //        printf("FastPredicate on %s\n",aOperator->String());
474         userFunc->DeclareRule(aPrecedence, aBody);
475     } else
476         userFunc->DeclareRule(aPrecedence, aPredicate, aBody);
477 }
478 
DefineRulePattern(const LispString * aOperator,int aArity,int aPrecedence,LispPtr & aPredicate,LispPtr & aBody)479 void LispEnvironment::DefineRulePattern(const LispString* aOperator,
480                                         int aArity,
481                                         int aPrecedence,
482                                         LispPtr& aPredicate,
483                                         LispPtr& aBody)
484 {
485     //    if (Protected(aOperator))
486     //        throw LispErrProtectedSymbol(*aOperator);
487 
488     // Find existing multiuser func.
489     auto i = iUserFunctions.find(aOperator);
490 
491     if (i == iUserFunctions.end())
492         throw LispErrCreatingRule();
493 
494     LispMultiUserFunction* multiUserFunc = &i->second;
495 
496     // Get the specific user function with the right arity
497     LispUserFunction* userFunc = multiUserFunc->UserFunc(aArity);
498 
499     if (!userFunc)
500         throw LispErrCreatingRule();
501 
502     // Declare a new evaluation rule
503     userFunc->DeclarePattern(aPrecedence, aPredicate, aBody);
504 }
505 
SetCommand(YacasEvalCaller aEvaluatorFunc,const char * aString,int aNrArgs,int aFlags)506 void LispEnvironment::SetCommand(YacasEvalCaller aEvaluatorFunc,
507                                  const char* aString,
508                                  int aNrArgs,
509                                  int aFlags)
510 {
511     const LispString* name = HashTable().LookUp(aString);
512     YacasEvaluator eval(aEvaluatorFunc, aNrArgs, aFlags);
513     auto i = iCoreCommands.find(name);
514     if (i != iCoreCommands.end())
515         i->second = eval;
516     else
517         iCoreCommands.insert(std::make_pair(name, eval));
518 }
519 
RemoveCoreCommand(char * aString)520 void LispEnvironment::RemoveCoreCommand(char* aString)
521 {
522     iCoreCommands.erase(HashTable().LookUp(aString));
523 }
524 
LispLocalEvaluator(LispEnvironment & aEnvironment,LispEvaluatorBase * aNewEvaluator)525 LispLocalEvaluator::LispLocalEvaluator(LispEnvironment& aEnvironment,
526                                        LispEvaluatorBase* aNewEvaluator) :
527     iPreviousEvaluator(aEnvironment.iEvaluator),
528     iEnvironment(aEnvironment)
529 {
530     aEnvironment.iEvaluator = aNewEvaluator;
531 }
~LispLocalEvaluator()532 LispLocalEvaluator::~LispLocalEvaluator()
533 {
534     delete iEnvironment.iEvaluator;
535     iEnvironment.iEvaluator = iPreviousEvaluator;
536 }
537 
LispLocalTrace(LispUserFunction * aUserFunc)538 LispLocalTrace::LispLocalTrace(LispUserFunction* aUserFunc) :
539     iUserFunc(aUserFunc)
540 {
541     if (iUserFunc != nullptr)
542         iUserFunc->Trace();
543 }
~LispLocalTrace()544 LispLocalTrace::~LispLocalTrace()
545 {
546     if (iUserFunc != nullptr)
547         iUserFunc->UnTrace();
548 }
549