PEBL 2.2
Psychology Experiment Building Language - Cross-platform psychological experiment development system
Evaluator-es.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-es.cpp
4// Purpose: Defines an class that can evaluate PNodes (Iterative Evaluator)
5// Author: Shane T. Mueller, Ph.D.
6// Copyright: (c) 2003--2017 Shane T. Mueller <smueller@obereed.net>
7// License: GPL 2
8//
9// ARCHITECTURE NOTE - Iterative Evaluator for Emscripten:
10//
11// This is the ITERATIVE evaluator implementation. It uses manual stack
12// management (mNodeStack and mStack) instead of C++ call stack recursion to
13// evaluate the PEBL abstract syntax tree (AST). Operations are split into
14// "head" and "tail" phases (e.g., PEBL_ADD -> PEBL_ADD_TAIL), with the
15// evaluator loop managing execution order explicitly.
16//
17// WHY THIS EXISTS:
18// This evaluator was specifically designed for Emscripten/WebAssembly builds
19// that use the Asyncify feature. The recursive evaluator (Evaluator.cpp) has
20// a hard recursion depth limit of 1 for PEBL user-defined recursive functions
21// when compiled with Asyncify, crashing at depth >= 2 with "index out of
22// bounds" errors. This occurs because Asyncify transforms the call stack to
23// enable pause/resume operations, breaking the recursive evaluator's function
24// call mechanism.
25//
26// This iterative evaluator avoids C++ call stack recursion entirely, using
27// manual stack management that is compatible with Asyncify transformations.
28// Testing confirms it handles arbitrary recursion depth correctly on
29// Emscripten.
30//
31// IMPLEMENTATION:
32// A single Evaluate1() loop processes nodes from mNodeStack. Each operation
33// type pushes child nodes and "tail" operations onto mNodeStack, with results
34// stored in mStack. This transforms recursive tree traversal into iterative
35// loop execution, avoiding C++ function call depth issues.
36//
37// The PEBL_ITERATIVE_EVAL macro controls which evaluator is compiled.
38// See src/apps/Globals.h for the selection logic.
39//
40// This file is part of the PEBL project.
41//
42// PEBL is free software; you can redistribute it and/or modify
43// it under the terms of the GNU General Public License as published by
44// the Free Software Foundation; either version 2 of the License, or
45// (at your option) any later version.
46//
47// PEBL is distributed in the hope that it will be useful,
48// but WITHOUT ANY WARRANTY; without even the implied warranty of
49// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
50// GNU General Public License for more details.
51//
52// You should have received a copy of the GNU General Public License
53// along with PEBL; if not, write to the Free Software
54// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
56
57
58#include "Evaluator-es.h"
59#include "PNode.h"
60#include "grammar.tab.hpp"
61#include "VariableMap.h"
62#include "Variant.h"
63#include "PComplexData.h"
64#include "PList.h"
65#include "FunctionMap.h"
66#include "../utility/PEBLUtility.h"
67#include "../utility/PError.h"
68#include "../utility/rc_ptrs.h"
69#include "../utility/Defs.h"
70
71#include "../objects/PCustomObject.h"
72#include "../apps/Globals.h"
73#include "../devices/PEventLoop-es.h"
74
75#include <iostream>
76#include <string>
77#include <strstream>
78#include <math.h>
79#include <algorithm>
80
81#undef PEBL_DEBUG_PRINT
82//#define PEBL_DEBUG_PRINT 1
83
84using std::cout;
85using std::endl;
86using std::flush;
87//using std::list;
88using std::string;
89using std::vector;
90
91
93 mStackMax(10000),
94 mScope("Base Scope")
95{
96#ifdef PEBL_DEBUG_PRINT
97 cout << "Creating Evaluator: " << mScope << endl;
98#endif
99
100
101 mEventLoop = new PEventLoop();
102
104}
105
106
107Evaluator::Evaluator(Variant & stacktop, string scope):
108 // mCallStack(callstack),
109 mStackMax(10000),
110 mScope(scope)
111{
112#ifdef PEBL_DEBUG_PRINT
113 cout << "Creating Evaluator: " << mScope << endl;
114#endif
115
116 //add everything in callstack onto mCallStack
117 //mCallStack = callstack;
118
119 mEventLoop = new PEventLoop();
120
121 //Push the current evalnode onto the stack, if
122 //it exists.
123 if(gEvalNode)
124 {
126
127 }
128 //Initialize the evaluator scope with a variant which is a list of variables
129 Push(stacktop);
130}
131
132
134{
135#ifdef PEBL_DEBUG_PRINT
136 cout << "Deleting Evaluator: " << mScope << endl;
137#endif
138
139 //NOTE: gCallStack is a GLOBAL variable shared across all evaluator instances.
140 //We should NOT clear it in the destructor, as it may contain entries from other
141 //evaluators that are still running. Each function call properly pushes/pops
142 //its own entry via PEBL_LAMBDAFUNCTION/PEBL_FUNCTION_TAIL_LIBFUNCTION.
143 //
144 //while( gCallStack.Size())
145 // gCallStack.Pop();
146
147
148}
149
150bool Evaluator::Evaluate1(const PNode * node)
151{
152
153#ifdef PEBL_EMSCRIPTEN
154
155 if(node == NULL) PError::ExitQuietly("Trying to evaluate null node\n");
156#else
157
158 if(node == NULL) PError::SignalFatalError("Trying to evaluate null node\n");
159#endif
160 //Set up the globally-accessible structure to allow
161 //better error reporting. Only change it if the new node's
162 //line number is greater than -1; if not, it is a PEBL-generated
163 //node that won't give very good information.
164 if(node->GetLineNumber() > -1)
165 gEvalNode = node;
166
167
168
169#ifdef PEBL_DEBUG_PRINT
170 cout << "Line: " << node->GetLineNumber() << endl;
171 cout << "PDP::Type: " << node->GetType() << endl;
172#endif
173
174 if(node->GetType() == PEBL_OP_NODE)
175 {
176 return Evaluate1((OpNode*)node);
177 }
178 else if (node->GetType() == PEBL_DATA_NODE)
179 {
180 return Evaluate1((DataNode*)node);
181 }
182 else
183 {
184#ifdef PEBL_DEBUG_PRINT
185 cout << "ERROR IN GENERIC EVALUATOR::EVALUATE" << endl;
186#endif
187 return false;
188 }
189
190 return true;
191}
192
193void Eval1(void*)
194{
195 myEval->Evaluate1();
196}
197
198
200{
201 const PNode * node = mNodeStack.top();
202
203#ifdef PEBL_DEBUG_PRINT
204 cout << "---------------async--a--------\n";
205#endif
206
207
208 mNodeStack.pop();
209
210 return Evaluate1(node);
211}
212
213
215
216bool Evaluator::Evaluate1(const OpNode * node)
217{
218
219 int numargs = -1;
220 if(node == NULL) PError::SignalFatalError("Trying to evaluate null node\n");
221 //Set up the globally-accessible structure to allow
222 //better error reporting.
223 if(node->GetLineNumber() > -1)
224 gEvalNode = node;
225
226#ifdef PEBL_DEBUG_PRINT
227 cout << "---------------async--b--------Evaluating OpNode ["<< node->GetOp() << "] of Type: " << node->GetOpName() << "------------\n";
228
229#endif
230
231
232
233 switch(node->GetOp())
234 {
235
236 case NULL:
237 break;
238
239 /******************************
240 Handled
241 ***********************************/
242
243 case PEBL_ASSIGN:
244 {
245 //The left node should contain a DataNode of Variant type Variable.
246 //The right node should contain an expression that evaluates to a number
247 //that should be assigned to the datanode.
248
249
250 const PNode * node2 = node->GetRight();
251 //Evaluate(node2);
252 const OpNode * tail = new OpNode(PEBL_ASSIGN_TAIL,NULL,NULL,
253 node->GetFilename(), node->GetLineNumber());
254
255
256 const PNode * variablenode = node->GetLeft();
257
258 mNodeStack.push(variablenode);
259 mNodeStack.push(tail);
260 mNodeStack.push(node2);
261
262 }
263
264 break;
265
266 case PEBL_ASSIGN_TAIL:
267 {
268 //The evaluated expression is now at the top of the stack.
269 //Leave it there, because this statement should return that value; but assign
270 //it to v2.
271 Variant v2 = mStack.top();
272
273 const PNode * node1 = mNodeStack.top();
274 mNodeStack.pop();
275
276 //Extract the variable name from the node.
277 Variant v1=((DataNode*)node1)->GetValue();
278
279
280#ifdef PEBL_DEBUG_PRINT
281 cout << "Initial Variable Name: [" << v1.GetVariableName() << "]" << endl;
282#endif
283
284 //Get the name of property being
285 string property =v1.GetVariablePropertyName();
286
287 //Add the variable name/value pair to the appropriate map structure
288 if(v1.IsLocalVariable())
289 {
290
291 if(property == "")
292 {
293 mLocalVariableMap.AddVariable(v1.GetVariableName(), v2);
294 }
295 else
296 {
297 //otherwise get the object from the variable store
298 //and set its property.
299
300 Variant v3 = mLocalVariableMap.RetrieveValue(v1.GetVariableBaseName());
301 PEBLUtility::SetPropertyChain(v3, property, v2);
302 }
303
304
305 }
306 else
307 {
308
309 if(property == "")
310 {
312 }
313 else
314 {
315 //otherwise get the object from the variable store
316 //and set its property.
318 PEBLUtility::SetPropertyChain(v3, property, v2);
319 }
320
321 }
322
323
324 }
325 break;
326
327 /*****************************
328 HANDLED ITERATIVE
329 ************************************/
330 case PEBL_ADD:
331 {
332 //Execute left and right nodes, which puts results on stack
333 const PNode * node1 = node->GetLeft();
334 const PNode * node2 = node->GetRight();
335 //Create new opnode to handle tail...
336 const OpNode * tail = new OpNode(PEBL_ADD_TAIL,NULL,NULL,node->GetFilename(), node->GetLineNumber());
337
338 mNodeStack.push(tail);
339 mNodeStack.push(node2);
340 mNodeStack.push(node1);
341
342 }
343 break;
344
345
346 case PEBL_ADD_TAIL:
347 {
348 //Get the top two items from the stack. The rightmost will
349 //be on top.
350 Variant v2 = Pop();
351 Variant v1 = Pop();
352
353 //There maybe should be more error checking to ensure
354 //that the atoms are numbers.
355
356 Push(v1+v2);
357
358 }
359 break;
360
361 /*******************************
362 ITERATED COMPLETE:
363 *******************************/
364 case PEBL_DIVIDE:
365 {
366 //Execute left and right nodes, which puts results on stack
367 const PNode * node1 = node->GetLeft();
368 const PNode * node2 = node->GetRight();
369 const OpNode * tail = new OpNode(PEBL_DIVIDE_TAIL,NULL,NULL,
370 node->GetFilename(), node->GetLineNumber());
371
372 mNodeStack.push(tail);
373 mNodeStack.push(node2);
374 mNodeStack.push(node1);
375
376 }
377 break;
378
379 case PEBL_DIVIDE_TAIL:
380 {
381 //Get the top two items from the stack
382 Variant v2 = Pop();
383 Variant v1 = Pop();
384
385 Push(v1/v2);
386 }
387 break;
388
389
390 /***************************
391 ITERATED!!!
392 **************************/
393 case PEBL_MULTIPLY:
394 {
395 //Execute left and right nodes, which puts results on stack
396 const PNode * node1 = node->GetLeft();
397 const PNode * node2 = node->GetRight();
398 const OpNode * tail = new OpNode(PEBL_MULTIPLY_TAIL,NULL,NULL,
399 node->GetFilename(), node->GetLineNumber());
400
401 mNodeStack.push(tail);
402 mNodeStack.push(node2);
403 mNodeStack.push(node1);
404 }
405 break;
406 case PEBL_MULTIPLY_TAIL:
407 {
408 //Get the top two items from the stack. The right will be on top
409 Variant v2 = Pop();
410 Variant v1 = Pop();
411
412 Push(v1*v2);
413 }
414 break;
415
416
417 /******************************
418 ITERATED!!!
419 ***********************************/
420 case PEBL_POWER:
421 {
422 //Execute left and right nodes, which puts results on stack
423 const PNode * node1 = node->GetLeft();
424 const PNode * node2 = node->GetRight();
425 const OpNode * tail = new OpNode(PEBL_POWER_TAIL,NULL,NULL,
426 node->GetFilename(), node->GetLineNumber());
427 mNodeStack.push(tail);
428 mNodeStack.push(node2);
429 mNodeStack.push(node1);
430
431 }
432 break;
433 case PEBL_POWER_TAIL:
434 {
435
436 //Get the top two items from the stack
437 Variant v2 = Pop();
438 Variant v1 = Pop();
439
440 Push(Variant(pow((pDouble)v1,(pDouble)v2)));
441 }
442 break;
443
444
445 /******************************
446 ITERATED!!!
447 ***********************************/
448 case PEBL_SUBTRACT:
449 {
450 //Execute left and right nodes, which puts results on stack
451 const PNode * node1 = node->GetLeft();
452 const PNode * node2 = node->GetRight();
453
454 const OpNode * tail = new OpNode(PEBL_SUBTRACT_TAIL,NULL,NULL,
455 node->GetFilename(), node->GetLineNumber());
456 mNodeStack.push(tail);
457 mNodeStack.push(node2);
458 mNodeStack.push(node1);
459
460 }
461 break;
462 case PEBL_SUBTRACT_TAIL:
463 {
464 //Get the top two items from the stack. The right will be on top
465 Variant v2 = Pop();
466 Variant v1 = Pop();
467
468 Push(v1 - v2);
469 }
470 break;
471
472
473 /**************************
474 HANDLED
475 **************************/
476 case PEBL_AND:
477 {
478 // Short-circuit AND: store node2 in the tail so PEBL_AND_TAIL
479 // can conditionally schedule it after seeing v1.
480 PNode * node1 = node->GetLeft();
481 PNode * node2 = node->GetRight();
482 const OpNode * tail = new OpNode(PEBL_AND_TAIL, node2, NULL,
483 node->GetFilename(), node->GetLineNumber());
484 mNodeStack.push(tail);
485 mNodeStack.push(node1);
486 }
487 break;
488 case PEBL_AND_TAIL:
489 {
490 if(node->GetLeft() != NULL)
491 {
492 // Phase 1: v1 is on the data stack; decide whether to evaluate v2.
493 Variant v1 = Pop();
494 if(!(bool)v1)
495 {
496 // Short-circuit: left is false, result is false.
497 Push(Variant((pInt)0));
498 }
499 else
500 {
501 // Left is true: push v1 back, schedule node2, then a finish TAIL.
502 Push(v1);
503 const OpNode * finish = new OpNode(PEBL_AND_TAIL, NULL, NULL,
504 node->GetFilename(), node->GetLineNumber());
505 mNodeStack.push(finish);
506 mNodeStack.push(node->GetLeft()); // node2
507 }
508 }
509 else
510 {
511 // Phase 2 (finish): both v1 and v2 are on the data stack.
512 Variant v2 = Pop();
513 Variant v1 = Pop();
514 Push(v1 && v2);
515 }
516 }
517 break;
518
519 /*****************************
520 HANDLED
521 ************************************/
522 case PEBL_OR:
523 {
524 // Short-circuit OR: store node2 in the tail so PEBL_OR_TAIL
525 // can conditionally schedule it after seeing v1.
526 PNode * node1 = node->GetLeft();
527 PNode * node2 = node->GetRight();
528 const OpNode * tail = new OpNode(PEBL_OR_TAIL, node2, NULL,
529 node->GetFilename(), node->GetLineNumber());
530 mNodeStack.push(tail);
531 mNodeStack.push(node1);
532 }
533 break;
534 case PEBL_OR_TAIL:
535 {
536 if(node->GetLeft() != NULL)
537 {
538 // Phase 1: v1 is on the data stack; decide whether to evaluate v2.
539 Variant v1 = Pop();
540 if((bool)v1)
541 {
542 // Short-circuit: left is true, result is true.
543 Push(Variant((pInt)1));
544 }
545 else
546 {
547 // Left is false: push v1 back, schedule node2, then a finish TAIL.
548 Push(v1);
549 const OpNode * finish = new OpNode(PEBL_OR_TAIL, NULL, NULL,
550 node->GetFilename(), node->GetLineNumber());
551 mNodeStack.push(finish);
552 mNodeStack.push(node->GetLeft()); // node2
553 }
554 }
555 else
556 {
557 // Phase 2 (finish): both v1 and v2 are on the data stack.
558 Variant v2 = Pop();
559 Variant v1 = Pop();
560 Push(v1 || v2);
561 }
562 }
563 break;
564
565 /*******************************
566 HANDLED:
567 **********************************/
568 case PEBL_NOT:
569 {
570 //Evaluate left nodes, and negate it.
571
572 //Execute left and right nodes, which puts results on stack
573 const PNode * node1 = node->GetLeft();
574 const OpNode * tail = new OpNode(PEBL_NOT_TAIL,NULL,NULL,
575 node->GetFilename(), node->GetLineNumber());
576
577 mNodeStack.push(tail);
578 mNodeStack.push(node1);
579
580
581 }
582 break;
583
584 case PEBL_NOT_TAIL:
585 {
586 //Get the top two items from the stack. The right will be on top
587 Variant v1 = Pop();
588 Push(!v1);
589 }
590 break;
591
592
593 /**************************
594 HANDLED
595 *********************************/
596 case PEBL_IF:
597 {
598
599 //Left node is the expression test;
600 //Right node is the code block to execute if true.
601
602 const PNode * node1 = node->GetLeft();
603 const OpNode * tail = new OpNode(PEBL_IF_TAIL,NULL,NULL,
604 node->GetFilename(), node->GetLineNumber());
605
606 const PNode * codeblock = node->GetRight();
607
608 //Put the codeblock on the stack; we will remove it
609 //later if need be.
610
611 mNodeStack.push(codeblock);
612 mNodeStack.push(tail);
613 mNodeStack.push(node1);
614
615 }
616 break;
617
618 case PEBL_IF_TAIL:
619 {
620 Variant v1 = Pop();
621 if(v1)
622 {
623 //The test was true, so execute the codeblock.
624 //We need a tail to check if the codeblock returned a STACK_BREAK
625 const OpNode * tail2 = new OpNode(PEBL_IF_TAIL2,NULL,NULL,
626 node->GetFilename(), node->GetLineNumber());
627 mNodeStack.push(tail2);
628 //The codeblock is already on the node stack and will execute next
629
630 }
631 else
632 {
633 //The code block we want to execute is on top...
634 //remove it because the test failed.
635
636 mNodeStack.pop();
637
638 //Put a dummy value on the stack as the return value.
639 Push(0);
640 }
641 }
642 break;
643
644 case PEBL_IF_TAIL2:
645 {
646 //The codeblock has executed. Check if it returned a STACK_BREAK
647 //If so, propagate it; otherwise leave the result as-is
648 //Actually, we don't need to do anything here - the result is already
649 //on the stack and will be checked by the enclosing STATEMENTS node
650 }
651 break;
652
653 /*****************************************************************/
654 case PEBL_IFELSE:
655 {
656 //Left node is the expression test;
657 //Right node is a PEBL_ELSE node, which
658 //has both code blocks on it, and
659 //decides what to do based on what is
660 //on the top o' the stack.
661
662
663 //Execute left node, which puts results on stack
664 const PNode * node1 = node->GetLeft();
665 const PNode * node2 = node->GetRight();
666
667 mNodeStack.push(node2);
668 mNodeStack.push(node1);
669
670 }
671 break;
672
673 /****************************
674
675 HANDLED
676 *************************************/
677 case PEBL_ELSE:
678 {
679 //This looks on the top of the stack and
680 //executes the left node if true; right node if false.
681
682 Variant v1 = Pop();
683
684 const PNode * node1;
685 if(v1)
686 {
687 node1 = node->GetLeft();
688 }
689 else
690 {
691 node1 = node->GetRight();
692 }
693
694 //Add a tail to check if the codeblock returns a STACK_BREAK
695 const OpNode * tail = new OpNode(PEBL_ELSE_TAIL,NULL,NULL,
696 node->GetFilename(), node->GetLineNumber());
697 mNodeStack.push(tail);
698 mNodeStack.push(node1);
699 }
700 break;
701
702 case PEBL_ELSE_TAIL:
703 {
704 //The codeblock has executed. Check if it returned a STACK_BREAK
705 //If so, propagate it; otherwise leave the result as-is
706 //The result is already on the stack and will be checked by the enclosing STATEMENTS node
707 }
708 break;
709
710 /*****************************************************************/
711 case PEBL_LAMBDAFUNCTION:
712 {
713 // This is the top node of an anonymous function. It is a
714 // node with two children: on the left is a variable list, and
715 // on the right is a code block. To make it a named function, just
716 // the parent node will be a PEBL_FUNCTION OpNode with a left child
717 // which is a function datanode.
718
719 // When this function is called, the top of the stack is a list that contains
720 // the bindings for the variables. Get the list and assign the values to the
721 // variables one-by-one, then execute the code block.
722
723 //Get the variable list.
724 const PNode * node1 = node->GetLeft();
725
726 //Get the argument list.
727 Variant v1 = Pop();
728
729 //Create a list to use.
730 //If v1 is a stacksignal list_head, there are no arguments
731 //provided.
732
733
735
736 if( v1.IsStackSignal() && v1.GetSignal() == STACK_LIST_HEAD)
737 {
738 //v1 is empty, so make a dummy parameter list to send.
739
740 tmpList = counted_ptr<PEBLObjectBase>(new PList());
741 }
742
743
744 if( v1.IsComplexData())
745 {
746
747 //extract the object out of v1 to send.
748 tmpList = v1.GetComplexData()->GetObject();
749 }
750
751
752
753
754 Variant v2 = 0;
755
756
757 //iterate through the lists and assign values to variables.
758 PList * tmp = (PList*)(tmpList.get());
759
760 vector <Variant>::iterator p = tmp->Begin();
761 Variant vdef = 0; // Initialize to prevent uninitialized reads
762
763 while(node1)
764 {
765 //Each variable could be either a variable name (global or local)
766 //or a varpair--a node pairing a global/local with a value.
767 vdef = 0; // Reset to 0 each iteration
768
769 //This loads the default values initially when they appear,
770 //and then overwrites them if actual values exist. This means
771 //the default value(if global) needs to exist--it probably shoudn't have to
772 bool hasdefault = false;
773 if(((OpNode*)(((OpNode*)node1)->GetLeft()))->GetOp()==PEBL_VARPAIR)
774 {
775 //this variable has a default value....
776 hasdefault = true;
777 //v2 is the variable name.
778 v2 = ((DataNode*) ( ((OpNode*)(((OpNode*)node1)->GetLeft()))->GetLeft()))->GetValue();
779
780 // Get the default value node - could be DataNode or OpNode
781 PNode* defaultNode = ((OpNode*)(((OpNode*)node1)->GetLeft()))->GetRight();
782
783 if(defaultNode->GetType() == PEBL_OP_NODE)
784 {
785 // The default value is an expression (e.g., -1, Min(data)).
786 // Evaluate it in the current function scope to get its value.
787 // This allows referencing previously-bound parameters.
788 mNodeStack.push(defaultNode);
789 while(mNodeStack.size() > 0)
790 {
791 Evaluate1();
792 }
793 vdef = Pop();
794 }
795 else
796 {
797 //It is a data node--default value.
798 vdef = ((DataNode*)defaultNode)->GetValue();
799 }
800 }
801 else
802 {
803 //it is a pure value here
804 v2 = ((DataNode*)(((OpNode*)node1)->GetLeft()))->GetValue();
805 }
806
807 // Now bind the parameter immediately (either from argument or default)
808 // This allows later parameters' defaults to reference earlier parameters.
809 if(p != tmp->End())
810 {
811 // We have an argument value - use it
812 mLocalVariableMap.AddVariable(v2, *p);
813 p++;
814 node1 = ((OpNode*)node1)->GetRight();
815 }
816 else if(hasdefault)
817 {
818 // No argument provided, but we have a default value
819 // Resolve variable references in the default value
820 if(vdef.IsGlobalVariable())
821 {
823 }
824 else if (vdef.IsLocalVariable())
825 {
826 // A local variable may be bound to a previously-specified parameter
827 vdef = mLocalVariableMap.RetrieveValue(vdef);
828 }
829 mLocalVariableMap.AddVariable(v2, vdef);
830 node1 = ((OpNode*)node1)->GetRight();
831 }
832 else
833 {
834 // No argument and no default - too few parameters
835 string message = "Too few arguments passed to function [" + mScope + "].";
836
837 if(mScope == "Start")
838 message += " (Make sure Start function has only one variable).";
839
841 }
842 }
843
844
845
846 //if(tmp && tmp->Length() > 0)
847 if(p != tmp->End())
848 {
849 //Too many arguments.
850 string message = string("Too many arguments passed to function [" + mScope + "].");
851
852 if(mScope == "Start") message += " (Make sure Start function has a variable).";
854 }
855
856
857 //Now, get the code block and execute it.
858 const PNode * node2 = node->GetRight();
859
860 //No tail function needed?
861 mNodeStack.push(node2);
862 }
863 break;
864
865 /*******************************
866 HANDLED (Nothing needed)
867 **********************************/
868 case PEBL_LIBRARYFUNCTION:
869 {
870
871
872 // This type of function is built in and precompiled. The loader examines
873 // the parse tree and
874 // identifies each function that is used.
875
876 //The left child of a PEBL_LIBRARYFUNCTION contains an PEBL_AND node containing two datanodes
877 //each containing integers describing the min and max number of arguments, respectively.
878
879
880 const OpNode * node0 =(OpNode*)(node->GetLeft());
881 //We should be able to tell the name of the function
882
883 std::string name = node->GetFunctionName();
884
885
886
887
888 const unsigned int min = ((DataNode*)(node0->GetLeft()))->GetValue().GetInteger();
889 const unsigned int max = ((DataNode*)(node0->GetRight()))->GetValue().GetInteger();
890
891
892
893 //The right child of a PEBL_LIBRARYFUNCTION contains a datanode which
894 //has a function pointer in it.
895
896 const PNode * node1 = node->GetRight();
897 Variant v1 = ((DataNode*)node1)->GetValue();
898
899 //All built-in functions take a single parameter: a Variant list.
900 //This variant should be on the top of the stack right now. So get it.
901 Variant v2 = Pop();
902
903 //Before we execute, check to see if v2 has a length between min and max.
904
905
906 PList * tmp = NULL;
907 if (v2.IsStackSignal())
908 numargs = 0;
909 else
910 {
911 if(v2.IsComplexData())
912 {
913 if((v2.GetComplexData())->IsList())
914 {
915 //#if !defined(PEBL_EMSCRIPTEN)
916
917 //Dont check parameter numbers in EMSCRIPTEN
918
919 tmp = (PList*)(v2.GetComplexData()->GetObject().get());
920 numargs = tmp->Length();
921 //#endif
922 }
923 else
924 {
925 cerr << "UNHANDLED ELSE CASE. NOT A LIST\n" ;
926 }
927 }
928 }
929
930 //#if !defined(PEBL_EMSCRIPTEN)
931 if(numargs < min || numargs > max)
932 {
933
934 Variant message = Variant("In scope [") + mScope + Variant("]:") +
935 Variant("function [") +name+Variant("]:")+
936 Variant("Incorrect number of arguments.. Wanted between ")+
937 Variant((int)min) + Variant(" and ") +Variant((int) max) + Variant(" but got ") + Variant( (int)numargs);
938
940 }
941 //#endif
942
943
944
945 //Execute the functionpointer and push the results onto the stack.
946 //Note that this will happen 'synchronously'; not through the eval loop.
947 Push((v1.GetFunctionPointer())(v2));
948 }
949
950 break;
951
952
953 /*********************************
954 HANDLED (Nothing needed directly)
955 ********************************/
956 case PEBL_FUNCTION:
957 {
958 // This controls the execution of a function. (not the
959 // loading of a defined function). A function node
960 // has a function datanode on its left child (specifies the function name)
961 // and an arglist (the head of a list of arguments) on its right child.
962
963 //Note that there is no direct link to the actual function code--it needs
964 //to be accessed via a lookup map. Once that code is identified and the paramaters are handled
965 //here, the rest is managed via a PEBL_LAMBDAFUNCTION node above.
966
967
968 //We need to evaluate the arguments on the right, then pick up later.
969
970 const PNode *node1 = node->GetRight();
971
972 const OpNode * tail = new OpNode(PEBL_FUNCTION_TAIL1,NULL,NULL,
973 node->GetFilename(), node->GetLineNumber());
974
975
976 //get the function name now, and put it on the stack, to get back later.
977 Variant funcname = dynamic_cast<DataNode*>(node->GetLeft())->GetValue();
978
979 Push(funcname); //Put the function name on the stack.
980
981
982 mNodeStack.push(tail);
983 mNodeStack.push(node1);
984 }
985 break;
986
987 case PEBL_FUNCTION_TAIL1:
988 {
989
990 //The arguments should have been evaluated, and are at the top of the stack.
991 //Right below them is the function name.
992
993 // The parameters for a function are in a list on the top of the stack.
994 // A function should pull the list off the stack and push it onto
995 // the stack of the new evaluator scope.
996
997 // cout << dynamic_cast<DataNode*>(node->GetLeft())->GetValue() << endl;
998 //Get the name of the function.
999
1000 Variant args = Pop();
1001
1002 Variant funcname = Pop(); //We would just put this back later, so only take a look.
1003
1004
1005 //Check for custom object method dispatch before looking up the function
1006 //If the first argument is a custom object with a property matching the function name,
1007 //redirect to that function name instead
1008 if(args.IsComplexData())
1009 {
1010 PList *plist = dynamic_cast<PList*>(args.GetComplexData()->GetObject().get());
1011 if(plist && plist->Length() > 0)
1012 {
1013 Variant first = plist->First();
1014
1015 if(first.IsComplexData())
1016 {
1017 if(first.GetComplexData()->IsCustomObject())
1018 {
1019 PCustomObject * pco = dynamic_cast<PCustomObject*>(first.GetComplexData()->GetObject().get());
1020
1021 if(OVE_SUCCESS == pco->ValidateProperty(funcname))
1022 {
1023 funcname = Variant(pco->GetProperty(funcname).GetString().c_str(), P_DATA_FUNCTION);
1024 }
1025 }
1026 }
1027 }
1028 }
1029
1030 //put the arguments back on top of the stack for the second tail, in
1031 //reverse order.
1032 Push(args);
1033
1034
1035 //The funcname is just used to update the scope variable. Maybe we could do that now instead
1036 //of putting it on the stack?
1037 Push(funcname);
1038
1039
1040 //now, get the actual function code from the function map:
1041 const PNode * node2 = mFunctionMap.GetFunction(funcname);
1042
1043
1044#ifdef PEBL_DEBUG_PRINT
1045 cout << "Calling a function with argument list. " << endl;
1046#endif
1047
1048
1049 //Now, node2 will either be a PEBL_LAMBDAFUNCTION or a PEBL_BUILTINFUNCTION
1050 //Lambda functions are just code blocks, but need to be executed in
1051 //a new scope. A built-in function is precompiled
1052 //and doesn't need its own new scope, so don't create one in that case.
1053
1054
1055 mNodeStack.push(node2);
1056
1057
1058
1059 const OpNode * tail2 = new OpNode(PEBL_FUNCTION_TAIL2,NULL,NULL,
1060 node->GetFilename(), node->GetLineNumber());
1061
1062 //schedule handling by the base function code. It will either
1063 //handle the lamdafunction path or the builtinfunction path.
1064 mNodeStack.push(tail2);
1065
1066 }
1067 break;
1068
1069 case PEBL_FUNCTION_TAIL2:
1070 {
1071 //function name is on top; next are the arguments.
1072 Variant funcname = Pop();
1073
1074 //right now, the top node should be the result of node->GetOp()...
1075
1076 const OpNode * node2 = (OpNode*)(mNodeStack.top());
1077 mNodeStack.pop();
1078
1079#ifdef PEBL_DEBUG_PRINT
1080 cout << "In function_tail2 OpNode ["<< node2->GetOp() << "] of Type: " << node2->GetOpName() << "------------\n";
1081#endif
1082
1083
1084 switch(((OpNode*)node2)->GetOp())
1085 {
1086
1087
1088
1089 case PEBL_LIBRARYFUNCTION:
1090 {
1091
1092 mNodeStack.push(node2);
1093
1094 }
1095 break;
1096
1097
1098 case PEBL_LAMBDAFUNCTION:
1099 { //Need to create a new scope to allow for variable declaration
1100 //within case statement
1101
1102 //We want to avoid creating multiple evaluators so that we can
1103 //more easily async the evaluator loop. So, create a stack
1104 //to hold them.
1105
1106 mVariableMapStack.push(mLocalVariableMap);
1107 //VariableMap & tmpmap = mLocalVariableMap;
1108
1109
1110 mScopeStack.push(mScope);
1111
1112
1113 mScope = funcname.GetFunctionName();
1114
1115 //Push the call site onto the call stack for error reporting
1116 gCallStack.Push(node);
1117
1118 //This cleans up the return arguments of the lambda function.
1119 const OpNode * tail = new OpNode( PEBL_FUNCTION_TAIL_LIBFUNCTION,NULL,NULL,
1120 node->GetFilename(), node->GetLineNumber());
1121
1122 mNodeStack.push(tail);
1123
1124 //Add this last so it gets executed first
1125 mNodeStack.push(node2);
1126
1127 }
1128 break;
1129
1130
1131
1132 default:
1133 PError::SignalFatalError("Unknown Function Type in PEBL_FUNCTION_TAIL2");
1134 break;
1135 }
1136
1137 }
1138
1139 break;
1140
1141 case PEBL_FUNCTION_TAIL_LIBFUNCTION:
1142 {
1143 //Pop the call site from the call stack for error reporting
1144 if(gCallStack.Size() == 0) {
1145 PError::SignalFatalError("gCallStack is empty in PEBL_FUNCTION_TAIL_LIBFUNCTION");
1146 }
1147 gCallStack.Pop();
1148
1149 //Go to the previous context/scope label and variables.
1150 if(mScopeStack.size() == 0) {
1151 PError::SignalFatalError("mScopeStack is empty in PEBL_FUNCTION_TAIL_LIBFUNCTION");
1152 }
1153
1154 mScope = mScopeStack.top();
1155 mScopeStack.pop();
1156
1157 if(mVariableMapStack.size() == 0) {
1158 PError::SignalFatalError("mVariableMapStack is empty in PEBL_FUNCTION_TAIL_LIBFUNCTION");
1159 }
1160
1161 mLocalVariableMap = mVariableMapStack.top();
1162 mVariableMapStack.pop();
1163
1164 }
1165 break;
1166
1167
1168 /*****************************************************************/
1169 case PEBL_WHILE:
1170 {
1171
1172
1173 //we might have gotten here from an iteration of while
1174 //that contained a break command. Check this
1175 if(GetStackDepth()>0)
1176 {
1177 Variant results = Peek();
1178 if(results.GetDataType() == P_DATA_STACK_SIGNAL &&
1179 results == Variant(STACK_BREAK))
1180 {
1181 Pop();
1182 Push(Variant(1)); //Put a dummy variant on the stack so the
1183 //stop signal does not get propogated.
1184 break;
1185 }
1186 }
1187
1188
1189 // The left child node is an expression to test
1190 // The right node is a code block.
1191 // Test the left node, then evaluate the right node if true,
1192 //then re-program a while node to repeat until not successful.
1193
1194
1195
1196
1197 const PNode * node1 = node->GetLeft();
1198
1199 const OpNode * tail = new OpNode( PEBL_WHILE_TAIL,NULL,NULL,
1200 node->GetFilename(), node->GetLineNumber());
1201
1202
1203 mNodeStack.push(node); //Push the while node back on the stack...we need this later.
1204 mNodeStack.push(tail); //the while_tail which checks the value...
1205 mNodeStack.push(node1); //first evaluate the left node value.
1206
1207 }
1208 break;
1209
1210 case PEBL_WHILE_TAIL:
1211 {
1212
1213 //Get the result of the evaluation
1214 Variant v1 = Pop();
1215
1216 //If the evaluation is false, break out of loop/switch
1217 if(v1)
1218 {
1219
1220 //This should handle a 'break'
1221 if(v1.GetDataType() == P_DATA_STACK_SIGNAL &&
1222 v1 == Variant(STACK_BREAK))
1223 {
1224 Variant results = Pop();
1225 Push(results);
1226 break;
1227 }
1228
1229 //Ok, the test was true, so Evaluate the right node
1230 //of the while(), which is on the top of the stack right now.
1231
1232 OpNode * whilenode = (OpNode*)(mNodeStack.top());
1233 OpNode * node2 = (OpNode*)(whilenode->GetRight());
1234
1235 const OpNode * whiletail2 = new OpNode( PEBL_WHILE_TAIL2,NULL,NULL,
1236 node->GetFilename(), node->GetLineNumber());
1237
1238 //Execute the code, then it will re-execute the while node
1239 //which is still on top of the stack.
1240 mNodeStack.push(whiletail2);
1241 mNodeStack.push(node2);
1242
1243 break;
1244
1245
1246 }
1247 else
1248 {
1249 //Remove the pending while node from the stack.
1250 mNodeStack.pop();
1251 Push(Variant(1)); //Add a value to the stack that will get popped at the next statement.
1252 break;
1253 }
1254
1255 }
1256 break;
1257
1258
1259 case PEBL_WHILE_TAIL2:
1260 {
1261 //At the end of executing the code block for a while loop, the result
1262 //is on top of the stack. Remove it now.
1263
1264 Variant value = Pop();
1265
1266 if(value.IsStackSignal() && value.GetSignal() == STACK_BREAK)
1267 {
1268 //put the stack break value back on the stack to
1269 //signal the while head.
1270 Push(value);
1271
1272 }
1273 }
1274 break;
1275
1276 /*****************************************************************/
1277 case PEBL_LOOP:
1278 {
1279 // The PEBL_LOOP node has two children: the left child is a variable/datum pair,
1280 // and the right child is a code block that should be evaluated once for each
1281 // element of the datum, with the variable being set to that element for
1282 // each iteration.
1283
1284 //Get the variabledatum pair. Evaluating this will create a list and
1285 //put it on top of the stack.
1286 const PNode * node1 = node->GetLeft(); //A variable-datum node.
1287 const PNode * codeblock = node->GetRight();
1288 const OpNode * tail = new OpNode(PEBL_LOOP_TAIL1,NULL,NULL,
1289 node->GetFilename(), node->GetLineNumber());
1290
1291
1292 const OpNode * tail2 = new OpNode(PEBL_LOOP_TAIL2,NULL,NULL,
1293 node->GetFilename(), node->GetLineNumber());
1294
1295
1296 mNodeStack.push(codeblock); //Put the codeblock on the stack for access later.
1297 mNodeStack.push(tail2); //This will re-program the loop when the codeblock is done.
1298 mNodeStack.push(codeblock); //put this on the stack for execution after variables are bound
1299 mNodeStack.push(tail); //execute the tail after the node1 is executed.
1300 mNodeStack.push(node1); //first, execute the variable-datum node to put the list
1301 // and the variable on the stack.
1302
1303 Push(Variant(1)); //now, add index 0 to the top of the stack.
1304 }
1305 break;
1306
1307
1308 case PEBL_LOOP_TAIL1:
1309 {
1310 //Now, the next two items on the stack should be the datum and the variable.
1311
1312 Variant list = Pop();
1313 Variant varname = Pop();
1314 const long unsigned int index = (const long unsigned int)Pop();
1315
1316
1317 PError::AssertType(varname, PEAT_VARIABLE,"Error: iterator of loop not a variable");
1318
1319 // Handle integer to list conversion (like Evaluator.cpp does)
1320 PList * tmp;
1321 if(list.IsInteger())
1322 {
1323 // Convert integer to list [1, 2, 3, ..., n]
1324 tmp = new PList();
1325 for(int count=1; count <= list.GetInteger(); count++)
1326 {
1327 tmp->PushBack(count);
1328 }
1329 // Update the list on the stack to be this new list
1331 PComplexData * pcd = new PComplexData(tmpptr);
1332 list = Variant(pcd);
1333 }
1334 else
1335 {
1336 PError::AssertType(list, PEAT_LIST,"Second argument of loop(<iterator>, <list>) must be a list.");
1337 tmp = (PList*)(list.GetComplexData()->GetObject().get());
1338 }
1339
1340 if(tmp->Length()<index)
1341 {
1342 //Looping is over!
1343 mNodeStack.pop(); //Get rid of the codeblock node, which should come next
1344 mNodeStack.pop(); //Get rid of the pre-programmed tail2 node
1345 mNodeStack.pop(); //Get rid of copy 2 of the codeblock.
1346 Push(1); //Handle the stack. We may need to adjust this for break.
1347 } else {
1348
1349
1350
1351 const Variant p = tmp->Nth(index);
1352 //Set the variable to the current list element, and increment p
1353
1354 if(varname.IsLocalVariable())
1355 {
1356 mLocalVariableMap.AddVariable(varname, p);
1357 }
1358 else if (varname.IsGlobalVariable())
1359 {
1361 }
1362
1363
1364 //Now, the codeblock will execute automatically. But need to reprogram the loop1
1365 //to occur after that.
1366
1367
1368 //Add the new index.
1369
1370 Push(index+1);
1371
1372 //Put the varname back on the stack, for the next time through
1373 Push(varname);
1374
1375 //Put the list back on the stack
1376 Push(list);
1377 }
1378 }
1379 break;
1380
1381
1382 case PEBL_LOOP_TAIL2:
1383 {
1384 //the code block has just been executed. We need to look at the results to
1385 //handle any breaks.
1386 //Normally, the top of the stack will be the next index to handle
1387
1388 Variant results = Pop();
1389
1390 if(results.GetDataType() == P_DATA_STACK_SIGNAL &&
1391 results == Variant(STACK_BREAK))
1392 {
1393 Pop(); //pop the list
1394 Pop(); //pop the variable name
1395 Pop(); //pop the next index
1396 Push(Variant(0)); //Push on a dummy return value
1397
1398 mNodeStack.pop(); //pop the codeblock (the original copy from PEBL_LOOP setup)
1399 } else {
1400
1401
1402 //reprogram another loop.
1403 //once again, the variable and the list need to be on top of the stack.
1404
1405 //The codeblock is currently at the top of the stack.
1406 const PNode * codeblock = mNodeStack.top();
1407
1408 //Program the next tail1/tail2 combo, with the codeblock sandwiched between:
1409 const OpNode * tail1 = new OpNode(PEBL_LOOP_TAIL1,NULL,NULL,
1410 node->GetFilename(), node->GetLineNumber());
1411
1412
1413 const OpNode * tail2 = new OpNode(PEBL_LOOP_TAIL2,NULL,NULL,
1414 node->GetFilename(), node->GetLineNumber());
1415
1416
1417 mNodeStack.push(tail2); //This will re-execute the loop when the codeblock is done.
1418 mNodeStack.push(codeblock); //(put this on the stack for later)
1419 mNodeStack.push(tail1);
1420
1421
1422 //Right now, the index is on the top of the data stack, so we don't need to push it
1423 //back on.
1424 }
1425
1426 }
1427 break;
1428
1429
1430 case PEBL_VARIABLEDATUM:
1431 {
1432 //The VARIABLDATUM node is used in the loop function. The variable is the
1433 //left child, a list of data in the form of an expression is the right.
1434
1435 //This node is executed once at the beginning
1436 //of the loop; it pushes the variable name and the evaluated list onto the stack.
1437 //PEBL_LOOP evaluation then gets them from the stack and iterates through the list,
1438 //setting the variable to each element of the data.
1439
1440 //Get the variable:
1441 const PNode * node1 = node->GetLeft();
1442
1443 //Push the variable name directly on the stack (without evaluating it.)
1444 Push(((DataNode*)node1)->GetValue());
1445
1446 //Get the datum:
1447 const PNode * node2 = node->GetRight();
1448
1449 //Evaluate the datum--it gets put on the stack.
1450 mNodeStack.push(node2);
1451 }
1452 break;
1453
1454
1455 /**********************************
1456 HANDLED: (no tail needed)
1457 *******************************/
1458 case PEBL_ARGLIST:
1459 {
1460 // This behaves almost exactly like a listhead. It will end up with a variant
1461 // on the top of the stack that is a list containing all of the arguments
1462 // for a function.
1463
1464
1465 //Create the stack signal variant and push it onto the stack.
1467 Push(v1);
1468
1469 //Get the top node of the list
1470 const PNode * node1 = node->GetLeft();
1471
1472 if(node1)
1473 {
1474 //If it exists, evaluate it.
1475 mNodeStack.push(node1);
1476 }
1477
1478 //if node1 doesn't exist, it is a null argument list--do nothing more.
1479
1480
1481 }
1482 break;
1483
1484 /***************************
1485 //Handled, no tail needed
1486 ***************************/
1487 case PEBL_LISTHEAD:
1488 {
1489 //When we get a list head, we need to evaluate each expression in the list,
1490 //pushing them onto the stack. When we get to the end of the list, we just need
1491 //to pop items, adding them to the list, until we get back up to the top.
1492 //To do this efficiently and avoid recreating lists iteratively, the bottom
1493 //item does this in a while loop. to signal the end of the while loop, the
1494 //PEBL_LISTHEAD must put a dummy item on the stack so the bottom item
1495 //knows when the front of the list has been reached. This is a variant of type STACK_LIST_HEAD
1496
1497 //Create the stack signal variant and push it onto the stack.
1499 Push(v1);
1500
1501 //Get the top node of the list and Evaluate it.
1502 const PNode * node1 = node->GetLeft();
1503 if(node1)
1504 {
1505
1506 mNodeStack.push(node1);
1507
1508 }
1509 else
1510 {
1511 //If node1 is NULL, we have an empty list.
1512 //Get rid of the STACK_LIST_HEAD on the top of the stack
1513 Pop();
1514
1515 //Make an empty list and push it onto the stack.
1517
1518 PComplexData pcd = PComplexData(tmpList);
1519 Variant v2 = Variant(&pcd);
1520 Push(v2);
1521 }
1522 }
1523 break;
1524
1525
1526 /*****************************************************************/
1527 case PEBL_LISTITEM:
1528 {
1529 // for a list item, the list is represented as a parsed tree with the
1530 // left node being the data and the right node being another listitem node.
1531 // This should create the list piece by piece recursively.
1532
1533 // To do this, Evaluate() each item so it gets executed and the value
1534 // gets pushed onto the stack. When you come to the end of the list,
1535 // pop the items off the list iteratively until you get back where you came from.
1536
1537 //The data: Get it out of the tree and evaluate it, so it gets
1538 //push onto the stack.
1539
1540
1541 const PNode * node1 = node->GetLeft();
1542 const PNode * node2 = node->GetRight();
1543
1544 if(node2)
1545 {
1546 mNodeStack.push(node2); //do the right node after the left node.
1547 }
1548 else
1549 {//we are at the end of the list, so do the cleanup stuff.
1550 const OpNode * tail = new OpNode(PEBL_LISTITEM_TAIL,NULL,NULL,
1551 node->GetFilename(), node->GetLineNumber());
1552 mNodeStack.push(tail);
1553 }
1554
1555
1556 mNodeStack.push(node1); //do the left node first.
1557
1558 }
1559 break;
1560
1561 case PEBL_LISTITEM_TAIL:
1562 {
1563
1564 //We are at the end of the list.
1565 //If node2 is NULL, then we are at the end of the list.
1566 //everything has been pushed to the stack. Just get everything
1567 //off the stack, make a list out of it, and put it back on the stack.
1568
1569
1570 //Must make a new list, and create a variant out of it.
1571 PList * tmpList = new PList();
1572 PList order;
1573 int ord = 0;
1574 //Now, pop off items from the list until you get to a
1575 //P_DATA_STACK_SIGNAL, then if it i
1576 Variant v1 = Pop();
1577
1578 //we need to create the list from the items on the stack.
1579 //But the top of the stack is the end of the list.
1580
1581 while(v1.GetDataType() != P_DATA_STACK_SIGNAL)
1582 {
1583
1584 //Add the item to the list.
1585 tmpList->PushBack(v1);
1586 order.PushBack(ord--);
1587 //Pop and repeat.
1588 v1 = Pop();
1589 }
1590
1591
1592 //add tmplist (in reverse order) to the stack as the argument list.
1593 std::reverse(tmpList->Begin(),tmpList->End());
1595 PComplexData pcd = PComplexData(pl);
1596 Variant v2 = Variant(&pcd);
1597 Push(v2);
1598 }
1599
1600 break;
1601
1602 /************************ HANDLED *****************************************/
1603 case PEBL_LT:
1604 {
1605 //Execute left and right nodes, which puts results on stack
1606 const PNode * node1 = node->GetLeft();
1607 const PNode * node2 = node->GetRight();
1608 const OpNode * tail = new OpNode(PEBL_LT_TAIL,NULL,NULL,
1609 node->GetFilename(), node->GetLineNumber());
1610
1611 mNodeStack.push(tail);
1612 mNodeStack.push(node2);
1613 mNodeStack.push(node1);
1614
1615 }
1616 break;
1617
1618 case PEBL_LT_TAIL:
1619 {
1620
1621 //Get the top two items from the stack. The right will be on top
1622 Variant v2 = Pop();
1623 Variant v1 = Pop();
1624
1625 Push(v1 < v2);
1626 }
1627 break;
1628
1629 /*********************************HANDLED ********************************/
1630 case PEBL_GT:
1631 {
1632
1633 //Execute left and right nodes, which puts results on stack
1634 const PNode * node1 = node->GetLeft();
1635 const PNode * node2 = node->GetRight();
1636 const OpNode * tail = new OpNode(PEBL_GT_TAIL,NULL,NULL,
1637 node->GetFilename(), node->GetLineNumber());
1638
1639 mNodeStack.push(tail);
1640 mNodeStack.push(node2);
1641 mNodeStack.push(node1);
1642 }
1643 break;
1644
1645 case PEBL_GT_TAIL:
1646 {
1647 //Get the top two items from the stack. The right will be on top
1648 Variant v2 = Pop();
1649 Variant v1 = Pop();
1650 Push(v1 > v2);
1651 }
1652 break;
1653
1654 /**********************************HANDLED*******************************/
1655 case PEBL_GE:
1656 {
1657 //Execute left and right nodes, which puts results on stack
1658
1659 const PNode * node1 = node->GetLeft();
1660 const PNode * node2 = node->GetRight();
1661 const OpNode * tail = new OpNode(PEBL_GE_TAIL,NULL,NULL,
1662 node->GetFilename(), node->GetLineNumber());
1663
1664
1665 mNodeStack.push(tail);
1666 mNodeStack.push(node2);
1667 mNodeStack.push(node1);
1668
1669 }
1670 break;
1671
1672 case PEBL_GE_TAIL:
1673 {
1674
1675
1676 //Get the top two items from the stack. The right will be on top
1677 Variant v2 = Pop();
1678 Variant v1 = Pop();
1679 Push(v1 >= v2);
1680 }
1681 break;
1682
1683 /*******************************HANDLED**********************************/
1684 case PEBL_LE:
1685 {
1686
1687 //Execute left and right nodes, which puts results on stack
1688 const PNode * node1 = node->GetLeft();
1689 const PNode * node2 = node->GetRight();
1690
1691 const OpNode * tail = new OpNode(PEBL_LE_TAIL,NULL,NULL,
1692 node->GetFilename(), node->GetLineNumber());
1693
1694 mNodeStack.push(tail);
1695 mNodeStack.push(node2);
1696 mNodeStack.push(node1);
1697 }
1698 break;
1699 case PEBL_LE_TAIL:
1700 {
1701 //Get the top two items from the stack. The right will be on top
1702 Variant v2 = Pop();
1703 Variant v1 = Pop();
1704
1705 Push(v1 <= v2);
1706 }
1707 break;
1708
1709
1710 /********************************HANDLED*********************************/
1711 case PEBL_EQ:
1712 {
1713 //Execute left and right nodes, which puts results on stack
1714 const PNode * node1 = node->GetLeft();
1715 const PNode * node2 = node->GetRight();
1716
1717
1718 const OpNode * tail = new OpNode(PEBL_EQ_TAIL,NULL,NULL,
1719 node->GetFilename(), node->GetLineNumber());
1720
1721 mNodeStack.push(tail);
1722 mNodeStack.push(node2);
1723 mNodeStack.push(node1);
1724 }
1725 break;
1726
1727 case PEBL_EQ_TAIL:
1728 {
1729 //Get the top two items from the stack. The right will be on top
1730 Variant v2 = Pop();
1731 Variant v1 = Pop();
1732 Push(v1 == v2);
1733 }
1734 break;
1735
1736 /******************************* HANDLED **********************************/
1737 case PEBL_NE:
1738 {
1739 //Execute left and right nodes, which puts results on stack
1740 const PNode * node1 = node->GetLeft();
1741 const PNode * node2 = node->GetRight();
1742
1743 const OpNode * tail = new OpNode(PEBL_NE_TAIL,NULL,NULL,
1744 node->GetFilename(), node->GetLineNumber());
1745
1746 mNodeStack.push(tail);
1747 mNodeStack.push(node2);
1748 mNodeStack.push(node1);
1749 }
1750 break;
1751
1752 case PEBL_NE_TAIL:
1753 {
1754 //Get the top two items from the stack. The right will be on top
1755 Variant v2 = Pop();
1756 Variant v1 = Pop();
1757
1758 Push(v1 != v2);
1759 }
1760 break;
1761
1762
1763 /*****************************************************************/
1764 case PEBL_STATEMENTS:
1765 {
1766 //This is a node that connects two statements.
1767
1768 // a STATEMENTS node connects a PEBL_STATEMENTS node on the left
1769 //with an arbitrary OpNode on the right.
1770 //Note that if you have a chain of PEBL_STATEMENTS nodes,
1771 //it works from the bottom up: the right node at the top
1772 //of the chain is really the last node that will be executed.
1773
1774
1775
1776#ifdef PEBL_DEBUG_PRINT
1777 cout << "Checking a PEBL_STATEMENTS: " << GetStackDepth() << endl;
1778#endif
1779
1780 //If the top of the stack is a STACK_BREAK, we should do nothing.
1781 if(GetStackDepth()>0)
1782 {
1783
1784 Variant v1 = Peek();
1785
1786
1787 if(v1.GetDataType() == P_DATA_STACK_SIGNAL &&
1788 v1 == Variant(STACK_BREAK))
1789 {
1790 //If this is a stack signal, and if
1791 //it is a break, we should just back out
1792 //stackbreak is already on top of the stack. Add a dummy to be taken off.
1793 Push(Variant(0));
1794 break;
1795 }
1796 }
1797
1798
1799
1800 const OpNode * tail = new OpNode(PEBL_STATEMENTS_TAIL1,NULL,NULL,
1801 node->GetFilename(), node->GetLineNumber());
1802
1803 //We need to execute the left node, and then process the right node, so
1804 //add them to the stack in inverse order.
1805
1806 mNodeStack.push(node->GetRight());
1807 mNodeStack.push(tail);
1808 mNodeStack.push(node->GetLeft());
1809
1810
1811
1812 }
1813 break;
1814
1815 case PEBL_STATEMENTS_TAIL1:
1816 {
1817
1818 //The results of the first statement are on top of the stack. Get them off:
1819 Variant v1 = Pop();
1820
1821
1822 if(v1.GetDataType() == P_DATA_STACK_SIGNAL &&
1823 v1 == Variant(STACK_BREAK))
1824 {
1825 //If this is a stack signal, and if
1826 //it is a break, we should just back out
1827 //But first, we need to pop the Right node from the node stack
1828 //to prevent it from executing
1829 mNodeStack.pop(); //Remove the right statement from the node stack
1831 break;
1832 }
1833
1834 }
1835 break;
1836
1837
1838
1839
1840 /***************************ITERATIVE--NOTHING IS NEEDED HERE
1841 **************************************/
1842 case PEBL_BREAK:
1843 {
1844
1845 //This exits out of the current loop, while, or function context.
1846 //it works by adding a STACK_BREAK stacksignalevent onto the top of the stack.
1847 //all relevant loops look for this type of event and
1848 //cleanly abort when that times comes.
1850 Push(v1);
1851
1852#ifdef PEBL_DEBUG_PRINT
1853 cout << "PEBL_BREAK: pushing break onto stack.\n";
1854#endif
1855
1856 }
1857 break;
1858
1859 /******************************
1860 Hnadled, no tail needed?
1861 ***********************************/
1862 case PEBL_RETURN:
1863 {
1864 //The return keyword is used ONLY at the very end of a
1865 //function. Its left node is the value to return; its right
1866 //node should be NULL. So evaluate the left node; then what's on
1867 //the stack will be the return value.
1868
1869 //Push(Variant(STACK_RETURN_DUMMY));
1870
1871 const PNode * node1 = node->GetLeft();
1872 mNodeStack.push(node1);
1873
1874 }
1875 break;
1876
1877 default:
1878 //Signal an error here.
1879 return false;
1880 break;
1881
1882 }
1883 return true;
1884}
1885
1886
1887
1888
1892{
1893
1894 //Set up the globally-accessible structure to allow
1895 //better error reporting.
1896 if(node->GetLineNumber() > -1)
1897 gEvalNode = node;
1898
1899#ifdef PEBL_DEBUG_PRINT
1900 cout << "-------------------------";
1901 cout << "Evaluating DataNode of Value: " << node->GetValue() << endl;;
1902 cout << "Line: " << node->GetLineNumber() << endl;
1903#endif
1904
1905 Variant v1, v2;
1906
1907 //A node of type P_DATA_NODE could be an integer, a float, a variable, a string, etc.
1908 //Grab the variant out of the node. If it is a Variable, extract the value.
1909 //Push the initial value or the variable onto the stack.
1910
1911 v1 = node->GetValue();
1912
1913 switch(v1.GetDataType())
1914 {
1915
1917 {
1918
1919 v2 = mLocalVariableMap.RetrieveValue(v1.GetVariableBaseName());
1920 //Get the name of property being
1921 string property =v1.GetVariablePropertyName();
1922
1923 if(property!="")
1924 {
1925 v2 = PEBLUtility::ResolvePropertyChain(v2, property);
1926 }
1927
1928 Push(v2);
1929 }
1930 break;
1931
1932
1933
1934
1936 {
1938
1939 //Get the name of property being
1940 string property =v1.GetVariablePropertyName();
1941 if(property!="")
1942 {
1943 v2 = PEBLUtility::ResolvePropertyChain(v2, property);
1944 }
1945
1946 Push(v2);
1947 }
1948 break;
1949
1952 case P_DATA_STRING:
1953 case P_DATA_COMPLEXDATA:
1954
1955#ifdef PEBL_DEBUG_PRINT
1956 cout << "Evaluating a normal Variant: ";
1957 cout << v1 << endl;
1958#endif
1959 Push(v1);
1960 break;
1961
1962 case P_DATA_UNDEFINED:
1963 default:
1964 //This should signal an error.
1965 PError::SignalFatalError("In Function [" + mScope + "Undefined Data Type in Evaluate::Evaluate(DataNode)");
1966 return false;
1967 break;
1968 }
1969
1970 return true;
1971}
1972
1973//
1974
1975
1976
1977
1983{
1984
1985
1986 // First get the right node (the argument list) and evaluate it.
1987 // This will end up with a list Variant on top of the stack.
1988 const PNode *node1 = node->GetRight();
1989
1990 Evaluate1(node1);
1991
1992
1993 // The parameters for a function are in a list on the top of the stack.
1994 // A function should pull the list off the stack and push it onto
1995 // the stack of the new evaluator scope.
1996
1997
1998 //Get the name of the function.
1999
2000 Variant funcname =dynamic_cast<DataNode*>(node->GetLeft())->GetValue();
2001
2002 //If the argument is a custom object, the custom object
2003 //might contain a class-specific method to use instead of the generic function
2004 //The argument is at the top of the stack right now.
2005
2006 Variant v = Peek();
2007 if(v.IsComplexData())
2008 {
2009 //if v is a comlpex data, it should be a parameter list.
2010
2011
2012 PList *plist = dynamic_cast<PList*>(v.GetComplexData()->GetObject().get());
2013 Variant first = plist->First();
2014
2015 if(first.IsComplexData())
2016 {
2017
2018 if( first.GetComplexData()->IsCustomObject())
2019 {
2020
2021 PCustomObject * pco = dynamic_cast<PCustomObject*>(first.GetComplexData()->GetObject().get());
2022
2023
2024 if(OVE_SUCCESS== pco->ValidateProperty(funcname))
2025 {
2026
2027 funcname = Variant(pco->GetProperty(funcname).GetString().c_str(), P_DATA_FUNCTION);
2028
2029 }
2030
2031 }
2032 }
2033 }
2034
2035
2036
2037 const PNode * node2 = mFunctionMap.GetFunction(funcname);
2038
2039
2040#ifdef PEBL_DEBUG_PRINT
2041 cout << "Calling a function with argument list: " << endl;
2042#endif
2043
2044
2045 //Now, node2 will either be a PEBL_LAMBDAFUNCTION or a PEBL_BUILTINFUNCTION
2046 //Lambda functions are just code blocks, but need to be executed in
2047 //a new scope, so need their own evaluator. A built-in function is precompiled
2048 //and doesn't need its own new scope, so don't create one in that case.
2049
2050
2051
2052
2053 switch(((OpNode*)node2)->GetOp())
2054 {
2055 case PEBL_LAMBDAFUNCTION:
2056 { //Need to create a new scope to allow for variable declaration
2057 //within case statement
2058
2059 //get the top item of the stack.
2060 Variant v = Pop();
2061
2062
2063 //Make a new evaluator for the function scope and v at the top of the stack.
2064 Evaluator myEval(v,funcname.GetFunctionName());
2065
2066 //The callstack just keeps track of the series of
2067 //evaluators for debugging purposes.
2068
2069 //Evaluate the lambda function in new scope.
2070
2071
2072 myEval.Evaluate1(node2);
2073
2074 //Now that myEval is finished, take the
2075 //node off the callstack.
2076 //gCallStack.Pop();
2077
2078 //If myEval has a stack depth of 1, it does not return anything.
2079 //If myEval has a stack depth of 2, it wants to return the top value.
2080 if(myEval.GetStackDepth() == 1)
2081 {
2082 //Add '1' to the stack as the default return value for a function (i.e., one without
2083 //an explicit return value.)
2084 cout << "Adding dummy value to end of null function\n";
2085 Push(1);
2086 }
2087 else if (myEval.GetStackDepth() == 2)
2088 {
2089 //Get the top of the function evaluator and push it onto the current evaluator.
2090 //cout << "Adding real value of subfunction to end of null function\n";
2091 Variant v1 = myEval.Pop();
2092 Push(v1);
2093 }
2094 }
2095 break;
2096
2097 case PEBL_LIBRARYFUNCTION:
2098 Evaluate1(node2);
2099
2100 break;
2101
2102
2103 default:
2104 PError::SignalFatalError("Unknown Function Type in Evaluator::CallFunction");
2105 break;
2106 }
2107
2108}
2109
2110
2111
2112
2113
2115{
2116
2117#ifdef PEBL_DEBUG_PRINT
2118 cout << "Pushing Stack: depth: "<< mStack.size() << "-->" << GetStackDepth() + 1;
2119#endif
2120
2121 if (mStack.size() > mStackMax)
2122 {
2123 PError::SignalFatalError("Maximum Stack Depth Exceeded. Runaway Function?");
2124 }
2125 mStack.push(v);
2126#ifdef PEBL_DEBUG_PRINT
2127 if(mStack.size())cout << " [" << mStack.top() << "] is on top.\n";
2128 else cout << endl;
2129#endif
2130
2131}
2132
2133
2135{
2136#ifdef PEBL_DEBUG_PRINT
2137 cout << "Popping Stack: depth: "<< mStack.size() << "-->" << GetStackDepth() - 1;
2138#endif
2139 if(mStack.size() <=0 )
2140 {
2141 PError::SignalFatalError("Error: Tried to Pop an empty stack.");
2142 }
2143
2144 Variant v = mStack.top();
2145 mStack.pop();
2146#ifdef PEBL_DEBUG_PRINT
2147 if(mStack.size())cout << " [" << mStack.top() << "] is on top.\n";
2148 else cout << endl;
2149#endif
2150 return v;
2151}
2152
2153
2155{
2156#ifdef PEBL_DEBUG_PRINT
2157 cout << "Peeking at top of stack: "<< mStack.size() << endl;
2158#endif
2159 if(mStack.size() <=0 )
2160 {
2161 PError::SignalFatalError("Error: Tried to Peek at an empty stack.");
2162 }
2163
2164 Variant v = mStack.top();
2165 return v;
2166}
2167
2168
2170{
2171 return gGlobalVariableMap.Exists(v) || mLocalVariableMap.Exists(v);
2172}
2173
2174
2175
2177{
2178 mNodeStack.push(node);
2179}
#define NULL
Definition BinReloc.cpp:317
#define pInt
Definition Defs.h:8
#define pDouble
Definition Defs.h:7
void Eval1(void *)
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
const Variant & GetValue() const
Definition PNode.h:152
This class has got everything you need to evaluate stuff.
friend class PEventLoop
static const PNode * gEvalNode
void NodeStackPush(const PNode *node)
bool Evaluate1()
void Push(Variant v)
static PCallStack gCallStack
static PEventLoop * mEventLoop
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 Push(const PNode *node)
Definition PError.h:111
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
Variant Nth(unsigned int n)
Definition PList.cpp:181
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 GetFilename() const
Definition PNode.h:68
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 ExitQuietly(const std::string &message, int exitCode=0)
Definition PError.cpp:126
void AssertType(Variant v, int type, const std::string &outsidemessage)
void SignalFatalError(const std::string &message)
int count
Definition test.cpp:12