PEBL 2.2
Psychology Experiment Building Language - Cross-platform psychological experiment development system
Evaluator.cpp
Go to the documentation of this file.
1//* -*- mode:C++; tab-width:4; c-basic-offset:4; indent-tabs-mode:nil -*- */
3// Name: src/base/Evaluator.cpp
4// Purpose: Defines an class that can evaluate PNodes (Recursive Evaluator)
5// Author: Shane T. Mueller, Ph.D.
6// Copyright: (c) 2003--2025 Shane T. Mueller <smueller@obereed.net>
7// License: GPL 2
8//
9// ARCHITECTURE NOTE - Recursive vs Iterative Evaluator:
10//
11// This is the RECURSIVE evaluator implementation. It uses C++ call stack
12// recursion to evaluate the PEBL abstract syntax tree (AST). Each call to
13// Evaluate() recurses through the AST using native C++ function calls.
14//
15// EMSCRIPTEN INCOMPATIBILITY:
16// This recursive evaluator is INCOMPATIBLE with Emscripten builds that use
17// the Asyncify feature (required for async I/O and event handling). Testing
18// shows a hard recursion depth limit of 1 for PEBL user-defined recursive
19// functions when compiled to WebAssembly. Any recursion depth >= 2 crashes
20// with "index out of bounds" errors. This occurs because Emscripten's
21// Asyncify transformation rewrites the call stack to enable pause/resume
22// operations, which breaks the recursive evaluator's function call mechanism.
23//
24// For Emscripten builds, use the iterative evaluator (Evaluator-es.cpp)
25// instead, which uses manual stack management and handles arbitrary recursion
26// depth correctly.
27//
28// The PEBL_ITERATIVE_EVAL macro controls which evaluator is compiled.
29// See src/apps/Globals.h for the selection logic.
30//
31// This file is part of the PEBL project.
32//
33// PEBL is free software; you can redistribute it and/or modify
34// it under the terms of the GNU General Public License as published by
35// the Free Software Foundation; either version 2 of the License, or
36// (at your option) any later version.
37//
38// PEBL is distributed in the hope that it will be useful,
39// but WITHOUT ANY WARRANTY; without even the implied warranty of
40// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
41// GNU General Public License for more details.
42//
43// You should have received a copy of the GNU General Public License
44// along with PEBL; if not, write to the Free Software
45// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
47
48#include "Evaluator.h"
49
50#include <iostream>
51#include <string>
52#include <math.h>
53#include <algorithm>
54
55#include "PNode.h"
56#include "grammar.tab.hpp"
57#include "VariableMap.h"
58#include "Variant.h"
59#include "PComplexData.h"
60#include "PList.h"
61#include "FunctionMap.h"
62
63#include "../utility/PError.h"
64#include "../utility/rc_ptrs.h"
65#include "../utility/Defs.h"
66
67#include "../objects/PCustomObject.h"
68#include "../devices/PEventLoop.h"
69
70#ifdef PEBL_EMSCRIPTEN
71#include <emscripten.h>
72#endif
73
74#undef PEBL_DEBUG_PRINT
75//#define PEBL_DEBUG_PRINT 1
76
77using std::cout;
78using std::endl;
79using std::flush;
80//using std::list;
81using std::string;
82using std::vector;
83
85 mStackMax(10000),
86 mScope("Base Scope")
87{
88#ifdef PEBL_DEBUG_PRINT
89 cout << "Creating Evaluator: " << mScope << endl;
90#endif
91
92
93 if(!mEventLoop)
94 mEventLoop = new PEventLoop();
95
96 gCallStack.Push(gEvalNode);
97
98}
99
100
101Evaluator::Evaluator(Variant & stacktop, string scope):
102 // mCallStack(callstack),
103 mStackMax(10000),
104 mScope(scope)
105{
106#ifdef PEBL_DEBUG_PRINT
107 cout << "Creating Evaluator: " << mScope << endl;
108#endif
109
110 //add everything in callstack onto mCallStack
111 //mCallStack = callstack;
112
113 if(!mEventLoop)
114 mEventLoop = new PEventLoop();
115
116 //Push the current evalnode onto the stack, if
117 //it exists.
118 if(gEvalNode)
119 {
120 gCallStack.Push(gEvalNode);
121
122 }
123 //Initialize the evaluator scope with a variant which is a list of variables
124 Push(stacktop);
125}
126
127
129
130{
131
132#ifdef PEBL_DEBUG_PRINT
133 cout << "Deleting Evaluator: " << mScope << endl;
134#endif
135
136 //Delete all local variables now.
137 if( gCallStack.Size())
138 gCallStack.Pop();
139
140
141}
142
143
147
148bool Evaluator::Evaluate(const PNode * node)
149{
150
151 if(node == NULL) PError::SignalFatalError("Trying to evaluate null node\n");
152 //Set up the globally-accessible structure to allow
153 //better error reporting. Only change it if the new node's
154 //line number is greater than -1; if not, it is a PEBL-generated
155 //node that won't give very good information.
156 if(node->GetLineNumber() > -1)
157 gEvalNode = node;
158
159#ifdef PEBL_DEBUG_PRINT
160 cout << "Line: " << node->GetLineNumber() << endl;
161 cout << "PDP::Type: " << node->GetType() << endl;
162 cout << "PDP::Type: " << node->GetType() << endl;
163#endif
164
165 if(node->GetType() == PEBL_OP_NODE)
166 {
167 return Evaluate((OpNode*)node);
168 }
169 else if (node->GetType() == PEBL_DATA_NODE)
170 {
171 return Evaluate((DataNode*)node);
172 }
173 else
174 {
175#ifdef PEBL_DEBUG_PRINT
176 cout << "ERROR IN GENERIC EVALUATOR::EVALUATE" << endl;
177#endif
178 return false;
179 }
180
181 return true;
182}
183
184
186bool Evaluator::Evaluate(const OpNode * node)
187{
188
189 unsigned long int numargs = 0;
190 if(node == NULL) PError::SignalFatalError("Trying to evaluate null node\n");
191 //Set up the globally-accessible structure to allow
192 //better error reporting.
193 if(node->GetLineNumber() > -1)
194 gEvalNode = node;
195
196#ifdef PEBL_DEBUG_PRINT
197 cout << "-------------------------Evaluating OpNode ["<< node->GetOp() << "] of Type: " << node->GetOpName() << "------------\n";
198#endif
199
200
202 switch(node->GetOp())
203 {
204
205 case NULL:
206 break;
207
208
209 case PEBL_ASSIGN:
210 {
211 //The left node should contain a DataNode of Variant type Variable.
212 //The right node should contain an expression that evaluates to a number
213 //that should be assigned to the datanode.
214
215
216 const PNode * node2 = node->GetRight();
217 Evaluate(node2);
218
219 //The evaluated expression is now at the top of the stack.
220 //Leave it there, because this statement should return that value; but assign
221 //it to v2.
222 Variant v2 = mStack.top();
223
224 const PNode * node1 =node->GetLeft();
225
226 //Extract the variable name from the node.
227
228 Variant v1=((DataNode*)node1)->GetValue();
229
230
231#ifdef PEBL_DEBUG_PRINT
232 cout << "Initial Variable Name: [" << v1.GetVariableName() << "]" << endl;
233#endif
234
235 //Get the name of property being set
236 string property =v1.GetVariablePropertyName();
237
238 //Add the variable name/value pair to the appropriate map structure
239 if(v1.IsLocalVariable())
240 {
241
242 if(property == "")
243 {
244 //Variable is added here. What
245 //happens if it overwrites something?
246
247 mLocalVariableMap.AddVariable(v1.GetVariableName(), v2);
248 }
249 else
250 {
251 //otherwise get the object from the variable store
252 //and set its property.
253
254 Variant v3 = mLocalVariableMap.RetrieveValue(v1.GetVariableBaseName());
255 PEBLUtility::SetPropertyChain(v3, property, v2);
256 }
257
258
259 }
260 else
261 {
262
263 if(property == "")
264 {
266 }
267 else
268 {
269 //otherwise get the object from the variable store
270 //and set its property.
272 PEBLUtility::SetPropertyChain(v3, property, v2);
273 }
274
275 }
276
277
278 }
279 break;
280
281 case PEBL_ADD:
282 {
283 //Execute left and right nodes, which puts results on stack
284 const PNode * node1 = node->GetLeft();
285 Evaluate( node1 );
286 const PNode * node2 = node->GetRight();
287 Evaluate(node2);
288
289 //Get the top two items from the stack. The rightmost will
290 //be on top.
291 Variant v2 = Pop();
292 Variant v1 = Pop();
293
294 //There maybe should be more error checking to ensure
295 //that the atoms are numbers.
296
297 Push(v1+v2);
298 }
299 break;
300
301
302 case PEBL_DIVIDE:
303 {
304 //Execute left and right nodes, which puts results on stack
305 const PNode * node1 = node->GetLeft();
306 Evaluate( node1 );
307 const PNode * node2 = node->GetRight();
308 Evaluate( node2);
309
310
311
312 //Get the top two items from the stack
313 Variant v2 = Pop();
314 Variant v1 = Pop();
315
316 Push(v1/v2);
317 }
318 break;
319
320
321 case PEBL_MULTIPLY:
322 {
323 //Execute left and right nodes, which puts results on stack
324 const PNode * node1 = node->GetLeft();
325 Evaluate( node1 );
326 const PNode * node2 = node->GetRight();
327 Evaluate( node2 );
328
329
330 //Get the top two items from the stack. The right will be on top
331 Variant v2 = Pop();
332 Variant v1 = Pop();
333
334 Push(v1*v2);
335 }
336 break;
337
338
339 case PEBL_POWER:
340 {
341 //Execute left and right nodes, which puts results on stack
342 const PNode * node1 = node->GetLeft();
343 Evaluate( node1 );
344 const PNode * node2 = node->GetRight();
345 Evaluate( node2);
346
347
348
349 //Get the top two items from the stack
350 Variant v2 = Pop();
351 Variant v1 = Pop();
352
353 Push(Variant(pow((pDouble)v1,(pDouble)v2)));
354 }
355 break;
356
357
358 case PEBL_SUBTRACT:
359 {
360 //Execute left and right nodes, which puts results on stack
361 const PNode * node1 = node->GetLeft();
362 Evaluate( node1 );
363 const PNode * node2 = node->GetRight();
364 Evaluate( node2 );
365
366
367 //Get the top two items from the stack. The right will be on top
368 Variant v2 = Pop();
369 Variant v1 = Pop();
370
371 Push(v1 - v2);
372 }
373 break;
374
375
376 case PEBL_AND:
377 {
378 // Short-circuit AND: evaluate left first; if false, skip right.
379 const PNode * node1 = node->GetLeft();
380 Evaluate( node1 );
381 Variant v1 = Peek();
382 if(!(bool)v1)
383 {
384 // Left is false; result is false without evaluating right.
385 // (v1 already on stack from Evaluate; replace it)
386 Pop();
387 Push(Variant(0));
388 }
389 else
390 {
391 Pop();
392 const PNode * node2 = node->GetRight();
393 Evaluate( node2 );
394 Variant v2 = Pop();
395 Push(v1 && v2);
396 }
397
398 }
399 break;
400
401 case PEBL_OR:
402 {
403 // Short-circuit OR: evaluate left first; if true, skip right.
404 const PNode * node1 = node->GetLeft();
405 Evaluate( node1 );
406 Variant v1 = Peek();
407 if((bool)v1)
408 {
409 // Left is true; result is true without evaluating right.
410 Pop();
411 Push(Variant(1));
412 }
413 else
414 {
415 Pop();
416 const PNode * node2 = node->GetRight();
417 Evaluate( node2 );
418 Variant v2 = Pop();
419 Push(v1 || v2);
420 }
421
422 }
423 break;
424
425 case PEBL_NOT:
426 {
427 //Evaluate left nodes, and negate it.
428
429 //Execute left and right nodes, which puts results on stack
430 const PNode * node1 = node->GetLeft();
431 Evaluate( node1 );
432
433 //Get the top two items from the stack. The right will be on top
434 Variant v1 = Pop();
435
436 Push(!v1);
437 }
438 break;
439
440
441 case PEBL_IF:
442 {
443
444 //Left node is the expression test;
445 //Right node is the code block to execute if true.
446
447 const PNode * node1 = node->GetLeft();
448 Evaluate( node1 );
449 Variant v1 = Pop();
450 if(v1)
451 {
452 PNode * node2 = node->GetRight();
453 Evaluate( node2 );
454 }
455 else
456 {
457 //If the IF is not true, we still need to leave something on the
458 // stack; leave a 0.
459 Push(0);
460
461 }
462 }
463 break;
464
465 case PEBL_IFELSE:
466 {
467 //Left node is the expression test;
468 //Right node is the THEN code block to execute.
469
470 //Execute left node, which puts results on stack
471 const PNode * node1=node->GetLeft();
472 Evaluate( node1 );
473
474 const PNode * node2 = node->GetRight();
475 Evaluate(node2);
476 }
477 break;
478
479 case PEBL_ELSE:
480 {
481 //This looks on the top of the stack and
482 //executes the left node if true; right node if false.
483
484 Variant v1 = Pop();
485
486 const PNode * node1;
487 if(v1)
488 {
489 node1 = node->GetLeft();
490 }
491 else
492 {
493 node1 = node->GetRight();
494 }
495 Evaluate(node1);
496 }
497 break;
498
499 case PEBL_LAMBDAFUNCTION:
500 {
501 // This is the top node of an anonymous function. It is a
502 // node with two children: on the left is a variable list, and
503 // on the right is a code block. To make it a named function, just
504 // the parent node will be a PEBL_FUNCTION OpNode with a left child
505 // which is a function datanode.
506
507 // When this function is called, the top of the stack is a list that contains
508 // the bindings for the variables. Get the list and assign the values to the
509 // variables one-by-one, then execute the code block.
510
511 //Get the variable list. This may have either empty or variable-pair default values.
512
513 const PNode * node1 = node->GetLeft();
514
515 //Get the argument list is at the top of the stack right now.
516
517 Variant v1 = Pop();
518
519 //Create a list to use.
520 //If v1 is a stacksignal list_head, there are no arguments
521 //provided.
522
523
524
526
527 if( v1.IsStackSignal() && v1.GetSignal() == STACK_LIST_HEAD)
528 {
529 //v1 is empty, so make a dummy parameter list to send.
530
531 tmpList = counted_ptr<PEBLObjectBase>(new PList());
532 }
533
534
535 if( v1.IsComplexData())
536 {
537
538 //extract the object out of v1 to send.
539 tmpList = v1.GetComplexData()->GetObject();
540 }
541
542
543 //now, tmpList is a list containing the sequence of arguments fed into the function call.
544 //They should map onto the first N arguments in the template.
545
546 Variant v2 = 0;
547
548 //iterate through the lists and assign values to variables.
549 PList * arglist = (PList*)(tmpList.get());
550
551 vector <Variant>::iterator p = arglist->Begin();
552 Variant vdef = 0; // Initialize to prevent uninitialized reads
553
554 while(node1)
555 {
556 //Each variable could be either a variable name (global or local)
557 //or a varpair--a node pairing a global/local with a value.
558 vdef = 0; // Reset to 0 each iteration (don't re-declare, would shadow outer vdef)
559
560 //This loads the default values initially when they appear,
561 //and then overwrites them if actual values exist. This means
562 //the default value(if global) needs to exist--it probably shoudn't have to
563 bool hasdefault = false;
564 if(((OpNode*)(((OpNode*)node1)->GetLeft()))->GetOp()==PEBL_VARPAIR)
565 {
566 //this variable has a default value....
567 hasdefault = true;
568 //v2 is the variable name.
569 DataNode* varNameNode = ((DataNode*)(((OpNode*)(((OpNode*)node1)->GetLeft()))->GetLeft()));
570 v2 = varNameNode->GetValue();
571
572 // Get the default value node - could be DataNode or OpNode
573 PNode* defaultNode = ((OpNode*)(((OpNode*)node1)->GetLeft()))->GetRight();
574
575 if(defaultNode->GetType() == PEBL_OP_NODE)
576 {
577 // The default value is an expression (e.g., -1, Min(data)).
578 // Evaluate it in the current function scope to get its value.
579 // This allows referencing previously-bound parameters.
580 Evaluate(defaultNode);
581 vdef = Pop();
582 }
583 else
584 {
585 //It is a data node--default value.
586 DataNode* defValueNode = (DataNode*)defaultNode;
587 vdef = defValueNode->GetValue();
588 }
589 }
590 else
591
592 {
593 //it is a pure value here
594 v2 = ((DataNode*)(((OpNode*)node1)->GetLeft()))->GetValue();
595
596 }
597
598
599
600 // Now bind the parameter immediately (either from argument or default)
601 // This allows later parameters' defaults to reference earlier parameters.
602 if(p != arglist->End())
603 {
604 // We have an argument value - use it
605 mLocalVariableMap.AddVariable(v2, *p);
606 p++;
607 node1 = ((OpNode*)node1)->GetRight();
608 }
609 else if(hasdefault)
610 {
611 // No argument provided, but we have a default value
612 // Resolve variable references in the default value
613 if(vdef.IsGlobalVariable())
614 {
616 }
617 else if (vdef.IsLocalVariable())
618 {
619 // A local variable may be bound to a previously-specified parameter
620 vdef = mLocalVariableMap.RetrieveValue(vdef);
621 }
622 mLocalVariableMap.AddVariable(v2, vdef);
623 node1 = ((OpNode*)node1)->GetRight();
624 }
625 else
626 {
627 // No argument and no default - too few parameters
628 string message = "Too few arguments passed to function [" + mScope + "].";
629
630 if(mScope == "Start")
631 message += " (Make sure Start function has only one variable).";
632
634 }
635 }
636
637
638
639 //if(arglist && arglist->Length() > 0)
640 if(p != arglist->End())
641 {
642 //Too many arguments.
643 string message = string("");
644
645 if(mScope == "Start")
646 message += "Start() function must be Start(p)).";
647 else
648 message += "Too many arguments passed to function [" + mScope + "].";
649
650
652 }
653
654
655 //Now, get the code block and execute it.
656 const PNode * node2 = node->GetRight();
657 Evaluate(node2);
658 }
659 break;
660
661
662 case PEBL_LIBRARYFUNCTION:
663 {
664
665 // This type of function is built in and precompiled. The loader examines
666 // the parse tree and
667 // identifies each function that is used.
668
669 //The left child of a PEBL_LIBRARYFUNCTION contains an PEBL_AND node containing two datanodes
670 //each containing integers describing the min and max number of arguments, respectively.
671
672
673 //We should be able to tell the name of the function
674 std::string name = node->GetFunctionName();
675
676 const OpNode * node0 =(OpNode*)(node->GetLeft());
677 const unsigned int min = ((DataNode*)(node0->GetLeft()))->GetValue().GetInteger();
678 const unsigned int max = ((DataNode*)(node0->GetRight()))->GetValue().GetInteger();
679
680 //The right child of a PEBL_LIBRARYFUNCTION contains a datanode which
681 //has a function pointer in it.
682
683
684 const PNode * node1 = node->GetRight();
685 Variant v1 = ((DataNode*)node1)->GetValue();
686
687
688 //All built-in functions take a single parameter: a Variant list.
689 //This variant should be on the top of the stack right now. So get it.
690
691 Variant v2 = Pop();
692
693
694 //Before we execute, check to see if v2 has a length between min and max.
695
696
697 PList * tmp = NULL;
698 if (v2.IsStackSignal())
699 numargs = 0;
700 else
701 {
702
703 // cout << "((("<<name<<"|"<<v2<<")))\n";
704
705 if(v2.IsComplexData())
706 {
707 if((v2.GetComplexData())->IsList())
708 {
709 //#if !defined(PEBL_EMSCRIPTEN)
710
711
712 //Dont check parameter numbers in EMSCRIPTEN
713 //cout << "[[[<"<<v2<<">]]]\n";
714 tmp = (PList*)(v2.GetComplexData()->GetObject().get());
715 numargs = tmp->Length();
716
717 //#endif
718 }
719 else
720 {
721 cout << "UNHANDLED ELSE CASE. NOT A LIST\n" ;
722 }
723 }
724 //cout << "numargs now: " << numargs << endl;
725 }
726
727
728 //#if !defined(PEBL_EMSCRIPTEN)
729 if(numargs < min || numargs > max)
730 {
731
732 Variant message = Variant("In scope [") + mScope + Variant("]:") +
733 Variant("function [") +name+Variant("]:")+
734 Variant("Incorrect number of arguments.. Wanted between ")+
735 Variant((int)min) + Variant(" and ") +Variant((int)max) + Variant(" but got ") + Variant((int)numargs);
736
737
738 cout << message << endl;
740 }
741 //#endif
742
743 //Execute the functionpointer and push the results onto the stack.
744 Push((v1.GetFunctionPointer())(v2));
745 }
746
747 break;
748
749
750 case PEBL_FUNCTION:
751 // This controls the execution of a function. (not the
752 // loading of a defined function). A function node
753 // has a function datanode on its left child and an arglist (the head of a list of
754 // arguments) on its right child. Just pass these along to the CallFunction Method.
755
756 CallFunction(node);
757
758
759 break;
760
761 case PEBL_WHILE:
762 {
763 // The left child node is an expression to test
764 // The right node is a code block.
765 // Test the left node, then evaluate the right node if true.
766 // Repeat until left node is false.
767
768 Variant results=0;
769 while(1)
770 {
771 //Execute left node, which puts results on stack
772 const PNode * node1=node->GetLeft();
773 Evaluate( node1 );
774
775 //Get the result of the evaluation
776 Variant v1 = Pop();
777
778 //If the evaluation is false, break out of loop/switch
779 if(!v1)
780 {
781 results = Variant(0);
782 break;
783 }
784
785
786 //Evaluate the right node.
787 const PNode * node2 = node->GetRight();
788 Evaluate(node2);
789
790 //Now, the results of the while block are on top of the stack.
791 //Pop them off so the stack doesn't grow; add them back at the end.
792 results = Pop();
793
794 if(results.GetDataType() == P_DATA_STACK_SIGNAL &&
795 results == Variant(STACK_BREAK))
796 {
797 results = Pop();
798 break;
799 }
800
801 }
802 //Push the last results back onto the stack.
803 Push(results);
804 }
805 break;
806
807
808 case PEBL_LOOP:
809 {
810 // The PEBL_LOOP node has two children: the left child is a variable/datum pair,
811 // and the right child is a code block that should be evaluated once for each
812 // element of the datum, with the variable being set to that element for
813 // each iteration.
814
815 //Get the variabledatum pair:
816 const PNode * node1 = node->GetLeft();
817
818 Evaluate(node1);
819
820 //Now, the top two items on the stack should be the datum and the variable.
821
822 Variant v1 = Pop();
823 Variant v2 = Pop();
824
825 PError::AssertType(v2, PEAT_VARIABLE,"Error: iterator of loop not a variable");
826
827 const PNode * node2 = node->GetRight();
828
829 //Now, iterate through the elements of v1. If v1 is not a list,
830 //just set the variable equal to v1 and execute once.
831
832
833 PList * tmp;
834 if(v1.IsInteger())
835 {
836 tmp = new PList();
837 for(int count=1; count <= v1.GetInteger(); count++)
838 {
839 tmp->PushBack(count);
840 }
841
842 //make a list here containing numbers 1...v1
843 }else{
844
845 PError::AssertType(v1, PEAT_LIST,"Second argument of loop(<iterator>, <list>) must be a list.");
846 tmp = (PList*)(v1.GetComplexData()->GetObject().get());
847 }
848
849 //Retrieve an iterator to the items in the list.
850 //PComplexData * tmpPCD = v1.GetComplexData();
851
852 vector<Variant>::const_iterator p = tmp->Begin();
853 vector<Variant>::const_iterator end = tmp->End();
854 Variant results=0;
855
856 while(p != end)
857 {
858
859 //Set the variable to the current list element, and increment p
860 if(v2.IsLocalVariable())
861 {
862 mLocalVariableMap.AddVariable(v2, *p);
863 }
864 else if (v2.IsGlobalVariable())
865 {
867 }
868
869 p++;
870
871 //Execute the code block.
872 Evaluate(node2);
873
874 //Now, the results of the while block are on top of the stack.
875 //Pop them off so the stack doesn't grow; add them back at the end.
876 results = Pop();
877
878 if(results.GetDataType() == P_DATA_STACK_SIGNAL &&
879 results == Variant(STACK_BREAK))
880 {
881 p = end;
882 Pop();
883 results = Variant(0);
884
885 }
886 }
887 //Push the last results back onto the stack.
888 Push(results);
889
890 }
891 break;
892
893 case PEBL_VARIABLEDATUM:
894 {
895 //The VARIABLDATUM node is used in the loop function. The variable is the
896 //left child, a list of data is the right. This node is executed once at the beginning
897 //of the loop; it pushes the variable name and the evaluated list onto the stack.
898 //PEBL_LOOP evaluation then gets them from the stack and iterates through the list,
899 //setting the variable to each element of the data.
900
901 //Get the variable:
902 const PNode * node1 = node->GetLeft();
903
904 //Push the variable name directly on the stack (without evaluating it.)
905 Push(((DataNode*)node1)->GetValue());
906
907 //Get the datum:
908 const PNode * node2 = node->GetRight();
909
910 //Evaluate the datum--it gets put on the stack.
911 Evaluate(node2);
912 }
913 break;
914
915
916 case PEBL_ARGLIST:
917 {
918 // This behaves almost exactly like a listhead. It will end up with a variant
919 // on the top of the stack that is a list containing all of the arguments
920 // for a function.
921
922
923 //Create the stack signal variant and push it onto the stack.
925 Push(v1);
926
927 //Get the top node of the list
928 const PNode * node1 = node->GetLeft();
929
930 if(node1)
931 {
932 //If it exists, evaluate it.
933 Evaluate(node1);
934 }
935
936 //if node1 doesn't exist, it is a null argument list--do nothing more.
937
938
939 }
940 break;
941
942 case PEBL_LISTHEAD:
943 {
944 //When we get a list head, we need to evaluate each expression in the list,
945 //pushing them onto the stack. When we get to the end of the list, we just need
946 //to pop items, adding them to the list, until we get back up to the top.
947 //To do this efficiently and avoid recreating lists iteratively, the bottom
948 //item does this in a while loop. to signal the end of the while loop, the
949 //PEBL_LISTHEAD must put a dummy item on the stack so the bottom item
950 //knows when the front of the list has been reached.
951 //This is a variant of type STACK_LIST_HEAD
952
953 //Create the stack signal variant and push it onto the stack.
955 Push(v1);
956
957 //Get the top node of the list and Evaluate it.
958 const PNode * node1 = node->GetLeft();
959 if(node1)
960 {
961 Evaluate(node1);
962 }
963 else
964 {
965 //If node1 is NULL, we have an empty list.
966 //Get rid of the STACK_LIST_HEAD on the top of the stack
967 Pop();
968
969 //Make an empty list and push it onto the stack.
971
972 PComplexData pcd = PComplexData(tmpList);
973 Variant v2 = Variant(&pcd);
974 Push(v2);
975 }
976 }
977 break;
978
979
980 case PEBL_LISTITEM:
981 {
982 // for a list item, the list is represented as a parsed tree with the
983 // left node being the data and the right node being another listitem node.
984 // This should create the list piece by piece recursively.
985
986 // To do this, Evaluate() each item so it gets executed and the value
987 // gets pushed onto the stack. When you come to the end of the list,
988 // pop the items off the list iteratively until you get back where you came from.
989
990 //The data: Get it out of the tree and evaluate it, so it gets
991 //push onto the stack.
992
993
994 const PNode * node1 = node->GetLeft();
995 Evaluate(node1);
996
997 //The rest of the list;
998 const PNode * node2 = node->GetRight();
999
1000 // If the right node not null, then we need to evaluate it until we get to the end
1001 // of the list. If it is null, we are at the end of the list; create
1002 // the beginning of a list and push it onto the stack.
1003 if(node2)
1004 {
1005 //Evaluate the tail of the list
1006 Evaluate(node2);
1007
1008 //When above is done, a list object will be at the top
1009 //of the stack. don't touch it
1010 }
1011 else
1012 {
1013
1014
1015 //If node2 is NULL, then we are at the end of the list.
1016 //everything has been pushed to the stack. Just get everything
1017 //off the stack, make a list out of it, and put it back on the stack.
1018
1019
1020 //Must make a new list, and create a variant out of it.
1021 PList * tmpList = new PList();
1022 PList order;
1023 int ord = 0;
1024 //Now, pop off items from the list until you get to a
1025 //P_DATA_STACK_SIGNAL, then if it i
1026 Variant v1 = Pop();
1027
1028 //we need to create the list from the items on the stack.
1029 //But the top of the stack is the end of the list.
1030 while(v1.GetDataType() != P_DATA_STACK_SIGNAL)
1031 {
1032
1033 //cout << "::::" << v1 << endl;
1034 //Add the item to the list.
1035 tmpList->PushBack(v1);//This used to be PushFront
1036 order.PushBack(ord--);
1037 //Pop and repeat.
1038 v1 = Pop();
1039 }
1040
1041 //When tmpList had a list inside it, we could use PushFront to
1042 //automatically reverse the list, which is now on the stack with
1043 //the last element first.
1044 //But now that it is a vector, it is inefficient to push onto the front
1045 //And involves recopying every element), so we will just reverse it now
1046 //that it is in a handy list format.
1047
1048 std::reverse(tmpList->Begin(),tmpList->End());
1049
1051 PComplexData pcd = PComplexData(pl);
1052 Variant v2 = Variant(&pcd);
1053 Push(v2);
1054 }
1055 }
1056 break;
1057
1058 case PEBL_LT:
1059 {
1060 //Execute left and right nodes, which puts results on stack
1061 const PNode * node1 = node->GetLeft();
1062 Evaluate( node1 );
1063 const PNode * node2 = node->GetRight();
1064 Evaluate( node2);
1065
1066
1067 //Get the top two items from the stack. The right will be on top
1068 Variant v2 = Pop();
1069 Variant v1 = Pop();
1070
1071
1072
1073 Push(v1 < v2);
1074 }
1075 break;
1076
1077 case PEBL_GT:
1078 {
1079
1080 //Execute left and right nodes, which puts results on stack
1081 const PNode * node1 = node->GetLeft();
1082 Evaluate( node1 );
1083 const PNode * node2 = node->GetRight();
1084 Evaluate( node2);
1085
1086 //Get the top two items from the stack. The right will be on top
1087 Variant v2 = Pop();
1088 Variant v1 = Pop();
1089 Push(v1 > v2);
1090 }
1091 break;
1092
1093 case PEBL_GE:
1094 {
1095 //Execute left and right nodes, which puts results on stack
1096 const PNode * node1 = node->GetLeft();
1097 Evaluate( node1 );
1098 const PNode * node2 = node->GetRight();
1099 Evaluate( node2);
1100
1101 //Get the top two items from the stack. The right will be on top
1102 Variant v2 = Pop();
1103 Variant v1 = Pop();
1104
1105 Push(v1 >= v2);
1106 }
1107 break;
1108
1109 case PEBL_LE:
1110 {
1111
1112 //Execute left and right nodes, which puts results on stack
1113 const PNode * node1 = node->GetLeft();
1114 Evaluate( node1 );
1115 const PNode * node2 = node->GetRight();
1116 Evaluate( node2);
1117
1118
1119 //Get the top two items from the stack. The right will be on top
1120 Variant v2 = Pop();
1121 Variant v1 = Pop();
1122
1123 Push(v1 <= v2);
1124 }
1125 break;
1126
1127 case PEBL_EQ:
1128 {
1129 //Execute left and right nodes, which puts results on stack
1130 const PNode * node1 = node->GetLeft();
1131 Evaluate( node1 );
1132 const PNode * node2 = node->GetRight();
1133 Evaluate( node2);
1134
1135 //Get the top two items from the stack. The right will be on top
1136 Variant v2 = Pop();
1137 Variant v1 = Pop();
1138 Push(v1 == v2);
1139 }
1140 break;
1141
1142 case PEBL_NE:
1143 {
1144 //Execute left and right nodes, which puts results on stack
1145 const PNode * node1 = node->GetLeft();
1146 Evaluate( node1 );
1147 const PNode * node2 = node->GetRight();
1148 Evaluate( node2);
1149
1150 //Get the top two items from the stack. The right will be on top
1151 Variant v2 = Pop();
1152 Variant v1 = Pop();
1153
1154 Push(v1 != v2);
1155 }
1156 break;
1157
1158 case PEBL_STATEMENTS:
1159 {
1160 //This is a node that connects two statements. Since a statement leaves its
1161 //evaluation on the stack, this should pop the stack after evaluating the LEFT
1162 //statement. This means that the last statement in a series remains on the stack.
1163
1164#ifdef PEBL_DEBUG_PRINT
1165 cout << "Checking a PEBL_STATEMENTS: " << GetStackDepth() << endl;
1166#endif
1167
1168 //If the top of the stack is a STACK_BREAK, we should do nothing.
1169 if(GetStackDepth())
1170 {
1171
1172 Variant v1 = Peek();
1173
1174
1175 if(v1.GetDataType() == P_DATA_STACK_SIGNAL &&
1176 v1 == Variant(STACK_BREAK))
1177 {
1178 //cout << "STACKBREAKING ON RIGHT\n";
1179
1180 //If this is a stack signal, and if
1181 //it is a break, we should just back out
1182 Push(Variant(0)); //why isn't this stack_break?
1183 break;
1184 }
1185 }
1186
1187 if(node->GetLeft())
1188 {
1189 Evaluate(node->GetLeft());
1190 Variant v1 = Pop();
1191
1192
1193 if(v1.GetDataType() == P_DATA_STACK_SIGNAL &&
1194 v1 == Variant(STACK_BREAK))
1195 {
1196 //cout << "STACKBREAKING ON LEFT\n";
1197 //If this is a stack signal, and if
1198 //it is a break, we should just back out
1200 break;
1201 }
1202 }
1203
1204 if(node->GetRight())
1205 Evaluate(node->GetRight());
1206 }
1207
1208 break;
1209
1210
1211 case PEBL_BREAK:
1212 {
1213 //This exits out of the current loop, while, or function context.
1214 //it works by adding a STACK_BREAK stacksignalevent onto the top of the stack.
1215 //all relevant loops look for this type of event and
1216 //cleanly abort when that times comes.
1217
1218 // cout << "PEBL_BREAK HANDLER\n";
1219
1221 Push(v1);
1222 Push(v1);
1223#ifdef PEBL_DEBUG_PRINT
1224 cout << "PEBL_BREAK: pushing break onto stack.\n";
1225#endif
1226
1227 }
1228 break;
1229
1230 case PEBL_RETURN:
1231 {
1232 //The return keyword is used ONLY at the very end of a
1233 //function. Here, we pushes a dummy variant
1234 //STACK_RETURN_DUMMY onto the stack and then evaluates
1235 //the left node. Consequently, after the return
1236 //expression is evaluated, the stack depth will be two
1237 //instead of one. The CallFunction() method then
1238 //extracts the top of the stack from the function
1239 //scope and puts it on the caller's stack, otherwise
1240 //it puts a '1' to indicate that the function has
1241 //returned successfully.
1242
1243 //This is done to avoid the potentially confusing
1244 //construct in LISP and others that the final
1245 //expression in a function is the return value.
1246
1247 //This is a brittle way to signal a return value.
1248
1249 //
1251
1252 const PNode * node1 = node->GetLeft();
1253 Evaluate(node1);
1254 }
1255 break;
1256
1257 default:
1258 //Signal an error here.
1259 return false;
1260 break;
1261
1262 }
1263 return true;
1264}
1265
1266
1267
1268
1272{
1273
1274 //Set up the globally-accessible structure to allow
1275 //better error reporting.
1276 if(node->GetLineNumber() > -1)
1277 gEvalNode = node;
1278
1279#ifdef PEBL_DEBUG_PRINT
1280 cout << "-------------------------";
1281 cout << "Evaluating DataNode of Value: " << node->GetValue() << endl;;
1282 cout << "Line: " << node->GetLineNumber() << endl;
1283#endif
1284
1285 Variant v1, v2;
1286
1287 //A node of type P_DATA_NODE could be an integer, a float, a variable, a string, etc.
1288 //Grab the variant out of the node. If it is a Variable, extract the value.
1289 //Push the initial value or the variable onto the stack.
1290
1291 v1 = node->GetValue();
1292
1293 switch(v1.GetDataType())
1294 {
1296 {
1297
1298 v2 = mLocalVariableMap.RetrieveValue(v1.GetVariableBaseName());
1299 //Get the name of property being
1300 string property =v1.GetVariablePropertyName();
1301
1302 if(property!="")
1303 {
1304 v2 = PEBLUtility::ResolvePropertyChain(v2, property);
1305 }
1306
1307 Push(v2);
1308 }
1309 break;
1310
1311
1312
1313
1315 {
1317
1318 //Get the name of property being
1319 string property =v1.GetVariablePropertyName();
1320 if(property!="")
1321 {
1322 v2 = PEBLUtility::ResolvePropertyChain(v2, property);
1323 }
1324
1325 Push(v2);
1326 }
1327 break;
1328
1331 case P_DATA_STRING:
1332 case P_DATA_COMPLEXDATA:
1333
1334#ifdef PEBL_DEBUG_PRINT
1335 cout << "Evaluating a normal Variant: ";
1336 cout << v1 << endl;
1337#endif
1338 Push(v1);
1339 break;
1340
1341 case P_DATA_UNDEFINED:
1342 default:
1343 //This should signal an error.
1344 PError::SignalFatalError("In Function [" + mScope + "Undefined Data Type in Evaluate::Evaluate(DataNode)");
1345 return false;
1346 break;
1347 }
1348
1349 return true;
1350}
1351
1352
1357void Evaluator::CallFunction(const OpNode * node)
1358{
1359
1360
1361 // First get the right node (the argument list) and evaluate it.
1362 // This will end up with a list Variant on top of the stack.
1363 const PNode *node1 = node->GetRight();
1364
1365 Evaluate(node1);
1366
1367
1368 // The parameters for a function are in a list on the top of the stack.
1369 // A function should pull the list off the stack and push it onto
1370 // the stack of the new evaluator scope.
1371
1372
1373 //Get the name of the function.
1374
1375 Variant funcname =dynamic_cast<DataNode*>(node->GetLeft())->GetValue();
1376
1377 //If the argument is a custom object, the custom object
1378 //might contain a class-specific method to use instead of the generic function
1379 //The argument is at the top of the stack right now.
1380
1381 Variant v = Peek();
1382 if(v.IsComplexData())
1383 {
1384 //if v is a comlpex data, it should be a parameter list.
1385
1386
1387 PList *plist = dynamic_cast<PList*>(v.GetComplexData()->GetObject().get());
1388 Variant first = plist->First();
1389
1390 if(first.IsComplexData())
1391 {
1392
1393 if( first.GetComplexData()->IsCustomObject())
1394 {
1395
1396 PCustomObject * pco = dynamic_cast<PCustomObject*>(first.GetComplexData()->GetObject().get());
1397
1398
1399 if(OVE_SUCCESS== pco->ValidateProperty(funcname))
1400 {
1401
1402 funcname = Variant(pco->GetProperty(funcname).GetString().c_str(), P_DATA_FUNCTION);
1403
1404 }
1405
1406 }
1407 }
1408 }
1409
1410
1411
1412 const PNode * node2 = mFunctionMap.GetFunction(funcname);
1413
1414
1415#ifdef PEBL_DEBUG_PRINT
1416 cout << "Calling a function with argument list: " << endl;
1417#endif
1418
1419
1420 //Now, node2 will either be a PEBL_LAMBDAFUNCTION or a PEBL_BUILTINFUNCTION
1421 //Lambda functions are just code blocks, but need to be executed in
1422 //a new scope, so need their own evaluator. A built-in function is precompiled
1423 //and doesn't need its own new scope, so don't create one in that case.
1424
1425
1426
1427
1428 switch(((OpNode*)node2)->GetOp())
1429 {
1430 case PEBL_LAMBDAFUNCTION:
1431 { //Need to create a new scope to allow for variable declaration
1432 //within case statement
1433
1434 //get the top item of the stack.
1435 Variant v = Pop();
1436
1437
1438 //Make a new evaluator for the function scope and v at the top of the stack.
1439 Evaluator myEval(v,funcname.GetFunctionName());
1440
1441 //The callstack just keeps track of the series of
1442 //evaluators for debugging purposes.
1443
1444 //Evaluate the lambda function in new scope.
1445
1446
1447 myEval.Evaluate(node2);
1448
1449 //Now that myEval is finished, take the
1450 //node off the callstack.
1451 //gCallStack.Pop();
1452
1453 //If myEval has a stack depth of 1, it does not return anything.
1454 //If myEval has a stack depth of 2, it wants to return the top value.
1455 if(myEval.GetStackDepth() == 1)
1456 {
1457 //Add '1' to the stack as the default return value for a function (i.e., one without
1458 //an explicit return value.)
1459 cout << "Adding dummy value to end of null function\n";
1460 Push(1);
1461 }
1462 else if (myEval.GetStackDepth() == 2)
1463 {
1464 //Get the top of the function evaluator and push it onto the current evaluator.
1465 //cout << "Adding real value of subfunction to end of null function\n";
1466 Variant v1 = myEval.Pop();
1467 Push(v1);
1468 }
1469 }
1470 break;
1471
1472 case PEBL_LIBRARYFUNCTION:
1473 Evaluate(node2);
1474
1475 break;
1476
1477
1478 default:
1479 PError::SignalFatalError("Unknown Function Type in Evaluator::CallFunction");
1480 break;
1481 }
1482
1483}
1484
1486{
1487
1488#ifdef PEBL_DEBUG_PRINT
1489 cout << "Pushing Stack: depth: "<< mStack.size() << "-->" << GetStackDepth() + 1;
1490#endif
1491
1492 if (mStack.size() > mStackMax)
1493 {
1494 PError::SignalFatalError("Maximum Stack Depth Exceeded. Runaway Function?");
1495 }
1496 mStack.push(v);
1497#ifdef PEBL_DEBUG_PRINT
1498 if(mStack.size())cout << " [" << mStack.top() << "] is on top.\n";
1499 else cout << endl;
1500#endif
1501
1502}
1503
1504
1506{
1507#ifdef PEBL_DEBUG_PRINT
1508 cout << "Popping Stack: depth: "<< mStack.size() << "-->" << GetStackDepth() - 1;
1509#endif
1510 if(mStack.size() <=0 )
1511 {
1512 PError::SignalFatalError("Error: Tried to Pop an empty stack.");
1513 }
1514
1515 Variant v = mStack.top();
1516 mStack.pop();
1517#ifdef PEBL_DEBUG_PRINT
1518 if(mStack.size())cout << " [" << mStack.top() << "] is on top.\n";
1519 else cout << endl;
1520#endif
1521 return v;
1522}
1523
1524
1526{
1527#ifdef PEBL_DEBUG_PRINT
1528 cout << "Peeking at top of stack: "<< mStack.size() << endl;
1529#endif
1530 if(mStack.size() <=0 )
1531 {
1532 PError::SignalFatalError("Error: Tried to Peek at an empty stack.");
1533 }
1534
1535 return mStack.top();
1536}
1537
1538
1540{
1541 return gGlobalVariableMap.Exists(v) || mLocalVariableMap.Exists(v);
1542}
#define NULL
Definition BinReloc.cpp:317
#define pDouble
Definition Defs.h:7
Evaluator * myEval
Definition PEBL.cpp:188
@ OVE_SUCCESS
Definition PEBLObject.h:38
@ PEAT_VARIABLE
Definition PError.h:49
@ PEAT_LIST
Definition PError.h:61
@ PEBL_OP_NODE
Definition PNode.h:35
@ PEBL_DATA_NODE
Definition PNode.h:36
@ P_DATA_UNDEFINED
Definition Variant.h:41
@ P_DATA_LOCALVARIABLE
Definition Variant.h:48
@ P_DATA_STACK_SIGNAL
Definition Variant.h:42
@ P_DATA_STRING
Definition Variant.h:47
@ P_DATA_COMPLEXDATA
Definition Variant.h:50
@ P_DATA_NUMBER_INTEGER
Definition Variant.h:45
@ P_DATA_NUMBER_FLOAT
Definition Variant.h:46
@ P_DATA_FUNCTION
Definition Variant.h:43
@ P_DATA_GLOBALVARIABLE
Definition Variant.h:49
@ STACK_LIST_HEAD
Definition Variant.h:56
@ STACK_BREAK
Definition Variant.h:59
@ STACK_RETURN_DUMMY
Definition Variant.h:57
const Variant & GetValue() const
Definition PNode.h:152
This class has got everything you need to evaluate stuff.
bool Evaluate(const PNode *node)
static const PNode * gEvalNode
void Push(Variant v)
static PCallStack gCallStack
bool IsVariableName(Variant v)
static VariableMap gGlobalVariableMap
Variant Peek()
Variant Pop()
static FunctionMap mFunctionMap
Initiate some static member data.
void CallFunction(const OpNode *node)
int GetStackDepth()
PNode * GetFunction(const std::string &funcname)
PNode * GetRight() const
Definition PNode.h:115
std::string GetOpName() const
Definition PNode.cpp:245
int GetOp() const
Definition PNode.h:111
PNode * GetLeft() const
Definition PNode.h:114
int Size()
Definition PError.h:113
void Pop()
Definition PError.h:112
counted_ptr< PEBLObjectBase > GetObject() const
bool IsCustomObject() const
This class simply represent an abstract text-based object.
virtual ObjectValidationError ValidateProperty(std::string, Variant v) const
virtual Variant GetProperty(std::string) const
Definition PList.h:45
std::vector< Variant >::const_iterator End() const
Definition PList.cpp:132
std::vector< Variant >::const_iterator Begin() const
Definition PList.cpp:127
unsigned long Length() const
Definition PList.h:89
void PushBack(const Variant &v)
Definition PList.cpp:149
Variant First()
Definition PList.cpp:169
Definition PNode.h:45
int GetLineNumber() const
Definition PNode.h:69
PNODE_TYPE GetType() const
Access mType data.
Definition PNode.h:61
std::string GetFunctionName() const
Definition PNode.h:71
bool Exists(const std::string &varname)
void AddVariable(const std::string &varname, const Variant &val)
Variant RetrieveValue(const std::string &varname)
bool IsComplexData() const
Definition Variant.cpp:984
pInt GetInteger() const
Definition Variant.cpp:997
bool IsGlobalVariable() const
Definition Variant.cpp:964
std::string GetVariablePropertyName() const
Definition Variant.cpp:1185
pFunc GetFunctionPointer() const
Definition Variant.cpp:1280
bool IsStackSignal() const
Definition Variant.cpp:989
std::string GetVariableName() const
Definition Variant.cpp:1138
std::string GetString() const
Definition Variant.cpp:1056
bool IsInteger() const
Definition Variant.cpp:942
StackSignalType GetSignal() const
Definition Variant.cpp:1265
PComplexData * GetComplexData() const
Definition Variant.cpp:1299
bool IsLocalVariable() const
Definition Variant.cpp:959
std::string GetVariableBaseName() const
Definition Variant.cpp:1160
std::string GetFunctionName() const
Definition Variant.cpp:1208
VariantDataType GetDataType() const
This returns the type as an enum.
Definition Variant.cpp:885
X * get() const
Definition rc_ptrs.h:110
Variant ResolvePropertyChain(Variant obj, const std::string &propertyChain)
void SetPropertyChain(Variant obj, const std::string &propertyChain, Variant value)
void AssertType(Variant v, int type, const std::string &outsidemessage)
void SignalFatalError(const std::string &message)
int count
Definition test.cpp:12