PEBL 2.2
Psychology Experiment Building Language - Cross-platform psychological experiment development system
Evaluator Class Reference

This class has got everything you need to evaluate stuff. More...

#include <Evaluator-es.h>

Public Member Functions

 Evaluator ()
 
 Evaluator (Variant &stacktop, string scope)
 
 ~Evaluator ()
 
void CallFunction (const OpNode *node)
 
bool Evaluate1 ()
 
bool Evaluate1 (const PNode *node)
 
bool Evaluate1 (const OpNode *node)
 This method evaluates OpNodes.
 
bool Evaluate1 (const DataNode *node)
 This method evaluates DataNodes.
 
void Push (Variant v)
 
Variant Pop ()
 
Variant Peek ()
 
void NodeStackPush (const PNode *node)
 
int GetNodeStackDepth ()
 
int GetStackDepth ()
 
PEventLoopGetEventLoop ()
 
bool IsVariableName (Variant v)
 
 Evaluator ()
 
 Evaluator (Variant &stacktop, string scope)
 
 ~Evaluator ()
 
void CallFunction (const OpNode *node)
 
bool Evaluate (const PNode *node)
 
bool Evaluate (const OpNode *node)
 This method evaluates OpNodes.
 
bool Evaluate (const DataNode *node)
 This method evaluates DataNodes.
 
void Push (const Variant &v)
 
Variant Pop ()
 
const VariantPeek () const
 
unsigned long int GetStackDepth ()
 
PEventLoopGetEventLoop ()
 
bool IsVariableName (Variant v)
 

Static Public Attributes

static FunctionMap mFunctionMap
 Initiate some static member data.
 
static VariableMap gGlobalVariableMap
 
static PEventLoopmEventLoop =NULL
 
static PEBLPath gPath
 
static const PNodegEvalNode = NULL
 
static PCallStack gCallStack
 

Friends

class PEventLoop
 

Detailed Description

This class has got everything you need to evaluate stuff.

Definition at line 58 of file Evaluator-es.h.

Constructor & Destructor Documentation

◆ Evaluator() [1/4]

Evaluator::Evaluator ( )

Definition at line 92 of file Evaluator-es.cpp.

92 :
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}
friend class PEventLoop
static const PNode * gEvalNode
static PCallStack gCallStack
static PEventLoop * mEventLoop
void Push(const PNode *node)
Definition PError.h:111

References gCallStack, gEvalNode, mEventLoop, PEventLoop, and PCallStack::Push().

◆ Evaluator() [2/4]

Evaluator::Evaluator ( Variant stacktop,
string  scope 
)

Definition at line 107 of file Evaluator-es.cpp.

107 :
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}
void Push(Variant v)

References gCallStack, gEvalNode, mEventLoop, PEventLoop, PCallStack::Push(), and Push().

◆ ~Evaluator() [1/2]

Evaluator::~Evaluator ( )

Definition at line 133 of file Evaluator-es.cpp.

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}

◆ Evaluator() [3/4]

Evaluator::Evaluator ( )

◆ Evaluator() [4/4]

Evaluator::Evaluator ( Variant stacktop,
string  scope 
)

◆ ~Evaluator() [2/2]

Evaluator::~Evaluator ( )

Member Function Documentation

◆ CallFunction() [1/2]

void Evaluator::CallFunction ( const OpNode node)

This method takes a PEBL_FUNCTION OpNode, which is comprised of a P_DATA_FUNCTION DataNode on the left and a parameter list on the right, Finds the function code in the FunctionMap, and Evaluates that code.

Definition at line 1982 of file Evaluator-es.cpp.

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}
Evaluator * myEval
Definition PEBL.cpp:188
@ OVE_SUCCESS
Definition PEBLObject.h:38
@ P_DATA_FUNCTION
Definition Variant.h:43
This class has got everything you need to evaluate stuff.
bool Evaluate1()
Variant Peek()
Variant Pop()
static FunctionMap mFunctionMap
Initiate some static member data.
int GetStackDepth()
PNode * GetFunction(const std::string &funcname)
PNode * GetRight() const
Definition PNode.h:115
PNode * GetLeft() const
Definition PNode.h:114
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
Variant First()
Definition PList.cpp:169
Definition PNode.h:45
bool IsComplexData() const
Definition Variant.cpp:984
std::string GetString() const
Definition Variant.cpp:1056
PComplexData * GetComplexData() const
Definition Variant.cpp:1299
std::string GetFunctionName() const
Definition Variant.cpp:1208
X * get() const
Definition rc_ptrs.h:110
void SignalFatalError(const std::string &message)

