DD4hep  1.30.0
Detector Description Toolkit for High Energy Physics
Evaluator.cpp
Go to the documentation of this file.
1 
2 // -*- C++ -*-
3 // ---------------------------------------------------------------------------
4 
5 #include "Evaluator/Evaluator.h"
6 #include "Evaluator/detail/Evaluator.h"
7 
8 #include <iostream>
9 #include <cmath> // for pow()
10 #include <sstream>
11 #include <mutex>
12 #include <condition_variable>
13 #include <cctype>
14 #include <cerrno>
15 #include <cstring>
16 #include <cstdlib> // for strtod()
17 #include <stack>
18 #include <string>
19 #include <unordered_map>
20 
21 // Disable some diagnostics, which we know, but need to ignore
22 #if defined(__GNUC__) && !defined(__APPLE__) && !defined(__llvm__)
23 /* This is OK:
24  ../DDCore/src/Evaluator/Evaluator.cpp: In execute_function 'int engine(pchar, pchar, double&, char*&, const dic_type&)':
25  ../DDCore/src/Evaluator/Evaluator.cpp:164:23: warning: 'pp[3]' may be used uninitialized in this execute_function [-Wmaybe-uninitialized]
26  result = (*fcn.f4)(pp[3],pp[2],pp[1],pp[0]);
27  ....
28 */
29 #pragma GCC diagnostic ignored "-Wmaybe-uninitialized"
30 #endif
31 
32 // fallthrough only exists from c++17
33 #if defined __has_cpp_attribute
34  #if __has_cpp_attribute(fallthrough)
35  #define ATTR_FALLTHROUGH [[fallthrough]]
36  #else
37  #define ATTR_FALLTHROUGH
38  #endif
39 #else
40  #define ATTR_FALLTHROUGH
41 #endif
42 
43 //---------------------------------------------------------------------------
44 #define EVAL dd4hep::tools::Evaluator
45 
46 namespace {
47 
49  struct Item {
50  enum { UNKNOWN, VARIABLE, EXPRESSION, FUNCTION, STRING } what;
51  double variable;
52  std::string expression;
53  void *function;
54 
55  explicit Item() : what(UNKNOWN), variable(0),expression(), function(0) {}
56  explicit Item(double x) : what(VARIABLE), variable(x),expression(), function(0) {}
57  explicit Item(std::string x) : what(EXPRESSION),variable(0),expression(std::move(x)),function(0) {}
58  explicit Item(void *x) : what(FUNCTION), variable(0),expression(), function(x) {}
59  };
60 
62  union FCN {
63  void* ptr;
64  double (*f0)();
65  double (*f1)(double);
66  double (*f2)(double,double);
67  double (*f3)(double,double,double);
68  double (*f4)(double,double,double,double);
69  double (*f5)(double,double,double,double,double);
70  FCN(void* p) { ptr = p; }
71  FCN(double (*f)()) { f0 = f; }
72  FCN(double (*f)(double)) { f1 = f; }
73  FCN(double (*f)(double,double)) { f2 = f; }
74  FCN(double (*f)(double,double,double)) { f3 = f; }
75  FCN(double (*f)(double,double,double,double)) { f4 = f; }
76  FCN(double (*f)(double,double,double,double,double)) { f5 = f; }
77  };
78 }
79 
80 //typedef char * pchar;
81 typedef std::unordered_map<std::string,Item> dic_type;
82 
85  // based on https://stackoverflow.com/a/58018604
86  struct ReadLock {
89  theStruct->theCond.wait(theLg);
91  }
92 
93  ReadLock(const ReadLock&) = delete;
96  while(theStruct->theReadersWaiting > 0)
97  theStruct->theCond.wait(theLg);
98  theStruct->theCond.notify_one();
99  }
101  std::unique_lock<std::mutex> theLg;
102 
103  };
104  struct WriteLock {
107  theStruct->theCond.wait(theLg);
108  }
109  WriteLock(const WriteLock&) = delete;
111  theStruct->theWriterWaiting = true;
112  while(theStruct->theReadersWaiting > 0)
113  theStruct->theCond.wait(theLg);
114  theStruct->theWriterWaiting = false;
115  theStruct->theCond.notify_all();
116  }
118  std::unique_lock<std::mutex> theLg;
119  };
120 
123  bool theWriterWaiting = false;
124  std::condition_variable theCond;
125  std::mutex theLock;
126 };
127 
128 //---------------------------------------------------------------------------
129 #define REMOVE_BLANKS \
130  for(pointer=name;;pointer++) if (!isspace(*pointer)) break; \
131  for(n=strlen(pointer);n>0;n--) if (!isspace(*(pointer+n-1))) break
132 
133 #define SKIP_BLANKS \
134  for(;;pointer++) { \
135  c = (pointer > end) ? '\0' : *pointer; \
136  if (!isspace(c)) break; \
137  }
138 
139 #define EVAL_EXIT(STATUS,POSITION) endp = POSITION; return STATUS
140 #define MAX_N_PAR 5
141 
142 static constexpr char sss[MAX_N_PAR+2] = "012345";
143 
144 enum { ENDL, LBRA, OR, AND, EQ, NE, GE, GT, LE, LT,
146 
147 static int engine(char const*, char const*, double &, char const* &, const dic_type &);
148 
149 static int variable(const std::string & name, double & result,
150  const dic_type & dictionary)
151 /***********************************************************************
152  * *
153  * Name: variable Date: 03.10.00 *
154  * Author: Evgeni Chernyaev Revised: *
155  * *
156  * Function: Finds value of the variable. *
157  * This function is used by operand(). *
158  * *
159  * Parameters: *
160  * name - name of the variable. *
161  * result - value of the variable. *
162  * dictionary - dictionary of available variables and functions. *
163  * *
164  ***********************************************************************/
165 {
166  dic_type::const_iterator iter = dictionary.find(name);
167  if (iter == dictionary.end())
168  return EVAL::ERROR_UNKNOWN_VARIABLE;
169  //NOTE: copying ::string not thread safe so must use ref
170  Item const& item = iter->second;
171  switch (item.what) {
172  case Item::VARIABLE:
173  result = item.variable;
174  return EVAL::OK;
175  case Item::EXPRESSION: {
176  char const* exp_begin = (item.expression.c_str());
177  char const* exp_end = exp_begin + strlen(exp_begin) - 1;
178  if (engine(exp_begin, exp_end, result, exp_end, dictionary) == EVAL::OK)
179  return EVAL::OK;
180  return EVAL::ERROR_CALCULATION_ERROR;
181  }
182  default:
183  return EVAL::ERROR_CALCULATION_ERROR;
184  }
185 }
186 
187 static int execute_function(const std::string & name, std::stack<double> & par,
188  double & result, const dic_type & dictionary)
189 /***********************************************************************
190  * *
191  * Name: execute_function Date: 03.10.00 *
192  * Author: Evgeni Chernyaev Revised: *
193  * *
194  * Function: Finds value of the function. *
195  * This function is used by operand(). *
196  * *
197  * Parameters: *
198  * name - name of the function. *
199  * par - stack of parameters. *
200  * result - value of the function. *
201  * dictionary - dictionary of available variables and functions. *
202  * *
203  ***********************************************************************/
204 {
205  int npar = par.size();
206  if (npar > MAX_N_PAR) return EVAL::ERROR_UNKNOWN_FUNCTION;
207 
208  dic_type::const_iterator iter = dictionary.find(sss[npar]+name);
209  if (iter == dictionary.end()) return EVAL::ERROR_UNKNOWN_FUNCTION;
210  //NOTE: copying ::string not thread safe so must use ref
211  Item const& item = iter->second;
212 
213  double pp[MAX_N_PAR];
214  for(int i=0; i<npar; i++) { pp[i] = par.top(); par.pop(); }
215  errno = 0;
216  if (item.function == 0) return EVAL::ERROR_CALCULATION_ERROR;
217  FCN fcn(item.function);
218  switch (npar) {
219  case 0:
220  result = (*fcn.f0)();
221  break;
222  case 1:
223  result = (*fcn.f1)(pp[0]);
224  break;
225  case 2:
226  result = (*fcn.f2)(pp[1], pp[0]);
227  break;
228  case 3:
229  result = (*fcn.f3)(pp[2],pp[1],pp[0]);
230  break;
231  case 4:
232  result = (*fcn.f4)(pp[3],pp[2],pp[1],pp[0]);
233  break;
234  case 5:
235  result = (*fcn.f5)(pp[4],pp[3],pp[2],pp[1],pp[0]);
236  break;
237  }
238  return (errno == 0) ? EVAL::OK : EVAL::ERROR_CALCULATION_ERROR;
239 }
240 
241 static int operand(char const* begin, char const* end, double & result,
242  char const* & endp, const dic_type & dictionary)
243 /***********************************************************************
244  * *
245  * Name: operand Date: 03.10.00 *
246  * Author: Evgeni Chernyaev Revised: *
247  * *
248  * Function: Finds value of the operand. The operand can be either *
249  * a number or a variable or a function. *
250  * This function is used by engine(). *
251  * *
252  * Parameters: *
253  * begin - pointer to the first character of the operand. *
254  * end - pointer to the last character of the character string. *
255  * result - value of the operand. *
256  * endp - pointer to the character where the evaluation stoped. *
257  * dictionary - dictionary of available variables and functions. *
258  * *
259  ***********************************************************************/
260 {
261  char const* pointer = begin;
262  int EVAL_STATUS;
263  char c;
264 
265  // G E T N U M B E R
266 
267  if (!isalpha(*pointer)) {
268  errno = 0;
269 #ifdef _WIN32
270  if ( pointer[0] == '0' && pointer < end && (pointer[1] == 'x' || pointer[1] == 'X') )
271  result = strtol(pointer, (char **)(&pointer), 0);
272  else
273 #endif
274  result = strtod(pointer, (char **)(&pointer));
275  if (errno == 0) {
276  EVAL_EXIT( EVAL::OK, --pointer );
277  }else{
278  EVAL_EXIT( EVAL::ERROR_CALCULATION_ERROR, begin );
279  }
280  }
281 
282  // G E T N A M E
283 
284  while(pointer <= end) {
285  c = *pointer;
286  if ( !(c == '_' || c == ':') && !isalnum(c)) break;
287  pointer++;
288  }
289  std::string name(begin, pointer-begin);
290 
291  // G E T V A R I A B L E
292 
293  result = 0.0;
294  SKIP_BLANKS;
295  if (c != '(') {
296  EVAL_STATUS = variable(name, result, dictionary);
297  EVAL_EXIT( EVAL_STATUS, (EVAL_STATUS == EVAL::OK) ? --pointer : begin);
298  }
299 
300  // G E T F U N C T I O N
301 
302  std::stack<char const*> pos; // position stack
303  std::stack<double> par; // parameter stack
304  double value;
305  char const* par_begin = pointer+1;
306  char const* par_end;
307 
308  for(;;pointer++) {
309  c = (pointer > end) ? '\0' : *pointer;
310  switch (c) {
311  case '\0':
312  EVAL_EXIT( EVAL::ERROR_UNPAIRED_PARENTHESIS, pos.top() );
313  case '(':
314  pos.push(pointer); break;
315  case ',':
316  if (pos.size() == 1) {
317  par_end = pointer-1;
318  EVAL_STATUS = engine(par_begin, par_end, value, par_end, dictionary);
319  if (EVAL_STATUS == EVAL::WARNING_BLANK_STRING)
320  { EVAL_EXIT( EVAL::ERROR_EMPTY_PARAMETER, --par_end ); }
321  if (EVAL_STATUS != EVAL::OK)
322  { EVAL_EXIT( EVAL_STATUS, par_end ); }
323  par.push(value);
324  par_begin = pointer + 1;
325  }
326  break;
327  case ')':
328  if (pos.size() > 1) {
329  pos.pop();
330  break;
331  }else{
332  par_end = pointer-1;
333  EVAL_STATUS = engine(par_begin, par_end, value, par_end, dictionary);
334  switch (EVAL_STATUS) {
335  case EVAL::OK:
336  par.push(value);
337  break;
338  case EVAL::WARNING_BLANK_STRING:
339  if (par.size() != 0)
340  { EVAL_EXIT( EVAL::ERROR_EMPTY_PARAMETER, --par_end ); }
341  break;
342  default:
343  EVAL_EXIT( EVAL_STATUS, par_end );
344  }
345  EVAL_STATUS = execute_function(name, par, result, dictionary);
346  EVAL_EXIT( EVAL_STATUS, (EVAL_STATUS == EVAL::OK) ? pointer : begin);
347  }
348  }
349  }
350 }
351 
352 /***********************************************************************
353  * *
354  * Name: maker Date: 28.09.00 *
355  * Author: Evgeni Chernyaev Revised: *
356  * *
357  * Function: Executes basic arithmetic operations on values in the top *
358  * of the stack. Result is placed back into the stack. *
359  * This function is used by engine(). *
360  * *
361  * Parameters: *
362  * op - code of the operation. *
363  * val - stack of values. *
364  * *
365  ***********************************************************************/
366 static int maker(int op, std::stack<double> & val)
367 {
368  if (val.size() < 2) return EVAL::ERROR_SYNTAX_ERROR;
369  double val2 = val.top(); val.pop();
370  double val1 = val.top();
371  switch (op) {
372  case OR: // operator ||
373  val.top() = (val1 || val2) ? 1. : 0.;
374  return EVAL::OK;
375  case AND: // operator &&
376  val.top() = (val1 && val2) ? 1. : 0.;
377  return EVAL::OK;
378  case EQ: // operator ==
379  val.top() = (val1 == val2) ? 1. : 0.;
380  return EVAL::OK;
381  case NE: // operator !=
382  val.top() = (val1 != val2) ? 1. : 0.;
383  return EVAL::OK;
384  case GE: // operator >=
385  val.top() = (val1 >= val2) ? 1. : 0.;
386  return EVAL::OK;
387  case GT: // operator >
388  val.top() = (val1 > val2) ? 1. : 0.;
389  return EVAL::OK;
390  case LE: // operator <=
391  val.top() = (val1 <= val2) ? 1. : 0.;
392  return EVAL::OK;
393  case LT: // operator <
394  val.top() = (val1 < val2) ? 1. : 0.;
395  return EVAL::OK;
396  case PLUS: // operator '+'
397  val.top() = val1 + val2;
398  return EVAL::OK;
399  case MINUS: // operator '-'
400  val.top() = val1 - val2;
401  return EVAL::OK;
402  case MULT: // operator '*'
403  val.top() = val1 * val2;
404  return EVAL::OK;
405  case DIV: // operator '/'
406  if (val2 == 0.0) return EVAL::ERROR_CALCULATION_ERROR;
407  val.top() = val1 / val2;
408  return EVAL::OK;
409  case POW: // operator '^' (or '**')
410  errno = 0;
411  val.top() = pow(val1,val2);
412  if (errno == 0) return EVAL::OK;
414  default:
415  return EVAL::ERROR_CALCULATION_ERROR;
416  }
417 }
418 
419 /***********************************************************************
420  * *
421  * Name: engine Date: 28.09.00 *
422  * Author: Evgeni Chernyaev Revised: *
423  * *
424  * Function: Evaluates arithmetic expression. *
425  * *
426  * Parameters: *
427  * begin - pointer to the character string with expression. *
428  * end - pointer to the end of the character string (it is needed *
429  * for recursive call of engine(), when there is no '\0'). *
430  * result - result of the evaluation. *
431  * endp - pointer to the character where the evaluation stoped. *
432  * dictionary - dictionary of available variables and functions. *
433  * *
434  ***********************************************************************/
435 static int engine(char const* begin, char const* end, double & result,
436  char const*& endp, const dic_type & dictionary)
437 {
438  static constexpr int SyntaxTable[17][17] = {
439  //E ( || && == != >= > <= < + - * / ^ ) V - current token
440  { 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 0, 0, 0, 0, 1 }, // E - previous
441  { 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 0, 0, 0, 0, 1 }, // ( token
442  { 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 }, // ||
443  { 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 }, // &&
444  { 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 }, // ==
445  { 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 }, // !=
446  { 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 }, // >=
447  { 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 }, // >
448  { 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 }, // <=
449  { 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 }, // <
450  { 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 }, // +
451  { 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 }, // -
452  { 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 }, // *
453  { 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 }, // /
454  { 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 }, // ^
455  { 3, 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0 }, // )
456  { 3, 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0 } // V = {.,N,C}
457  };
458  static constexpr int ActionTable[15][16] = {
459  //E ( || && == != >= > <= < + - * / ^ ) - current operator
460  { 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,-1 }, // E - top operator
461  {-1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3 }, // ( in stack
462  { 4, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 4 }, // ||
463  { 4, 1, 4, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 4 }, // &&
464  { 4, 1, 4, 4, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 4 }, // ==
465  { 4, 1, 4, 4, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 4 }, // !=
466  { 4, 1, 4, 4, 4, 4, 2, 2, 2, 2, 1, 1, 1, 1, 1, 4 }, // >=
467  { 4, 1, 4, 4, 4, 4, 2, 2, 2, 2, 1, 1, 1, 1, 1, 4 }, // >
468  { 4, 1, 4, 4, 4, 4, 2, 2, 2, 2, 1, 1, 1, 1, 1, 4 }, // <=
469  { 4, 1, 4, 4, 4, 4, 2, 2, 2, 2, 1, 1, 1, 1, 1, 4 }, // <
470  { 4, 1, 4, 4, 4, 4, 4, 4, 4, 4, 2, 2, 1, 1, 1, 4 }, // +
471  { 4, 1, 4, 4, 4, 4, 4, 4, 4, 4, 2, 2, 1, 1, 1, 4 }, // -
472  { 4, 1, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 2, 2, 1, 4 }, // *
473  { 4, 1, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 2, 2, 1, 4 }, // /
474  { 4, 1, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4 } // ^
475  };
476 
477  std::stack<int> op; // operator stack
478  std::stack<char const*> pos; // position stack
479  std::stack<double> val; // value stack
480  double value;
481  char const* pointer = begin;
482  int iWhat, iCur, iPrev = 0, iTop, EVAL_STATUS;
483  char c;
484 
485  op.push(0); pos.push(pointer); // push EOL to the stack
486  SKIP_BLANKS;
487  if (c == '\0') { EVAL_EXIT( EVAL::WARNING_BLANK_STRING, begin ); }
488  for(;;pointer++) {
489 
490  // N E X T T O K E N
491 
492  c = (pointer > end) ? '\0' : *pointer;
493  if (isspace(c)) continue; // skip space, tab etc.
494  switch (c) {
495  case '\0': iCur = ENDL; break;
496  case '(': iCur = LBRA; break;
497  case '|':
498  if (*(pointer+1) == '|') {
499  pointer++; iCur = OR; break;
500  }else{
501  EVAL_EXIT( EVAL::ERROR_UNEXPECTED_SYMBOL, pointer );
502  }
503  case '&':
504  if (*(pointer+1) == '&') {
505  pointer++; iCur = AND; break;
506  }else{
507  EVAL_EXIT( EVAL::ERROR_UNEXPECTED_SYMBOL, pointer );
508  }
509  case '=':
510  if (*(pointer+1) == '=') {
511  pointer++; iCur = EQ; break;
512  }else{
513  EVAL_EXIT( EVAL::ERROR_UNEXPECTED_SYMBOL, pointer );
514  }
515  case '!':
516  if (*(pointer+1) == '=') {
517  pointer++; iCur = NE; break;
518  }else{
519  EVAL_EXIT( EVAL::ERROR_UNEXPECTED_SYMBOL, pointer );
520  }
521  case '>':
522  if (*(pointer+1) == '=') { pointer++; iCur = GE; } else { iCur = GT; }
523  break;
524  case '<':
525  if (*(pointer+1) == '=') { pointer++; iCur = LE; } else { iCur = LT; }
526  break;
527  case '+': iCur = PLUS; break;
528  case '-': iCur = MINUS; break;
529  case '*':
530  if (*(pointer+1) == '*') { pointer++; iCur = POW; }else{ iCur = MULT; }
531  break;
532  case '/': iCur = DIV; break;
533  case '^': iCur = POW; break;
534  case ')': iCur = RBRA; break;
535  default:
536  if (c == '.' || isalnum(c)) {
537  iCur = VALUE; break;
538  }else{
539  EVAL_EXIT( EVAL::ERROR_UNEXPECTED_SYMBOL, pointer );
540  }
541  }
542 
543  // S Y N T A X A N A L I S Y S
544 
545  iWhat = SyntaxTable[iPrev][iCur];
546  iPrev = iCur;
547  switch (iWhat) {
548  case 0: // syntax error
549  EVAL_EXIT( EVAL::ERROR_SYNTAX_ERROR, pointer );
550  case 1: // operand: number, variable, function
551  EVAL_STATUS = operand(pointer, end, value, pointer, dictionary);
552  if (EVAL_STATUS != EVAL::OK) { EVAL_EXIT( EVAL_STATUS, pointer ); }
553  val.push(value);
554  continue;
555  case 2: // unary + or unary -
556  val.push(0.0);
557  case 3: default: // next operator
558  break;
559  }
560 
561  // N E X T O P E R A T O R
562 
563  for(;;) {
564  if (op.size() == 0) { EVAL_EXIT( EVAL::ERROR_SYNTAX_ERROR, pointer ); }
565  iTop = op.top();
566  switch (ActionTable[iTop][iCur]) {
567  case -1: // syntax error
568  if (op.size() > 1) pointer = pos.top();
569  EVAL_EXIT( EVAL::ERROR_UNPAIRED_PARENTHESIS, pointer );
570  case 0: // last operation (assignment)
571  if (val.size() == 1) {
572  result = val.top();
573  EVAL_EXIT( EVAL::OK, pointer );
574  }else{
575  EVAL_EXIT( EVAL::ERROR_SYNTAX_ERROR, pointer );
576  }
577  case 1: // push current operator in stack
578  op.push(iCur); pos.push(pointer);
579  break;
580  case 2: // execute top operator
581  EVAL_STATUS = maker(iTop, val); // put current operator in stack
582  if (EVAL_STATUS != EVAL::OK) {
583  EVAL_EXIT( EVAL_STATUS, pos.top() );
584  }
585  op.top() = iCur; pos.top() = pointer;
586  break;
587  case 3: // delete '(' from stack
588  op.pop(); pos.pop();
589  break;
590  case 4: default: // execute top operator and
591  EVAL_STATUS = maker(iTop, val); // delete it from stack
592  if (EVAL_STATUS != EVAL::OK) { // repete with the same iCur
593  EVAL_EXIT( EVAL_STATUS, pos.top() );
594  }
595  op.pop(); pos.pop();
596  continue;
597  }
598  break;
599  }
600  }
601 }
602 
603 //---------------------------------------------------------------------------
604 static int setItem(const char * prefix, const char * name,
605  const Item & item, EVAL::Object::Struct* imp) {
606 
607  if (name == 0 || *name == '\0') {
608  return EVAL::ERROR_NOT_A_NAME;
609  }
610 
611  // R E M O V E L E A D I N G A N D T R A I L I N G S P A C E S
612 
613  const char * pointer; int n; REMOVE_BLANKS;
614 
615  // C H E C K N A M E
616 
617  if (n == 0) {
618  return EVAL::ERROR_NOT_A_NAME;
619  }
620  for(int i=0; i<n; i++) {
621  char c = *(pointer+i);
622  if ( !(c == '_' || c== ':') && !isalnum(c)) {
623  return EVAL::ERROR_NOT_A_NAME;
624  }
625  }
626 
627  // A D D I T E M T O T H E D I C T I O N A R Y
628 
629  std::string item_name = prefix + std::string(pointer,n);
631  dic_type::iterator iter = imp->theDictionary.find(item_name);
632  if (iter != imp->theDictionary.end()) {
633  iter->second = item;
634  if (item_name == name) {
635  return EVAL::WARNING_EXISTING_VARIABLE;
636  }else{
637  return EVAL::WARNING_EXISTING_FUNCTION;
638  }
639  }
640  imp->theDictionary[item_name] = item;
641  return EVAL::OK;
642 }
643 
644 //---------------------------------------------------------------------------
645  static void print_error_status(std::ostream& os, int status, char const* extra) {
646  static char prefix[] = "Evaluator::Object : ";
647  const char* opt = (extra ? extra : "");
648  switch (status) {
649  case EVAL::WARNING_EXISTING_VARIABLE:
650  os << prefix << "existing variable";
651  return;
652  case EVAL::WARNING_EXISTING_FUNCTION:
653  os << prefix << "existing function";
654  return;
655  case EVAL::WARNING_BLANK_STRING:
656  os << prefix << "blank string detected";
657  return;
658  case EVAL::ERROR_NOT_A_NAME:
659  os << prefix << "invalid name : " << opt;
660  return;
661  case EVAL::ERROR_SYNTAX_ERROR:
662  os << prefix << "systax error" ;
663  return;
664  case EVAL::ERROR_UNPAIRED_PARENTHESIS:
665  os << prefix << "unpaired parenthesis";
666  return;
667  case EVAL::ERROR_UNEXPECTED_SYMBOL:
668  os << prefix << "unexpected symbol : " << opt;
669  return;
670  case EVAL::ERROR_UNKNOWN_VARIABLE:
671  os << prefix << "unknown variable : " << opt;
672  return;
673  case EVAL::ERROR_UNKNOWN_FUNCTION:
674  os << prefix << "unknown function : " << opt;
675  return;
676  case EVAL::ERROR_EMPTY_PARAMETER:
677  os << prefix << "empty parameter in function call: " << opt;
678  return;
679  case EVAL::ERROR_CALCULATION_ERROR:
680  os << prefix << "calculation error";
681  return;
682  default:
683  return;
684  }
685 }
686 
687 //---------------------------------------------------------------------------
688 using namespace dd4hep::tools;
689 
690 //---------------------------------------------------------------------------
691 Evaluator::Object::Object(double meter, double kilogram, double second, double ampere, double kelvin
692  , double mole, double candela, double radians) : imp( new Struct()) {
693  setStdMath();
694  setSystemOfUnits(meter, kilogram, second, ampere, kelvin
695  , mole, candela, radians );
696 }
697 
698 //---------------------------------------------------------------------------
699 Evaluator::Object::~Object() {
700  delete imp;
701 }
702 
703 //---------------------------------------------------------------------------
704 Evaluator::Object::EvalStatus Evaluator::Object::evaluate(const char * expression) const {
705  EvalStatus s;
706  if (expression != 0) {
707  Struct::ReadLock guard(imp);
708  s.theStatus = engine(expression,
709  expression+strlen(expression)-1,
710  s.theResult,
711  s.thePosition,
712  imp->theDictionary);
713  }
714  return s;
715 }
716 
717 //---------------------------------------------------------------------------
718 int Evaluator::Object::EvalStatus::status() const {
719  return theStatus;
720 }
721 
722 //---------------------------------------------------------------------------
723 double Evaluator::Object::EvalStatus::result() const {
724  return theResult;
725 }
726 
727 //---------------------------------------------------------------------------
728 int Evaluator::Object::EvalStatus::error_position(const char* expression) const {
729  return thePosition - expression;
730 }
731 
732 //---------------------------------------------------------------------------
733 void Evaluator::Object::EvalStatus::print_error() const {
734  std::stringstream str;
735  print_error(str);
736  if ( str.str().empty() ) return;
737  std::cerr << str.str() << std::endl;
738 }
739 
740 //---------------------------------------------------------------------------
741 void Evaluator::Object::EvalStatus::print_error(std::ostream& os) const {
742  print_error_status(os, theStatus, thePosition);
743 }
744 
745 //---------------------------------------------------------------------------
746 int Evaluator::Object::setEnviron(const char* name, const char* value) {
747  std::string prefix = "${";
748  std::string item_name = prefix + std::string(name) + std::string("}");
749 
750  //Need to take lock before creating Item since since Item to be destroyed
751  // before the lock in order avoid ::string ref count thread problem
752  Struct::WriteLock guard(imp);
753  Item item;
754  item.what = Item::STRING;
755  item.expression = value;
756  item.function = 0;
757  item.variable = 0;
758  dic_type::iterator iter = imp->theDictionary.find(item_name);
759  if (iter != imp->theDictionary.end()) {
760  iter->second = std::move(item);
761  if (item_name == name) {
762  return EVAL::WARNING_EXISTING_VARIABLE;
763  }else{
764  return EVAL::WARNING_EXISTING_FUNCTION;
765  }
766  }else{
767  imp->theDictionary[item_name] = std::move(item);
768  return EVAL::OK;
769  }
770 }
771 
772 //---------------------------------------------------------------------------
773 std::pair<const char*,int> Evaluator::Object::getEnviron(const char* name) const {
774  Struct::ReadLock guard(imp);
775  Struct const* cImp = imp;
776  dic_type::const_iterator iter = cImp->theDictionary.find(name);
777  if (iter != cImp->theDictionary.end()) {
778  return std::make_pair(iter->second.expression.c_str(), EVAL::OK);
779  }
780  if ( ::strlen(name) > 3 ) {
781  // Need to remove braces from ${xxxx} for call to getenv()
782  std::string env_name(name+2,::strlen(name)-3);
783  const char* env_str = ::getenv(env_name.c_str());
784  if ( 0 != env_str ) {
785  return std::make_pair(env_str, EVAL::OK);
786  }
787  }
788  return std::make_pair(nullptr,EVAL::ERROR_UNKNOWN_VARIABLE);
789 }
790 
791 //---------------------------------------------------------------------------
792 int Evaluator::Object::setVariable(const char * name, double value) {
793  return setItem("", name, Item(value), imp);
794 }
795 
796 int Evaluator::Object::setVariable(const char * name, const char * expression) {
797  Item item(expression);
798  auto returnValue = setItem("", name, item, imp);
799  {
800  //We need to decrement the ref count on the item.expression while holding
801  // the lock since the ::string was copied into the dictionary
802  Struct::WriteLock guard(imp);
803  item.expression = "";
804  }
805  return returnValue;
806 }
807 
808 void Evaluator::Object::setVariableNoLock(const char * name, double value) {
809  std::string item_name = name;
810  imp->theDictionary[item_name] = Item(value);
811 }
812 
813 int Evaluator::Object::setFunction(const char * name,double (*fun)()) {
814  return setItem("0", name, Item(FCN(fun).ptr), imp);
815 }
816 
817 int Evaluator::Object::setFunction(const char * name,double (*fun)(double)) {
818  return setItem("1", name, Item(FCN(fun).ptr), imp);
819 }
820 
821 int Evaluator::Object::setFunction(const char * name, double (*fun)(double,double)) {
822  return setItem("2", name, Item(FCN(fun).ptr), imp);
823 }
824 
825 int Evaluator::Object::setFunction(const char * name, double (*fun)(double,double,double)) {
826  return setItem("3", name, Item(FCN(fun).ptr), imp);
827 }
828 
829 int Evaluator::Object::setFunction(const char * name, double (*fun)(double,double,double,double)) {
830  return setItem("4", name, Item(FCN(fun).ptr), imp);
831 }
832 
833 int Evaluator::Object::setFunction(const char * name, double (*fun)(double,double,double,double,double)) {
834  return setItem("5", name, Item(FCN(fun).ptr), imp);
835 }
836 
837 void Evaluator::Object::setFunctionNoLock(const char * name,double (*fun)(double)) {
838  std::string item_name = "1"+std::string(name);
839  imp->theDictionary[item_name] = Item(FCN(fun).ptr);
840 }
841 
842 void Evaluator::Object::setFunctionNoLock(const char * name, double (*fun)(double,double)) {
843  std::string item_name = "2"+std::string(name);
844  imp->theDictionary[item_name] = Item(FCN(fun).ptr);
845 }
846 
847 
848 //---------------------------------------------------------------------------
849 bool Evaluator::Object::findVariable(const char * name) const {
850  if (name == 0 || *name == '\0') return false;
851  const char * pointer; int n; REMOVE_BLANKS;
852  if (n == 0) return false;
853  Struct::ReadLock guard(imp);
854  return
855  (imp->theDictionary.find(std::string(pointer,n)) == imp->theDictionary.end()) ?
856  false : true;
857 }
858 
859 //---------------------------------------------------------------------------
860 bool Evaluator::Object::findFunction(const char * name, int npar) const {
861  if (name == 0 || *name == '\0') return false;
862  if (npar < 0 || npar > MAX_N_PAR) return false;
863  const char * pointer; int n; REMOVE_BLANKS;
864  if (n == 0) return false;
865  Struct::ReadLock guard(imp);
866  return (imp->theDictionary.find(sss[npar]+std::string(pointer,n)) ==
867  imp->theDictionary.end()) ? false : true;
868 }
869 
870 //---------------------------------------------------------------------------
871 void Evaluator::Object::removeVariable(const char * name) {
872  if (name == 0 || *name == '\0') return;
873  const char * pointer; int n; REMOVE_BLANKS;
874  if (n == 0) return;
875  Struct::WriteLock guard(imp);
876  imp->theDictionary.erase(std::string(pointer,n));
877 }
878 
879 //---------------------------------------------------------------------------
880 void Evaluator::Object::removeFunction(const char * name, int npar) {
881  if (name == 0 || *name == '\0') return;
882  if (npar < 0 || npar > MAX_N_PAR) return;
883  const char * pointer; int n; REMOVE_BLANKS;
884  if (n == 0) return;
885  Struct::WriteLock guard(imp);
886  imp->theDictionary.erase(sss[npar]+std::string(pointer,n));
887 }
888 
889 //---------------------------------------------------------------------------
890 Evaluator::Evaluator(double meter, double kilogram, double second, double ampere, double kelvin
891  , double mole, double candela, double radians) {
892  object = new Object(meter, kilogram, second, ampere, kelvin, mole, candela, radians);
893 }
894 
895 //---------------------------------------------------------------------------
896 Evaluator::Evaluator(Evaluator&& other):object(other.object) {
897  other.object=nullptr;
898 }
899 
900 //---------------------------------------------------------------------------
901 Evaluator::~Evaluator() {
902  delete object;
903 }
904 
905 //---------------------------------------------------------------------------
906 std::pair<int,double> Evaluator::evaluate(const std::string& expression) const {
907  auto result = object->evaluate(expression.c_str());
908  return std::make_pair(result.status(),result.result());
909 }
910 
911 //---------------------------------------------------------------------------
912 std::pair<int,double> Evaluator::evaluate(const std::string& expression, std::ostream& os) const {
913  auto result = object->evaluate(expression.c_str());
914  int status = result.status();
915  if ( status != OK ) {
916  result.print_error(os);
917  }
918  return std::make_pair(result.status(),result.result());
919 }
920 
921 //---------------------------------------------------------------------------
922 int Evaluator::setEnviron(const std::string& name, const std::string& value) const {
923  int result = object->setEnviron(name.c_str(), value.c_str());
924  return result;
925 }
926 
927 //---------------------------------------------------------------------------
928 std::pair<int,std::string> Evaluator::getEnviron(const std::string& name) const {
929  std::pair<int,std::string> result;
930  auto env_status = object->getEnviron(name.c_str());
931  result.first = env_status.second;
932  if( env_status.first ) result.second = env_status.first;
933  return result;
934 }
935 
936 //---------------------------------------------------------------------------
937 std::pair<int,std::string> Evaluator::getEnviron(const std::string& name, std::ostream& os) const {
938  std::pair<int,std::string> result;
939  auto env_status = object->getEnviron(name.c_str());
940  result.first = env_status.second;
941  if ( env_status.first ) {
942  result.second = env_status.first;
943  }
944  if ( result.first != OK ) {
945  print_error_status(os, result.first, name.c_str());
946  }
947  return result;
948 }
949 
950 //---------------------------------------------------------------------------
951 int Evaluator::setVariable(const std::string& name, double value) const {
952  int result = object->setVariable(name.c_str(), value);
953  return result;
954 }
955 
956 //---------------------------------------------------------------------------
957 int Evaluator::setVariable(const std::string& name, double value, std::ostream& os) const {
958  int result = object->setVariable(name.c_str(), value);
959  if ( result != OK ) {
960  print_error_status(os, result, name.c_str());
961  }
962  return result;
963 }
964 
965 //---------------------------------------------------------------------------
966 int Evaluator::setVariable(const std::string& name, const std::string& value) const {
967  int result = object->setVariable(name.c_str(), value.c_str());
968  return result;
969 }
970 
971 //---------------------------------------------------------------------------
972 int Evaluator::setVariable(const std::string& name, const std::string& value, std::ostream& os) const {
973  int result = object->setVariable(name.c_str(), value.c_str());
974  if ( result != OK ) {
975  print_error_status(os, result, name.c_str());
976  }
977  return result;
978 }
979 
980 //---------------------------------------------------------------------------
981 bool Evaluator::findVariable(const std::string& name) const {
982  bool ret;
983  ret = object->findVariable(name.c_str());
984  return ret;
985 }
986 
987 //---------------------------------------------------------------------------
988 int Evaluator::setFunction(const std::string& name, double (*fun)()) const {
989  int result = object->setFunction(name.c_str(), fun);
990  return result;
991 }
992 
993 //---------------------------------------------------------------------------
994 int Evaluator::setFunction(const std::string& name, double (*fun)(double)) const {
995  int result = object->setFunction(name.c_str(), fun);
996  return result;
997 }
998 
999 //---------------------------------------------------------------------------
1000 int Evaluator::setFunction(const std::string& name, double (*fun)(double, double)) const {
1001  int result = object->setFunction(name.c_str(), fun);
1002  return result;
1003 }
1004 
1005 //---------------------------------------------------------------------------
1006 int Evaluator::setFunction(const std::string& name, double (*fun)(double, double, double)) const {
1007  int result = object->setFunction(name.c_str(), fun);
1008  return result;
1009 }
1010 
1011 //---------------------------------------------------------------------------
1012 int Evaluator::setFunction(const std::string& name, double (*fun)(double, double, double, double)) const {
1013  int result = object->setFunction(name.c_str(), fun);
1014  return result;
1015 }
1016 
1017 //---------------------------------------------------------------------------
1018 int Evaluator::setFunction(const std::string& name, double (*fun)(double, double, double, double, double)) const {
1019  int result =object->setFunction(name.c_str(), fun);
1020  return result;
1021 }
1022 
1023 //---------------------------------------------------------------------------
1024 bool Evaluator::findFunction(const std::string& name, int npar) const {
1025  bool ret;
1026  ret = object->findFunction(name.c_str(), npar);
1027  return ret;
1028 }
MAX_N_PAR
#define MAX_N_PAR
Definition: Evaluator.cpp:140
EVAL::Object::Struct::theDictionary
dic_type theDictionary
Definition: Evaluator.cpp:121
REMOVE_BLANKS
#define REMOVE_BLANKS
Definition: Evaluator.cpp:129
LT
@ LT
Definition: Evaluator.cpp:144
MINUS
@ MINUS
Definition: Evaluator.cpp:145
EVAL::Object::Struct
Internal expression evaluator helper class.
Definition: Evaluator.cpp:84
GT
@ GT
Definition: Evaluator.cpp:144
ENDL
@ ENDL
Definition: Evaluator.cpp:144
GE
@ GE
Definition: Evaluator.cpp:144
EVAL::Object::Struct::ReadLock::ReadLock
ReadLock(Struct *s)
Definition: Evaluator.cpp:87
AND
@ AND
Definition: Evaluator.cpp:144
EVAL::Object::Struct::theReadersWaiting
int theReadersWaiting
Definition: Evaluator.cpp:122
POW
@ POW
Definition: Evaluator.cpp:145
LBRA
@ LBRA
Definition: Evaluator.cpp:144
EQ
@ EQ
Definition: Evaluator.cpp:144
EVAL::Object::Struct::WriteLock::theStruct
Struct * theStruct
Definition: Evaluator.cpp:117
EVAL::Object::Struct::theCond
std::condition_variable theCond
Definition: Evaluator.cpp:124
EVAL::Object::Struct::theWriterWaiting
bool theWriterWaiting
Definition: Evaluator.cpp:123
EVAL::Object::Struct::WriteLock::~WriteLock
~WriteLock()
Definition: Evaluator.cpp:110
EVAL::Object::Struct::ReadLock
Definition: Evaluator.cpp:86
EVAL::Object::Struct::ReadLock::ReadLock
ReadLock(const ReadLock &)=delete
EVAL::Object::Struct::theLock
std::mutex theLock
Definition: Evaluator.cpp:125
MULT
@ MULT
Definition: Evaluator.cpp:145
DIV
@ DIV
Definition: Evaluator.cpp:145
LE
@ LE
Definition: Evaluator.cpp:144
OR
@ OR
Definition: Evaluator.cpp:144
NE
@ NE
Definition: Evaluator.cpp:144
ATTR_FALLTHROUGH
#define ATTR_FALLTHROUGH
Definition: Evaluator.cpp:40
RBRA
@ RBRA
Definition: Evaluator.cpp:145
EVAL::Object::Struct::WriteLock::theLg
std::unique_lock< std::mutex > theLg
Definition: Evaluator.cpp:118
SKIP_BLANKS
#define SKIP_BLANKS
Definition: Evaluator.cpp:133
EVAL::Object::Struct::ReadLock::theLg
std::unique_lock< std::mutex > theLg
Definition: Evaluator.cpp:101
EVAL::Object::Struct::WriteLock::WriteLock
WriteLock(const WriteLock &)=delete
EVAL::Object::Struct::WriteLock::WriteLock
WriteLock(Struct *s)
Definition: Evaluator.cpp:105
EVAL::Object::Struct::WriteLock
Definition: Evaluator.cpp:104
PLUS
@ PLUS
Definition: Evaluator.cpp:145
EVAL::Object::Struct::ReadLock::~ReadLock
~ReadLock()
Definition: Evaluator.cpp:94
dd4hep::json::getEnviron
std::string getEnviron(const std::string &env)
Helper function to lookup environment from the expression evaluator.
Definition: Elements.cpp:184
dic_type
std::unordered_map< std::string, Item > dic_type
Definition: Evaluator.cpp:81
dd4hep::tools
Definition: setStdMath.cpp:70
EVAL_EXIT
#define EVAL_EXIT(STATUS, POSITION)
Definition: Evaluator.cpp:139
VALUE
@ VALUE
Definition: Evaluator.cpp:145
EVAL::Object::Struct::ReadLock::theStruct
Struct * theStruct
Definition: Evaluator.cpp:100