SeExpr

Interpreter.cpp
Go to the documentation of this file.
1 /*
2 Copyright Disney Enterprises, Inc. All rights reserved.
3
4 Licensed under the Apache License, Version 2.0 (the "License");
5 you may not use this file except in compliance with the License
6 and the following modification to it: Section 6 Trademarks.
7 deleted and replaced with:
8
9 6. Trademarks. This License does not grant permission to use the
10 trade names, trademarks, service marks, or product names of the
11 Licensor and its affiliates, except as required for reproducing
12 the content of the NOTICE file.
13
14 You may obtain a copy of the License at
15 http://www.apache.org/licenses/LICENSE-2.0
16 */
17 #include "ExprNode.h"
18 #include "Interpreter.h"
19 #include "VarBlock.h"
20 #include "Platform.h"
21 #include <iostream>
22 #include <cstdio>
23 #include <algorithm>
24 #if !defined(WINDOWS)
25 #include <dlfcn.h>
26 #endif
27
28 // TODO: optimize to write to location directly on a CondNode
29 namespace SeExpr2 {
30
31 void Interpreter::eval(VarBlock* block, bool debug) {
32 // get pointers to the working data
33 double* fp = d.data();
34 char** str = s.data();
35
36 // if we have a VarBlock instance, we need to update the working data
37 if (block) {
38 // if the VarBlock is flagged as thread safe, copy the interpreter's data to it.
39 if (block->threadSafe == true) {
40 // copy double data
41 block->d.resize(d.size());
42 fp = block->d.data();
43 memcpy(fp, d.data(), d.size() * sizeof(double));
44
45 // copy string data
46 block->s.resize(s.size());
47 str = block->s.data();
48 memcpy(str, s.data(), s.size() * sizeof(char*));
49 }
50
51 // set the variable evaluation data
52 str[0] = reinterpret_cast<char*>(block->data());
53 str[1] = reinterpret_cast<char*>(static_cast<size_t>(block->indirectIndex));
54 }
55
56 int pc = _pcStart;
57 int end = static_cast<int>(ops.size());
58 while (pc < end) {
59 if (debug) {
60 std::cerr << "Running op at " << pc << std::endl;
61 print(pc);
62 }
63 const std::pair<OpF, int>& op = ops[pc];
64 int* opCurr = &opData[0] + op.second;
65 pc += op.first(opCurr, fp, str, callStack);
66 }
67 }
68
69 void Interpreter::print(int pc) const {
70 std::cerr << "---- ops ----------------------" << std::endl;
71 for (size_t i = 0; i < ops.size(); i++) {
72 const char* name = "";
73 #if !defined(WINDOWS)
74 Dl_info info;
75 if (dladdr((void*)ops[i].first, &info)) name = info.dli_sname;
76 #endif
77 fprintf(stderr, "%s %s %p (", pc == (int)i ? "-->" : " ", name, ops[i].first);
78 int nextGuy = (i == ops.size() - 1 ? static_cast<int>(opData.size()) : ops[i + 1].second);
79 for (int k = ops[i].second; k < nextGuy; k++) {
80 fprintf(stderr, " %d", opData[k]);
81 }
82 fprintf(stderr, ")\n");
83 }
84 std::cerr << "---- opdata ----------------------" << std::endl;
85 for (size_t k = 0; k < opData.size(); k++) {
86 std::cerr << "opData[" << k << "]= " << opData[k] << std::endl;
87 ;
88 }
89 std::cerr << "----- fp --------------------------" << std::endl;
90 for (size_t k = 0; k < d.size(); k++) {
91 std::cerr << "fp[" << k << "]= " << d[k] << std::endl;
92 ;
93 }
94 std::cerr << "---- str ----------------------" << std::endl;
95 std::cerr << "s[0] reserved for datablock = " << reinterpret_cast<size_t>(s[0]) << std::endl;
96 std::cerr << "s[1] is indirectIndex = " << reinterpret_cast<size_t>(s[1]) << std::endl;
97 for (size_t k = 2; k < s.size(); k++) {
98 std::cerr << "s[" << k << "]= 0x" << s[k];
99 if (s[k]) std::cerr << " '" << s[k][0] << s[k][1] << s[k][2] << s[k][3] << "...'";
100 std::cerr << std::endl;
101 }
102 }
103
104 // template Interpreter::OpF* getTemplatizedOp<Promote<1> >(int);
105 // template Interpreter::OpF* getTemplatizedOp<Promote<2> >(int);
106 // template Interpreter::OpF* getTemplatizedOp<Promote<3> >(int);
107
109 // template using c)
110 template <char c, template <char c1, int d> class T>
111 static Interpreter::OpF getTemplatizedOp2(int i) {
112 switch (i) {
113 case 1:
114 return T<c, 1>::f;
115 case 2:
116 return T<c, 2>::f;
117 case 3:
118 return T<c, 3>::f;
119 case 4:
120 return T<c, 4>::f;
121 case 5:
122 return T<c, 5>::f;
123 case 6:
124 return T<c, 6>::f;
125 case 7:
126 return T<c, 7>::f;
127 case 8:
128 return T<c, 8>::f;
129 case 9:
130 return T<c, 9>::f;
131 case 10:
132 return T<c, 10>::f;
133 case 11:
134 return T<c, 11>::f;
135 case 12:
136 return T<c, 12>::f;
137 case 13:
138 return T<c, 13>::f;
139 case 14:
140 return T<c, 14>::f;
141 case 15:
142 return T<c, 15>::f;
143 case 16:
144 return T<c, 16>::f;
145 default:
146 assert(false && "Invalid dynamic parameter (not supported template)");
147 break;
148 }
149 return 0;
150 }
151
152 namespace {
153
155 struct BinaryStringOp {
156 static int f(int* opData, double* fp, char** c, std::vector<int>& callStack) {
157 // get the operand data
158 char*& out = *(char**)c[opData[0]];
159 char* in1 = c[opData[1]];
160 char* in2 = c[opData[2]];
161
162 // delete previous data and allocate a new buffer, only if needed
163 // NOTE: this is more efficient, but might consume more memory...
164 // Maybe make this behaviour configurable ?
165 int len1 = static_cast<int>(strlen(in1));
166 int len2 = static_cast<int>(strlen(in2));
167 if (out == 0 || len1 + len2 + 1 > strlen(out))
168 {
169 delete [] out;
170 out = new char [len1 + len2 + 1];
171 }
172
173 // clear previous evaluation content
174 memset(out, 0, len1 + len2 + 1);
175
176 // concatenate strings
177 strcat(out, in1);
178 strcat(out + len1, in2);
179 out[len1 + len2] = '\0';
180
181 // copy to the output
182 c[opData[3]] = out;
183
184 return 1;
185 }
186 };
187
189 template <char op, int d>
190 struct BinaryOp {
191 static double niceMod(double a, double b) {
192 if (b == 0) return 0;
193 return a - floor(a / b) * b;
194 }
195
196 static int f(int* opData, double* fp, char** c, std::vector<int>& callStack) {
197 double* in1 = fp + opData[0];
198 double* in2 = fp + opData[1];
199 double* out = fp + opData[2];
200
201 for (int k = 0; k < d; k++) {
202 switch (op) {
203 case '+':
204 *out = (*in1) + (*in2);
205 break;
206 case '-':
207 *out = (*in1) - (*in2);
208 break;
209 case '*':
210 *out = (*in1) * (*in2);
211 break;
212 case '/':
213 *out = (*in1) / (*in2);
214 break;
215 case '%':
216 *out = niceMod(*in1, *in2);
217 break;
218 case '^':
219 *out = pow(*in1, *in2);
220 break;
221 // these only make sense with d==1
222 case '<':
223 *out = (*in1) < (*in2);
224 break;
225 case '>':
226 *out = (*in1) > (*in2);
227 break;
228 case 'l':
229 *out = (*in1) <= (*in2);
230 break;
231 case 'g':
232 *out = (*in1) >= (*in2);
233 break;
234 case '&':
235 *out = (*in1) && (*in2);
236 break;
237 case '|':
238 *out = (*in1) || (*in2);
239 break;
240 default:
241 assert(false);
242 }
243 in1++;
244 in2++;
245 out++;
246 }
247 return 1;
248 }
249 };
250
252 template <char op, int d>
253 struct UnaryOp {
254 static int f(int* opData, double* fp, char** c, std::vector<int>& callStack) {
255 double* in = fp + opData[0];
256 double* out = fp + opData[1];
257 for (int k = 0; k < d; k++) {
258 switch (op) {
259 case '-':
260 *out = -(*in);
261 break;
262 case '~':
263 *out = 1 - (*in);
264 break;
265 case '!':
266 *out = !*in;
267 break;
268 default:
269 assert(false);
270 }
271 in++;
272 out++;
273 }
274 return 1;
275 }
276 };
277
279 template <int d>
280 struct Subscript {
281 static int f(int* opData, double* fp, char** c, std::vector<int>& callStack) {
282 int tuple = opData[0];
283 int subscript = int(fp[opData[1]]);
284 int out = opData[2];
285 if (subscript >= d || subscript < 0)
286 fp[out] = 0;
287 else
288 fp[out] = fp[tuple + subscript];
289 return 1;
290 }
291 };
292
294 template <int d>
295 struct Tuple {
296 static int f(int* opData, double* fp, char** c, std::vector<int>& callStack) {
297 int out = opData[d];
298 for (int k = 0; k < d; k++) {
299 fp[out + k] = fp[opData[k]];
300 }
301 return 1;
302 }
303 };
304
306 template <int d>
307 struct AssignOp {
308 static int f(int* opData, double* fp, char** c, std::vector<int>& callStack) {
309 int in = opData[0];
310 int out = opData[1];
311 for (int k = 0; k < d; k++) {
312 fp[out + k] = fp[in + k];
313 }
314 return 1;
315 }
316 };
317
319 struct AssignStrOp {
320 static int f(int* opData, double* fp, char** c, std::vector<int>& callStack) {
321 int in = opData[0];
322 int out = opData[1];
323 c[out] = c[in];
324 return 1;
325 }
326 };
327
329 struct CondJmpRelativeIfFalse {
330 static int f(int* opData, double* fp, char** c, std::vector<int>& callStack) {
331 bool cond = (bool)fp[opData[0]];
332 if (!cond)
333 return opData[1];
334 else
335 return 1;
336 }
337 };
338
340 struct CondJmpRelativeIfTrue {
341 static int f(int* opData, double* fp, char** c, std::vector<int>& callStack) {
342 bool cond = (bool)fp[opData[0]];
343 if (cond)
344 return opData[1];
345 else
346 return 1;
347 }
348 };
349
351 struct JmpRelative {
352 static int f(int* opData, double* fp, char** c, std::vector<int>& callStack) { return opData[0]; }
353 };
354
356 struct EvalVar {
357 static int f(int* opData, double* fp, char** c, std::vector<int>& callStack) {
358 ExprVarRef* ref = reinterpret_cast<ExprVarRef*>(c[opData[0]]);
359 if (ref->type().isFP()) {
360 ref->eval(fp + opData[1]);
361 } else {
362 ref->eval(const_cast<const char**>(c + opData[1]));
363 }
364 return 1;
365 }
366 };
367
369 template <int dim>
370 struct EvalVarBlock {
371 static int f(int* opData, double* fp, char** c, std::vector<int>& callStack) {
372 if (c[0]) {
373 double* basePointer = reinterpret_cast<double*>(c[0]) + opData[0];
374 double* destPointer = fp + opData[1];
375 for (int i = 0; i < dim; i++) destPointer[i] = basePointer[i];
376 }
377 return 1;
378 }
379 };
380
382 template <char uniform, int dim>
383 struct EvalVarBlockIndirect {
384 static int f(int* opData, double* fp, char** c, std::vector<int>& callStack) {
385 if (c[0]) {
386 int stride = opData[2];
387 int outputVarBlockOffset = opData[0];
388 int destIndex = opData[1];
389 size_t indirectIndex = reinterpret_cast<size_t>(c[1]);
390 double* basePointer =
391 reinterpret_cast<double**>(c[0])[outputVarBlockOffset] + (uniform ? 0 : (stride * indirectIndex));
392 double* destPointer = fp + destIndex;
393 for (int i = 0; i < dim; i++) destPointer[i] = basePointer[i];
394 } else {
395 // TODO: this happens in initial evaluation!
396 // std::cerr<<"Did not get data block"<<std::endl;
397 // assert(false && "Did not get data block");
398 }
399 return 1;
400 }
401 };
402
403 template <char op, int d>
404 struct CompareEqOp {
405 static int f(int* opData, double* fp, char** c, std::vector<int>& callStack) {
406 bool result = true;
407 double* in0 = fp + opData[0];
408 double* in1 = fp + opData[1];
409 double* out = fp + opData[2];
410 for (int k = 0; k < d; k++) {
411 switch (op) {
412 case '=':
413 result &= (*in0) == (*in1);
414 break;
415 case '!':
416 result &= (*in0) != (*in1);
417 break;
418 default:
419 assert(false);
420 }
421 in0++;
422 in1++;
423 }
424 *out = result;
425 return 1;
426 }
427 };
428
429 template <char op>
430 struct CompareEqOp<op, 3> {
431 static int f(int* opData, double* fp, char** c, std::vector<int>& callStack) {
432 bool eq = fp[opData[0]] == fp[opData[1]] && fp[opData[0] + 1] == fp[opData[1] + 1] &&
433 fp[opData[0] + 2] == fp[opData[1] + 2];
434 if (op == '=') fp[opData[2]] = eq;
435 if (op == '!') fp[opData[2]] = !eq;
436 return 1;
437 }
438 };
439
440 template <char op, int d>
441 struct StrCompareEqOp {
442 // TODO: this should rely on tokenization and not use strcmp
443 static int f(int* opData, double* fp, char** c, std::vector<int>& callStack) {
444 switch (op) {
445 case '=':
446 fp[opData[2]] = strcmp(c[opData[0]], c[opData[1]]) == 0;
447 break;
448 case '!':
449 fp[opData[2]] = strcmp(c[opData[0]], c[opData[1]]) != 0;
450 break;
451 }
452 return 1;
453 }
454 };
455 }
456
457 namespace {
458 int ProcedureReturn(int* opData, double* fp, char** c, std::vector<int>& callStack) {
459 int newPC = callStack.back();
460 callStack.pop_back();
461 return newPC - opData[0];
462 }
463 }
464
465 namespace {
466 int ProcedureCall(int* opData, double* fp, char** c, std::vector<int>& callStack) {
467 callStack.push_back(opData[0]);
468 return opData[1];
469 }
470 }
471
472 int ExprLocalFunctionNode::buildInterpreter(Interpreter* interpreter) const {
473 _procedurePC = interpreter->nextPC();
474 int lastOperand = 0;
475 for (int c = 0; c < numChildren(); c++) lastOperand = child(c)->buildInterpreter(interpreter);
476 int basePC = interpreter->nextPC();
477 ;
478 interpreter->addOp(ProcedureReturn);
479 // int endPC =
480 interpreter->addOperand(basePC);
481 interpreter->endOp(false);
482 _returnedDataOp = lastOperand;
483
484 return 0;
485 }
486
487 int ExprLocalFunctionNode::buildInterpreterForCall(const ExprFuncNode* callerNode, Interpreter* interpreter) const {
488 std::vector<int> operands;
489 for (int c = 0; c < callerNode->numChildren(); c++) {
490 const ExprNode* child = callerNode->child(c);
491 // evaluate the argument
492 int operand = callerNode->child(c)->buildInterpreter(interpreter);
493 if (child->type().isFP()) {
494 if (callerNode->promote(c) != 0) {
495 // promote the argument to the needed type
496 interpreter->addOp(getTemplatizedOp<Promote>(callerNode->promote(c)));
497 // int promotedOperand=interpreter->allocFP(callerNode->promote(c));
498 interpreter->addOperand(operand);
499 interpreter->addOperand(prototype()->interpreterOps(c));
500 interpreter->endOp();
501 } else {
502 interpreter->addOp(getTemplatizedOp<AssignOp>(child->type().dim()));
503 interpreter->addOperand(operand);
504 interpreter->addOperand(prototype()->interpreterOps(c));
505 interpreter->endOp();
506 }
507 } else {
508 // TODO: string
509 assert(false);
510 }
511 operands.push_back(operand);
512 }
513 int outoperand = -1;
514 if (callerNode->type().isFP())
515 outoperand = interpreter->allocFP(callerNode->type().dim());
516 else if (callerNode->type().isString())
517 outoperand = interpreter->allocPtr();
518 else
519 assert(false);
520
521 int basePC = interpreter->nextPC();
522 interpreter->addOp(ProcedureCall);
523 int returnAddress = interpreter->addOperand(0);
524 interpreter->addOperand(_procedurePC - basePC);
525 interpreter->endOp(false);
526 // set return address
527 interpreter->opData[returnAddress] = interpreter->nextPC();
528
529 // TODO: copy result back and string
530 interpreter->addOp(getTemplatizedOp<AssignOp>(callerNode->type().dim()));
531 interpreter->addOperand(_returnedDataOp);
532 interpreter->addOperand(outoperand);
533 interpreter->endOp();
534
535 return outoperand;
536 }
537
538 int ExprNode::buildInterpreter(Interpreter* interpreter) const {
539 for (int c = 0; c < numChildren(); c++) child(c)->buildInterpreter(interpreter);
540 return -1;
541 }
542
543 int ExprNumNode::buildInterpreter(Interpreter* interpreter) const {
544 int loc = interpreter->allocFP(1);
545 interpreter->d[loc] = value();
546 return loc;
547 }
548
549 int ExprStrNode::buildInterpreter(Interpreter* interpreter) const {
550 int loc = interpreter->allocPtr();
551 interpreter->s[loc] = const_cast<char*>(_str.c_str());
552 return loc;
553 }
554
555 int ExprVecNode::buildInterpreter(Interpreter* interpreter) const {
556 std::vector<int> locs;
557 for (int k = 0; k < numChildren(); k++) {
558 const ExprNode* c = child(k);
559 locs.push_back(c->buildInterpreter(interpreter));
560 }
561 interpreter->addOp(getTemplatizedOp<Tuple>(numChildren()));
562 for (int k = 0; k < numChildren(); k++) interpreter->addOperand(locs[k]);
563 int loc = interpreter->allocFP(numChildren());
564 interpreter->addOperand(loc);
565 interpreter->endOp();
566 return loc;
567 }
568
569 int ExprBinaryOpNode::buildInterpreter(Interpreter* interpreter) const {
570 const ExprNode* child0 = child(0), *child1 = child(1);
571 int dim0 = child0->type().dim(), dim1 = child1->type().dim(), dimout = type().dim();
572 int op0 = child0->buildInterpreter(interpreter);
573 int op1 = child1->buildInterpreter(interpreter);
574 if (dimout > 1) {
575 if (dim0 != dimout) {
576 interpreter->addOp(getTemplatizedOp<Promote>(dimout));
577 int promoteOp0 = interpreter->allocFP(dimout);
578 interpreter->addOperand(op0);
579 interpreter->addOperand(promoteOp0);
580 op0 = promoteOp0;
581 interpreter->endOp();
582 }
583 if (dim1 != dimout) {
584 interpreter->addOp(getTemplatizedOp<Promote>(dimout));
585 int promoteOp1 = interpreter->allocFP(dimout);
586 interpreter->addOperand(op1);
587 interpreter->addOperand(promoteOp1);
588 op1 = promoteOp1;
589 interpreter->endOp();
590 }
591 }
592
593 // check if the node will output a string of numerical value
594 bool isString = child0->type().isString() || child1->type().isString();
595
596 // add the operator
597 if (isString == false) {
598 switch (_op) {
599 case '+':
600 interpreter->addOp(getTemplatizedOp2<'+', BinaryOp>(dimout));
601 break;
602 case '-':
603 interpreter->addOp(getTemplatizedOp2<'-', BinaryOp>(dimout));
604 break;
605 case '*':
606 interpreter->addOp(getTemplatizedOp2<'*', BinaryOp>(dimout));
607 break;
608 case '/':
609 interpreter->addOp(getTemplatizedOp2<'/', BinaryOp>(dimout));
610 break;
611 case '^':
612 interpreter->addOp(getTemplatizedOp2<'^', BinaryOp>(dimout));
613 break;
614 case '%':
615 interpreter->addOp(getTemplatizedOp2<'%', BinaryOp>(dimout));
616 break;
617 default:
618 assert(false);
619 }
620 } else {
621 switch (_op) {
622 case '+': {
623 interpreter->addOp(BinaryStringOp::f);
624 int intermediateOp = interpreter->allocPtr();
625 interpreter->s[intermediateOp] = (char*)(&_out);
626 interpreter->addOperand(intermediateOp);
627 break;
628 }
629 default:
630 assert(false);
631 }
632 }
633
634 // allocate the output
635 int op2 = -1;
636 if (isString == false) {
637 op2 = interpreter->allocFP(dimout);
638 } else {
639 op2 = interpreter->allocPtr();
640 }
641
642 interpreter->addOperand(op0);
643 interpreter->addOperand(op1);
644 interpreter->addOperand(op2);
645
646 // NOTE: one of the operand can be a function. If it's the case for
647 // strings, since functions are not immediately executed (they have
648 // endOp(false)) using endOp() here would result in a nullptr
649 // input operand during eval, thus the following arg to endOp.
650 //
651 // TODO: only stop execution if one of the operand is either a
652 // function of a var ref.
653 interpreter->endOp(isString == false);
654
655 return op2;
656 }
657
658 int ExprUnaryOpNode::buildInterpreter(Interpreter* interpreter) const {
659 const ExprNode* child0 = child(0);
660 int dimout = type().dim();
661 int op0 = child0->buildInterpreter(interpreter);
662
663 switch (_op) {
664 case '-':
665 interpreter->addOp(getTemplatizedOp2<'-', UnaryOp>(dimout));
666 break;
667 case '~':
668 interpreter->addOp(getTemplatizedOp2<'~', UnaryOp>(dimout));
669 break;
670 case '!':
671 interpreter->addOp(getTemplatizedOp2<'!', UnaryOp>(dimout));
672 break;
673 default:
674 assert(false);
675 }
676 int op1 = interpreter->allocFP(dimout);
677 interpreter->addOperand(op0);
678 interpreter->addOperand(op1);
679 interpreter->endOp();
680
681 return op1;
682 }
683
684 int ExprSubscriptNode::buildInterpreter(Interpreter* interpreter) const {
685 const ExprNode* child0 = child(0), *child1 = child(1);
686 int dimin = child0->type().dim();
687 int op0 = child0->buildInterpreter(interpreter);
688 int op1 = child1->buildInterpreter(interpreter);
689 int op2 = interpreter->allocFP(1);
690
691 interpreter->addOp(getTemplatizedOp<Subscript>(dimin));
692 interpreter->addOperand(op0);
693 interpreter->addOperand(op1);
694 interpreter->addOperand(op2);
695 interpreter->endOp();
696 return op2;
697 }
698
699 int ExprVarNode::buildInterpreter(Interpreter* interpreter) const {
700 if (const ExprLocalVar* var = _localVar) {
701 // if (const ExprLocalVar* phi = var->getPhi()) var = phi;
702 Interpreter::VarToLoc::iterator i = interpreter->varToLoc.find(var);
703 if (i != interpreter->varToLoc.end())
704 return i->second;
705 else
706 throw std::runtime_error("Unallocated variable encountered.");
707 } else if (const ExprVarRef* var = _var) {
708 ExprType type = var->type();
709 int destLoc = -1;
710 if (type.isFP()) {
711 int dim = type.dim();
712 destLoc = interpreter->allocFP(dim);
713 } else
714 destLoc = interpreter->allocPtr();
715 if (const auto* blockVarRef = dynamic_cast<const VarBlockCreator::Ref*>(var)) {
716 // TODO: handle strings
717 if (blockVarRef->type().isLifetimeUniform())
718 interpreter->addOp(getTemplatizedOp2<1, EvalVarBlockIndirect>(type.dim()));
719 else
720 interpreter->addOp(getTemplatizedOp2<0, EvalVarBlockIndirect>(type.dim()));
721 interpreter->addOperand(blockVarRef->offset());
722 interpreter->addOperand(destLoc);
723 interpreter->addOperand(blockVarRef->stride());
724 interpreter->endOp();
725 } else {
726 int varRefLoc = interpreter->allocPtr();
727 interpreter->addOp(EvalVar::f);
728 interpreter->s[varRefLoc] = const_cast<char*>(reinterpret_cast<const char*>(var));
729 interpreter->addOperand(varRefLoc);
730 interpreter->addOperand(destLoc);
731 interpreter->endOp();
732 }
733 return destLoc;
734 }
735 return -1;
736 }
737
738 int ExprLocalVar::buildInterpreter(Interpreter* interpreter) const {
739 return interpreter->varToLoc[this] =
740 _type.isFP() ? interpreter->allocFP(_type.dim()) : _type.isString() ? interpreter->allocPtr() : -1;
741 }
742
743 int ExprAssignNode::buildInterpreter(Interpreter* interpreter) const {
744 int loc = _localVar->buildInterpreter(interpreter);
745 assert(loc != -1 && "Invalid type found");
746
747 ExprType child0Type = child(0)->type();
748 int op0 = child(0)->buildInterpreter(interpreter);
749 if (child0Type.isFP()) {
750 interpreter->addOp(getTemplatizedOp<AssignOp>(child0Type.dim()));
751 } else if (child0Type.isString()) {
752 interpreter->addOp(AssignStrOp::f);
753 } else {
754 assert(false && "Invalid desired assign type");
755 return -1;
756 }
757 interpreter->addOperand(op0);
758 interpreter->addOperand(loc);
759 interpreter->endOp(child0Type.isString() == false);
760 return loc;
761 }
762
763 void copyVarToPromotedPosition(Interpreter* interpreter, ExprLocalVar* varSource, ExprLocalVar* varDest) {
764 if (varDest->type().isFP()) {
765 int destDim = varDest->type().dim();
766 if (destDim != varSource->type().dim()) {
767 assert(varSource->type().dim() == 1);
768 interpreter->addOp(getTemplatizedOp<Promote>(destDim));
769 } else {
770 interpreter->addOp(getTemplatizedOp<AssignOp>(destDim));
771 }
772 interpreter->addOperand(interpreter->varToLoc[varSource]);
773 interpreter->addOperand(interpreter->varToLoc[varDest]);
774 interpreter->endOp();
775 } else if (varDest->type().isString()) {
776 interpreter->addOp(AssignStrOp::f);
777 interpreter->addOperand(interpreter->varToLoc[varSource]);
778 interpreter->addOperand(interpreter->varToLoc[varDest]);
779 interpreter->endOp();
780 } else {
781 assert(false && "failed to promote invalid type");
782 }
783 }
784
785 int ExprIfThenElseNode::buildInterpreter(Interpreter* interpreter) const {
786 int condop = child(0)->buildInterpreter(interpreter);
787 int basePC = interpreter->nextPC();
788
789 const auto& merges = _varEnv->merge(_varEnvMergeIndex);
790 // Allocate spots for all the join variables
791 // they are before in the sequence of operands, but it doesn't matter
792 // NOTE: at this point the variables thenVar and elseVar have not been codegen'd
793 for (auto& it : merges) {
794 ExprLocalVarPhi* finalVar = it.second;
795 if (finalVar->valid()) {
796 finalVar->buildInterpreter(interpreter);
797 }
798 }
799
800 // Setup the conditional jump
801 interpreter->addOp(CondJmpRelativeIfFalse::f);
802 interpreter->addOperand(condop);
803 int destFalse = interpreter->addOperand(0);
804 interpreter->endOp();
805
806 // Then block (build interpreter and copy variables out then jump to end)
807 child(1)->buildInterpreter(interpreter);
808 for (auto& it : merges) {
809 ExprLocalVarPhi* finalVar = it.second;
810 if (finalVar->valid()) {
811 copyVarToPromotedPosition(interpreter, finalVar->_thenVar, finalVar);
812 }
813 }
814 interpreter->addOp(JmpRelative::f);
815 int destEnd = interpreter->addOperand(0);
816 interpreter->endOp();
817
818 // Else block (build interpreter, copy variables out and then we're at end)
819 int child2PC = interpreter->nextPC();
820 child(2)->buildInterpreter(interpreter);
821 for (auto& it : merges) {
822 ExprLocalVarPhi* finalVar = it.second;
823 if (finalVar->valid()) {
824 copyVarToPromotedPosition(interpreter, finalVar->_elseVar, finalVar);
825 }
826 }
827
828 // Patch the jump addresses in the conditional
829 interpreter->opData[destFalse] = child2PC - basePC;
830 interpreter->opData[destEnd] = interpreter->nextPC() - (child2PC - 1);
831
832 return -1;
833 }
834
835 int ExprCompareNode::buildInterpreter(Interpreter* interpreter) const {
836 const ExprNode* child0 = child(0), *child1 = child(1);
837 assert(type().dim() == 1 && type().isFP());
838
839 if (_op == '&' || _op == '|') {
840 // Handle short circuiting
841
842 // allocate output
843 int op2 = interpreter->allocFP(1);
844 // unconditionally evaluate first argument
845 int op0 = child0->buildInterpreter(interpreter);
846 // conditional to check if that argument could continue
847 int basePC = (interpreter->nextPC());
848 interpreter->addOp(_op == '&' ? CondJmpRelativeIfFalse::f : CondJmpRelativeIfTrue::f);
849 interpreter->addOperand(op0);
850 int destFalse = interpreter->addOperand(0);
851 interpreter->endOp();
852 // this is the no-branch case (op1=true for & and op0=false for |), so eval op1
853 int op1 = child1->buildInterpreter(interpreter);
854 // combine with &
855 interpreter->addOp(_op == '&' ? getTemplatizedOp2<'&', BinaryOp>(1) : getTemplatizedOp2<'|', BinaryOp>(1));
856 interpreter->addOperand(op0);
857 interpreter->addOperand(op1);
858 interpreter->addOperand(op2);
859 interpreter->endOp();
860 interpreter->addOp(JmpRelative::f);
861 int destEnd = interpreter->addOperand(0);
862 interpreter->endOp();
863 // this is the branch case (op1=false for & and op0=true for |) so no eval of op1 required
864 // just copy from the op0's value
865 int falseConditionPC = interpreter->nextPC();
866 interpreter->addOp(AssignOp<1>::f);
867 interpreter->addOperand(op0);
868 interpreter->addOperand(op2);
869 interpreter->endOp();
870
871 // fix PC relative jump addressses
872 interpreter->opData[destFalse] = falseConditionPC - basePC;
873 interpreter->opData[destEnd] = interpreter->nextPC() - (falseConditionPC - 1);
874
875 return op2;
876
877 } else {
878 // Noraml case, always have to evaluatee everything
879 int op0 = child0->buildInterpreter(interpreter);
880 int op1 = child1->buildInterpreter(interpreter);
881 switch (_op) {
882 case '<':
883 interpreter->addOp(getTemplatizedOp2<'<', BinaryOp>(1));
884 break;
885 case '>':
886 interpreter->addOp(getTemplatizedOp2<'>', BinaryOp>(1));
887 break;
888 case 'l':
889 interpreter->addOp(getTemplatizedOp2<'l', BinaryOp>(1));
890 break;
891 case 'g':
892 interpreter->addOp(getTemplatizedOp2<'g', BinaryOp>(1));
893 break;
894 case '&':
895 assert(false); // interpreter->addOp(getTemplatizedOp2<'&',BinaryOp>(1));break;
896 case '|':
897 assert(false); // interpreter->addOp(getTemplatizedOp2<'|',BinaryOp>(1));break;
898 default:
899 assert(false);
900 }
901 int op2 = interpreter->allocFP(1);
902 interpreter->addOperand(op0);
903 interpreter->addOperand(op1);
904 interpreter->addOperand(op2);
905 interpreter->endOp();
906 return op2;
907 }
908 }
909
910 int ExprPrototypeNode::buildInterpreter(Interpreter* interpreter) const {
911 // set up parents
912 _interpreterOps.clear();
913 for (int c = 0; c < numChildren(); c++) {
914 if (const ExprVarNode* childVarNode = dynamic_cast<const ExprVarNode*>(child(c))) {
915 ExprType childType = childVarNode->type();
916 if (childType.isFP()) {
917 int operand = interpreter->allocFP(childType.dim());
918 _interpreterOps.push_back(operand);
919 interpreter->varToLoc[childVarNode->localVar()] = operand;
920 }
921 } else {
922 assert(false);
923 }
924 child(c)->buildInterpreter(interpreter);
925
926 // make sure we have a slot in our global activation record for the variables!
927 }
928 return 0;
929 }
930
931 int ExprCompareEqNode::buildInterpreter(Interpreter* interpreter) const {
932 const ExprNode* child0 = child(0), *child1 = child(1);
933 int op0 = child0->buildInterpreter(interpreter);
934 int op1 = child1->buildInterpreter(interpreter);
935
936 if (child0->type().isFP()) {
937 int dim0 = child0->type().dim(), dim1 = child1->type().dim();
938 int dimCompare = std::max(dim0, dim1);
939 if (dimCompare > 1) {
940 if (dim0 == 1) {
941 interpreter->addOp(getTemplatizedOp<Promote>(dim1));
942 int promotedOp0 = interpreter->allocFP(dim1);
943 interpreter->addOperand(op0);
944 interpreter->addOperand(promotedOp0);
945 interpreter->endOp();
946 op0 = promotedOp0;
947 }
948 if (dim1 == 1) {
949 interpreter->addOp(getTemplatizedOp<Promote>(dim0));
950 int promotedOp1 = interpreter->allocFP(dim0);
951 interpreter->addOperand(op1);
952 interpreter->addOperand(promotedOp1);
953 interpreter->endOp();
954 op1 = promotedOp1;
955 }
956 }
957 if (_op == '=')
958 interpreter->addOp(getTemplatizedOp2<'=', CompareEqOp>(dimCompare));
959 else if (_op == '!')
960 interpreter->addOp(getTemplatizedOp2<'!', CompareEqOp>(dimCompare));
961 else
962 assert(false && "Invalid operation");
963 } else if (child0->type().isString()) {
964 if (_op == '=')
965 interpreter->addOp(getTemplatizedOp2<'=', StrCompareEqOp>(1));
966 else if (_op == '!')
967 interpreter->addOp(getTemplatizedOp2<'!', StrCompareEqOp>(1));
968 else
969 assert(false && "Invalid operation");
970 } else
971 assert(false && "Invalid type for comparison");
972 int op2 = interpreter->allocFP(1);
973 interpreter->addOperand(op0);
974 interpreter->addOperand(op1);
975 interpreter->addOperand(op2);
976 interpreter->endOp(child0->type().isString() == false);
977 return op2;
978 }
979
980 int ExprCondNode::buildInterpreter(Interpreter* interpreter) const {
981 int opOut = -1;
982 // TODO: handle strings!
983 int dimout = type().dim();
984
985 // conditional
986 int condOp = child(0)->buildInterpreter(interpreter);
987 int basePC = (interpreter->nextPC());
988 interpreter->addOp(CondJmpRelativeIfFalse::f);
989 interpreter->addOperand(condOp);
990 int destFalse = interpreter->addOperand(0);
991 interpreter->endOp();
992
993 // true way of working
994 int op1 = child(1)->buildInterpreter(interpreter);
995 if (type().isFP())
996 interpreter->addOp(getTemplatizedOp<AssignOp>(dimout));
997 else if (type().isString())
998 interpreter->addOp(AssignStrOp::f);
999 else
1000 assert(false);
1001 interpreter->addOperand(op1);
1002 int dataOutTrue = interpreter->addOperand(-1);
1003 interpreter->endOp(false);
1004
1005 // jump past false way of working
1006 interpreter->addOp(JmpRelative::f);
1007 int destEnd = interpreter->addOperand(0);
1008 interpreter->endOp();
1009
1010 // record start of false condition
1011 int child2PC = interpreter->nextPC();
1012
1013 // false way of working
1014 int op2 = child(2)->buildInterpreter(interpreter);
1015 if (type().isFP())
1016 interpreter->addOp(getTemplatizedOp<AssignOp>(dimout));
1017 else if (type().isString())
1018 interpreter->addOp(AssignStrOp::f);
1019 else
1020 assert(false);
1021 interpreter->addOperand(op2);
1022 int dataOutFalse = interpreter->addOperand(-1);
1023 interpreter->endOp(false);
1024
1025 // patch up relative jumps
1026 interpreter->opData[destFalse] = child2PC - basePC;
1027 interpreter->opData[destEnd] = interpreter->nextPC() - (child2PC - 1);
1028
1029 // allocate output
1030 if (type().isFP())
1031 opOut = interpreter->allocFP(type().dim());
1032 else if (type().isString())
1033 opOut = interpreter->allocPtr();
1034 else
1035 assert(false);
1036
1037 // patch outputs on assigns in each condition
1038 interpreter->opData[dataOutTrue] = opOut;
1039 interpreter->opData[dataOutFalse] = opOut;
1040
1041 return opOut;
1042 }
1043
1044 int ExprBlockNode::buildInterpreter(Interpreter* interpreter) const {
1045 assert(numChildren() == 2);
1046 child(0)->buildInterpreter(interpreter);
1047 return child(1)->buildInterpreter(interpreter);
1048 }
1049
1050 int ExprModuleNode::buildInterpreter(Interpreter* interpreter) const {
1051 int lastIdx = 0;
1052 for (int c = 0; c < numChildren(); c++) {
1053 if (c == numChildren() - 1) interpreter->setPCStart(interpreter->nextPC());
1054 lastIdx = child(c)->buildInterpreter(interpreter);
1055 }
1056 return lastIdx;
1057 }
1058 }
SeExpr2::ExprType::isString
bool isString() const
Definition: ExprType.h:169
SeExpr2::ExprVarNode::_localVar
ExprLocalVar * _localVar
Definition: ExprNode.h:481
SeExpr2::ExprLocalFunctionNode::prototype
const ExprPrototypeNode * prototype() const
TODO: Accessor for prototype (probably not needed when we use prep right)
Definition: ExprNode.h:317
SeExpr2::Interpreter::varToLoc
VarToLoc varToLoc
Definition: Interpreter.h:51
SeExpr2::isString
const ExprStrNode * isString(const ExprNode *testee)
Definition: ExprPatterns.h:44
SeExpr2::Interpreter::eval
void eval(VarBlock *varBlock, bool debug=false)
Evaluate program.
Definition: Interpreter.cpp:31
f
with numParticles numAttributes A variable block contains variable names and types but doesn t care what the values are< pre > void f(const std::string &s, MyParticleData *p, int outputDim=3)
Definition: varblocks.txt:35
SeExpr2::ExprUnaryOpNode::_op
char _op
Definition: ExprNode.h:402
SeExpr2::Interpreter::endOp
void endOp(bool execute=true)
Definition: Interpreter.h:83
SeExpr2::VarBlock
A thread local evaluation context. Just allocate and fill in with data.
Definition: VarBlock.h:33
SeExpr2::ExprNode::numChildren
int numChildren() const
Number of children.
Definition: ExprNode.h:114
SeExpr2::ExprNode::type
const ExprType & type() const
The type of the node.
Definition: ExprNode.h:145
SeExpr2::Interpreter::addOp
int addOp(OpF op)
! adds an operator to the program (pointing to the data at the current location)
Definition: Interpreter.h:73
SeExpr2::ExprIfThenElseNode::_varEnvMergeIndex
size_t _varEnvMergeIndex
Definition: ExprNode.h:351
SeExpr2::ExprNumNode::value
double value() const
Definition: ExprNode.h:493
SeExpr2::ExprLocalFunctionNode::buildInterpreter
int buildInterpreter(Interpreter *interpreter) const
Build the interpreter.
Definition: Interpreter.cpp:472
SeExpr2::Interpreter::d
std::vector< double > d
Double data (constants and evaluated)
Definition: Interpreter.h:43
SeExpr2::ExprUnaryOpNode::buildInterpreter
virtual int buildInterpreter(Interpreter *interpreter) const
builds an interpreter. Returns the location index for the evaluated data
Definition: Interpreter.cpp:658
SeExpr2::ExprIfThenElseNode::_varEnv
ExprVarEnv * _varEnv
Definition: ExprNode.h:350
SeExpr2::ExprNode
Definition: ExprNode.h:72
SeExpr2::ExprLocalFunctionNode::_procedurePC
int _procedurePC
Definition: ExprNode.h:326
Interpreter.h
SeExpr2::ExprVarNode::var
const ExprVarRef * var() const
Definition: ExprNode.h:477
SeExpr2::Interpreter::opData
std::vector< int > opData
Ooperands to op.
Definition: Interpreter.h:47
SeExpr2::copyVarToPromotedPosition
void copyVarToPromotedPosition(Interpreter *interpreter, ExprLocalVar *varSource, ExprLocalVar *varDest)
Definition: Interpreter.cpp:763
SeExpr2::ExprLocalVarPhi
ExprLocalVar join (merge) references. Remembers which variables are possible assigners to this.
Definition: ExprEnv.h:67
SeExpr2::ExprType::dim
int dim() const
Definition: ExprType.h:160
SeExpr2::ExprBinaryOpNode::_out
char * _out
Definition: ExprNode.h:461
SeExpr2::ExprType
Definition: ExprType.h:39
SeExpr2::VarBlock::d
std::vector< double > d
copy of Interpreter's double data
Definition: VarBlock.h:68
SeExpr2::ExprFuncNode
Node that calls a function.
Definition: ExprNode.h:517
Platform.h
Platform-specific classes, functions, and includes.
SeExpr2::ExprCompareNode::_op
char _op
_op '<' less-than, 'l' less-than-eq, '>' greater-than, 'g' greater-than-eq
Definition: ExprNode.h:447
endif
endif() set(ANIMLIB_SRCS "") if(DEFINED ANIMLIB_DIR) set(CE_MOC_HDRS CE/CECurveListUI.h CE/CEDragHandlers.h CE/CEGraphCurve.h CE/CEGraphKey.h CE/CEGraphSeg.h CE/CEGraphUI.h CE/CEMainUI.h CE/CESegEditUI.h CE/CETool.h) set(CE_CPPS CE/CECurveListUI.cpp CE/CEDragHandlers.cpp CE/CEGraphCurve.cpp CE/CEGraphKey.cpp CE/CEGraphSeg.cpp CE/CEGraphUI.cpp CE/CEMainUI.cpp CE/CESegEditUI.cpp CE/CETool.cpp) if(ENABLE_QT5) qt5_wrap_cpp(CE_MOC_SRCS $
Definition: CMakeLists.txt:47
SeExpr2::ExprLocalVar::type
ExprType type() const
returns type of the variable
Definition: ExprEnv.h:51
SeExpr2::ExprNumNode::buildInterpreter
virtual int buildInterpreter(Interpreter *interpreter) const
builds an interpreter. Returns the location index for the evaluated data
Definition: Interpreter.cpp:543
SeExpr2::ExprVarNode::buildInterpreter
virtual int buildInterpreter(Interpreter *interpreter) const
builds an interpreter. Returns the location index for the evaluated data
Definition: Interpreter.cpp:699
SeExpr2::ExprLocalVar::_type
ExprType _type
Definition: ExprEnv.h:39
SeExpr2::ExprLocalFunctionNode::buildInterpreterForCall
int buildInterpreterForCall(const ExprFuncNode *callerNode, Interpreter *interpreter) const
Build interpreter if we are called.
Definition: Interpreter.cpp:487
SeExpr2
Definition: Context.h:22
SeExpr2::ExprIfThenElseNode::buildInterpreter
virtual int buildInterpreter(Interpreter *interpreter) const
builds an interpreter. Returns the location index for the evaluated data
Definition: Interpreter.cpp:785
SeExpr2::ExprAssignNode::buildInterpreter
virtual int buildInterpreter(Interpreter *interpreter) const
builds an interpreter. Returns the location index for the evaluated data
Definition: Interpreter.cpp:743
SeExpr2::ExprLocalVar::buildInterpreter
int buildInterpreter(Interpreter *interpreter) const
Allocates variable for interpreter.
Definition: Interpreter.cpp:738
ExprNode.h
SeExpr2::ExprNode::buildInterpreter
virtual int buildInterpreter(Interpreter *interpreter) const
builds an interpreter. Returns the location index for the evaluated data
Definition: Interpreter.cpp:538
SeExpr2::ExprVarNode::_var
ExprVarRef * _var
Definition: ExprNode.h:482
SeExpr2::Interpreter::nextPC
int nextPC()
Return the position that the next instruction will be placed at.
Definition: Interpreter.h:70
SeExpr2::Interpreter::allocFP
int allocFP(int n)
! Allocate a floating point set of data of dimension n
Definition: Interpreter.h:104
SeExpr2::ExprCompareNode::buildInterpreter
virtual int buildInterpreter(Interpreter *interpreter) const
builds an interpreter. Returns the location index for the evaluated data
Definition: Interpreter.cpp:835
SeExpr2::Interpreter::callStack
std::vector< int > callStack
Definition: Interpreter.h:57
it
you may not use this file except in compliance with the License and the following modification to it
Definition: license.txt:10
SeExpr2::ExprLocalVarPhi::valid
bool valid() const
Definition: ExprEnv.h:81
SeExpr2::ExprType::isFP
bool isFP() const
Direct is predicate checks.
Definition: ExprType.h:164
SeExpr2::VarBlock::indirectIndex
int indirectIndex
indirect index to add to pointer based data
Definition: VarBlock.h:62
SeExpr2::Interpreter::s
std::vector< char * > s
constant and evaluated pointer data
Definition: Interpreter.h:45
SeExpr2::ExprBinaryOpNode::buildInterpreter
virtual int buildInterpreter(Interpreter *interpreter) const
builds an interpreter. Returns the location index for the evaluated data
Definition: Interpreter.cpp:569
a
Defined as a *alpha b *alpha< br ></div >< br > float< b > float a
Definition: userdoc.txt:174
SeExpr2::ExprBinaryOpNode::_op
char _op
Definition: ExprNode.h:460
SeExpr2::ExprSubscriptNode::buildInterpreter
virtual int buildInterpreter(Interpreter *interpreter) const
builds an interpreter. Returns the location index for the evaluated data
Definition: Interpreter.cpp:684
SeExpr2::VarBlock::threadSafe
bool threadSafe
if true, interpreter's data will be copied to this instance before evaluation.
Definition: VarBlock.h:65
SeExpr2::ExprAssignNode::_localVar
ExprLocalVar * _localVar
Definition: ExprNode.h:375
SeExpr2::ExprLocalVarPhi::_thenVar
ExprLocalVar * _thenVar
Definition: ExprEnv.h:90
SeExpr2::Interpreter::OpF
int(* OpF)(int *, double *, char **, std::vector< int > &)
Op function pointer arguments are (int* currOpData,double* currD,char** c,std::stack<int>& callStacku...
Definition: Interpreter.h:54
SeExpr2::Interpreter::allocPtr
int allocPtr()
Allocate a pointer location (can be anything, but typically space for char*)
Definition: Interpreter.h:111
SeExpr2::VarBlock::s
std::vector< char * > s
copy of Interpreter's str data
Definition: VarBlock.h:71
SeExpr2::VarBlock::data
char ** data()
Raw data of the data block pointer (used by compiler)
Definition: VarBlock.h:74
SeExpr2::ExprVarRef
abstract class for implementing variable references
Definition: Expression.h:45
SeExpr2::ExprVarEnv::merge
std::vector< std::pair< std::string, ExprLocalVarPhi * > > & merge(size_t index)
Definition: ExprEnv.h:142
SeExpr2::ExprNode::child
const ExprNode * child(size_t i) const
Get 0 indexed child.
Definition: ExprNode.h:117
SeExpr2::ExprStrNode::buildInterpreter
virtual int buildInterpreter(Interpreter *interpreter) const
builds an interpreter. Returns the location index for the evaluated data
Definition: Interpreter.cpp:549
SeExpr2::ExprVarRef::type
virtual ExprType type() const
returns (current) type
Definition: Expression.h:59
SeExpr2::ExprLocalFunctionNode::_returnedDataOp
int _returnedDataOp
Definition: ExprNode.h:327
SeExpr2::Interpreter::ops
std::vector< std::pair< OpF, int > > ops
Definition: Interpreter.h:56
SeExpr2::ExprFuncNode::promote
int promote(int i) const
Definition: ExprNode.h:590
SeExpr2::getTemplatizedOp2
static Interpreter::OpF getTemplatizedOp2(int i)
Return the function f encapsulated in class T for the dynamic i converted to a static d....
Definition: Interpreter.cpp:111
SeExpr2::Interpreter
Definition: Interpreter.h:40
SeExpr2::ExprLocalVar
ExprLocalVar reference, all local variables in seexpr are subclasses of this or this itself.
Definition: ExprEnv.h:37
SeExpr2::ExprStrNode::_str
std::string _str
Definition: ExprNode.h:513
SeExpr2::Interpreter::_pcStart
int _pcStart
Definition: Interpreter.h:61
b
Between a and b
Definition: userdoc.txt:180
SeExpr2::Interpreter::print
void print(int pc=-1) const
Debug by printing program.
Definition: Interpreter.cpp:69
pow
< br > pow($a, 0.5)+ $b< br >< br ></div > External variables can also be overridden by local assignment. &nbsp
SeExpr2::Interpreter::addOperand
int addOperand(int param)
! Adds an operand. Note this should be done after doing the addOp!
Definition: Interpreter.h:96
SeExpr2::ExprLocalVarPhi::_elseVar
ExprLocalVar * _elseVar
Definition: ExprEnv.h:90
VarBlock.h
SeExpr2::ExprVecNode::buildInterpreter
virtual int buildInterpreter(Interpreter *interpreter) const
builds an interpreter. Returns the location index for the evaluated data
Definition: Interpreter.cpp:555