References Evaluate1(), PList::First(), counted_ptr< X >::get(), Variant::GetComplexData(), FunctionMap::GetFunction(), Variant::GetFunctionName(), OpNode::GetLeft(), PComplexData::GetObject(), PCustomObject::GetProperty(), OpNode::GetRight(), GetStackDepth(), Variant::GetString(), Variant::IsComplexData(), PComplexData::IsCustomObject(), mFunctionMap, myEval, OVE_SUCCESS, P_DATA_FUNCTION, Peek(), Pop(), Push(), PError::SignalFatalError(), and PCustomObject::ValidateProperty().

Referenced by PEBLEnvironment::CallFunction(), and Evaluate().

◆ CallFunction() [2/2]

void Evaluator::CallFunction ( const OpNode node)

◆ Evaluate() [1/3]

bool Evaluator::Evaluate ( const DataNode node)

This method evaluates DataNodes.

Definition at line 1271 of file Evaluator.cpp.

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}
@ P_DATA_UNDEFINED
Definition Variant.h:41
@ P_DATA_LOCALVARIABLE
Definition Variant.h:48
@ 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_GLOBALVARIABLE
Definition Variant.h:49
const Variant & GetValue() const
Definition PNode.h:152
static VariableMap gGlobalVariableMap
int GetLineNumber() const
Definition PNode.h:69
Variant RetrieveValue(const std::string &varname)
std::string GetVariablePropertyName() const
Definition Variant.cpp:1185
std::string GetVariableBaseName() const
Definition Variant.cpp:1160
VariantDataType GetDataType() const
This returns the type as an enum.
Definition Variant.cpp:885
Variant ResolvePropertyChain(Variant obj, const std::string &propertyChain)

References Variant::GetDataType(), PNode::GetLineNumber(), DataNode::GetValue(), Variant::GetVariableBaseName(), Variant::GetVariablePropertyName(), gEvalNode, gGlobalVariableMap, P_DATA_COMPLEXDATA, P_DATA_GLOBALVARIABLE, P_DATA_LOCALVARIABLE, P_DATA_NUMBER_FLOAT, P_DATA_NUMBER_INTEGER, P_DATA_STRING, P_DATA_UNDEFINED, Push(), PEBLUtility::ResolvePropertyChain(), VariableMap::RetrieveValue(), and PError::SignalFatalError().

◆ Evaluate() [2/3]

bool Evaluator::Evaluate ( const OpNode node)

This method evaluates OpNodes.

This switch could probably be improved with an STL Map

Definition at line 186 of file Evaluator.cpp.

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}
#define NULL
Definition BinReloc.cpp:317
#define pDouble
Definition Defs.h:7
@ PEAT_VARIABLE
Definition PError.h:49
@ PEAT_LIST
Definition PError.h:61
@ PEBL_OP_NODE
Definition PNode.h:35
@ P_DATA_STACK_SIGNAL
Definition Variant.h:42
@ STACK_LIST_HEAD
Definition Variant.h:56
@ STACK_BREAK
Definition Variant.h:59
@ STACK_RETURN_DUMMY
Definition Variant.h:57
bool Evaluate(const PNode *node)
void CallFunction(const OpNode *node)
std::string GetOpName() const
Definition PNode.cpp:245
int GetOp() const
Definition PNode.h:111
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
PNODE_TYPE GetType() const
Access mType data.
Definition PNode.h:61
std::string GetFunctionName() const
Definition PNode.h:71
void AddVariable(const std::string &varname, const Variant &val)
pInt GetInteger() const
Definition Variant.cpp:997
bool IsGlobalVariable() const
Definition Variant.cpp:964
pFunc GetFunctionPointer() const
Definition Variant.cpp:1280
bool IsStackSignal() const
Definition Variant.cpp:989
std::string GetVariableName() const
Definition Variant.cpp:1138
bool IsInteger() const
Definition Variant.cpp:942
StackSignalType GetSignal() const
Definition Variant.cpp:1265
bool IsLocalVariable() const
Definition Variant.cpp:959
void SetPropertyChain(Variant obj, const std::string &propertyChain, Variant value)
void AssertType(Variant v, int type, const std::string &outsidemessage)
int count
Definition test.cpp:12

