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
17
double
ExprEval::ParseAtom
(
EVAL_CHAR
*& expr)
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
40
_err
=
EEE_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
60
_err
=
EEE_WRONG_CHAR
;
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
{
72
_err
=
EEE_INCORRECT_VAR
;
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
83
double
ExprEval::ParseFactors
(
EVAL_CHAR
*& expr)
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) {
101
_err
=
EEE_DIVIDE_BY_ZERO
;
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
114
double
ExprEval::ParseSummands
(
EVAL_CHAR
*& expr)
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 ==
')'
) {
144
_err
=
EEE_PARENTHESIS
;
145
_err_pos
= expr;
146
return
0;
147
}
148
if
(*expr !=
'\0'
) {
149
_err
=
EEE_WRONG_CHAR
;
150
_err_pos
= expr;
151
return
0;
152
}
153
return
res;
154
}
155
156
EXPR_EVAL_ERR
ExprEval::GetErr
()
157
{
158
return
_err
;
159
}
160
161
EVAL_CHAR
*
ExprEval::GetErrPos
()
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
BaseVarMap::getErrorCode
virtual int getErrorCode()=0
ExprEval::_var_map
BaseVarMap * _var_map
Definition:
ExprEval.hpp:33
ExprEval::ParseAtom
double ParseAtom(EVAL_CHAR *&expr)
Definition:
ExprEval.cpp:17
EVAL_CHAR
#define EVAL_CHAR
Definition:
ExprEval.hpp:19
ExprEval::Eval
double Eval(EVAL_CHAR *expr, BaseVarMap *var_map)
Definition:
ExprEval.cpp:134
ExprEval::_err
EXPR_EVAL_ERR _err
Definition:
ExprEval.hpp:28
ExprEval
Definition:
ExprEval.hpp:26
EEE_WRONG_CHAR
Definition:
ExprEval.hpp:13
ExprEval::GetErrPos
EVAL_CHAR * GetErrPos()
Definition:
ExprEval.cpp:161
ExprEval::_err_pos
EVAL_CHAR * _err_pos
Definition:
ExprEval.hpp:29
BaseVarMap::isVariable
virtual bool isVariable(char *expr)=0
EEE_PARENTHESIS
Definition:
ExprEval.hpp:12
EEE_NO_ERROR
Definition:
ExprEval.hpp:11
main
int main(int argc, char *argv[])
Definition:
main.cpp:125
BaseVarMap
Definition:
BaseVarMap.hpp:50
ExprEval.hpp
ExprEval::ParseSummands
double ParseSummands(EVAL_CHAR *&expr)
Definition:
ExprEval.cpp:114
BaseVarMap::getValue
virtual double getValue(unsigned int var)=0
EEE_INCORRECT_VAR
Definition:
ExprEval.hpp:15
EEE_DIVIDE_BY_ZERO
Definition:
ExprEval.hpp:14
ExprEval::ParseFactors
double ParseFactors(EVAL_CHAR *&expr)
Definition:
ExprEval.cpp:83
ExprEval::GetErr
EXPR_EVAL_ERR GetErr()
Definition:
ExprEval.cpp:156
EXPR_EVAL_ERR
EXPR_EVAL_ERR
Definition:
ExprEval.hpp:10
ExprEval::_paren_count
int _paren_count
Definition:
ExprEval.hpp:30
src
lib
support
ExprEval.cpp
Generated by
1.8.13