HPCToolkit
ExprEval.cpp
Go to the documentation of this file.
1 // (c) Peter Kankowski, 2007. http://smallcode.weblogs.us mailto:kankowski@narod.ru
2 // This file is a modified version from Expression Evaluator published at
3 // https://www.strchr.com/expression_evaluator
4 #include <assert.h>
5 #include <stdlib.h>
6 #include <stdio.h>
7 #include <string.h>
8 #include <iostream>
9 
10 #include "lib/support/ExprEval.hpp"
11 
12 // ================================
13 // Simple expression evaluator
14 // ================================
15 
16 // Parse a number or an expression in parenthesis
18 {
19  // Skip spaces
20  while(*expr == ' ')
21  expr++;
22 
23  // Handle the sign before parenthesis (or before number)
24  bool negative = false;
25  if(*expr == '-') {
26  negative = true;
27  expr++;
28  }
29  if(*expr == '+') {
30  expr++;
31  }
32 
33  // Check if there is parenthesis
34  if(*expr == '(') {
35  expr++;
36  _paren_count++;
37  double res = ParseSummands(expr);
38  if(*expr != ')') {
39  // Unmatched opening parenthesis
41  _err_pos = expr;
42  return 0;
43  }
44  expr++;
45  _paren_count--;
46  return negative ? -res : res;
47  }
48 
49  // check if this is a variable
50  bool variable = _var_map ? _var_map->isVariable(expr) : false;
51  if (variable) {
52  expr++;
53  }
54 
55  // It should be a number; convert it to double
56  char* end_ptr;
57  double res = strtod(expr, &end_ptr);
58  if(end_ptr == expr) {
59  // Report error
61  _err_pos = expr;
62  return 0;
63  }
64 
65  // if the atom is a variable, substitute it
66  if (variable) {
67  unsigned int index_metric = (unsigned int) res;
68  double val = _var_map->getValue(index_metric);
69  if (_var_map->getErrorCode() == 0) {
70  res = val;
71  } else {
73  return 0;
74  }
75  }
76 
77  // Advance the pointer and return the result
78  expr = end_ptr;
79  return negative ? -res : res;
80 }
81 
82 // Parse multiplication and division
84 {
85  double num1 = ParseAtom(expr);
86  for(;;) {
87  // Skip spaces
88  while(*expr == ' ')
89  expr++;
90  // Save the operation and position
91  EVAL_CHAR op = *expr;
92  EVAL_CHAR* pos = expr;
93  if(op != '/' && op != '*')
94  return num1;
95  expr++;
96  double num2 = ParseAtom(expr);
97  // Perform the saved operation
98  if(op == '/') {
99  // Handle division by zero
100  if(num2 == 0) {
102  _err_pos = pos;
103  return 0;
104  }
105  num1 /= num2;
106  }
107  else
108  num1 *= num2;
109  }
110  return num1;
111 }
112 
113 // Parse addition and subtraction
115 {
116  double num1 = ParseFactors(expr);
117  for(;;) {
118  // Skip spaces
119  while(*expr == ' ')
120  expr++;
121  EVAL_CHAR op = *expr;
122  if(op != '-' && op != '+')
123  return num1;
124  expr++;
125  double num2 = ParseFactors(expr);
126  if(op == '-')
127  num1 -= num2;
128  else
129  num1 += num2;
130  }
131  return num1;
132 }
133 
134 double ExprEval::Eval(EVAL_CHAR* expr, BaseVarMap *var_map)
135 {
136  _paren_count = 0;
137  _err = EEE_NO_ERROR;
138  _var_map = var_map;
139 
140  double res = ParseSummands(expr);
141 
142  // Now, expr should point to '\0', and _paren_count should be zero
143  if(_paren_count != 0 || *expr == ')') {
145  _err_pos = expr;
146  return 0;
147  }
148  if(*expr != '\0') {
150  _err_pos = expr;
151  return 0;
152  }
153  return res;
154 }
155 
157 {
158  return _err;
159 }
160 
162 {
163  return _err_pos;
164 }
165 
166 
167 // =======
168 // Tests
169 // =======
170 
171 #ifdef _DEBUG
172 void TestExprEval() {
173  ExprEval eval;
174  // Some simple expressions
175  assert(eval.Eval("1234") == 1234 && eval.GetErr() == EEE_NO_ERROR);
176  assert(eval.Eval("1+2*3") == 7 && eval.GetErr() == EEE_NO_ERROR);
177 
178  // Parenthesis
179  assert(eval.Eval("5*(4+4+1)") == 45 && eval.GetErr() == EEE_NO_ERROR);
180  assert(eval.Eval("5*(2*(1+3)+1)") == 45 && eval.GetErr() == EEE_NO_ERROR);
181  assert(eval.Eval("5*((1+3)*2+1)") == 45 && eval.GetErr() == EEE_NO_ERROR);
182 
183  // Spaces
184  assert(eval.Eval("5 * ((1 + 3) * 2 + 1)") == 45 && eval.GetErr() == EEE_NO_ERROR);
185  assert(eval.Eval("5 - 2 * ( 3 )") == -1 && eval.GetErr() == EEE_NO_ERROR);
186  assert(eval.Eval("5 - 2 * ( ( 4 ) - 1 )") == -1 && eval.GetErr() == EEE_NO_ERROR);
187 
188  // Sign before parenthesis
189  assert(eval.Eval("-(2+1)*4") == -12 && eval.GetErr() == EEE_NO_ERROR);
190  assert(eval.Eval("-4*(2+1)") == -12 && eval.GetErr() == EEE_NO_ERROR);
191 
192  // Fractional numbers
193  assert(eval.Eval("1.5/5") == 0.3 && eval.GetErr() == EEE_NO_ERROR);
194  assert(eval.Eval("1/5e10") == 2e-11 && eval.GetErr() == EEE_NO_ERROR);
195  assert(eval.Eval("(4-3)/(4*4)") == 0.0625 && eval.GetErr() == EEE_NO_ERROR);
196  assert(eval.Eval("1/2/2") == 0.25 && eval.GetErr() == EEE_NO_ERROR);
197  assert(eval.Eval("0.25 * .5 * 0.5") == 0.0625 && eval.GetErr() == EEE_NO_ERROR);
198  assert(eval.Eval(".25 / 2 * .5") == 0.0625 && eval.GetErr() == EEE_NO_ERROR);
199 
200  // Repeated operators
201  assert(eval.Eval("1+-2") == -1 && eval.GetErr() == EEE_NO_ERROR);
202  assert(eval.Eval("--2") == 2 && eval.GetErr() == EEE_NO_ERROR);
203  assert(eval.Eval("2---2") == 0 && eval.GetErr() == EEE_NO_ERROR);
204  assert(eval.Eval("2-+-2") == 4 && eval.GetErr() == EEE_NO_ERROR);
205 
206  // === Errors ===
207  // Parenthesis error
208  eval.Eval("5*((1+3)*2+1");
209  assert(eval.GetErr() == EEE_PARENTHESIS && strcmp(eval.GetErrPos(), "") == 0);
210  eval.Eval("5*((1+3)*2)+1)");
211  assert(eval.GetErr() == EEE_PARENTHESIS && strcmp(eval.GetErrPos(), ")") == 0);
212 
213  // Repeated operators (wrong)
214  eval.Eval("5*/2");
215  assert(eval.GetErr() == EEE_WRONG_CHAR && strcmp(eval.GetErrPos(), "/2") == 0);
216 
217  // Wrong position of an operator
218  eval.Eval("*2");
219  assert(eval.GetErr() == EEE_WRONG_CHAR && strcmp(eval.GetErrPos(), "*2") == 0);
220  eval.Eval("2+");
221  assert(eval.GetErr() == EEE_WRONG_CHAR && strcmp(eval.GetErrPos(), "") == 0);
222  eval.Eval("2*");
223  assert(eval.GetErr() == EEE_WRONG_CHAR && strcmp(eval.GetErrPos(), "") == 0);
224 
225  // Division by zero
226  eval.Eval("2/0");
227  assert(eval.GetErr() == EEE_DIVIDE_BY_ZERO && strcmp(eval.GetErrPos(), "/0") == 0);
228  eval.Eval("3+1/(5-5)+4");
229  assert(eval.GetErr() == EEE_DIVIDE_BY_ZERO && strcmp(eval.GetErrPos(), "/(5-5)+4") == 0);
230  eval.Eval("2/"); // Erroneously detected as division by zero, but that's ok for us
231  assert(eval.GetErr() == EEE_DIVIDE_BY_ZERO && strcmp(eval.GetErrPos(), "/") == 0);
232 
233  // Invalid characters
234  eval.Eval("~5");
235  assert(eval.GetErr() == EEE_WRONG_CHAR && strcmp(eval.GetErrPos(), "~5") == 0);
236  eval.Eval("5x");
237  assert(eval.GetErr() == EEE_WRONG_CHAR && strcmp(eval.GetErrPos(), "x") == 0);
238 
239  // Multiply errors
240  eval.Eval("3+1/0+4$"); // Only one error will be detected (in this case, the last one)
241  assert(eval.GetErr() == EEE_WRONG_CHAR && strcmp(eval.GetErrPos(), "$") == 0);
242  eval.Eval("3+1/0+4");
243  assert(eval.GetErr() == EEE_DIVIDE_BY_ZERO && strcmp(eval.GetErrPos(), "/0+4") == 0);
244  eval.Eval("q+1/0)"); // ...or the first one
245  assert(eval.GetErr() == EEE_WRONG_CHAR && strcmp(eval.GetErrPos(), "q+1/0)") == 0);
246  eval.Eval("+1/0)");
247  assert(eval.GetErr() == EEE_PARENTHESIS && strcmp(eval.GetErrPos(), ")") == 0);
248  eval.Eval("+1/0");
249  assert(eval.GetErr() == EEE_DIVIDE_BY_ZERO && strcmp(eval.GetErrPos(), "/0") == 0);
250 
251  // An emtpy string
252  eval.Eval("");
253  assert(eval.GetErr() == EEE_WRONG_CHAR && strcmp(eval.GetErrPos(), "") == 0);
254 }
255 #endif
256 
257 // ============
258 // Main program
259 // ============
260 #ifdef _DEBUG
261 
262 int main() {
263  TestExprEval();
264 }
265 #endif
virtual int getErrorCode()=0
BaseVarMap * _var_map
Definition: ExprEval.hpp:33
double ParseAtom(EVAL_CHAR *&expr)
Definition: ExprEval.cpp:17
#define EVAL_CHAR
Definition: ExprEval.hpp:19
double Eval(EVAL_CHAR *expr, BaseVarMap *var_map)
Definition: ExprEval.cpp:134
EXPR_EVAL_ERR _err
Definition: ExprEval.hpp:28
EVAL_CHAR * GetErrPos()
Definition: ExprEval.cpp:161
EVAL_CHAR * _err_pos
Definition: ExprEval.hpp:29
virtual bool isVariable(char *expr)=0
int main(int argc, char *argv[])
Definition: main.cpp:125
double ParseSummands(EVAL_CHAR *&expr)
Definition: ExprEval.cpp:114
virtual double getValue(unsigned int var)=0
double ParseFactors(EVAL_CHAR *&expr)
Definition: ExprEval.cpp:83
EXPR_EVAL_ERR GetErr()
Definition: ExprEval.cpp:156
EXPR_EVAL_ERR
Definition: ExprEval.hpp:10
int _paren_count
Definition: ExprEval.hpp:30