References VariableMap::AddVariable(), PError::AssertType(), PList::Begin(), CallFunction(), count, PList::End(), Evaluate(), counted_ptr< X >::get(), Variant::GetComplexData(), Variant::GetDataType(), PNode::GetFunctionName(), Variant::GetFunctionPointer(), Variant::GetInteger(), OpNode::GetLeft(), PNode::GetLineNumber(), PComplexData::GetObject(), OpNode::GetOp(), OpNode::GetOpName(), OpNode::GetRight(), Variant::GetSignal(), GetStackDepth(), PNode::GetType(), DataNode::GetValue(), Variant::GetVariableBaseName(), Variant::GetVariableName(), Variant::GetVariablePropertyName(), gEvalNode, gGlobalVariableMap, Variant::IsComplexData(), Variant::IsGlobalVariable(), Variant::IsInteger(), Variant::IsLocalVariable(), Variant::IsStackSignal(), PList::Length(), NULL, P_DATA_STACK_SIGNAL, pDouble, PEAT_LIST, PEAT_VARIABLE, PEBL_OP_NODE, Peek(), Pop(), Push(), PList::PushBack(), VariableMap::RetrieveValue(), PEBLUtility::SetPropertyChain(), PError::SignalFatalError(), STACK_BREAK, STACK_LIST_HEAD, and STACK_RETURN_DUMMY.

◆ Evaluate() [3/3]

bool Evaluator::Evaluate ( const PNode node)

This is the generic PNode evaluator

Definition at line 148 of file Evaluator.cpp.

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}
@ PEBL_DATA_NODE
Definition PNode.h:36

References Evaluate(), PNode::GetLineNumber(), PNode::GetType(), gEvalNode, NULL, PEBL_DATA_NODE, PEBL_OP_NODE, and PError::SignalFatalError().

Referenced by PEBLEnvironment::CallFunction(), Evaluate(), Evaluate(), PEBLInterpret(), and PEBLInterpret().

◆ Evaluate1() [1/4]

bool Evaluator::Evaluate1 ( )

Definition at line 199 of file Evaluator-es.cpp.

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}

References Evaluate1().

Referenced by CallFunction(), PEBLEnvironment::CallFunction(), Eval1(), Evaluate1(), Evaluate1(), Evaluate1(), PEventLoop::Loop(), and PEBLInterpret().

◆ Evaluate1() [2/4]

bool Evaluator::Evaluate1 ( const DataNode node)

This method evaluates DataNodes.

Definition at line 1891 of file Evaluator-es.cpp.

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}

References Variant::GetDataType(), PNode::GetLineNumber(), DataNode::GetValue(), Variant::GetVariableBaseName(), Variant::GetVariablePropertyName(), gEvalNode, gGlobalVariableMap, P_DATA_COMPLEXDATA, P_DATA_GLOBALVARIABLE, P_DATA_LOCALVARIABLE, P_DATA_NUMBER_FLOAT, P_DATA_NUMBER_INTEGER, P_DATA_STRING, P_DATA_UNDEFINED, Push(), PEBLUtility::ResolvePropertyChain(), VariableMap::RetrieveValue(), and PError::SignalFatalError().

◆ Evaluate1() [3/4]

bool Evaluator::Evaluate1 ( const OpNode node)

This method evaluates OpNodes.

Definition at line 216 of file Evaluator-es.cpp.

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}
#define pInt
Definition Defs.h:8
int Size()
Definition PError.h:113
void Pop()
Definition PError.h:112
Variant Nth(unsigned int n)
Definition PList.cpp:181
std::string GetFilename() const
Definition PNode.h:68

