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