CAEN Utility  2.0.2
Utilities for CAEN projects
CAENExpressionEvaluator.c
Go to the documentation of this file.
1 /******************************************************************************
2 *
3 * CAEN SpA - Software Division
4 * Via Vetraia, 11 - 55049 - Viareggio ITALY
5 * +39 0594 388 398 - www.caen.it
6 *
7 *******************************************************************************
8 *
9 * Copyright (C) 2019-2022 CAEN SpA
10 *
11 * This file is part of the CAEN Utility.
12 *
13 * The CAEN Utility is free software; you can redistribute it and/or
14 * modify it under the terms of the GNU Lesser General Public
15 * License as published by the Free Software Foundation; either
16 * version 3 of the License, or (at your option) any later version.
17 *
18 * The CAEN Utility is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
21 * Lesser General Public License for more details.
22 *
23 * You should have received a copy of the GNU Lesser General Public
24 * License along with the CAEN Utility; if not, see
25 * https://www.gnu.org/licenses/.
26 *
27 * SPDX-License-Identifier: LGPL-3.0-or-later
28 *
29 ***************************************************************************/
38 
39 #include <assert.h>
40 #include <string.h>
41 #include <stdlib.h>
42 #include <stdio.h>
43 #include <inttypes.h>
44 #include <stdbool.h>
45 
46 #include <CAENMultiplatform.h>
47 
48 int32_t _paren_count;
50 const char* _err_pos;
51 
52 int64_t ParseAtom(const char ** expr);
53 int64_t ParseFactors(const char ** expr);
54 int64_t ParseSummands(const char ** expr);
55 int64_t ParseShiftands(const char ** expr);
56 int64_t ParseOR_ands(const char ** expr);
57 int64_t ParseAND_ands(const char ** expr);
58 
59 // Parse a number or an expression in parenthesis
60 int64_t ParseAtom(const char * * expr) {
61  bool isNegative = FALSE;
62  int64_t res;
63  char* end_ptr;
64 
65  // Skip spaces
66  while (*(*expr) == ' ')
67  (*expr)++;
68 
69  // Handle the sign before parenthesis (or before number)
70  if (*(*expr) == '-') {
71  isNegative = TRUE;
72  (*expr)++;
73  }
74  if (*(*expr) == '+') {
75  (*expr)++;
76  }
77 
78  // Check if there is parenthesis
79  if (*(*expr) == '(') {
80  (*expr)++;
81  _paren_count++;
82  res = ParseAND_ands(expr);
83  if (*(*expr) != ')') {
84  // Unmatched opening parenthesis
86  _err_pos = *expr;
87  return 0;
88  }
89  (*expr)++;
90  _paren_count--;
91  return isNegative ? -res : res;
92  }
93 
94  // It should be a number; convert it to double
95  res = strtol(*expr, &end_ptr, 0);
96  if (end_ptr == *expr) {
97  // Report error
99  _err_pos = *expr;
100  return 0;
101  }
102  // Advance the pointer and return the result
103  *expr = end_ptr;
104  return isNegative ? -res : res;
105 }
106 
107 // Parse multiplication and division
108 int64_t ParseFactors(const char ** expr) {
109  int64_t num1 = ParseAtom(expr);
110  for(;;) {
111  int64_t num2;
112  char op;
113  const char* pos;
114 
115  // Skip spaces
116  while (*(*expr) == ' ')
117  (*expr)++;
118  // Save the operation and position
119  op = *(*expr);
120  pos = (*expr);
121  if (op != '/' && op != '*' && op != '%')
122  return num1;
123  (*expr)++;
124  num2 = ParseAtom(expr);
125  // Perform the saved operation
126  if (op == '/') { // division
127  // Handle division by zero
128  if (num2 == 0) {
130  _err_pos = pos;
131  return 0;
132  }
133  num1 = (int64_t)((double)num1 / (double)num2);
134  }
135  else if (op == '*') { // multiplication
136  num1 *= num2;
137  }
138  else { // module
139  num1 = num1 % num2;
140  }
141  }
142 }
143 
144 // Parse addition and subtraction
145 int64_t ParseSummands(const char ** expr) {
146  int64_t num1 = ParseFactors(expr);
147  for(;;) {
148  int64_t num2;
149  char op;
150 
151  // Skip spaces
152  while (*(*expr) == ' ')
153  (*expr)++;
154  op = *(*expr);
155  if (op != '-' && op != '+')
156  return num1;
157  (*expr)++;
158  num2 = ParseFactors(expr);
159  if (op == '-')
160  num1 -= num2;
161  else
162  num1 += num2;
163  }
164 }
165 
166 // Parse bit-shifting
167 int64_t ParseShiftands(const char ** expr) {
168  int64_t num1 = ParseSummands(expr);
169  for(;;) {
170  int64_t num2;
171  char op;
172 
173  // Skip spaces
174  while (*(*expr) == ' ')
175  (*expr)++;
176  op = *(*expr);
177  if (op != '<' && op != '>')
178  return num1;
179  (*expr)++;
180  if (*(*expr) != op) { // '<' or '>' instead of '<<' or '>>'
181  // Report error
183  _err_pos = *expr;
184  return 0;
185  }
186  (*expr)++;
187  num2 = ParseSummands(expr);
188  if (op == '<')
189  num1 <<= num2;
190  else
191  num1 >>= num2;
192  }
193 }
194 
195 // Parse bitwise OR
196 int64_t ParseOR_ands(const char ** expr) {
197  int64_t num1 = ParseShiftands(expr);
198  for(;;) {
199  int64_t num2;
200  char op;
201 
202  // Skip spaces
203  while (*(*expr) == ' ')
204  (*expr)++;
205  op = *(*expr);
206  if (op != '|')
207  return num1;
208  (*expr)++;
209  num2 = ParseShiftands(expr);
210  num1 |= num2;
211  }
212 }
213 
214 // Parse bitwise AND
215 int64_t ParseAND_ands(const char ** expr) {
216  int64_t num1 = ParseOR_ands(expr);
217  for(;;) {
218  int64_t num2;
219  char op;
220 
221  // Skip spaces
222  while (*(*expr) == ' ')
223  (*expr)++;
224  op = *(*expr);
225  if (op != '&')
226  return num1;
227  (*expr)++;
228  num2 = ParseOR_ands(expr);
229  num1 &= num2;
230  }
231 }
232 
233 int64_t c_ee_exprEval(const char * expr) {
234  int64_t res;
235  _paren_count = 0;
236  _err = EEE_NO_ERROR;
237  res = ParseAND_ands(&expr);
238  // Now, expr should point to '\0', and _paren_count should be zero
239  if (_paren_count != 0 || *expr == ')') {
241  _err_pos = expr;
242  return 0;
243  }
244  if (*expr != '\0') {
246  _err_pos = expr;
247  return 0;
248  }
249  return res;
250 }
251 
253  return _err;
254 }
255 
257  return _err_pos;
258 }
259 
260 #if 0
261 #define TEST_EXPRESSION(EXPR) assert(c_ee_exprEval(#EXPR) == (EXPR) && c_ee_getErr() == EEE_NO_ERROR)
262 void c_ee_testExprEval(void) {
263  // Some simple expressions
264  assert(c_ee_exprEval("1234") == 1234 && c_ee_getErr() == EEE_NO_ERROR);
265  assert(c_ee_exprEval("1+2*3") == 7 && c_ee_getErr() == EEE_NO_ERROR);
266 
267  // Parenthesis
268  assert(c_ee_exprEval("5*(4+4+1)") == 45 && c_ee_getErr() == EEE_NO_ERROR);
269  assert(c_ee_exprEval("5*(2*(1+3)+1)") == 45 && c_ee_getErr() == EEE_NO_ERROR);
270  assert(c_ee_exprEval("5*((1+3)*2+1)") == 45 && c_ee_getErr() == EEE_NO_ERROR);
271 
272  // Spaces
273  assert(c_ee_exprEval("5 * ((1 + 3) * 2 + 1)") == 45 && c_ee_getErr() == EEE_NO_ERROR);
274  assert(c_ee_exprEval("5 - 2 * ( 3 )") == -1 && c_ee_getErr() == EEE_NO_ERROR);
275  assert(c_ee_exprEval("5 - 2 * ( ( 4 ) - 1 )") == -1 && c_ee_getErr() == EEE_NO_ERROR);
276 
277  // Sign before parenthesis
278  assert(c_ee_exprEval("-(2+1)*4") == -12 && c_ee_getErr() == EEE_NO_ERROR);
279  assert(c_ee_exprEval("-4*(2+1)") == -12 && c_ee_getErr() == EEE_NO_ERROR);
280 
281  // Other
282  assert(c_ee_exprEval("0x1080 + 0x1 * 256") == (0x1080 + (0x1 << 8)) && c_ee_getErr() == EEE_NO_ERROR);
283  assert(c_ee_exprEval("(0x1080 + 0x2) << 8") == ((0x1080 + 0x2) << 8) && c_ee_getErr() == EEE_NO_ERROR);
284  assert(c_ee_exprEval("0x1080 | (0x3 << 8)") == (0x1080 | (0x3 << 8)) && c_ee_getErr() == EEE_NO_ERROR);
285  TEST_EXPRESSION(0x1080 | (0x3 << 8));
286  TEST_EXPRESSION(0x8180 + (4 * 4));
287  TEST_EXPRESSION(0xF * 3 % 2);
288  TEST_EXPRESSION((0xF * 3) % 2);
289  TEST_EXPRESSION(0xF * (3 % 2));
290  TEST_EXPRESSION(0xF % 5 * 2);
291  TEST_EXPRESSION((0xF % 5) * 2);
292  TEST_EXPRESSION(0xF % (5 * 2));
293 
294  // Fractional numbers
295  /*assert(c_ee_exprEval("1.5/5") == 0.3 && c_ee_getErr() == EEE_NO_ERROR);
296  assert(c_ee_exprEval("1/5e10") == 2e-11 && c_ee_getErr() == EEE_NO_ERROR);
297  assert(c_ee_exprEval("(4-3)/(4*4)") == 0.0625 && c_ee_getErr() == EEE_NO_ERROR);
298  assert(c_ee_exprEval("1/2/2") == 0.25 && c_ee_getErr() == EEE_NO_ERROR);
299  assert(c_ee_exprEval("0.25 * .5 * 0.5") == 0.0625 && c_ee_getErr() == EEE_NO_ERROR);
300  assert(c_ee_exprEval(".25 / 2 * .5") == 0.0625 && c_ee_getErr() == EEE_NO_ERROR);*/
301 
302  // Repeated operators
303  assert(c_ee_exprEval("1+-2") == -1 && c_ee_getErr() == EEE_NO_ERROR);
304  assert(c_ee_exprEval("--2") == 2 && c_ee_getErr() == EEE_NO_ERROR);
305  assert(c_ee_exprEval("2---2") == 0 && c_ee_getErr() == EEE_NO_ERROR);
306  assert(c_ee_exprEval("2-+-2") == 4 && c_ee_getErr() == EEE_NO_ERROR);
307 
308  // === Errors ===
309  // Parenthesis error
310  c_ee_exprEval("5*((1+3)*2+1");
311  assert(c_ee_getErr() == EEE_PARENTHESIS && strcmp(c_ee_getErrPos(), "") == 0);
312  c_ee_exprEval("5*((1+3)*2)+1)");
313  assert(c_ee_getErr() == EEE_PARENTHESIS && strcmp(c_ee_getErrPos(), ")") == 0);
314 
315  // Repeated operators (wrong)
316  c_ee_exprEval("5*/2");
317  assert(c_ee_getErr() == EEE_WRONG_CHAR && strcmp(c_ee_getErrPos(), "/2") == 0);
318 
319  // Wrong position of an operator
320  c_ee_exprEval("*2");
321  assert(c_ee_getErr() == EEE_WRONG_CHAR && strcmp(c_ee_getErrPos(), "*2") == 0);
322  c_ee_exprEval("2+");
323  assert(c_ee_getErr() == EEE_WRONG_CHAR && strcmp(c_ee_getErrPos(), "") == 0);
324  c_ee_exprEval("2*");
325  assert(c_ee_getErr() == EEE_WRONG_CHAR && strcmp(c_ee_getErrPos(), "") == 0);
326 
327  // Division by zero
328  c_ee_exprEval("2/0");
329  assert(c_ee_getErr() == EEE_DIVIDE_BY_ZERO && strcmp(c_ee_getErrPos(), "/0") == 0);
330  c_ee_exprEval("3+1/(5-5)+4");
331  assert(c_ee_getErr() == EEE_DIVIDE_BY_ZERO && strcmp(c_ee_getErrPos(), "/(5-5)+4") == 0);
332  c_ee_exprEval("2/"); // Erroneously detected as division by zero, but that's ok for us
333  assert(c_ee_getErr() == EEE_DIVIDE_BY_ZERO && strcmp(c_ee_getErrPos(), "/") == 0);
334 
335  // Invalid characters
336  c_ee_exprEval("~5");
337  assert(c_ee_getErr() == EEE_WRONG_CHAR && strcmp(c_ee_getErrPos(), "~5") == 0);
338  c_ee_exprEval("5x");
339  assert(c_ee_getErr() == EEE_WRONG_CHAR && strcmp(c_ee_getErrPos(), "x") == 0);
340 
341  // Multiply errors
342  c_ee_exprEval("3+1/0+4$"); // Only one error will be detected (in this case, the last one)
343  assert(c_ee_getErr() == EEE_WRONG_CHAR && strcmp(c_ee_getErrPos(), "$") == 0);
344  c_ee_exprEval("3+1/0+4");
345  assert(c_ee_getErr() == EEE_DIVIDE_BY_ZERO && strcmp(c_ee_getErrPos(), "/0+4") == 0);
346  c_ee_exprEval("q+1/0)"); // ...or the first one
347  assert(c_ee_getErr() == EEE_WRONG_CHAR && strcmp(c_ee_getErrPos(), "q+1/0)") == 0);
348  c_ee_exprEval("+1/0)");
349  assert(c_ee_getErr() == EEE_PARENTHESIS && strcmp(c_ee_getErrPos(), ")") == 0);
350  c_ee_exprEval("+1/0");
351  assert(c_ee_getErr() == EEE_DIVIDE_BY_ZERO && strcmp(c_ee_getErrPos(), "/0") == 0);
352 
353  // An emtpy string
354  c_ee_exprEval("");
355  assert(c_ee_getErr() == EEE_WRONG_CHAR && strcmp(c_ee_getErrPos(), "") == 0);
356 }
357 #endif
358 
359 void c_ee_substituteIdxExpression(const char *expression, int32_t value, char *dest, size_t maxsize) {
360  if (expression == NULL || dest == NULL)
361  return;
362 
363  char *expr_mod_tmp = c_malloc(maxsize);
364  if (expr_mod_tmp == NULL)
365  goto QuitFunction;
366 
367  char wildcard[] = "idx";
368  size_t wcnchar = sizeof(wildcard) - 1;
369  char *ptr;
370 
371  dest[0] = '\0';
372  strncat(dest, expression, maxsize - 1);
373 
374  while((ptr = strstr(dest, wildcard)) != NULL) {
375  //char end = *(ptr + wcnchar);
376  *ptr = '\0'; // temporary terminate string here
377  snprintf(expr_mod_tmp, maxsize, "%s%"PRIi32"%s", dest, value, (ptr + wcnchar));
378  dest[0] = '\0';
379  strncat(dest, expr_mod_tmp, maxsize - 1);
380  }
381 
382 QuitFunction:
383  c_free(expr_mod_tmp);
384 }
385 
386 int32_t c_ee_modifyAndEvaluateExpression(const char *expression, int32_t idx, int64_t *result) {
387  size_t originalsize = strlen(expression);
388  const size_t additionalsize = 20;
389  size_t totalsize = originalsize + additionalsize;
390  char *expr_mod = c_malloc(totalsize);
391  if (expr_mod == NULL)
392  return -1;
393 
394  // substitue all "idx" entries in 'expression' with the value of 'idx'.
395  c_ee_substituteIdxExpression(expression, idx, expr_mod, totalsize);
396 
397  int64_t res = c_ee_exprEval(expr_mod);
398  if ((res == 0) && (c_ee_getErr() != EEE_NO_ERROR)) {
399  c_free(expr_mod);
400  return -1;
401  }
402  *result = res;
403  c_free(expr_mod);
404  return 0;
405 }
c_ee_err_t c_ee_getErr(void)
int64_t ParseAtom(const char **expr)
const char * c_ee_getErrPos(void)
void c_free(void *ptr)
int64_t c_ee_exprEval(const char *expr)
c_ee_err_t _err
int64_t ParseShiftands(const char **expr)
const char * _err_pos
#define TRUE
#define FALSE
int64_t ParseAND_ands(const char **expr)
void * c_malloc(size_t size)
Generic wrappers to platform-dependent functions.
Tools to compute the numeric result of a string.
int64_t ParseFactors(const char **expr)
int32_t _paren_count
void c_ee_substituteIdxExpression(const char *expression, int32_t value, char *dest, size_t maxsize)
#define c_use_decl_annotations
Definition: CAENUtility.h:278
int64_t ParseOR_ands(const char **expr)
int32_t c_ee_modifyAndEvaluateExpression(const char *expression, int32_t idx, int64_t *result)
int64_t ParseSummands(const char **expr)