References VariableMap::AddVariable(), PError::AssertType(), PList::Begin(), count, PList::End(), Evaluate1(), PList::First(), gCallStack, counted_ptr< X >::get(), Variant::GetComplexData(), Variant::GetDataType(), PNode::GetFilename(), FunctionMap::GetFunction(), PNode::GetFunctionName(), Variant::GetFunctionName(), Variant::GetFunctionPointer(), Variant::GetInteger(), OpNode::GetLeft(), PNode::GetLineNumber(), PComplexData::GetObject(), OpNode::GetOp(), OpNode::GetOpName(), PCustomObject::GetProperty(), OpNode::GetRight(), Variant::GetSignal(), GetStackDepth(), Variant::GetString(), PNode::GetType(), Variant::GetVariableBaseName(), Variant::GetVariableName(), Variant::GetVariablePropertyName(), gEvalNode, gGlobalVariableMap, Variant::IsComplexData(), PComplexData::IsCustomObject(), Variant::IsGlobalVariable(), Variant::IsInteger(), Variant::IsLocalVariable(), Variant::IsStackSignal(), PList::Length(), mFunctionMap, PList::Nth(), NULL, OVE_SUCCESS, P_DATA_FUNCTION, P_DATA_STACK_SIGNAL, pDouble, PEAT_LIST, PEAT_VARIABLE, PEBL_OP_NODE, Peek(), pInt, Pop(), PCallStack::Pop(), PCallStack::Push(), Push(), PList::PushBack(), VariableMap::RetrieveValue(), PEBLUtility::SetPropertyChain(), PError::SignalFatalError(), PCallStack::Size(), STACK_BREAK, STACK_LIST_HEAD, and PCustomObject::ValidateProperty().

◆ Evaluate1() [4/4]

bool Evaluator::Evaluate1 ( const PNode node)

Definition at line 150 of file Evaluator-es.cpp.

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}
void ExitQuietly(const std::string &message, int exitCode=0)
Definition PError.cpp:126

References Evaluate1(), PError::ExitQuietly(), PNode::GetLineNumber(), PNode::GetType(), gEvalNode, NULL, PEBL_DATA_NODE, PEBL_OP_NODE, and PError::SignalFatalError().

◆ GetEventLoop() [1/2]

PEventLoop * Evaluator::GetEventLoop ( )
inline

Definition at line 90 of file Evaluator-es.h.

90{return mEventLoop;};

References mEventLoop.

◆ GetEventLoop() [2/2]

PEventLoop * Evaluator::GetEventLoop ( )
inline

Definition at line 80 of file Evaluator.h.

80{return mEventLoop;};

References mEventLoop.

◆ GetNodeStackDepth()

int Evaluator::GetNodeStackDepth ( )
inline

Definition at line 86 of file Evaluator-es.h.

86{return mNodeStack.size();};

Referenced by PEBLEnvironment::CallFunction(), and PEBLInterpret().

◆ GetStackDepth() [1/2]

int Evaluator::GetStackDepth ( )
inline

Definition at line 87 of file Evaluator-es.h.

87{return mStack.size();};

Referenced by CallFunction(), PEBLEnvironment::CallFunction(), Evaluate(), Evaluate1(), PEBLInterpret(), Pop(), Push(), and Push().

◆ GetStackDepth() [2/2]

unsigned long int Evaluator::GetStackDepth ( )
inline

Definition at line 78 of file Evaluator.h.

78{return mStack.size();};

◆ IsVariableName() [1/2]

bool Evaluator::IsVariableName ( Variant  v)

Definition at line 2169 of file Evaluator-es.cpp.

2170{
2171 return gGlobalVariableMap.Exists(v) || mLocalVariableMap.Exists(v);
2172}
bool Exists(const std::string &varname)

References VariableMap::Exists(), and gGlobalVariableMap.

Referenced by PEBLEnvironment::VariableExists().

◆ IsVariableName() [2/2]

bool Evaluator::IsVariableName ( Variant  v)

