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