◆ NodeStackPush()

void Evaluator::NodeStackPush ( const PNode node)

Definition at line 2176 of file Evaluator-es.cpp.

2177{
2178 mNodeStack.push(node);
2179}

Referenced by PEBLEnvironment::CallFunction(), and PEventLoop::Loop1().

◆ Peek() [1/2]

Variant Evaluator::Peek ( )

Definition at line 2154 of file Evaluator-es.cpp.

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}

References PError::SignalFatalError().

Referenced by CallFunction(), Evaluate(), and Evaluate1().

◆ Peek() [2/2]

const Variant & Evaluator::Peek ( ) const

Definition at line 1525 of file Evaluator.cpp.

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}

References PError::SignalFatalError().

◆ Pop() [1/2]

Variant Evaluator::Pop ( )

Definition at line 2134 of file Evaluator-es.cpp.

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}

References GetStackDepth(), and PError::SignalFatalError().

Referenced by CallFunction(), PEBLEnvironment::CallFunction(), Evaluate(), Evaluate1(), and PEBLInterpret().

◆ Pop() [2/2]

Variant Evaluator::Pop ( )

◆ Push() [1/2]

void Evaluator::Push ( const Variant v)

Definition at line 1485 of file Evaluator.cpp.

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}

References GetStackDepth(), and PError::SignalFatalError().

◆ Push() [2/2]

void Evaluator::Push ( Variant  v)

Definition at line 2114 of file Evaluator-es.cpp.

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}

References GetStackDepth(), and PError::SignalFatalError().

Referenced by CallFunction(), PEBLEnvironment::CallFunction(), Evaluate(), Evaluate(), Evaluate1(), Evaluate1(), and Evaluator().

Friends And Related Symbol Documentation

◆ PEventLoop

friend class PEventLoop
friend

Definition at line 61 of file Evaluator-es.h.

Referenced by Evaluator(), and Evaluator().

Member Data Documentation

◆ gCallStack

static PCallStack Evaluator::gCallStack
static

Definition at line 120 of file Evaluator-es.h.

Referenced by Evaluate1(), Evaluator(), and Evaluator().

◆ gEvalNode

const PNode * Evaluator::gEvalNode = NULL
static

This is used for error detection. Every time gEvalNode is updated, this is set to gEvalNode. But, it is done for all evaluators. Thus, it always points to the last node processed, and error reporting routines can look for it and find out where it came from.

Definition at line 117 of file Evaluator-es.h.

Referenced by Evaluate(), Evaluate(), Evaluate(), Evaluate1(), Evaluate1(), Evaluate1(), Evaluator(), Evaluator(), and PError::SignalWarning().

◆ gGlobalVariableMap

VariableMap Evaluator::gGlobalVariableMap
static

The static keyword defines a 'Global' variable map that can be accessed\all Evaluators. It needs to be initialized somewhere outside the class, however. This is done in the main program file.

Definition at line 104 of file Evaluator-es.h.

Referenced by CaptureSignal(), Evaluate(), Evaluate(), Evaluate1(), Evaluate1(), PlatformWindow::Initialize(), IsVariableName(), PEventLoop::Loop(), PEventLoop::Loop1(), main(), PEBLObjects::MakeTextBox(), PEBLObjects::MakeWindow(), PEBLInterpret(), PlatformEventQueue::Prime(), and PlatformWindow::Resize().

◆ gPath

◆ mEventLoop

◆ mFunctionMap

FunctionMap Evaluator::mFunctionMap
static

Initiate some static member data.

This holds a pointer to a FunctionMap, which is loaded from the initial PNode tree by the loader. It is static and public, which means that all Evaluators have access to the same one, and anything else can access it with Evaluator::mFunctionMap

Definition at line 99 of file Evaluator-es.h.

Referenced by CallFunction(), PEBLEnvironment::CallFunction(), CaptureSignal(), Evaluate1(), Loader::GetMainPEBLFunction(), Loader::LoadLibraryFunctions(), Loader::LoadUserFunctions(), and PEBLInterpret().


The documentation for this class was generated from the following files: