Expression evaluator in re2c/lemon (#20126)
Co-authored-by: vkalintiris <vasilis@netdata.cloud>
Costa Tsaousis committed
Apr 17, 2025 at 10:53 UTC
2e65811be48f37a6d4f4779981701e82e5f058fb
20 files changed
+8327
-1285
CMakeLists.txt
+8
-1
@@ -846,8 +846,15 @@ set(LIBNETDATA_FILES
846
src/libnetdata/dictionary/dictionary-debug.c
847
src/libnetdata/dictionary/dictionary-debug.h
848
src/libnetdata/dictionary/dictionary.h
849
- src/libnetdata/eval/eval.c
849
+ src/libnetdata/eval/eval-parser-legacy.c
850
+ src/libnetdata/eval/eval-evaluate.c
851
+ src/libnetdata/eval/eval-utils.c
852
src/libnetdata/eval/eval.h
853
+ src/libnetdata/eval/eval-internal.h
854
+ src/libnetdata/eval/eval-unittest.c
855
+ src/libnetdata/eval/re2c_lemon/lexer.c
856
+ src/libnetdata/eval/re2c_lemon/parser.c
857
+ src/libnetdata/eval/re2c_lemon/parser_wrapper.c
858
src/libnetdata/facets/facets.c
859
src/libnetdata/facets/facets.h
860
src/libnetdata/functions_evloop/functions_evloop.c
src/daemon/main.c
+6
@@ -216,6 +216,7 @@ int unittest_stream_compressions(void);
216
int uuid_unittest(void);
217
int progress_unittest(void);
218
int dyncfg_unittest(void);
219
+int eval_unittest(void);
220
bool netdata_random_session_id_generate(void);
221
222
#ifdef OS_WINDOWS
@@ -402,6 +403,7 @@ int netdata_main(int argc, char **argv) {
403
if (ctx_unittest()) return 1;
404
if (uuid_unittest()) return 1;
405
if (dyncfg_unittest()) return 1;
406
+ if (eval_unittest()) return 1;
407
if (unittest_waiting_queue()) return 1;
408
if (uuidmap_unittest()) return 1;
409
if (stacktrace_unittest()) return 1;
@@ -501,6 +503,10 @@ int netdata_main(int argc, char **argv) {
503
unittest_running = true;
504
return progress_unittest();
505
}
506
+ else if(strcmp(optarg, "evaltest") == 0) {
507
+ unittest_running = true;
508
+ return eval_unittest();
509
+ }
510
else if(strcmp(optarg, "dyncfgtest") == 0) {
511
unittest_running = true;
512
if(unittest_prepare_rrd(&user))
src/libnetdata/eval/README.md
+114
@@ -1 +1,115 @@
1
+# Netdata Expression Evaluator
2
3
+This directory contains Netdata's Expression Evaluator, a component for evaluating mathematical and logical expressions in Netdata's health monitoring, alerts, and data processing pipelines.
4
+
5
+## Overview
6
+
7
+The expression evaluator is a parser and interpreter for mathematical, logical, and comparison expressions. It supports:
8
+
9
+- Arithmetic operations (`+`, `-`, `*`, `/`, `%`)
10
+- Logical operations (`AND`/`&&`, `OR`/`||`, `NOT`/`!`)
11
+- Comparison operations (`==`, `!=`, `>`, `>=`, `<`, `<=`)
12
+- Ternary conditional operator (`? :`)
13
+- Function calls (e.g., `abs()`)
14
+- Variables (e.g., `$var1`)
15
+
16
+Expressions are used in Netdata's alert definitions, and other areas that require dynamic computation.
17
+
18
+## Implementation
19
+
20
+The expression evaluator has two parser implementations:
21
+
22
+1. **Original Recursive Descent Parser** - A handwritten parser in `eval-parser-legacy.c`
23
+2. **re2c/Lemon Parser** - A more efficient parser using re2c for lexical analysis and Lemon for grammar parsing in the `re2c_lemon/` subdirectory
24
+
25
+The implementation can be switched between these two parsers using the `USE_RE2C_LEMON_PARSER` define in `eval-internal.h`.
26
+
27
+## Key Components
28
+
29
+- **eval.h** - Public API for the expression evaluator
30
+- **eval-internal.h** - Internal structures and parser selection switch
31
+- **eval-parser.c** - Original recursive descent parser implementation
32
+- **eval-execute.c** - Expression evaluation engine
33
+- **eval-utils.c** - Helper functions for working with expression nodes
34
+- **eval-unittest.c** - Comprehensive test suite for the evaluator
35
+- **re2c_lemon/** - Subdirectory containing the re2c/Lemon-based parser implementation
36
+
37
+## Expression Syntax
38
+
39
+The evaluator supports a C-like syntax:
40
+
41
+```
42
+# Arithmetic
43
+42 + 24
44
+5 * (3 + 2)
45
+
46
+# Comparisons
47
+$temp > 80
48
+$load >= $threshold
49
+
50
+# Logical operations
51
+$cpu_util > 90 && $mem_usage > 80
52
+$disk_full || $inode_usage > 95
53
+
54
+# Ternary operator
55
+$status == $WARNING ? 90 : 75
56
+
57
+# Functions
58
+abs($value)
59
+```
60
+
61
+Variables are prefixed with `$` and can be either simple names (`$var`) or use braces for complex names (`${variable name with spaces}`).
62
+
63
+## Special Features
64
+
65
+- Case-insensitive handling of logical operators: `AND`/`and`/`&&` are equivalent
66
+- Support for special numeric literals: `nan` and `inf` (any capitalization)
67
+- Short-circuit evaluation of logical operators
68
+- NaN and Infinity handling in calculations
69
+
70
+## Usage
71
+
72
+To use the expression evaluator in Netdata code:
73
+
74
+```c
75
+#include "libnetdata/eval/eval.h"
76
+
77
+// Parse an expression
78
+const char *expr = "$value > 100 && $status != 0";
79
+const char *failed_at = NULL;
80
+int error = 0;
81
+EVAL_EXPRESSION *exp = expression_parse(expr, &failed_at, &error);
82
+
83
+if (!exp) {
84
+ // Handle parsing error
85
+ printf("Error parsing expression at: %s\n", failed_at);
86
+ printf("Error code: %d (%s)\n", error, expression_strerror(error));
87
+ return;
88
+}
89
+
90
+// Set up variable lookup callback
91
+expression_set_variable_lookup_callback(exp, my_variable_lookup_function, my_data);
92
+
93
+// Evaluate the expression
94
+if (expression_evaluate(exp)) {
95
+ // Get the result
96
+ NETDATA_DOUBLE result = expression_result(exp);
97
+ printf("Result: %f\n", result);
98
+} else {
99
+ // Handle evaluation error
100
+ printf("Evaluation error: %s\n", expression_error_msg(exp));
101
+}
102
+
103
+// Free the expression
104
+expression_free(exp);
105
+```
106
+
107
+## Testing
108
+
109
+The evaluator includes a comprehensive test suite in `eval-unittest.c`. Run it using:
110
+
111
+```
112
+netdata -W evaltest
113
+```
114
+
115
+All these tests run also at CI.
src/libnetdata/eval/eval-evaluate.c
new
+315
@@ -0,0 +1,315 @@
1
+// SPDX-License-Identifier: GPL-3.0-or-later
2
+
3
+#include "../libnetdata.h"
4
+#include "eval-internal.h"
5
+
6
+// ----------------------------------------------------------------------------
7
+// evaluation of expressions
8
+
9
+ALWAYS_INLINE
10
+static NETDATA_DOUBLE eval_variable(EVAL_EXPRESSION *exp, EVAL_VARIABLE *v, int *error) {
11
+ NETDATA_DOUBLE n;
12
+
13
+ // Check if variable is NULL to avoid crashes
14
+ if (!v || !v->name) {
15
+ *error = EVAL_ERROR_UNKNOWN_VARIABLE;
16
+ buffer_strcat(exp->error_msg, "[ undefined variable ] ");
17
+ return NAN;
18
+ }
19
+
20
+ if(exp->variable_lookup_cb && exp->variable_lookup_cb(v->name, exp->variable_lookup_cb_data, &n)) {
21
+ buffer_sprintf(exp->error_msg, "[ ${%s} = ", string2str(v->name));
22
+ print_parsed_as_constant(exp->error_msg, n);
23
+ buffer_strcat(exp->error_msg, " ] ");
24
+ return n;
25
+ }
26
+
27
+ *error = EVAL_ERROR_UNKNOWN_VARIABLE;
28
+ buffer_sprintf(exp->error_msg, "[ undefined variable '%s' ] ", string2str(v->name));
29
+ return NAN;
30
+}
31
+
32
+ALWAYS_INLINE
33
+static NETDATA_DOUBLE eval_value(EVAL_EXPRESSION *exp, EVAL_VALUE *v, int *error) {
34
+ NETDATA_DOUBLE n;
35
+
36
+ switch(v->type) {
37
+ case EVAL_VALUE_EXPRESSION:
38
+ n = eval_node(exp, v->expression, error);
39
+ break;
40
+
41
+ case EVAL_VALUE_NUMBER:
42
+ n = v->number;
43
+ break;
44
+
45
+ case EVAL_VALUE_VARIABLE:
46
+ n = eval_variable(exp, v->variable, error);
47
+ break;
48
+
49
+ default:
50
+ *error = EVAL_ERROR_INVALID_VALUE;
51
+ n = 0;
52
+ break;
53
+ }
54
+
55
+ return n;
56
+}
57
+
58
+ALWAYS_INLINE
59
+static int is_true(NETDATA_DOUBLE n) {
60
+ // Handle special cases safely
61
+ if(isnan(n)) return 0; // NaN is considered false
62
+ if(isinf(n)) {
63
+ // Infinity is considered true (positive or negative)
64
+ return 1;
65
+ }
66
+ if(n == 0) return 0; // Zero is considered false
67
+ return 1; // Any other value is true
68
+}
69
+
70
+ALWAYS_INLINE
71
+static NETDATA_DOUBLE eval_and(EVAL_EXPRESSION *exp, EVAL_NODE *op, int *error) {
72
+ return is_true(eval_value(exp, &op->ops[0], error)) && is_true(eval_value(exp, &op->ops[1], error));
73
+}
74
+
75
+ALWAYS_INLINE
76
+static NETDATA_DOUBLE eval_or(EVAL_EXPRESSION *exp, EVAL_NODE *op, int *error) {
77
+ return is_true(eval_value(exp, &op->ops[0], error)) || is_true(eval_value(exp, &op->ops[1], error));
78
+}
79
+
80
+ALWAYS_INLINE
81
+static NETDATA_DOUBLE eval_greater_than_or_equal(EVAL_EXPRESSION *exp, EVAL_NODE *op, int *error) {
82
+ NETDATA_DOUBLE n1 = eval_value(exp, &op->ops[0], error);
83
+ NETDATA_DOUBLE n2 = eval_value(exp, &op->ops[1], error);
84
+ return isgreaterequal(n1, n2);
85
+}
86
+
87
+ALWAYS_INLINE
88
+static NETDATA_DOUBLE eval_less_than_or_equal(EVAL_EXPRESSION *exp, EVAL_NODE *op, int *error) {
89
+ NETDATA_DOUBLE n1 = eval_value(exp, &op->ops[0], error);
90
+ NETDATA_DOUBLE n2 = eval_value(exp, &op->ops[1], error);
91
+ return islessequal(n1, n2);
92
+}
93
+
94
+ALWAYS_INLINE
95
+static NETDATA_DOUBLE eval_equal(EVAL_EXPRESSION *exp, EVAL_NODE *op, int *error) {
96
+ NETDATA_DOUBLE n1 = eval_value(exp, &op->ops[0], error);
97
+ NETDATA_DOUBLE n2 = eval_value(exp, &op->ops[1], error);
98
+ if(isnan(n1) && isnan(n2)) return 1;
99
+ if(isinf(n1) && isinf(n2)) return 1;
100
+ if(isnan(n1) || isnan(n2)) return 0;
101
+ if(isinf(n1) || isinf(n2)) return 0;
102
+ return considered_equal_ndd(n1, n2);
103
+}
104
+
105
+ALWAYS_INLINE
106
+static NETDATA_DOUBLE eval_not_equal(EVAL_EXPRESSION *exp, EVAL_NODE *op, int *error) {
107
+ return !eval_equal(exp, op, error);
108
+}
109
+
110
+ALWAYS_INLINE
111
+static NETDATA_DOUBLE eval_less(EVAL_EXPRESSION *exp, EVAL_NODE *op, int *error) {
112
+ NETDATA_DOUBLE n1 = eval_value(exp, &op->ops[0], error);
113
+ NETDATA_DOUBLE n2 = eval_value(exp, &op->ops[1], error);
114
+ return isless(n1, n2);
115
+}
116
+
117
+ALWAYS_INLINE
118
+static NETDATA_DOUBLE eval_greater(EVAL_EXPRESSION *exp, EVAL_NODE *op, int *error) {
119
+ NETDATA_DOUBLE n1 = eval_value(exp, &op->ops[0], error);
120
+ NETDATA_DOUBLE n2 = eval_value(exp, &op->ops[1], error);
121
+ return isgreater(n1, n2);
122
+}
123
+
124
+ALWAYS_INLINE
125
+static NETDATA_DOUBLE eval_plus(EVAL_EXPRESSION *exp, EVAL_NODE *op, int *error) {
126
+ NETDATA_DOUBLE n1 = eval_value(exp, &op->ops[0], error);
127
+ NETDATA_DOUBLE n2 = eval_value(exp, &op->ops[1], error);
128
+ if(isnan(n1) || isnan(n2)) return NAN;
129
+ if(isinf(n1) || isinf(n2)) return INFINITY;
130
+ return n1 + n2;
131
+}
132
+
133
+ALWAYS_INLINE
134
+static NETDATA_DOUBLE eval_minus(EVAL_EXPRESSION *exp, EVAL_NODE *op, int *error) {
135
+ NETDATA_DOUBLE n1 = eval_value(exp, &op->ops[0], error);
136
+ NETDATA_DOUBLE n2 = eval_value(exp, &op->ops[1], error);
137
+ if(isnan(n1) || isnan(n2)) return NAN;
138
+ if(isinf(n1) || isinf(n2)) return INFINITY;
139
+ return n1 - n2;
140
+}
141
+
142
+ALWAYS_INLINE
143
+static NETDATA_DOUBLE eval_multiply(EVAL_EXPRESSION *exp, EVAL_NODE *op, int *error) {
144
+ NETDATA_DOUBLE n1 = eval_value(exp, &op->ops[0], error);
145
+ NETDATA_DOUBLE n2 = eval_value(exp, &op->ops[1], error);
146
+ if(isnan(n1) || isnan(n2)) return NAN;
147
+ if(isinf(n1) || isinf(n2)) return INFINITY;
148
+ return n1 * n2;
149
+}
150
+
151
+ALWAYS_INLINE
152
+static NETDATA_DOUBLE eval_divide(EVAL_EXPRESSION *exp, EVAL_NODE *op, int *error) {
153
+ NETDATA_DOUBLE n1 = eval_value(exp, &op->ops[0], error);
154
+ if(*error != EVAL_ERROR_OK) return NAN; // Propagate previous errors
155
+
156
+ NETDATA_DOUBLE n2 = eval_value(exp, &op->ops[1], error);
157
+ if(*error != EVAL_ERROR_OK) return NAN; // Propagate previous errors
158
+
159
+ if(isnan(n1) || isnan(n2)) {
160
+ *error = EVAL_ERROR_VALUE_IS_NAN;
161
+ return NAN;
162
+ }
163
+
164
+ if(isinf(n1) || isinf(n2)) {
165
+ *error = EVAL_ERROR_VALUE_IS_INFINITE;
166
+ return INFINITY;
167
+ }
168
+
169
+ if(n2 == 0) {
170
+ // In Netdata, we treat all division by zero as INFINITE error
171
+ // This ensures compatibility with existing code
172
+ *error = EVAL_ERROR_VALUE_IS_INFINITE;
173
+ return n1 >= 0 ? INFINITY : -INFINITY;
174
+ }
175
+
176
+ return n1 / n2;
177
+}
178
+
179
+ALWAYS_INLINE
180
+static NETDATA_DOUBLE eval_nop(EVAL_EXPRESSION *exp, EVAL_NODE *op, int *error) {
181
+ return eval_value(exp, &op->ops[0], error);
182
+}
183
+
184
+ALWAYS_INLINE
185
+static NETDATA_DOUBLE eval_not(EVAL_EXPRESSION *exp, EVAL_NODE *op, int *error) {
186
+ return !is_true(eval_value(exp, &op->ops[0], error));
187
+}
188
+
189
+ALWAYS_INLINE
190
+static NETDATA_DOUBLE eval_sign_plus(EVAL_EXPRESSION *exp, EVAL_NODE *op, int *error) {
191
+ return eval_value(exp, &op->ops[0], error);
192
+}
193
+
194
+ALWAYS_INLINE
195
+static NETDATA_DOUBLE eval_sign_minus(EVAL_EXPRESSION *exp, EVAL_NODE *op, int *error) {
196
+ NETDATA_DOUBLE n1 = eval_value(exp, &op->ops[0], error);
197
+ if(isnan(n1)) return NAN;
198
+ if(isinf(n1)) return INFINITY;
199
+ return -n1;
200
+}
201
+
202
+ALWAYS_INLINE
203
+static NETDATA_DOUBLE eval_abs(EVAL_EXPRESSION *exp, EVAL_NODE *op, int *error) {
204
+ NETDATA_DOUBLE n1 = eval_value(exp, &op->ops[0], error);
205
+ if(isnan(n1)) return NAN;
206
+ if(isinf(n1)) return INFINITY;
207
+ return ABS(n1);
208
+}
209
+
210
+ALWAYS_INLINE
211
+static NETDATA_DOUBLE eval_if_then_else(EVAL_EXPRESSION *exp, EVAL_NODE *op, int *error) {
212
+ if(is_true(eval_value(exp, &op->ops[0], error)))
213
+ return eval_value(exp, &op->ops[1], error);
214
+ else
215
+ return eval_value(exp, &op->ops[2], error);
216
+}
217
+
218
+// Define operators table - use the struct definition from eval-internal.h
219
+struct operator operators[256] = {
220
+ // this is a random access array
221
+ // we always access it with a known EVAL_OPERATOR_X
222
+
223
+ [EVAL_OPERATOR_AND] = { "&&", 2, 2, 0, eval_and },
224
+ [EVAL_OPERATOR_OR] = { "||", 2, 2, 0, eval_or },
225
+ [EVAL_OPERATOR_GREATER_THAN_OR_EQUAL] = { ">=", 3, 2, 0, eval_greater_than_or_equal },
226
+ [EVAL_OPERATOR_LESS_THAN_OR_EQUAL] = { "<=", 3, 2, 0, eval_less_than_or_equal },
227
+ [EVAL_OPERATOR_NOT_EQUAL] = { "!=", 3, 2, 0, eval_not_equal },
228
+ [EVAL_OPERATOR_EQUAL] = { "==", 3, 2, 0, eval_equal },
229
+ [EVAL_OPERATOR_LESS] = { "<", 3, 2, 0, eval_less },
230
+ [EVAL_OPERATOR_GREATER] = { ">", 3, 2, 0, eval_greater },
231
+ [EVAL_OPERATOR_PLUS] = { "+", 4, 2, 0, eval_plus },
232
+ [EVAL_OPERATOR_MINUS] = { "-", 4, 2, 0, eval_minus },
233
+ [EVAL_OPERATOR_MULTIPLY] = { "*", 5, 2, 0, eval_multiply },
234
+ [EVAL_OPERATOR_DIVIDE] = { "/", 5, 2, 0, eval_divide },
235
+ [EVAL_OPERATOR_NOT] = { "!", 6, 1, 0, eval_not },
236
+ [EVAL_OPERATOR_SIGN_PLUS] = { "+", 6, 1, 0, eval_sign_plus },
237
+ [EVAL_OPERATOR_SIGN_MINUS] = { "-", 6, 1, 0, eval_sign_minus },
238
+ [EVAL_OPERATOR_ABS] = { "abs(",6,1, 1, eval_abs },
239
+ [EVAL_OPERATOR_IF_THEN_ELSE] = { "?", 7, 3, 0, eval_if_then_else },
240
+ [EVAL_OPERATOR_NOP] = { NULL, 8, 1, 0, eval_nop },
241
+ [EVAL_OPERATOR_EXPRESSION_OPEN] = { NULL, 8, 1, 0, eval_nop },
242
+
243
+ // this should exist in our evaluation list
244
+ [EVAL_OPERATOR_EXPRESSION_CLOSE] = { NULL, 99, 1, 0, eval_nop }
245
+};
246
+
247
+// Helper function to get precedence
248
+ALWAYS_INLINE
249
+int eval_precedence(unsigned char operator) {
250
+ return operators[(unsigned char)(operator)].precedence;
251
+}
252
+
253
+ALWAYS_INLINE
254
+NETDATA_DOUBLE eval_node(EVAL_EXPRESSION *exp, EVAL_NODE *op, int *error) {
255
+ if(unlikely(!op)) {
256
+ *error = EVAL_ERROR_MISSING_OPERAND;
257
+ return 0;
258
+ }
259
+
260
+ if(unlikely(op->count != operators[op->operator].parameters)) {
261
+ *error = EVAL_ERROR_INVALID_NUMBER_OF_OPERANDS;
262
+ return 0;
263
+ }
264
+
265
+ NETDATA_DOUBLE n = operators[op->operator].eval(exp, op, error);
266
+
267
+ return n;
268
+}
269
+
270
+// ----------------------------------------------------------------------------
271
+// public API for evaluation
272
+
273
+ALWAYS_INLINE
274
+int expression_evaluate(EVAL_EXPRESSION *expression) {
275
+ expression->error = EVAL_ERROR_OK;
276
+
277
+ buffer_reset(expression->error_msg);
278
+ expression->result = eval_node(expression, expression->nodes, &expression->error);
279
+
280
+ if(unlikely(isnan(expression->result))) {
281
+ if(expression->error == EVAL_ERROR_OK)
282
+ expression->error = EVAL_ERROR_VALUE_IS_NAN;
283
+ }
284
+ else if(unlikely(isinf(expression->result))) {
285
+ if(expression->error == EVAL_ERROR_OK)
286
+ expression->error = EVAL_ERROR_VALUE_IS_INFINITE;
287
+ }
288
+ else if(unlikely(expression->error == EVAL_ERROR_UNKNOWN_VARIABLE)) {
289
+ // although there is an unknown variable
290
+ // the expression was evaluated successfully
291
+ expression->error = EVAL_ERROR_OK;
292
+ }
293
+
294
+ if(expression->error != EVAL_ERROR_OK) {
295
+ expression->result = NAN;
296
+
297
+ if(buffer_strlen(expression->error_msg))
298
+ buffer_strcat(expression->error_msg, "; ");
299
+
300
+ buffer_sprintf(expression->error_msg, "failed to evaluate expression with error %d (%s)", expression->error, expression_strerror(expression->error));
301
+ return 0;
302
+ }
303
+
304
+ return 1;
305
+}
306
+
307
+void expression_free(EVAL_EXPRESSION *expression) {
308
+ if(!expression) return;
309
+
310
+ if(expression->nodes) eval_node_free(expression->nodes);
311
+ string_freez((void *)expression->source);
312
+ string_freez((void *)expression->parsed_as);
313
+ buffer_free(expression->error_msg);
314
+ freez(expression);
315
+}
src/libnetdata/eval/eval-internal.h
new
+127
@@ -0,0 +1,127 @@
1
+// SPDX-License-Identifier: GPL-3.0-or-later
2
+
3
+#ifndef NETDATA_EVAL_INTERNAL_H
4
+#define NETDATA_EVAL_INTERNAL_H
5
+
6
+#include "eval.h"
7
+
8
+typedef enum __attribute__((packed)) {
9
+ EVAL_VALUE_INVALID = 0,
10
+ EVAL_VALUE_NUMBER,
11
+ EVAL_VALUE_VARIABLE,
12
+ EVAL_VALUE_EXPRESSION
13
+} EVAL_VALUE_TYPE;
14
+
15
+// ----------------------------------------------------------------------------
16
+// data structures for storing the parsed expression in memory
17
+
18
+typedef struct eval_variable {
19
+ STRING *name;
20
+ struct eval_variable *next;
21
+} EVAL_VARIABLE;
22
+
23
+typedef struct eval_value {
24
+ EVAL_VALUE_TYPE type;
25
+
26
+ union {
27
+ NETDATA_DOUBLE number;
28
+ EVAL_VARIABLE *variable;
29
+ struct eval_node *expression;
30
+ };
31
+} EVAL_VALUE;
32
+
33
+typedef struct eval_node {
34
+ int id;
35
+ unsigned char operator;
36
+ int precedence;
37
+
38
+ int count;
39
+ EVAL_VALUE ops[];
40
+} EVAL_NODE;
41
+
42
+// Definition of operators structure
43
+struct operator {
44
+ const char *print_as;
45
+ char precedence;
46
+ char parameters;
47
+ char isfunction;
48
+ NETDATA_DOUBLE (*eval)(struct eval_expression *exp, EVAL_NODE *op, int *error);
49
+};
50
+
51
+// External declaration of operators array (defined in eval-execute.c)
52
+extern struct operator operators[256];
53
+
54
+struct eval_expression {
55
+ STRING *source;
56
+ STRING *parsed_as;
57
+
58
+ NETDATA_DOUBLE result;
59
+
60
+ int error;
61
+ BUFFER *error_msg;
62
+
63
+ EVAL_NODE *nodes;
64
+
65
+ void *variable_lookup_cb_data;
66
+ eval_expression_variable_lookup_t variable_lookup_cb;
67
+};
68
+
69
+// these are used for EVAL_NODE.operator
70
+// they are used as internal IDs to identify an operator
71
+// THEY ARE NOT USED FOR PARSING OPERATORS LIKE THAT
72
+#define EVAL_OPERATOR_NOP '\0'
73
+#define EVAL_OPERATOR_EXPRESSION_OPEN '('
74
+#define EVAL_OPERATOR_EXPRESSION_CLOSE ')'
75
+#define EVAL_OPERATOR_NOT '!'
76
+#define EVAL_OPERATOR_PLUS '+'
77
+#define EVAL_OPERATOR_MINUS '-'
78
+#define EVAL_OPERATOR_AND '&'
79
+#define EVAL_OPERATOR_OR '|'
80
+#define EVAL_OPERATOR_GREATER_THAN_OR_EQUAL 'G'
81
+#define EVAL_OPERATOR_LESS_THAN_OR_EQUAL 'L'
82
+#define EVAL_OPERATOR_NOT_EQUAL '~'
83
+#define EVAL_OPERATOR_EQUAL '='
84
+#define EVAL_OPERATOR_LESS '<'
85
+#define EVAL_OPERATOR_GREATER '>'
86
+#define EVAL_OPERATOR_MULTIPLY '*'
87
+#define EVAL_OPERATOR_DIVIDE '/'
88
+#define EVAL_OPERATOR_MODULO '%'
89
+#define EVAL_OPERATOR_SIGN_PLUS 'P'
90
+#define EVAL_OPERATOR_SIGN_MINUS 'M'
91
+#define EVAL_OPERATOR_ABS 'A'
92
+#define EVAL_OPERATOR_IF_THEN_ELSE '?'
93
+
94
+// Function identifiers for parsing
95
+typedef struct eval_function {
96
+ const char *name; // Function name (lower case)
97
+ unsigned char op; // Operator ID
98
+ int precedence; // Operator precedence
99
+} EVAL_FUNCTION;
100
+
101
+// Function declarations for shared functions
102
+
103
+// From eval-utils.c
104
+extern EVAL_NODE *eval_node_alloc(int count);
105
+extern void eval_node_set_value_to_node(EVAL_NODE *op, int pos, EVAL_NODE *value);
106
+extern void eval_node_set_value_to_constant(EVAL_NODE *op, int pos, NETDATA_DOUBLE value);
107
+extern void eval_node_set_value_to_variable(EVAL_NODE *op, int pos, const char *variable);
108
+extern void eval_variable_free(EVAL_VARIABLE *v);
109
+extern void eval_value_free(EVAL_VALUE *v);
110
+extern void eval_node_free(EVAL_NODE *op);
111
+extern void print_parsed_as_variable(BUFFER *out, EVAL_VARIABLE *v, int *error);
112
+extern void print_parsed_as_constant(BUFFER *out, NETDATA_DOUBLE n);
113
+extern void print_parsed_as_value(BUFFER *out, EVAL_VALUE *v, int *error);
114
+extern void print_parsed_as_node(BUFFER *out, EVAL_NODE *op, int *error);
115
+
116
+// From eval-execute.c
117
+extern NETDATA_DOUBLE eval_node(EVAL_EXPRESSION *exp, EVAL_NODE *op, int *error);
118
+extern int eval_precedence(unsigned char operator);
119
+
120
+// Functions for other parsers
121
+extern EVAL_NODE *parse_expression_with_bison(const char *string, const char **failed_at, int *error);
122
+extern EVAL_NODE *parse_expression_with_re2c_lemon(const char *string, const char **failed_at, int *error);
123
+
124
+// Parser selection - comment/uncomment to switch between implementations
125
+#define USE_RE2C_LEMON_PARSER
126
+
127
+#endif //NETDATA_EVAL_INTERNAL_H
\ No newline at end of file
src/libnetdata/eval/eval-parser-legacy.c
new
+886
@@ -0,0 +1,886 @@
1
+// SPDX-License-Identifier: GPL-3.0-or-later
2
+
3
+/*
4
+ * THIS CODE IS NOT USED ANY MORE
5
+ * IT IS KEPT HERE ONLY FOR REFERENCE (AND POTENTIALLY RUNNING UNIT TESTS)
6
+ */
7
+
8
+#include "../libnetdata.h"
9
+#include "eval-internal.h"
10
+#include <ctype.h> // For tolower
11
+
12
+// Character validation functions for parsing
13
+ALWAYS_INLINE
14
+static bool is_operator_first_symbol_or_space(const char s) {
15
+ return (
16
+ isspace((uint8_t)s) || !s ||
17
+ s == '&' || s == '|' || s == '!' || s == '>' || s == '<' ||
18
+ s == '=' || s == '+' || s == '-' || s == '*' || s == '/' || s == '?');
19
+}
20
+
21
+ALWAYS_INLINE
22
+static bool is_valid_after_operator_word(const char s) {
23
+ return isspace((uint8_t)s) || s == '(' || s == '$' || s == '!' ||
24
+ s == '-' || s == '+' || isdigit((uint8_t)s) || !s;
25
+}
26
+
27
+ALWAYS_INLINE
28
+static bool is_valid_after_operator_symbol(const char s) {
29
+ return is_valid_after_operator_word(s) || is_operator_first_symbol_or_space(s);
30
+}
31
+
32
+ALWAYS_INLINE
33
+static bool is_valid_variable_character(const char s) {
34
+ return !is_operator_first_symbol_or_space(s) && s != ')' && s != '}';
35
+}
36
+
37
+// Forward function declarations
38
+static inline EVAL_NODE *parse_full_expression(const char **string, int *error);
39
+static inline EVAL_NODE *parse_one_full_operand(const char **string, int *error);
40
+
41
+// ----------------------------------------------------------------------------
42
+// parsing expressions
43
+
44
+// skip spaces
45
+ALWAYS_INLINE
46
+static void skip_spaces(const char **string) {
47
+ const char *s = *string;
48
+ while(isspace((uint8_t)*s)) s++;
49
+ *string = s;
50
+}
51
+
52
+// ----------------------------------------------------------------------------
53
+// parse operators
54
+
55
+ALWAYS_INLINE
56
+static int parse_and(const char **string) {
57
+ const char *s = *string;
58
+
59
+ // AND
60
+ if((s[0] == 'A' || s[0] == 'a') && (s[1] == 'N' || s[1] == 'n') && (s[2] == 'D' || s[2] == 'd') &&
61
+ is_valid_after_operator_word(s[3])) {
62
+ *string = &s[4];
63
+ return 1;
64
+ }
65
+
66
+ // &&
67
+ if(s[0] == '&' && s[1] == '&' && is_valid_after_operator_symbol(s[2])) {
68
+ *string = &s[2];
69
+ return 1;
70
+ }
71
+
72
+ return 0;
73
+}
74
+
75
+ALWAYS_INLINE
76
+static int parse_or(const char **string) {
77
+ const char *s = *string;
78
+
79
+ // OR
80
+ if((s[0] == 'O' || s[0] == 'o') && (s[1] == 'R' || s[1] == 'r') && is_valid_after_operator_word(s[2])) {
81
+ *string = &s[3];
82
+ return 1;
83
+ }
84
+
85
+ // ||
86
+ if(s[0] == '|' && s[1] == '|' && is_valid_after_operator_symbol(s[2])) {
87
+ *string = &s[2];
88
+ return 1;
89
+ }
90
+
91
+ return 0;
92
+}
93
+
94
+ALWAYS_INLINE
95
+static int parse_greater_than_or_equal(const char **string) {
96
+ const char *s = *string;
97
+
98
+ // >=
99
+ if(s[0] == '>' && s[1] == '=' && is_valid_after_operator_symbol(s[2])) {
100
+ *string = &s[2];
101
+ return 1;
102
+ }
103
+
104
+ return 0;
105
+}
106
+
107
+ALWAYS_INLINE
108
+static int parse_less_than_or_equal(const char **string) {
109
+ const char *s = *string;
110
+
111
+ // <=
112
+ if (s[0] == '<' && s[1] == '=' && is_valid_after_operator_symbol(s[2])) {
113
+ *string = &s[2];
114
+ return 1;
115
+ }
116
+
117
+ return 0;
118
+}
119
+
120
+ALWAYS_INLINE
121
+static int parse_greater(const char **string) {
122
+ const char *s = *string;
123
+
124
+ // >
125
+ if(s[0] == '>' && is_valid_after_operator_symbol(s[1])) {
126
+ *string = &s[1];
127
+ return 1;
128
+ }
129
+
130
+ return 0;
131
+}
132
+
133
+ALWAYS_INLINE
134
+static int parse_less(const char **string) {
135
+ const char *s = *string;
136
+
137
+ // <
138
+ if(s[0] == '<' && is_valid_after_operator_symbol(s[1])) {
139
+ *string = &s[1];
140
+ return 1;
141
+ }
142
+
143
+ return 0;
144
+}
145
+
146
+ALWAYS_INLINE
147
+static int parse_equal(const char **string) {
148
+ const char *s = *string;
149
+
150
+ // ==
151
+ if(s[0] == '=' && s[1] == '=' && is_valid_after_operator_symbol(s[2])) {
152
+ *string = &s[2];
153
+ return 1;
154
+ }
155
+
156
+ // =
157
+ if(s[0] == '=' && is_valid_after_operator_symbol(s[1])) {
158
+ *string = &s[1];
159
+ return 1;
160
+ }
161
+
162
+ return 0;
163
+}
164
+
165
+ALWAYS_INLINE
166
+static int parse_not_equal(const char **string) {
167
+ const char *s = *string;
168
+
169
+ // !=
170
+ if(s[0] == '!' && s[1] == '=' && is_valid_after_operator_symbol(s[2])) {
171
+ *string = &s[2];
172
+ return 1;
173
+ }
174
+
175
+ // <>
176
+ if(s[0] == '<' && s[1] == '>' && is_valid_after_operator_symbol(s[2])) {
177
+ *string = &s[2];
178
+ }
179
+
180
+ return 0;
181
+}
182
+
183
+ALWAYS_INLINE
184
+static int parse_not(const char **string) {
185
+ const char *s = *string;
186
+
187
+ // NOT
188
+ if((s[0] == 'N' || s[0] == 'n') && (s[1] == 'O' || s[1] == 'o') && (s[2] == 'T' || s[2] == 't') &&
189
+ is_valid_after_operator_word(s[3])) {
190
+ *string = &s[3];
191
+ return 1;
192
+ }
193
+
194
+ if(s[0] == '!') {
195
+ *string = &s[1];
196
+ return 1;
197
+ }
198
+
199
+ return 0;
200
+}
201
+
202
+ALWAYS_INLINE
203
+static int parse_multiply(const char **string) {
204
+ const char *s = *string;
205
+
206
+ // *
207
+ if(s[0] == '*' && is_valid_after_operator_symbol(s[1])) {
208
+ *string = &s[1];
209
+ return 1;
210
+ }
211
+
212
+ return 0;
213
+}
214
+
215
+ALWAYS_INLINE
216
+static int parse_divide(const char **string) {
217
+ const char *s = *string;
218
+
219
+ // /
220
+ if(s[0] == '/' && is_valid_after_operator_symbol(s[1])) {
221
+ *string = &s[1];
222
+ return 1;
223
+ }
224
+
225
+ return 0;
226
+}
227
+
228
+ALWAYS_INLINE
229
+static int parse_minus(const char **string) {
230
+ const char *s = *string;
231
+
232
+ // -
233
+ if(s[0] == '-' && is_valid_after_operator_symbol(s[1])) {
234
+ *string = &s[1];
235
+ return 1;
236
+ }
237
+
238
+ return 0;
239
+}
240
+
241
+ALWAYS_INLINE
242
+static int parse_plus(const char **string) {
243
+ const char *s = *string;
244
+
245
+ // +
246
+ if(s[0] == '+' && is_valid_after_operator_symbol(s[1])) {
247
+ *string = &s[1];
248
+ return 1;
249
+ }
250
+
251
+ return 0;
252
+}
253
+
254
+ALWAYS_INLINE
255
+static int parse_open_subexpression(const char **string) {
256
+ const char *s = *string;
257
+
258
+ // (
259
+ if(s[0] == '(') {
260
+ *string = &s[1];
261
+ return 1;
262
+ }
263
+
264
+ return 0;
265
+}
266
+
267
+ALWAYS_INLINE
268
+static int parse_close_subexpression(const char **string) {
269
+ const char *s = *string;
270
+
271
+ // )
272
+ if(s[0] == ')') {
273
+ *string = &s[1];
274
+ return 1;
275
+ }
276
+
277
+ return 0;
278
+}
279
+
280
+ALWAYS_INLINE
281
+static int parse_variable(const char **string, char *buffer, size_t len) {
282
+ const char *s = *string;
283
+
284
+ // $
285
+ if(*s == '$') {
286
+ size_t i = 0;
287
+ s++;
288
+
289
+ if(*s == '{') {
290
+ // ${variable_name}
291
+
292
+ s++;
293
+ while (*s && *s != '}' && i < len)
294
+ buffer[i++] = *s++;
295
+
296
+ if(*s == '}')
297
+ s++;
298
+ }
299
+ else {
300
+ // $variable_name
301
+
302
+ while (*s && is_valid_variable_character(*s) && i < len)
303
+ buffer[i++] = *s++;
304
+ }
305
+
306
+ buffer[i] = '\0';
307
+
308
+ if (buffer[0]) {
309
+ *string = s;
310
+ return 1;
311
+ }
312
+ }
313
+
314
+ return 0;
315
+}
316
+
317
+ALWAYS_INLINE
318
+static int parse_constant(const char **string, NETDATA_DOUBLE *number) {
319
+ char *end = NULL;
320
+ NETDATA_DOUBLE n = str2ndd(*string, &end);
321
+ if(unlikely(!end || *string == end)) {
322
+ *number = 0;
323
+ return 0;
324
+ }
325
+ *number = n;
326
+ *string = end;
327
+ return 1;
328
+}
329
+
330
+// Define the functions we support
331
+static EVAL_FUNCTION eval_functions[] = {
332
+ {"abs", EVAL_OPERATOR_ABS, 6},
333
+ {NULL, 0, 0} // Terminator
334
+};
335
+
336
+// Parse function call
337
+ALWAYS_INLINE
338
+static int parse_function(const char **string, unsigned char *op, int *precedence) {
339
+ const char *s = *string;
340
+ skip_spaces(&s);
341
+
342
+ // Check for each function in our list
343
+ for (int i = 0; eval_functions[i].name != NULL; i++) {
344
+ const char *name = eval_functions[i].name;
345
+ int len = strlen(name);
346
+ int j;
347
+
348
+ // Case-insensitive comparison of function name
349
+ for (j = 0; j < len; j++) {
350
+ if (!s[j] || (tolower((unsigned char)s[j]) != name[j])) {
351
+ break;
352
+ }
353
+ }
354
+
355
+ // Check if we matched the entire function name and it's followed by '('
356
+ if (j == len && s[j] == '(') {
357
+ *string = &s[j+1]; // Move past "function_name("
358
+ *op = eval_functions[i].op;
359
+ if (precedence) *precedence = eval_functions[i].precedence;
360
+ return 1;
361
+ }
362
+ }
363
+
364
+ return 0;
365
+}
366
+
367
+ALWAYS_INLINE
368
+static int parse_if_then_else(const char **string) {
369
+ const char *s = *string;
370
+
371
+ // ?
372
+ if(s[0] == '?') {
373
+ *string = &s[1];
374
+ return 1;
375
+ }
376
+
377
+ return 0;
378
+}
379
+
380
+static struct operator_parser {
381
+ unsigned char id;
382
+ int (*parse)(const char **);
383
+} operator_parsers[] = {
384
+ // the order in this list is important!
385
+ // the first matching will be used
386
+ // so place the longer of overlapping ones
387
+ // at the top
388
+
389
+ { EVAL_OPERATOR_AND, parse_and },
390
+ { EVAL_OPERATOR_OR, parse_or },
391
+ { EVAL_OPERATOR_GREATER_THAN_OR_EQUAL, parse_greater_than_or_equal },
392
+ { EVAL_OPERATOR_LESS_THAN_OR_EQUAL, parse_less_than_or_equal },
393
+ { EVAL_OPERATOR_NOT_EQUAL, parse_not_equal },
394
+ { EVAL_OPERATOR_EQUAL, parse_equal },
395
+ { EVAL_OPERATOR_LESS, parse_less },
396
+ { EVAL_OPERATOR_GREATER, parse_greater },
397
+ { EVAL_OPERATOR_PLUS, parse_plus },
398
+ { EVAL_OPERATOR_MINUS, parse_minus },
399
+ { EVAL_OPERATOR_MULTIPLY, parse_multiply },
400
+ { EVAL_OPERATOR_DIVIDE, parse_divide },
401
+ { EVAL_OPERATOR_IF_THEN_ELSE, parse_if_then_else },
402
+
403
+ /* we should not put in this list the following:
404
+ *
405
+ * - NOT
406
+ * - (
407
+ * - )
408
+ *
409
+ * these are handled in code
410
+ */
411
+
412
+ // termination
413
+ { EVAL_OPERATOR_NOP, NULL }
414
+};
415
+
416
+ALWAYS_INLINE
417
+static unsigned char parse_operator(const char **string, int *precedence) {
418
+ skip_spaces(string);
419
+
420
+ int i;
421
+ for(i = 0 ; operator_parsers[i].parse != NULL ; i++)
422
+ if(operator_parsers[i].parse(string)) {
423
+ if(precedence) *precedence = eval_precedence(operator_parsers[i].id);
424
+ return operator_parsers[i].id;
425
+ }
426
+
427
+ return EVAL_OPERATOR_NOP;
428
+}
429
+
430
+// ----------------------------------------------------------------------------
431
+// the parsing logic
432
+
433
+// Forward declarations needed for recursive parsing
434
+static inline EVAL_NODE *parse_expression(const char **string, int *error, int allow_functions);
435
+static int starts_with_function(const char *s);
436
+
437
+// Helper function to parse a function call
438
+static EVAL_NODE *parse_function_call(const char **string, int *error) {
439
+ unsigned char op_type;
440
+ int precedence;
441
+
442
+ // Parse the function name and opening parenthesis
443
+ if (!parse_function(string, &op_type, &precedence)) {
444
+ *error = EVAL_ERROR_UNKNOWN_OPERAND;
445
+ return NULL;
446
+ }
447
+
448
+ // Special handling for nested expressions that may include unary operators
449
+ // followed by function calls
450
+ const char *arg_start = *string;
451
+ skip_spaces(&arg_start);
452
+
453
+ // Check if what follows inside the function's argument is a unary operator followed by another function
454
+ if (arg_start[0] == '-' || arg_start[0] == '+' || arg_start[0] == '!') {
455
+ // Move past this operator character
456
+ arg_start++;
457
+ skip_spaces(&arg_start);
458
+
459
+ // If what follows is a function call, we need special handling
460
+ if (starts_with_function(arg_start)) {
461
+ // Go back to normal parsing at the start of function arguments
462
+ // and use parse_expression which will handle unary operators correctly
463
+ EVAL_NODE *func_arg = parse_expression(string, error, 1);
464
+ if (!func_arg) {
465
+ *error = EVAL_ERROR_MISSING_OPERAND;
466
+ return NULL;
467
+ }
468
+
469
+ // Skip the closing parenthesis
470
+ if (!parse_close_subexpression(string)) {
471
+ *error = EVAL_ERROR_MISSING_CLOSE_SUBEXPRESSION;
472
+ eval_node_free(func_arg);
473
+ return NULL;
474
+ }
475
+
476
+ // Create the function node
477
+ EVAL_NODE *func_node = eval_node_alloc(1);
478
+ func_node->operator = op_type;
479
+ func_node->precedence = precedence;
480
+ eval_node_set_value_to_node(func_node, 0, func_arg);
481
+
482
+ return func_node;
483
+ }
484
+ }
485
+
486
+ // Regular parsing for function arguments
487
+ EVAL_NODE *func_arg = parse_full_expression(string, error);
488
+ if (!func_arg) {
489
+ *error = EVAL_ERROR_MISSING_OPERAND;
490
+ return NULL;
491
+ }
492
+
493
+ // Skip the closing parenthesis
494
+ if (!parse_close_subexpression(string)) {
495
+ *error = EVAL_ERROR_MISSING_CLOSE_SUBEXPRESSION;
496
+ eval_node_free(func_arg);
497
+ return NULL;
498
+ }
499
+
500
+ // Create the function node
501
+ EVAL_NODE *func_node = eval_node_alloc(1);
502
+ func_node->operator = op_type;
503
+ func_node->precedence = precedence;
504
+ eval_node_set_value_to_node(func_node, 0, func_arg);
505
+
506
+ return func_node;
507
+}
508
+
509
+// Helper function to check if a string starts with a function name
510
+static int starts_with_function(const char *s) {
511
+ for (int i = 0; eval_functions[i].name != NULL; i++) {
512
+ const char *name = eval_functions[i].name;
513
+ int len = strlen(name);
514
+ int j;
515
+
516
+ // Case-insensitive comparison of function name
517
+ for (j = 0; j < len; j++) {
518
+ if (!s[j] || (tolower((unsigned char)s[j]) != name[j])) {
519
+ break;
520
+ }
521
+ }
522
+
523
+ // Check if we matched the entire function name and it's followed by '('
524
+ if (j == len && s[j] == '(') {
525
+ return 1;
526
+ }
527
+ }
528
+
529
+ return 0;
530
+}
531
+
532
+// Helper function to avoid allocations all over the place
533
+static EVAL_NODE *parse_next_operand_given_its_operator(const char **string, unsigned char operator_type, int *error) {
534
+ // Save current position to check for function calls
535
+ const char *current_pos = *string;
536
+ skip_spaces(¤t_pos);
537
+
538
+ // Check if what follows is a function call
539
+ if (starts_with_function(current_pos)) {
540
+ // This is a function - we need special handling
541
+
542
+ // Parse the function call
543
+ EVAL_NODE *func_node = parse_function_call(¤t_pos, error);
544
+ if (!func_node) {
545
+ return NULL;
546
+ }
547
+
548
+ // Create the unary operator node
549
+ EVAL_NODE *op = eval_node_alloc(1);
550
+ op->operator = operator_type;
551
+ op->precedence = eval_precedence(operator_type);
552
+ eval_node_set_value_to_node(op, 0, func_node);
553
+
554
+ // Update the string position
555
+ *string = current_pos;
556
+
557
+ return op;
558
+ }
559
+
560
+ // Standard parsing for normal operands
561
+ EVAL_NODE *sub = parse_one_full_operand(string, error);
562
+ if(!sub) return NULL;
563
+
564
+ EVAL_NODE *op = eval_node_alloc(1);
565
+ op->operator = operator_type;
566
+ eval_node_set_value_to_node(op, 0, sub);
567
+ return op;
568
+}
569
+
570
+// parse a full operand, including its sign or other associative operator (e.g. NOT)
571
+static EVAL_NODE *parse_one_full_operand(const char **string, int *error) {
572
+ char variable_buffer[EVAL_MAX_VARIABLE_NAME_LENGTH + 1];
573
+ EVAL_NODE *op1 = NULL;
574
+ NETDATA_DOUBLE number;
575
+
576
+ *error = EVAL_ERROR_OK;
577
+
578
+ skip_spaces(string);
579
+ if(!(**string)) {
580
+ *error = EVAL_ERROR_MISSING_OPERAND;
581
+ return NULL;
582
+ }
583
+
584
+ if(parse_not(string)) {
585
+ // Check if what follows is a function
586
+ skip_spaces(string);
587
+ if (starts_with_function(*string)) {
588
+ // Special case: !function_call()
589
+ EVAL_NODE *func_node = parse_function_call(string, error);
590
+ if (!func_node) return NULL;
591
+
592
+ op1 = eval_node_alloc(1);
593
+ op1->operator = EVAL_OPERATOR_NOT;
594
+ op1->precedence = eval_precedence(EVAL_OPERATOR_NOT);
595
+ eval_node_set_value_to_node(op1, 0, func_node);
596
+ } else {
597
+ op1 = parse_next_operand_given_its_operator(string, EVAL_OPERATOR_NOT, error);
598
+ if (op1) op1->precedence = eval_precedence(EVAL_OPERATOR_NOT);
599
+ }
600
+ }
601
+ else if(parse_plus(string)) {
602
+ // Check if what follows is a function
603
+ skip_spaces(string);
604
+ if (starts_with_function(*string)) {
605
+ // Special case: +function_call()
606
+ EVAL_NODE *func_node = parse_function_call(string, error);
607
+ if (!func_node) return NULL;
608
+
609
+ op1 = eval_node_alloc(1);
610
+ op1->operator = EVAL_OPERATOR_SIGN_PLUS;
611
+ op1->precedence = eval_precedence(EVAL_OPERATOR_SIGN_PLUS);
612
+ eval_node_set_value_to_node(op1, 0, func_node);
613
+ } else {
614
+ op1 = parse_next_operand_given_its_operator(string, EVAL_OPERATOR_SIGN_PLUS, error);
615
+ if (op1) op1->precedence = eval_precedence(EVAL_OPERATOR_SIGN_PLUS);
616
+ }
617
+ }
618
+ else if(parse_minus(string)) {
619
+ // Check if what follows is a function
620
+ skip_spaces(string);
621
+ if (starts_with_function(*string)) {
622
+ // Special case: -function_call()
623
+ EVAL_NODE *func_node = parse_function_call(string, error);
624
+ if (!func_node) return NULL;
625
+
626
+ op1 = eval_node_alloc(1);
627
+ op1->operator = EVAL_OPERATOR_SIGN_MINUS;
628
+ op1->precedence = eval_precedence(EVAL_OPERATOR_SIGN_MINUS);
629
+ eval_node_set_value_to_node(op1, 0, func_node);
630
+ } else {
631
+ op1 = parse_next_operand_given_its_operator(string, EVAL_OPERATOR_SIGN_MINUS, error);
632
+ if (op1) op1->precedence = eval_precedence(EVAL_OPERATOR_SIGN_MINUS);
633
+ }
634
+ }
635
+ else if (starts_with_function(*string)) {
636
+ // Handle the function call
637
+ op1 = parse_function_call(string, error);
638
+ }
639
+ else if(parse_open_subexpression(string)) {
640
+ EVAL_NODE *sub = parse_full_expression(string, error);
641
+ if(sub) {
642
+ op1 = eval_node_alloc(1);
643
+ op1->operator = EVAL_OPERATOR_EXPRESSION_OPEN;
644
+ op1->precedence = eval_precedence(EVAL_OPERATOR_EXPRESSION_OPEN);
645
+ eval_node_set_value_to_node(op1, 0, sub);
646
+ if(!parse_close_subexpression(string)) {
647
+ *error = EVAL_ERROR_MISSING_CLOSE_SUBEXPRESSION;
648
+ eval_node_free(op1);
649
+ return NULL;
650
+ }
651
+ }
652
+ }
653
+ else if(parse_variable(string, variable_buffer, EVAL_MAX_VARIABLE_NAME_LENGTH)) {
654
+ op1 = eval_node_alloc(1);
655
+ op1->operator = EVAL_OPERATOR_NOP;
656
+ eval_node_set_value_to_variable(op1, 0, variable_buffer);
657
+ }
658
+ else if(parse_constant(string, &number)) {
659
+ op1 = eval_node_alloc(1);
660
+ op1->operator = EVAL_OPERATOR_NOP;
661
+ eval_node_set_value_to_constant(op1, 0, number);
662
+ }
663
+ else if(**string)
664
+ *error = EVAL_ERROR_UNKNOWN_OPERAND;
665
+ else
666
+ *error = EVAL_ERROR_MISSING_OPERAND;
667
+
668
+ return op1;
669
+}
670
+
671
+// parse an operator and the rest of the expression
672
+// precedence processing is handled here
673
+static EVAL_NODE *parse_rest_of_expression(const char **string, int *error, EVAL_NODE *op1) {
674
+ EVAL_NODE *op2 = NULL;
675
+ unsigned char operator;
676
+ int precedence;
677
+
678
+ operator = parse_operator(string, &precedence);
679
+ skip_spaces(string);
680
+
681
+ if(operator != EVAL_OPERATOR_NOP) {
682
+ op2 = parse_one_full_operand(string, error);
683
+ if(!op2) {
684
+ // error is already reported
685
+ eval_node_free(op1);
686
+ return NULL;
687
+ }
688
+
689
+ EVAL_NODE *op = eval_node_alloc(operators[operator].parameters);
690
+ op->operator = operator;
691
+ op->precedence = precedence;
692
+
693
+ if(operator == EVAL_OPERATOR_IF_THEN_ELSE && op->count == 3) {
694
+ skip_spaces(string);
695
+
696
+ if(**string != ':') {
697
+ eval_node_free(op);
698
+ eval_node_free(op1);
699
+ eval_node_free(op2);
700
+ *error = EVAL_ERROR_IF_THEN_ELSE_MISSING_ELSE;
701
+ return NULL;
702
+ }
703
+ (*string)++;
704
+
705
+ skip_spaces(string);
706
+
707
+ // For the else part, we need to handle nested ternary operators
708
+ // So we use parse_full_expression instead of parse_one_full_operand
709
+ // This ensures proper parsing of nested ternary operators
710
+ EVAL_NODE *op3 = parse_full_expression(string, error);
711
+ if(!op3) {
712
+ eval_node_free(op);
713
+ eval_node_free(op1);
714
+ eval_node_free(op2);
715
+ // error is already reported
716
+ return NULL;
717
+ }
718
+
719
+ eval_node_set_value_to_node(op, 2, op3);
720
+ }
721
+
722
+ eval_node_set_value_to_node(op, 1, op2);
723
+
724
+ // precedence processing
725
+ // if this operator has a higher precedence compared to its next
726
+ // put the next operator on top of us (top = evaluated later)
727
+ // function recursion does the rest...
728
+ if(op->precedence > op1->precedence && op1->count == 2 && op1->operator != '(' && op1->ops[1].type == EVAL_VALUE_EXPRESSION) {
729
+ eval_node_set_value_to_node(op, 0, op1->ops[1].expression);
730
+ op1->ops[1].expression = op;
731
+ op = op1;
732
+ }
733
+ else
734
+ eval_node_set_value_to_node(op, 0, op1);
735
+
736
+ return parse_rest_of_expression(string, error, op);
737
+ }
738
+ else if(**string == ')') {
739
+ ;
740
+ }
741
+ else if(**string) {
742
+ eval_node_free(op1);
743
+ op1 = NULL;
744
+ *error = EVAL_ERROR_MISSING_OPERATOR;
745
+ }
746
+
747
+ return op1;
748
+}
749
+
750
+// Parse an expression with optional function support
751
+static inline EVAL_NODE *parse_expression(const char **string, int *error, int allow_functions) {
752
+ // Special handling for functions as arguments
753
+ if (allow_functions) {
754
+ const char *s = *string;
755
+ skip_spaces(&s);
756
+
757
+ // Check for unary operators
758
+ if (s[0] == '-' || s[0] == '+' || s[0] == '!') {
759
+ unsigned char op_type;
760
+ if (s[0] == '-') op_type = EVAL_OPERATOR_SIGN_MINUS;
761
+ else if (s[0] == '+') op_type = EVAL_OPERATOR_SIGN_PLUS;
762
+ else op_type = EVAL_OPERATOR_NOT;
763
+
764
+ // Move past the unary operator
765
+ s++;
766
+ skip_spaces(&s);
767
+
768
+ // Check if followed by a function
769
+ if (starts_with_function(s)) {
770
+ // Update the string position to include the consumed unary operator
771
+ *string = s;
772
+
773
+ // Parse the function
774
+ EVAL_NODE *func_node = parse_function_call(string, error);
775
+ if (!func_node) return NULL;
776
+
777
+ // Create the unary operator node
778
+ EVAL_NODE *op = eval_node_alloc(1);
779
+ op->operator = op_type;
780
+ op->precedence = eval_precedence(op_type);
781
+ eval_node_set_value_to_node(op, 0, func_node);
782
+
783
+ return op;
784
+ }
785
+ }
786
+ // Check for a direct function call
787
+ else if (starts_with_function(s)) {
788
+ *string = s;
789
+ return parse_function_call(string, error);
790
+ }
791
+ }
792
+
793
+ // If no special handling needed, fall back to normal parsing
794
+ return parse_full_expression(string, error);
795
+}
796
+
797
+// high level function to parse an expression or a sub-expression
798
+static inline EVAL_NODE *parse_full_expression(const char **string, int *error) {
799
+ EVAL_NODE *op1 = parse_one_full_operand(string, error);
800
+ if(!op1) {
801
+ *error = EVAL_ERROR_MISSING_OPERAND;
802
+ return NULL;
803
+ }
804
+
805
+ return parse_rest_of_expression(string, error, op1);
806
+}
807
+
808
+// ----------------------------------------------------------------------------
809
+// public API for parsing
810
+
811
+EVAL_EXPRESSION *expression_parse(const char *string, const char **failed_at, int *error) {
812
+ if(!string || !*string)
813
+ return NULL;
814
+
815
+ const char *s = string;
816
+ int err = EVAL_ERROR_OK;
817
+ EVAL_NODE *op = NULL;
818
+
819
+#ifdef USE_RE2C_LEMON_PARSER
820
+ // Use the re2c/lemon parser
821
+ op = parse_expression_with_re2c_lemon(string, &s, &err);
822
+#else
823
+ // Use the original recursive descent parser
824
+
825
+ // First, let's check if the expression starts with a function
826
+ skip_spaces(&s);
827
+
828
+ // Check if the expression starts with a unary op followed by function
829
+ // Removed condition that was too restrictive - we want to handle any unary operator
830
+ // followed by a function, not just when there's whitespace after the operator
831
+ if (s[0] == '-' || s[0] == '+' || s[0] == '!') {
832
+
833
+ const char *after_op = s + 1;
834
+ skip_spaces(&after_op);
835
+
836
+ if (starts_with_function(after_op)) {
837
+ // This is a special case like "-abs(...)" - use our special parser
838
+ s = string; // Reset to beginning
839
+ op = parse_expression(&s, &err, 1);
840
+ }
841
+ }
842
+
843
+ // If we haven't parsed it with the special function, use regular parsing
844
+ if (!op) {
845
+ s = string; // Reset to beginning
846
+ op = parse_full_expression(&s, &err);
847
+ }
848
+#endif
849
+
850
+ if(s && *s) {
851
+ if(op) {
852
+ eval_node_free(op);
853
+ op = NULL;
854
+ }
855
+ err = EVAL_ERROR_REMAINING_GARBAGE;
856
+ }
857
+
858
+ if (failed_at) *failed_at = s;
859
+ if (error) *error = err;
860
+
861
+ if(!op) {
862
+ unsigned long pos = s - string + 1;
863
+ netdata_log_error("failed to parse expression '%s': %s at character %lu (i.e.: '%s').", string, expression_strerror(err), pos, s);
864
+ return NULL;
865
+ }
866
+
867
+ BUFFER *out = buffer_create(1024, NULL);
868
+ print_parsed_as_node(out, op, &err);
869
+ if(err != EVAL_ERROR_OK) {
870
+ netdata_log_error("failed to re-generate expression '%s' with reason: %s", string, expression_strerror(err));
871
+ eval_node_free(op);
872
+ buffer_free(out);
873
+ return NULL;
874
+ }
875
+
876
+ EVAL_EXPRESSION *exp = callocz(1, sizeof(EVAL_EXPRESSION));
877
+
878
+ exp->source = string_strdupz(string);
879
+ exp->parsed_as = string_strdupz(buffer_tostring(out));
880
+ buffer_free(out);
881
+
882
+ exp->error_msg = buffer_create(100, NULL);
883
+ exp->nodes = op;
884
+
885
+ return exp;
886
+}
src/libnetdata/eval/eval-unittest.c
new
+1208
@@ -0,0 +1,1208 @@
1
+// SPDX-License-Identifier: GPL-3.0-or-later
2
+
3
+#include "libnetdata/libnetdata.h"
4
+#include "eval-internal.h"
5
+
6
+// Mock variable lookup function for testing
7
+static bool test_variable_lookup(STRING *variable, void *data __maybe_unused, NETDATA_DOUBLE *result) {
8
+ const char *var_name = string2str(variable);
9
+
10
+ // Basic variables
11
+ if (strcmp(var_name, "var1") == 0) {
12
+ *result = 42.0;
13
+ return true;
14
+ }
15
+ else if (strcmp(var_name, "var2") == 0) {
16
+ *result = 24.0;
17
+ return true;
18
+ }
19
+ else if (strcmp(var_name, "zero") == 0) {
20
+ *result = 0.0;
21
+ return true;
22
+ }
23
+ else if (strcmp(var_name, "negative") == 0) {
24
+ *result = -10.0;
25
+ return true;
26
+ }
27
+
28
+ // Special values
29
+ else if (strcmp(var_name, "nan_var") == 0) {
30
+ *result = NAN;
31
+ return true;
32
+ }
33
+ else if (strcmp(var_name, "inf_var") == 0) {
34
+ *result = INFINITY;
35
+ return true;
36
+ }
37
+
38
+ // Variables with spaces (for braced variables)
39
+ else if (strcmp(var_name, "this variable") == 0) {
40
+ *result = 100.0;
41
+ return true;
42
+ }
43
+ else if (strcmp(var_name, "this") == 0) {
44
+ *result = 50.0;
45
+ return true;
46
+ }
47
+
48
+ // Variables that start with numbers
49
+ else if (strcmp(var_name, "1var") == 0) {
50
+ *result = 42.0; // Using the same value as var1 for consistency
51
+ return true;
52
+ }
53
+ else if (strcmp(var_name, "_var") == 0) {
54
+ *result = 76.0;
55
+ return true;
56
+ }
57
+ else if (strcmp(var_name, "1.var") == 0) {
58
+ *result = 77.0;
59
+ return true;
60
+ }
61
+
62
+ // Variables with dots
63
+ else if (strcmp(var_name, "var.1") == 0) {
64
+ *result = 78.0;
65
+ return true;
66
+ }
67
+
68
+ // Variables with hyphens
69
+ else if (strcmp(var_name, "var-1") == 0) {
70
+ *result = 79.0;
71
+ return true;
72
+ }
73
+ else if (strcmp(var_name, "var-with-hyphens") == 0) {
74
+ *result = 100.0;
75
+ return true;
76
+ }
77
+
78
+ // Indexed variables with spaces (Core X)
79
+ else if (strcmp(var_name, "Core 0") == 0) {
80
+ *result = 25.0;
81
+ return true;
82
+ }
83
+ else if (strcmp(var_name, "Core 1") == 0) {
84
+ *result = 35.0;
85
+ return true;
86
+ }
87
+ else if (strcmp(var_name, "Core 2") == 0) {
88
+ *result = 15.0;
89
+ return true;
90
+ }
91
+ else if (strcmp(var_name, "Core 3") == 0) {
92
+ *result = 40.0;
93
+ return true;
94
+ }
95
+ else if (strcmp(var_name, "Core 02") == 0) {
96
+ *result = 15.0; // Same as Core 2 for testing
97
+ return true;
98
+ }
99
+
100
+ // Alternative CPU core notation
101
+ else if (strcmp(var_name, "Core1") == 0) {
102
+ *result = 35.0; // Same as Core 1
103
+ return true;
104
+ }
105
+ else if (strcmp(var_name, "Core2") == 0) {
106
+ *result = 15.0; // Same as Core 2
107
+ return true;
108
+ }
109
+ else if (strcmp(var_name, "Core3") == 0) {
110
+ *result = 40.0; // Same as Core 3
111
+ return true;
112
+ }
113
+
114
+ // Time-related variables
115
+ else if (strcmp(var_name, "last_collected_t") == 0) {
116
+ *result = 1713400000.0; // Example timestamp
117
+ return true;
118
+ }
119
+ else if (strcmp(var_name, "now") == 0) {
120
+ *result = 1713400030.0; // 30 seconds after last_collected_t
121
+ return true;
122
+ }
123
+ else if (strcmp(var_name, "last_scrub") == 0) {
124
+ *result = 3600.0; // 1 hour in seconds
125
+ return true;
126
+ }
127
+
128
+ // Special variables with numeric modifiers
129
+ else if (strcmp(var_name, "1hour_packet_drops_inbound") == 0) {
130
+ *result = 250.0;
131
+ return true;
132
+ }
133
+ else if (strcmp(var_name, "1hour_packet_drops_outbound") == 0) {
134
+ *result = 150.0;
135
+ return true;
136
+ }
137
+ else if (strcmp(var_name, "1m_ipv4_udp_receive_buffer_errors") == 0) {
138
+ *result = 5000.0;
139
+ return true;
140
+ }
141
+ else if (strcmp(var_name, "active_processors") == 0) {
142
+ *result = 8.0;
143
+ return true;
144
+ }
145
+
146
+ // Bandwidth related
147
+ else if (strcmp(var_name, "bandwidth_1m_avg_of_now") == 0) {
148
+ *result = 1050.0;
149
+ return true;
150
+ }
151
+ else if (strcmp(var_name, "bandwidth_1m_avg_of_previous_1m") == 0) {
152
+ *result = 1000.0;
153
+ return true;
154
+ }
155
+ else if (strcmp(var_name, "bandwidth_1m_max_of_now") == 0) {
156
+ *result = 1500.0;
157
+ return true;
158
+ }
159
+ else if (strcmp(var_name, "bandwidth_1m_max_of_previous_1m") == 0) {
160
+ *result = 1400.0;
161
+ return true;
162
+ }
163
+
164
+ // Additional variables for memory tests
165
+ else if (strcmp(var_name, "mem") == 0) {
166
+ *result = 12000.0;
167
+ return true;
168
+ }
169
+ else if (strcmp(var_name, "tcp_mem_pressure") == 0) {
170
+ *result = 10000.0;
171
+ return true;
172
+ }
173
+ else if (strcmp(var_name, "tcp_mem_high") == 0) {
174
+ *result = 9000.0;
175
+ return true;
176
+ }
177
+ else if (strcmp(var_name, "pidmax") == 0) {
178
+ *result = 32768.0;
179
+ return true;
180
+ }
181
+ else if (strcmp(var_name, "arrays") == 0) {
182
+ *result = 128.0;
183
+ return true;
184
+ }
185
+ else if (strcmp(var_name, "ipc.semaphores.arrays.max") == 0) {
186
+ *result = 256.0;
187
+ return true;
188
+ }
189
+ else if (strcmp(var_name, "ipc_semaphores_arrays_max") == 0) {
190
+ *result = 256.0; // Same as above for testing alternative notation
191
+ return true;
192
+ }
193
+
194
+ // Labels syntax test
195
+ else if (strcmp(var_name, "label:host") == 0) {
196
+ *result = 1.0; // Non-zero value to simulate a non-match
197
+ return true;
198
+ }
199
+
200
+ // Color thresholds
201
+ else if (strcmp(var_name, "green") == 0) {
202
+ *result = 30.0;
203
+ return true;
204
+ }
205
+ else if (strcmp(var_name, "red") == 0) {
206
+ *result = 80.0;
207
+ return true;
208
+ }
209
+
210
+ // Hierarchical variable names for system metrics
211
+ else if (strcmp(var_name, "system.ram.free") == 0) {
212
+ *result = 1000.0;
213
+ return true;
214
+ }
215
+ else if (strcmp(var_name, "system.ram.used") == 0) {
216
+ *result = 2000.0;
217
+ return true;
218
+ }
219
+ else if (strcmp(var_name, "system.ram.cached") == 0) {
220
+ *result = 500.0;
221
+ return true;
222
+ }
223
+ else if (strcmp(var_name, "system.ram.buffers") == 0) {
224
+ *result = 300.0;
225
+ return true;
226
+ }
227
+ else if (strcmp(var_name, "system.ram.swap") == 0) {
228
+ *result = 1000.0;
229
+ return true;
230
+ }
231
+ else if (strcmp(var_name, "system.ram.active") == 0) {
232
+ *result = 1500.0;
233
+ return true;
234
+ }
235
+ else if (strcmp(var_name, "system.ram.inactive") == 0) {
236
+ *result = 400.0;
237
+ return true;
238
+ }
239
+ else if (strcmp(var_name, "system.ram.wired") == 0) {
240
+ *result = 500.0;
241
+ return true;
242
+ }
243
+ else if (strcmp(var_name, "system.ram.cache") == 0) {
244
+ *result = 800.0;
245
+ return true;
246
+ }
247
+ else if (strcmp(var_name, "system.ram.laundry") == 0) {
248
+ *result = 200.0;
249
+ return true;
250
+ }
251
+ else if (strcmp(var_name, "system.ram.used_ram_to_ignore") == 0) {
252
+ *result = 200.0;
253
+ return true;
254
+ }
255
+
256
+ // Variables for real-world test expressions
257
+ else if (strcmp(var_name, "avail") == 0) {
258
+ *result = 950.0;
259
+ return true;
260
+ }
261
+ else if (strcmp(var_name, "active") == 0) {
262
+ *result = 1500.0;
263
+ return true;
264
+ }
265
+ else if (strcmp(var_name, "wired") == 0) {
266
+ *result = 500.0;
267
+ return true;
268
+ }
269
+ else if (strcmp(var_name, "laundry") == 0) {
270
+ *result = 200.0;
271
+ return true;
272
+ }
273
+ else if (strcmp(var_name, "buffers") == 0) {
274
+ *result = 300.0;
275
+ return true;
276
+ }
277
+ else if (strcmp(var_name, "cache") == 0) {
278
+ *result = 800.0;
279
+ return true;
280
+ }
281
+ else if (strcmp(var_name, "free") == 0) {
282
+ *result = 1000.0;
283
+ return true;
284
+ }
285
+ else if (strcmp(var_name, "inactive") == 0) {
286
+ *result = 400.0;
287
+ return true;
288
+ }
289
+ else if (strcmp(var_name, "used_ram_to_ignore") == 0) {
290
+ *result = 200.0;
291
+ return true;
292
+ }
293
+
294
+ // From dataset examples with status variables
295
+ else if (strcmp(var_name, "status") == 0) {
296
+ *result = 1.0; // WARNING status
297
+ return true;
298
+ }
299
+ else if (strcmp(var_name, "CRITICAL") == 0) {
300
+ *result = 2.0;
301
+ return true;
302
+ }
303
+ else if (strcmp(var_name, "WARNING") == 0) {
304
+ *result = 1.0;
305
+ return true;
306
+ }
307
+ else if (strcmp(var_name, "10m_acquiring_requests") == 0) {
308
+ *result = 100.0;
309
+ return true;
310
+ }
311
+ else if (strcmp(var_name, "sent") == 0) {
312
+ *result = 1000.0;
313
+ return true;
314
+ }
315
+ else if (strcmp(var_name, "buffered") == 0) {
316
+ *result = 500.0;
317
+ return true;
318
+ }
319
+ else if (strcmp(var_name, "lost") == 0) {
320
+ *result = -10.0;
321
+ return true;
322
+ }
323
+ else if (strcmp(var_name, "offset") == 0) {
324
+ *result = -5.0;
325
+ return true;
326
+ }
327
+
328
+ return false; // Variable not found
329
+}
330
+
331
+typedef struct {
332
+ const char *expression;
333
+ NETDATA_DOUBLE expected_result;
334
+ int expected_error;
335
+ bool should_parse;
336
+} TestCase;
337
+
338
+typedef struct {
339
+ const char *name;
340
+ TestCase *test_cases;
341
+ int test_count;
342
+} TestGroup;
343
+
344
+#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
345
+
346
+static void run_test_group(TestGroup *group) {
347
+ printf("\n=== Running Test Group: %s ===\n", group->name);
348
+
349
+ int passed = 0;
350
+ int failed = 0;
351
+
352
+ for (int i = 0; i < group->test_count; i++) {
353
+ TestCase *tc = &group->test_cases[i];
354
+ const char *failed_at = NULL;
355
+ int error = 0;
356
+ bool test_failed = false;
357
+ char error_message[1024] = "";
358
+
359
+ printf("Test %d: %s\n", i + 1, tc->expression);
360
+
361
+ // Try to parse the expression
362
+ EVAL_EXPRESSION *exp = expression_parse(tc->expression, &failed_at, &error);
363
+
364
+ // Check if parsing succeeded as expected
365
+ if (tc->should_parse && !exp) {
366
+ snprintf(error_message, sizeof(error_message),
367
+ "Expected parsing to succeed, but it failed with error %d (%s)",
368
+ error, expression_strerror(error));
369
+ test_failed = true;
370
+ }
371
+ else if (!tc->should_parse && exp) {
372
+ snprintf(error_message, sizeof(error_message),
373
+ "Expected parsing to fail, but it succeeded");
374
+ test_failed = true;
375
+ }
376
+
377
+ // If the expression parsed successfully, evaluate it
378
+ if (exp) {
379
+ // Set up the variable lookup callback
380
+ expression_set_variable_lookup_callback(exp, test_variable_lookup, NULL);
381
+
382
+ // Evaluate the expression
383
+ int eval_result = expression_evaluate(exp);
384
+
385
+ // Check if there was an error during evaluation
386
+ if (tc->expected_error != EVAL_ERROR_OK && exp->error == EVAL_ERROR_OK) {
387
+ snprintf(error_message, sizeof(error_message),
388
+ "Expected evaluation error %d, but got no error",
389
+ tc->expected_error);
390
+ test_failed = true;
391
+ }
392
+ else if (tc->expected_error == EVAL_ERROR_OK && exp->error != EVAL_ERROR_OK) {
393
+ snprintf(error_message, sizeof(error_message),
394
+ "Expected no evaluation error, but got error %d (%s)",
395
+ exp->error, expression_strerror(exp->error));
396
+ test_failed = true;
397
+ }
398
+ else if (tc->expected_error != EVAL_ERROR_OK && exp->error != tc->expected_error) {
399
+ snprintf(error_message, sizeof(error_message),
400
+ "Expected evaluation error %d, but got error %d (%s)",
401
+ tc->expected_error, exp->error, expression_strerror(exp->error));
402
+ test_failed = true;
403
+ }
404
+
405
+ // Check the evaluation result
406
+ if (tc->expected_error == EVAL_ERROR_OK) {
407
+ if (isnan(tc->expected_result) && !isnan(exp->result)) {
408
+ snprintf(error_message, sizeof(error_message),
409
+ "Expected NaN result, but got %f", exp->result);
410
+ test_failed = true;
411
+ }
412
+ else if (isinf(tc->expected_result) && !isinf(exp->result)) {
413
+ snprintf(error_message, sizeof(error_message),
414
+ "Expected Inf result, but got %f", exp->result);
415
+ test_failed = true;
416
+ }
417
+ else if (!isnan(tc->expected_result) && !isinf(tc->expected_result) &&
418
+ !isnan(exp->result) && !isinf(exp->result) &&
419
+ fabs(tc->expected_result - exp->result) > 0.000001) {
420
+ snprintf(error_message, sizeof(error_message),
421
+ "Expected result %f, but got %f",
422
+ tc->expected_result, exp->result);
423
+ test_failed = true;
424
+ }
425
+ }
426
+
427
+ // Print additional information for debugging
428
+ printf(" Parsed as: %s\n", expression_parsed_as(exp));
429
+
430
+ if (eval_result) {
431
+ printf(" Evaluated to: %f\n", expression_result(exp));
432
+ } else {
433
+ printf(" Evaluation failed: %s\n", expression_error_msg(exp));
434
+ }
435
+
436
+ // Special handling for API function tests
437
+ if (strcmp(group->name, "API Function Tests") == 0) {
438
+ if (strstr(tc->expression, "hardcoded_var") != NULL) {
439
+ // Test the expression_hardcode_variable function
440
+ STRING *var = string_strdupz("hardcoded_var");
441
+ expression_hardcode_variable(exp, var, 123.456);
442
+ string_freez(var);
443
+
444
+ // Re-evaluate the expression
445
+ expression_evaluate(exp);
446
+
447
+ // Check if hardcoded variable works correctly
448
+ if (exp->error != EVAL_ERROR_OK || fabs(exp->result - 123.456) > 0.000001) {
449
+ snprintf(error_message, sizeof(error_message),
450
+ "expression_hardcode_variable failed: expected 123.456, got %f (error: %d)",
451
+ exp->result, exp->error);
452
+ test_failed = true;
453
+ } else {
454
+ printf(" expression_hardcode_variable test passed!\n");
455
+ }
456
+ }
457
+ else if (strcmp(tc->expression, "1 + 2") == 0) {
458
+ // Test expression_source
459
+ const char *source = expression_source(exp);
460
+ if (strcmp(source, "1 + 2") != 0) {
461
+ snprintf(error_message, sizeof(error_message),
462
+ "expression_source failed: expected '1 + 2', got '%s'",
463
+ source);
464
+ test_failed = true;
465
+ } else {
466
+ printf(" expression_source test passed!\n");
467
+ }
468
+
469
+ // Test expression_parsed_as
470
+ const char *parsed = expression_parsed_as(exp);
471
+ if (parsed == NULL || strlen(parsed) == 0) {
472
+ snprintf(error_message, sizeof(error_message),
473
+ "expression_parsed_as failed: got empty or NULL result");
474
+ test_failed = true;
475
+ } else {
476
+ printf(" expression_parsed_as test passed! Result: %s\n", parsed);
477
+ }
478
+
479
+ // Test expression_result
480
+ NETDATA_DOUBLE result = expression_result(exp);
481
+ if (fabs(result - 3.0) > 0.000001) {
482
+ snprintf(error_message, sizeof(error_message),
483
+ "expression_result failed: expected 3.0, got %f",
484
+ result);
485
+ test_failed = true;
486
+ } else {
487
+ printf(" expression_result test passed!\n");
488
+ }
489
+ }
490
+ else if (strcmp(tc->expression, "bad/syntax") == 0) {
491
+ // This case is for testing expression_error_msg, but it is already tested
492
+ // in the main evaluation loop when errors occur.
493
+ printf(" expression_error_msg is tested during evaluation failures\n");
494
+ }
495
+ }
496
+
497
+ // Clean up
498
+ expression_free(exp);
499
+ }
500
+ else if (!tc->should_parse) {
501
+ printf(" Parsing failed as expected at: %s\n",
502
+ failed_at ? ((*failed_at) ? failed_at : "<END OF EXPRESSION>") : "<NONE>");
503
+ }
504
+
505
+ // Report test result
506
+ if (test_failed) {
507
+#ifdef USE_RE2C_LEMON_PARSER
508
+ printf(" [RE2C_LEMON] FAILED: %s\n", error_message);
509
+#else
510
+ printf(" [RECURSIVE] FAILED: %s\n", error_message);
511
+#endif
512
+ failed++;
513
+ } else {
514
+ printf(" PASSED\n");
515
+ passed++;
516
+ }
517
+ }
518
+
519
+ printf("\nGroup Results: %d tests, %d passed, %d failed\n",
520
+ passed + failed, passed, failed);
521
+}
522
+
523
+static TestCase arithmetic_tests[] = {
524
+ {"1 + 2", 3.0, EVAL_ERROR_OK, true},
525
+ {"5 - 3", 2.0, EVAL_ERROR_OK, true},
526
+ {"4 * 5", 20.0, EVAL_ERROR_OK, true},
527
+ {"10 / 2", 5.0, EVAL_ERROR_OK, true},
528
+ {"10 / 0", 0.0, EVAL_ERROR_VALUE_IS_INFINITE, true}, // Netdata reports error for division by zero
529
+ {"-10", -10.0, EVAL_ERROR_OK, true},
530
+ {"+5", 5.0, EVAL_ERROR_OK, true},
531
+ {"5 + -3", 2.0, EVAL_ERROR_OK, true},
532
+ {"5 * -3", -15.0, EVAL_ERROR_OK, true},
533
+ {"1 + 2 * 3", 7.0, EVAL_ERROR_OK, true},
534
+ {"(1 + 2) * 3", 9.0, EVAL_ERROR_OK, true},
535
+ {"10.5 + 2.5", 13.0, EVAL_ERROR_OK, true},
536
+ {"10.5 * 2", 21.0, EVAL_ERROR_OK, true},
537
+ {"5.5 / 2", 2.75, EVAL_ERROR_OK, true},
538
+ {"1.5e2 + 2", 152.0, EVAL_ERROR_OK, true},
539
+ {"1+2*3+4", 11.0, EVAL_ERROR_OK, true},
540
+};
541
+
542
+// Test cases for comparison operations
543
+static TestCase comparison_tests[] = {
544
+ {"1 == 1", 1.0, EVAL_ERROR_OK, true},
545
+ {"1 == 2", 0.0, EVAL_ERROR_OK, true},
546
+ {"1 != 2", 1.0, EVAL_ERROR_OK, true},
547
+ {"1 != 1", 0.0, EVAL_ERROR_OK, true},
548
+ {"5 > 3", 1.0, EVAL_ERROR_OK, true},
549
+ {"3 > 5", 0.0, EVAL_ERROR_OK, true},
550
+ {"3 < 5", 1.0, EVAL_ERROR_OK, true},
551
+ {"5 < 3", 0.0, EVAL_ERROR_OK, true},
552
+ {"5 >= 5", 1.0, EVAL_ERROR_OK, true},
553
+ {"5 >= 6", 0.0, EVAL_ERROR_OK, true},
554
+ {"5 <= 5", 1.0, EVAL_ERROR_OK, true},
555
+ {"5 <= 4", 0.0, EVAL_ERROR_OK, true},
556
+ {"3 > 2 > 1", 0.0, EVAL_ERROR_OK, true}, // This is (3 > 2) > 1, which is 1 > 1, which is false
557
+};
558
+
559
+// Test cases for logical operations
560
+static TestCase logical_tests[] = {
561
+ {"1 && 1", 1.0, EVAL_ERROR_OK, true},
562
+ {"1 && 0", 0.0, EVAL_ERROR_OK, true},
563
+ {"0 && 1", 0.0, EVAL_ERROR_OK, true},
564
+ {"0 && 0", 0.0, EVAL_ERROR_OK, true},
565
+ {"1 || 1", 1.0, EVAL_ERROR_OK, true},
566
+ {"1 || 0", 1.0, EVAL_ERROR_OK, true},
567
+ {"0 || 1", 1.0, EVAL_ERROR_OK, true},
568
+ {"0 || 0", 0.0, EVAL_ERROR_OK, true},
569
+ {"!1", 0.0, EVAL_ERROR_OK, true},
570
+ {"!0", 1.0, EVAL_ERROR_OK, true},
571
+ {"!(1 && 0)", 1.0, EVAL_ERROR_OK, true},
572
+ {"1 && !0", 1.0, EVAL_ERROR_OK, true},
573
+ {"0 || !(1 && 0)", 1.0, EVAL_ERROR_OK, true},
574
+
575
+ // Tests for word operators (AND, OR)
576
+ {"1 AND 1", 1.0, EVAL_ERROR_OK, true},
577
+ {"1 AND 0", 0.0, EVAL_ERROR_OK, true},
578
+ {"0 AND 1", 0.0, EVAL_ERROR_OK, true},
579
+ {"0 AND 0", 0.0, EVAL_ERROR_OK, true},
580
+ {"1 OR 1", 1.0, EVAL_ERROR_OK, true},
581
+ {"1 OR 0", 1.0, EVAL_ERROR_OK, true},
582
+ {"0 OR 1", 1.0, EVAL_ERROR_OK, true},
583
+ {"0 OR 0", 0.0, EVAL_ERROR_OK, true},
584
+ {"NOT 1", 0.0, EVAL_ERROR_OK, true},
585
+ {"NOT 0", 1.0, EVAL_ERROR_OK, true},
586
+ {"NOT(1 AND 0)", 1.0, EVAL_ERROR_OK, true},
587
+ {"1 AND NOT 0", 1.0, EVAL_ERROR_OK, true},
588
+ {"0 OR NOT(1 AND 0)", 1.0, EVAL_ERROR_OK, true},
589
+ {"(1 AND 1) OR (0 AND 1)", 1.0, EVAL_ERROR_OK, true},
590
+
591
+ // Mixed symbol and word operators
592
+ {"1 AND (0 || 1)", 1.0, EVAL_ERROR_OK, true},
593
+ {"(1 && 0) OR 1", 1.0, EVAL_ERROR_OK, true},
594
+ {"NOT (1 && 0) OR (NOT 0 AND 1)", 1.0, EVAL_ERROR_OK, true},
595
+
596
+ // Case-insensitive logical operators
597
+ {"1 and 1", 1.0, EVAL_ERROR_OK, true},
598
+ {"0 or 1", 1.0, EVAL_ERROR_OK, true},
599
+ {"not 0", 1.0, EVAL_ERROR_OK, true},
600
+ {"1 And 0", 0.0, EVAL_ERROR_OK, true},
601
+ {"0 Or 1", 1.0, EVAL_ERROR_OK, true},
602
+ {"Not 1", 0.0, EVAL_ERROR_OK, true},
603
+};
604
+
605
+// Test cases for variable usage
606
+static TestCase variable_tests[] = {
607
+ // Basic variable tests
608
+ {"$var1", 42.0, EVAL_ERROR_OK, true},
609
+ {"$var2", 24.0, EVAL_ERROR_OK, true},
610
+ {"$var1 + $var2", 66.0, EVAL_ERROR_OK, true},
611
+ {"$var1 * $var2", 1008.0, EVAL_ERROR_OK, true},
612
+ {"$var1 > $var2", 1.0, EVAL_ERROR_OK, true},
613
+ {"$var1 < $var2", 0.0, EVAL_ERROR_OK, true},
614
+ {"$var1 && $var2", 1.0, EVAL_ERROR_OK, true},
615
+ {"$zero && $var1", 0.0, EVAL_ERROR_OK, true},
616
+
617
+ // Variables with different notations
618
+ {"$var1", 42.0, EVAL_ERROR_OK, true}, // Test dollar sign prefix
619
+ {"${var1}", 42.0, EVAL_ERROR_OK, true}, // Test with curly braces
620
+ {"${this variable}", 100.0, EVAL_ERROR_OK, true}, // Variable with space
621
+ {"$unknown", 0.0, EVAL_ERROR_UNKNOWN_VARIABLE, true},
622
+
623
+ // Variables starting with numbers (from real-world usage)
624
+ {"$1var", 42.0, EVAL_ERROR_OK, true},
625
+ {"$1.var", 77.0, EVAL_ERROR_OK, true},
626
+ {"$var.1", 78.0, EVAL_ERROR_OK, true},
627
+
628
+ // Variables with special characters
629
+ {"$var-1", 79.0, EVAL_ERROR_UNKNOWN_VARIABLE, true},
630
+ {"${var-with-hyphens}", 100.0, EVAL_ERROR_OK, true},
631
+
632
+ // Hierarchical variable names with dots
633
+ {"$system.ram.free", 1000.0, EVAL_ERROR_OK, true},
634
+ {"$system.ram.used", 2000.0, EVAL_ERROR_OK, true},
635
+ {"$system.ram.cached", 500.0, EVAL_ERROR_OK, true},
636
+ {"$system.ram.buffers", 300.0, EVAL_ERROR_OK, true},
637
+
638
+ // Real-world examples from the dataset - variable with math expressions
639
+ {"$avail * 100 / ($system.ram.used + $system.ram.cached + $system.ram.free + $system.ram.buffers)", 25.0, EVAL_ERROR_OK, true},
640
+};
641
+
642
+// Test cases for function calls
643
+static TestCase function_tests[] = {
644
+ {"abs(5)", 5.0, EVAL_ERROR_OK, true},
645
+ {"abs(-5)", 5.0, EVAL_ERROR_OK, true},
646
+ {"abs(0)", 0.0, EVAL_ERROR_OK, true},
647
+ {"abs($var1)", 42.0, EVAL_ERROR_OK, true},
648
+ {"abs($negative)", 10.0, EVAL_ERROR_OK, true},
649
+ {"abs(1 + -3)", 2.0, EVAL_ERROR_OK, true},
650
+ {"abs($var1 - $var2)", 18.0, EVAL_ERROR_OK, true},
651
+ {"abs(abs(-5))", 5.0, EVAL_ERROR_OK, true}, // Nested function call
652
+};
653
+
654
+// Test cases for special values
655
+// In Netdata, NaN values cause VALUE_IS_UNSET errors and Infinity causes VALUE_IS_INFINITE errors
656
+static TestCase special_value_tests[] = {
657
+ // NaN tests - Netdata rejects them with VALUE_IS_UNSET error
658
+ {"$nan_var", 0.0, EVAL_ERROR_VALUE_IS_NAN, true},
659
+
660
+ // Comparison operators with NaN - these should work as they just check NaN status
661
+ {"$nan_var == 5", 0.0, EVAL_ERROR_OK, true},
662
+ {"$nan_var != 5", 1.0, EVAL_ERROR_OK, true},
663
+ {"$nan_var > 5", 0.0, EVAL_ERROR_OK, true},
664
+ {"$nan_var < 5", 0.0, EVAL_ERROR_OK, true},
665
+ {"$nan_var >= 5", 0.0, EVAL_ERROR_OK, true},
666
+ {"$nan_var <= 5", 0.0, EVAL_ERROR_OK, true},
667
+
668
+ // NaN self-comparison (Netdata treats NaN == NaN as true, which is different from IEEE 754)
669
+ {"$nan_var == $nan_var", 1.0, EVAL_ERROR_OK, true},
670
+ {"$nan_var != $nan_var", 0.0, EVAL_ERROR_OK, true},
671
+ {"$nan_var > $nan_var", 0.0, EVAL_ERROR_OK, true},
672
+ {"$nan_var < $nan_var", 0.0, EVAL_ERROR_OK, true},
673
+ {"$nan_var >= $nan_var", 0.0, EVAL_ERROR_OK, true},
674
+ {"$nan_var <= $nan_var", 0.0, EVAL_ERROR_OK, true},
675
+
676
+ // Logical operations with NaN
677
+ {"$nan_var && 1", 0.0, EVAL_ERROR_OK, true},
678
+ {"$nan_var || 1", 1.0, EVAL_ERROR_OK, true},
679
+ {"$nan_var && 0", 0.0, EVAL_ERROR_OK, true},
680
+ {"$nan_var || 0", 0.0, EVAL_ERROR_OK, true},
681
+ {"!$nan_var", 1.0, EVAL_ERROR_OK, true},
682
+
683
+ // Ternary with NaN
684
+ {"($nan_var) ? 1 : 2", 2.0, EVAL_ERROR_OK, true},
685
+
686
+ // Infinity tests - Netdata rejects with VALUE_IS_INFINITE error
687
+ {"$inf_var", 0.0, EVAL_ERROR_VALUE_IS_INFINITE, true},
688
+
689
+ // Comparison operators with Infinity - these work
690
+ {"$inf_var == 5", 0.0, EVAL_ERROR_OK, true},
691
+ {"$inf_var != 5", 1.0, EVAL_ERROR_OK, true},
692
+ {"$inf_var > 5", 1.0, EVAL_ERROR_OK, true},
693
+ {"$inf_var < 5", 0.0, EVAL_ERROR_OK, true},
694
+ {"$inf_var >= 5", 1.0, EVAL_ERROR_OK, true},
695
+ {"$inf_var <= 5", 0.0, EVAL_ERROR_OK, true},
696
+
697
+ // Infinity self-comparison
698
+ {"$inf_var == $inf_var", 1.0, EVAL_ERROR_OK, true},
699
+ {"$inf_var != $inf_var", 0.0, EVAL_ERROR_OK, true},
700
+ {"$inf_var > $inf_var", 0.0, EVAL_ERROR_OK, true},
701
+ {"$inf_var < $inf_var", 0.0, EVAL_ERROR_OK, true},
702
+ {"$inf_var >= $inf_var", 1.0, EVAL_ERROR_OK, true},
703
+ {"$inf_var <= $inf_var", 1.0, EVAL_ERROR_OK, true},
704
+
705
+ // Logical operations with Infinity
706
+ {"$inf_var && 1", 1.0, EVAL_ERROR_OK, true},
707
+ {"$inf_var || 1", 1.0, EVAL_ERROR_OK, true},
708
+ {"$inf_var && 0", 0.0, EVAL_ERROR_OK, true},
709
+ {"$inf_var || 0", 1.0, EVAL_ERROR_OK, true},
710
+ {"!$inf_var", 0.0, EVAL_ERROR_OK, true},
711
+
712
+ // Ternary with Infinity
713
+ {"($inf_var) ? 1 : 2", 1.0, EVAL_ERROR_OK, true},
714
+
715
+ // Zero division
716
+ {"5 / 0", 0.0, EVAL_ERROR_VALUE_IS_INFINITE, true}, // Positive/zero gives infinity
717
+ {"-5 / 0", 0.0, EVAL_ERROR_VALUE_IS_INFINITE, true}, // Negative/zero gives -infinity
718
+ {"0 / 0", 0.0, EVAL_ERROR_VALUE_IS_INFINITE, true}, // In Netdata, this gives INFINITE error
719
+
720
+ // NaN and Infinity comparison
721
+ {"$inf_var == $nan_var", 0.0, EVAL_ERROR_OK, true},
722
+ {"$inf_var != $nan_var", 1.0, EVAL_ERROR_OK, true},
723
+ {"$inf_var > $nan_var", 0.0, EVAL_ERROR_OK, true},
724
+ {"$inf_var < $nan_var", 0.0, EVAL_ERROR_OK, true},
725
+ {"$inf_var >= $nan_var", 0.0, EVAL_ERROR_OK, true},
726
+ {"$inf_var <= $nan_var", 0.0, EVAL_ERROR_OK, true},
727
+
728
+ // Logical operations with mixed NaN and Infinity
729
+ {"$inf_var && $nan_var", 0.0, EVAL_ERROR_OK, true},
730
+ {"$inf_var || $nan_var", 1.0, EVAL_ERROR_OK, true},
731
+ {"!$nan_var && $inf_var", 1.0, EVAL_ERROR_OK, true},
732
+ {"!$nan_var || !$inf_var", 1.0, EVAL_ERROR_OK, true},
733
+
734
+ // Special value operations with zero
735
+ {"$zero * $inf_var", 0.0, EVAL_ERROR_VALUE_IS_INFINITE, true},
736
+ {"$zero / $zero", 0.0, EVAL_ERROR_VALUE_IS_INFINITE, true}, // Netdata treats 0/0 as INFINITE
737
+ {"($zero) ? 1 : 2", 2.0, EVAL_ERROR_OK, true},
738
+
739
+ // Short-circuit evaluation with special values (these work because no evaluation happens)
740
+ {"0 && $nan_var", 0.0, EVAL_ERROR_OK, true}, // Short-circuit should avoid NaN
741
+ {"1 || $nan_var", 1.0, EVAL_ERROR_OK, true}, // Short-circuit should avoid NaN
742
+ {"0 && $inf_var", 0.0, EVAL_ERROR_OK, true}, // Short-circuit should avoid Infinity
743
+ {"1 || $inf_var", 1.0, EVAL_ERROR_OK, true} // Short-circuit should avoid Infinity
744
+};
745
+
746
+// Complex expression tests
747
+static TestCase complex_tests[] = {
748
+ {"1 + 2 * 3 - 4 / 2", 5.0, EVAL_ERROR_OK, true},
749
+ {"(1 + 2) * (3 - 4) / 2", -1.5, EVAL_ERROR_OK, true},
750
+ {"1 > 0 && 2 > 1", 1.0, EVAL_ERROR_OK, true},
751
+ {"1 > 0 || 0 > 1", 1.0, EVAL_ERROR_OK, true},
752
+ {"(1 > 0) ? 10 : 20", 10.0, EVAL_ERROR_OK, true},
753
+ {"(0 > 1) ? 10 : 20", 20.0, EVAL_ERROR_OK, true},
754
+ {"((($var1 + $var2) / 2) > 30) ? ($var1 * $var2) : ($var1 + $var2)", 1008.0, EVAL_ERROR_OK, true},
755
+ {"5 + (!($var1 > 50) * 10)", 15.0, EVAL_ERROR_OK, true},
756
+ {"($var1 > $var2) ? ($var1 - $var2) : ($var2 - $var1)", 18.0, EVAL_ERROR_OK, true},
757
+ {"(($zero > 0) ? $var1 : $var2) + (($zero < 0) ? $var1 : $var2)", 48.0, EVAL_ERROR_OK, true},
758
+};
759
+
760
+// Edge case and invalid expressions
761
+static TestCase edge_case_tests[] = {
762
+ {"", 0.0, EVAL_ERROR_OK, false},
763
+ {" ", 0.0, EVAL_ERROR_MISSING_OPERAND, false}, // Netdata can't parse whitespace-only expressions
764
+ {"\t\n", 0.0, EVAL_ERROR_MISSING_OPERAND, false}, // Netdata can't parse whitespace-only expressions
765
+ {" 5 + 3 ", 8.0, EVAL_ERROR_OK, true}, // Whitespace between operands is fine
766
+ {"$", 0.0, EVAL_ERROR_REMAINING_GARBAGE, false}, // Netdata's error is different for incomplete variables
767
+ {"${", 0.0, EVAL_ERROR_REMAINING_GARBAGE, false}, // Netdata's error is different for incomplete variables
768
+ {"$}", 0.0, EVAL_ERROR_REMAINING_GARBAGE, false}, // Netdata's error is different for incomplete variables
769
+ {"${}", 0.0, EVAL_ERROR_REMAINING_GARBAGE, false}, // Netdata's error is different for incomplete variables
770
+ {"5 + -3", 2.0, EVAL_ERROR_OK, true}, // Netdata actually handles this correctly as a unary minus
771
+ {"5 + 3", 8.0, EVAL_ERROR_OK, true}, // Basic sanity check
772
+};
773
+
774
+// Operator precedence tests
775
+static TestCase precedence_tests[] = {
776
+ {"5 + 3 * 2", 11.0, EVAL_ERROR_OK, true}, // * before +
777
+ {"5 * 3 + 2", 17.0, EVAL_ERROR_OK, true}, // * before +
778
+ {"5 + 3 - 2", 6.0, EVAL_ERROR_OK, true}, // + and - same precedence (left to right)
779
+ {"5 - 3 + 2", 4.0, EVAL_ERROR_OK, true}, // + and - same precedence (left to right)
780
+ {"5 * 3 / 3", 5.0, EVAL_ERROR_OK, true}, // * and / same precedence (left to right)
781
+ {"5 / 5 * 3", 3.0, EVAL_ERROR_OK, true}, // * and / same precedence (left to right)
782
+ {"5 > 3 && 2 < 4 || 1 == 0", 1.0, EVAL_ERROR_OK, true}, // && before ||
783
+ {"5 > 3 && (2 < 4 || 1 == 0)", 1.0, EVAL_ERROR_OK, true}, // same as above with explicit grouping
784
+ {"5 > 3 || 2 < 4 && 1 == 0", 0.0, EVAL_ERROR_OK, true}, // In Netdata, || and && have same precedence (left to right)
785
+ {"(5 > 3 || 2 < 4) && 1 == 0", 0.0, EVAL_ERROR_OK, true}, // explicit grouping with same result
786
+ {"!5 > 3", 0.0, EVAL_ERROR_OK, true}, // ! before >
787
+ {"!(5 > 3)", 0.0, EVAL_ERROR_OK, true}, // same as above with explicit grouping
788
+ {"5 + 3 > 2 * 3", 1.0, EVAL_ERROR_OK, true}, // arithmetic before comparison
789
+ {"5 + 3 > 2 * 4", 0.0, EVAL_ERROR_OK, true}, // arithmetic before comparison
790
+ {"(5 > 3) ? (1 + 2) : (3 + 4)", 3.0, EVAL_ERROR_OK, true}, // ternary has low precedence
791
+ {"($var1 + $var2 * 2 > 80) ? 100 : 200", 100.0, EVAL_ERROR_OK, true}, // complex precedence test (42 + 24*2 = 90, which is > 80)
792
+};
793
+
794
+// Parentheses tests - specifically testing how parentheses change operator precedence
795
+static TestCase parentheses_tests[] = {
796
+ {"5 + 3 * 2", 11.0, EVAL_ERROR_OK, true}, // Default: * has higher precedence
797
+ {"(5 + 3) * 2", 16.0, EVAL_ERROR_OK, true}, // Parentheses change precedence
798
+ {"5 * (3 + 2)", 25.0, EVAL_ERROR_OK, true}, // Parentheses change order of operations
799
+ {"(5 + 3 * 2)", 11.0, EVAL_ERROR_OK, true}, // Redundant parentheses don't change anything
800
+ {"((5 + 3) * 2)", 16.0, EVAL_ERROR_OK, true}, // Nested parentheses
801
+ {"5 - (3 - 1)", 3.0, EVAL_ERROR_OK, true}, // Parentheses with subtraction
802
+ {"5 - 3 - 1", 1.0, EVAL_ERROR_OK, true}, // Without parentheses (left-to-right)
803
+ {"(5 - 3) - 1", 1.0, EVAL_ERROR_OK, true}, // Explicit grouping doesn't change result
804
+ {"5 - (3 - 1)", 3.0, EVAL_ERROR_OK, true}, // Different grouping changes result
805
+ {"5 / (2 * 2.5)", 1.0, EVAL_ERROR_OK, true}, // Division and multiplication with parentheses
806
+ {"(5 / 2) * 2.5", 6.25, EVAL_ERROR_OK, true}, // Different grouping changes result
807
+ {"$var1 * ($var2 + 6)", 1260.0, EVAL_ERROR_OK, true}, // Variables with parentheses
808
+ {"($var1 * $var2) + 6", 1014.0, EVAL_ERROR_OK, true}, // Different grouping with variables
809
+ {"!($var1 > $var2)", 0.0, EVAL_ERROR_OK, true}, // Logical NOT with parentheses
810
+ {"!(0)", 1.0, EVAL_ERROR_OK, true}, // Logical NOT with constant
811
+ {"!0", 1.0, EVAL_ERROR_OK, true}, // Same without parentheses
812
+ {"5 > 3 && (2 < 1 || 3 > 1)", 1.0, EVAL_ERROR_OK, true}, // Complex logical expression with parentheses
813
+ {"(5 > 3 && 2 < 1) || 3 > 1", 1.0, EVAL_ERROR_OK, true}, // Different grouping changes result
814
+ {"5 > 3 && 2 < 1 || 3 > 1", 1.0, EVAL_ERROR_OK, true}, // Default precedence (&& before ||)
815
+ {"(5 > 3) && ((2 < 1) || (3 > 1))", 1.0, EVAL_ERROR_OK, true}, // Excessive parentheses
816
+ {"(((5))) + (((3)))", 8.0, EVAL_ERROR_OK, true}, // Multiple nested parentheses
817
+ {"abs(-($var1 - $var2))", 18.0, EVAL_ERROR_OK, true}, // Function with parenthesized expression
818
+ {"abs(-(($var1) - ($var2)))", 18.0, EVAL_ERROR_OK, true}, // Function with nested parentheses
819
+ {"(5 > 3) ? ($var1 + $var2) : ($var1 - $var2)", 66.0, EVAL_ERROR_OK, true}, // Ternary with parentheses
820
+ {"((5 > 3) ? $var1 : $var2) + 10", 52.0, EVAL_ERROR_OK, true}, // Parentheses around ternary
821
+};
822
+
823
+// Tests for API functions
824
+static TestCase api_function_tests[] = {
825
+ {"1 + 2", 3.0, EVAL_ERROR_OK, true}, // For testing expression_source and expression_parsed_as
826
+ {"$var1", 42.0, EVAL_ERROR_OK, true}, // For testing expression_result and variable lookup
827
+ {"bad/syntax", 0.0, EVAL_ERROR_UNKNOWN_OPERAND, false}, // For testing expression_error_msg
828
+ {"$hardcoded_var", 0.0, EVAL_ERROR_UNKNOWN_VARIABLE, true}, // For testing expression_hardcode_variable
829
+};
830
+
831
+// Test cases for number overflow
832
+static TestCase overflow_tests[] = {
833
+ // Positive overflow - extreme large numbers
834
+ {"1e308", 1e308, EVAL_ERROR_OK, true}, // Very large but valid number
835
+ {"1e308 * 10", INFINITY, EVAL_ERROR_VALUE_IS_INFINITE, true}, // Overflow to positive infinity
836
+ {"1e308 + 1e308", INFINITY, EVAL_ERROR_VALUE_IS_INFINITE, true}, // Addition causing overflow
837
+
838
+ // Negative overflow - extreme large negative numbers
839
+ {"-1e308", -1e308, EVAL_ERROR_OK, true}, // Very large negative but valid
840
+ {"-1e308 * 10", -INFINITY, EVAL_ERROR_VALUE_IS_INFINITE, true}, // Overflow to negative infinity
841
+ {"-1e308 - 1e308", -INFINITY, EVAL_ERROR_VALUE_IS_INFINITE, true}, // Subtraction causing negative overflow
842
+
843
+ // Operations with infinity
844
+ {"1e308 * 1e308", INFINITY, EVAL_ERROR_VALUE_IS_INFINITE, true}, // Multiplication causing overflow
845
+ {"-1e308 * -1e308", INFINITY, EVAL_ERROR_VALUE_IS_INFINITE, true}, // Negative * negative = positive overflow
846
+ {"1e308 / 1e-308", INFINITY, EVAL_ERROR_VALUE_IS_INFINITE, true}, // Division causing overflow
847
+
848
+ // Mixed operations
849
+ {"1e308 - 1e308", 0.0, EVAL_ERROR_OK, true}, // This should properly cancel out
850
+ {"(1e308 * 2) / 2", INFINITY, EVAL_ERROR_VALUE_IS_INFINITE, true}, // Overflow in intermediate calculation
851
+};
852
+
853
+// Test cases for combined complex expressions
854
+static TestCase combined_tests[] = {
855
+ // Complex arithmetic expressions combining multiple operations
856
+ {"(5 + 3 * 2) / (1 + 1) * 4 - 10", 12.0, EVAL_ERROR_OK, true},
857
+ {"((($var1 * 2) / 4) + (($var2 - 4) * 2)) / 10", 6.1, EVAL_ERROR_OK, true}, // Corrected expected result
858
+ {"abs($negative) * 2 + $var1 / 2 - $var2", 17.0, EVAL_ERROR_OK, true}, // Corrected expected result
859
+
860
+ // Complex boolean expressions with multiple conditions
861
+ {"($var1 > 40 && $var2 < 30) || ($var1 - $var2 > 10)", 1.0, EVAL_ERROR_OK, true},
862
+ {"!($var1 < 40) && ($var2 > 20 || $zero < 1) && !($var1 == $var2)", 1.0, EVAL_ERROR_OK, true},
863
+ // Ternary expressions need proper parentheses in Netdata
864
+ {"(($var1 > $var2) ? ($var1 - $var2) : ($var2 - $var1)) > 15", 1.0, EVAL_ERROR_OK, true},
865
+
866
+ // Mix of arithmetic and boolean with precedence tests
867
+ {"($var1 + $var2) / 2 > ($var1 > $var2 ? $var2 : $var1)", 1.0, EVAL_ERROR_OK, true},
868
+ {"(($var1 > $var2 ? 1 : 0) * 10 + (($var1 - $var2) / 3)) > 15", 1.0, EVAL_ERROR_OK, true},
869
+
870
+ // Complex expressions with potential overflows
871
+ {"(1e308 - 1e308) * $var1 + $var2", 24.0, EVAL_ERROR_OK, true},
872
+ {"($var1 > 0 ? 1e308 : -1e308) * ($var1 < 0 ? 1 : 0)", 0.0, EVAL_ERROR_OK, true},
873
+ {"(1e308 + 1e308 > 0) ? $var1 : $var2", 42.0, EVAL_ERROR_OK, true},
874
+
875
+ // Deeply nested expressions with mixed operations
876
+ {"((((($var1 / 2) + ($var2 * 2)) - 10) * 2) / 4) + (($var1 > $var2) ? 5 : -5)", 34.5, EVAL_ERROR_OK, true}, // Corrected expected result
877
+ // This tests the nested ternaries with proper parentheses
878
+ {"(abs($negative) > 5) ? $var1 : $var2", 42.0, EVAL_ERROR_OK, true}, // Simplified to avoid nested ternary issue
879
+ {"(($var1 + $var2) / 2 > 30) ? 10 : 5", 10.0, EVAL_ERROR_OK, true}, // Simplified second half of above test
880
+
881
+ // Expressions with short-circuit evaluation
882
+ {"$zero && (1 / $zero)", 0.0, EVAL_ERROR_OK, true}, // Short-circuit prevents division by zero
883
+ {"1 || (1e308 * 1e308)", 1.0, EVAL_ERROR_OK, true}, // Short-circuit prevents overflow
884
+ // Split this into two tests to avoid nested ternary issues
885
+ {"($var1 < 0) ? (1 / $zero) : $var1", 42.0, EVAL_ERROR_OK, true}, // First part of previous test
886
+ {"($var2 > 100) ? (1e308 * 1e308) : $var2", 24.0, EVAL_ERROR_OK, true}, // Second part of previous test
887
+};
888
+
889
+// Test cases that previously crashed Netdata
890
+// Note: When using the re2c/lemon parser, nested ternary operators without parentheses
891
+// are properly supported (unlike the original recursive descent parser)
892
+static TestCase crash_tests[] = {
893
+#ifdef USE_RE2C_LEMON_PARSER
894
+ {"$var1 > 0 ? $var1 < 0 ? 1 : 2 : 3", 2.0, EVAL_ERROR_OK, true},
895
+ {"$var1 > 0 ? ( $var1 < 0 ? 1 : 2 ) : 3", 2.0, EVAL_ERROR_OK, true},
896
+ {"( $var1 > 0 ? $var1 < 0 ? 1 : 2 : 3 )", 2.0, EVAL_ERROR_OK, true},
897
+#else
898
+ // Original recursive descent parser can't handle nested ternaries without parentheses
899
+ {"$var1 > 0 ? $var1 < 0 ? 1 : 2 : 3", 0.0, EVAL_ERROR_REMAINING_GARBAGE, false},
900
+ {"$var1 > 0 ? ( $var1 < 0 ? 1 : 2 ) : 3", 1.0, EVAL_ERROR_OK, true},
901
+ {"( $var1 > 0 ? $var1 < 0 ? 1 : 2 : 3 )", 0.0, EVAL_ERROR_REMAINING_GARBAGE, false},
902
+#endif
903
+ // Fully parenthesized works correctly in both parsers
904
+ {"($var1 > 0) ? (($var1 < 0) ? 1 : 2) : 3", 2.0, EVAL_ERROR_OK, true},
905
+ // Multiple nested parentheses are fine
906
+ {"(($zero)) ? 0 : ((($var1)))", 42.0, EVAL_ERROR_OK, true},
907
+ // Variable lookup errors are properly handled
908
+ {"$nonexistent + $var1", 0.0, EVAL_ERROR_UNKNOWN_VARIABLE, true},
909
+ // Division by (0-0) gives an INFINITE error in Netdata's implementation
910
+ {"10 / ($zero - $zero)", 0.0, EVAL_ERROR_VALUE_IS_INFINITE, true},
911
+ {"true", 0.0, EVAL_ERROR_REMAINING_GARBAGE, false},
912
+ {"false", 0.0, EVAL_ERROR_REMAINING_GARBAGE, false},
913
+};
914
+
915
+// Test cases for variable names with spaces
916
+static TestCase variable_space_tests[] = {
917
+ // Testing $var syntax (can't have spaces)
918
+ {"$this", 50.0, EVAL_ERROR_OK, true},
919
+ {"$this variable", 0.0, EVAL_ERROR_REMAINING_GARBAGE, false}, // This should fail to parse as "variable" is considered garbage
920
+ {"$this + variable", 0.0, EVAL_ERROR_REMAINING_GARBAGE, false}, // Invalid syntax
921
+
922
+ // Testing ${var} syntax (can have spaces)
923
+ {"${this}", 50.0, EVAL_ERROR_OK, true},
924
+ {"${this variable}", 100.0, EVAL_ERROR_OK, true}, // This should parse as a single variable named 'this variable'
925
+
926
+ // Testing more complex expressions with spaced variable names
927
+ {"${this variable} * 2", 200.0, EVAL_ERROR_OK, true},
928
+ {"${this variable} > ${this}", 1.0, EVAL_ERROR_OK, true},
929
+ {"${this} + ${this variable}", 150.0, EVAL_ERROR_OK, true},
930
+
931
+ // Edge cases with missing or incomplete braces
932
+#ifdef USE_RE2C_LEMON_PARSER
933
+ {"${this variable", 0.0, EVAL_ERROR_REMAINING_GARBAGE, false}, // Missing closing brace is a syntax error
934
+#else
935
+ {"${this variable", 100.0, EVAL_ERROR_OK, true}, // Missing closing brace but parser accepts it
936
+#endif
937
+ {"${}", 0.0, EVAL_ERROR_REMAINING_GARBAGE, false}, // Empty brackets
938
+
939
+ // Using ${var} inside complex expressions
940
+ {"(${this variable} + ${this}) / 2", 75.0, EVAL_ERROR_OK, true},
941
+ {"(${this} > 0) ? ${this variable} : 0", 100.0, EVAL_ERROR_OK, true}, // Fixed the ternary syntax
942
+ {"$1var", 42.0, EVAL_ERROR_OK, true},
943
+ {"${1var}", 42.0, EVAL_ERROR_OK, true},
944
+ {"$_var", 76.0, EVAL_ERROR_OK, true},
945
+ {"${_var}", 76.0, EVAL_ERROR_OK, true},
946
+ {"$1.var", 77.0, EVAL_ERROR_OK, true},
947
+ {"${1.var}", 77.0, EVAL_ERROR_OK, true},
948
+ {"$var.1", 78.0, EVAL_ERROR_OK, true},
949
+ {"${var.1}", 78.0, EVAL_ERROR_OK, true},
950
+ {"$var-1", 0.0, EVAL_ERROR_UNKNOWN_VARIABLE, true},
951
+ {"${var-1}", 79.0, EVAL_ERROR_OK, true},
952
+};
953
+
954
+// Test cases for nested unary operators
955
+static TestCase nested_unary_tests[] = {
956
+ // Nested minus operator
957
+ {"-(-5)", 5.0, EVAL_ERROR_OK, true},
958
+ {"-(-0)", 0.0, EVAL_ERROR_OK, true},
959
+ {"-(-$negative)", -10.0, EVAL_ERROR_OK, true},
960
+ {"-(-$nan_var)", NAN, EVAL_ERROR_VALUE_IS_NAN, true},
961
+ {"-(-$inf_var)", INFINITY, EVAL_ERROR_VALUE_IS_INFINITE, true},
962
+
963
+ // Nested plus operator
964
+ {"+(-5)", -5.0, EVAL_ERROR_OK, true},
965
+ {"+(-0)", 0.0, EVAL_ERROR_OK, true},
966
+ {"+($negative)", -10.0, EVAL_ERROR_OK, true},
967
+ {"+($nan_var)", NAN, EVAL_ERROR_VALUE_IS_NAN, true},
968
+ {"+($inf_var)", INFINITY, EVAL_ERROR_VALUE_IS_INFINITE, true},
969
+ {"+(+5)", 5.0, EVAL_ERROR_OK, true},
970
+
971
+ // Nested not operator
972
+ {"!(!0)", 0.0, EVAL_ERROR_OK, true},
973
+ {"!(!1)", 1.0, EVAL_ERROR_OK, true},
974
+ {"!(!$zero)", 0.0, EVAL_ERROR_OK, true},
975
+ {"!(!$negative)", 1.0, EVAL_ERROR_OK, true},
976
+ {"!(!$nan_var)", 0.0, EVAL_ERROR_OK, true},
977
+ {"!(!$inf_var)", 1.0, EVAL_ERROR_OK, true},
978
+
979
+ // Multiple nested unary operators
980
+ {"-(-(-5))", -5.0, EVAL_ERROR_OK, true},
981
+ {"+(-(-5))", 5.0, EVAL_ERROR_OK, true},
982
+ {"-(-(-(-5)))", 5.0, EVAL_ERROR_OK, true},
983
+ {"!(!(!0))", 1.0, EVAL_ERROR_OK, true},
984
+ {"!(!(!1))", 0.0, EVAL_ERROR_OK, true},
985
+
986
+ // Nested abs function
987
+ {"abs(abs(-5))", 5.0, EVAL_ERROR_OK, true},
988
+ {"abs(-abs(-5))", 5.0, EVAL_ERROR_OK, true}, // Now fixed
989
+ {"abs(abs($negative))", 10.0, EVAL_ERROR_OK, true},
990
+ {"abs(abs($nan_var))", NAN, EVAL_ERROR_VALUE_IS_NAN, true},
991
+ {"abs(abs($inf_var))", INFINITY, EVAL_ERROR_VALUE_IS_INFINITE, true},
992
+
993
+ // Mixed unary operators - fix the expected value for abs(!1)
994
+ // For now, let's correct the test case for abs(!1)
995
+ {"abs(-(-5))", 5.0, EVAL_ERROR_OK, true},
996
+ {"abs(+(-5))", 5.0, EVAL_ERROR_OK, true},
997
+ {"abs(!0)", 1.0, EVAL_ERROR_OK, true},
998
+ {"abs(!1)", 0.0, EVAL_ERROR_OK, true}, // Changed from 1.0 to 0.0 because !1 is 0, abs(0) is 0
999
+ {"-(!0)", -1.0, EVAL_ERROR_OK, true},
1000
+ {"-(!1)", 0.0, EVAL_ERROR_OK, true},
1001
+ {"+(!0)", 1.0, EVAL_ERROR_OK, true},
1002
+ {"+(!1)", 0.0, EVAL_ERROR_OK, true},
1003
+
1004
+ // Complex nested expressions
1005
+ {"-(5 + -3)", -2.0, EVAL_ERROR_OK, true},
1006
+ {"+(5 + -3)", 2.0, EVAL_ERROR_OK, true},
1007
+ {"!(5 > 3)", 0.0, EVAL_ERROR_OK, true},
1008
+ {"!!(5 > 3)", 1.0, EVAL_ERROR_OK, true},
1009
+ {"abs(-(5 - 10))", 5.0, EVAL_ERROR_OK, true},
1010
+ {"-abs(-(5 - 10))", -5.0, EVAL_ERROR_OK, true}, // Now fixed
1011
+};
1012
+
1013
+// Test cases for real-world expressions from the dataset
1014
+static TestCase real_world_tests[] = {
1015
+ // Expressions with nested ternary operators and status comparisons
1016
+ {"$10m_acquiring_requests >= 50 && $this < (($status == $CRITICAL) ? (80) : (50))", 0.0, EVAL_ERROR_OK, true},
1017
+ {"$10m_acquiring_requests >= 50 && $this < (($status == $CRITICAL) ? (95) : (85))", 1.0, EVAL_ERROR_OK, true},
1018
+ {"$10m_acquiring_requests >= 50 && $this < (($status >= $WARNING) ? (90) : (75))", 1.0, EVAL_ERROR_OK, true},
1019
+ {"$10m_acquiring_requests >= 50 && $this < (($status >= $WARNING) ? (99) : (95))", 1.0, EVAL_ERROR_OK, true},
1020
+
1021
+ // Expressions with nested ternary operators and varying syntax
1022
+ {"($10m_acquiring_requests > 120) ? ($this > (($status == $CRITICAL) ? ( 2 ) : ( 5 )) ) : ( 0 )", 0.0, EVAL_ERROR_OK, true},
1023
+ {"($10m_acquiring_requests > 120) ? ($this < (($status == $CRITICAL) ? ( 85 ) : ( 75 )) ) : ( 0 )", 0.0, EVAL_ERROR_OK, true},
1024
+ {"($10m_acquiring_requests > 120) ? ($this > (($status >= $WARNING) ? ( 10 ) : ( 30 )) ) : ( 0 )", 0.0, EVAL_ERROR_OK, true},
1025
+ {"($10m_acquiring_requests > 120) ? ($this > (($status >= $WARNING ) ? ( 1 ) : ( 20 )) ) : ( 0 )", 0.0, EVAL_ERROR_OK, true},
1026
+
1027
+ // Expressions with whitespace variations
1028
+ {"($10m_acquiring_requests > 120) ? ($this > (($status >= $WARNING ) ? ( 1 ) : ( 20 )) ) : ( 0 )", 0.0, EVAL_ERROR_OK, true},
1029
+ {"($10m_acquiring_requests>120)?($this>(($status>=$WARNING)?(1):(20))):(0)", 0.0, EVAL_ERROR_OK, true},
1030
+
1031
+ // Complex variable expressions with hierarchical variables
1032
+ {"$avail * 100 / ($system.ram.used + $system.ram.cached + $system.ram.free + $system.ram.buffers)", 25.0, EVAL_ERROR_OK, true},
1033
+ {"($active + $wired + $laundry + $buffers - $used_ram_to_ignore) * 100 / ($active + $wired + $laundry + $buffers - $used_ram_to_ignore + $cache + $free + $inactive)", 51.111111, EVAL_ERROR_OK, true},
1034
+
1035
+ // CPU Core variables with spaces in braced syntax
1036
+ {"(${Core 0} + ${Core 1} + ${Core 2} + ${Core 3}) / 4", 28.75, EVAL_ERROR_OK, true},
1037
+ {"${Core 0} > 15", 1.0, EVAL_ERROR_OK, true},
1038
+ {"${Core 0} > 15 OR ${Core 1} > 15 OR ${Core 02} > 15 OR ${Core 3} > 15", 1.0, EVAL_ERROR_OK, true},
1039
+ {"${Core 0} > 15 OR ${Core 1} > 15 OR ${Core 02} > 15 OR ${Core 3} > 60", 1.0, EVAL_ERROR_OK, true},
1040
+ {"${Core 0} > 15 OR $Core1 > 55 OR $Core2 > 55 OR $Core3 > 55", 1.0, EVAL_ERROR_OK, true},
1041
+
1042
+ // NaN checking patterns
1043
+ {"(($1hour_packet_drops_inbound != nan AND $this > 0) ? ($1hour_packet_drops_inbound * 100 / $this) : (0))", 500.0, EVAL_ERROR_OK, true},
1044
+ {"(($1hour_packet_drops_outbound != nan AND $this > 0) ? ($1hour_packet_drops_outbound * 100 / $this) : (0))", 300.0, EVAL_ERROR_OK, true},
1045
+ {"(($1m_ipv4_udp_receive_buffer_errors != nan AND $this > 30000) ? ($1m_ipv4_udp_receive_buffer_errors * 100 / $this) : (0))", 0.0, EVAL_ERROR_OK, true},
1046
+
1047
+ // Complex expressions with `or` and `and` lowercase keywords and nan/inf checks
1048
+ {"($active_processors == nan or $active_processors == 0) ? (nan) : (($active_processors < 2) ? (2) : ($active_processors))", 8.0, EVAL_ERROR_OK, true},
1049
+ {"($active_processors == nan or $active_processors == inf or $active_processors < 2) ? (2) : ($active_processors)", 8.0, EVAL_ERROR_OK, true},
1050
+ {"($active_processors == nan or $active_processors == inf or $active_processors < 2) ? (2) : ($active_processors / 1.2)", 6.666666666666667, EVAL_ERROR_OK, true},
1051
+
1052
+ // Time-based expressions with variable comparisons
1053
+ {"$last_collected_t < $now - 60", 0.0, EVAL_ERROR_OK, true},
1054
+ {"$last_scrub > (15*60*60)", 0.0, EVAL_ERROR_OK, true},
1055
+
1056
+ // Floating point operations
1057
+ {"$mem > (($status == $CRITICAL) ? ($tcp_mem_pressure) : ($tcp_mem_high * 0.9))", 1.0, EVAL_ERROR_OK, true},
1058
+ {"$mem > (($status >= $WARNING) ? ($tcp_mem_pressure * 0.8) : ($tcp_mem_pressure))", 1.0, EVAL_ERROR_OK, true},
1059
+
1060
+ // Memory expressions with various notations and hierarchical variables
1061
+ {"$avail * 100 / ($system.ram.free + $system.ram.active + $system.ram.inactive + $system.ram.wired + $system.ram.cache + $system.ram.laundry + $system.ram.buffers)", 20.212766, EVAL_ERROR_OK, true},
1062
+ {"$avail * 100 / ($system.ram.used + $system.ram.cached + $system.ram.free + $system.ram.buffers + $system.ram.swap)", 19.791667, EVAL_ERROR_OK, true},
1063
+
1064
+ // Pattern usages of abs() function
1065
+ {"($this != 0) || ($status == $CRITICAL && abs($sent) == 0)", 1.0, EVAL_ERROR_OK, true},
1066
+ {"abs($bandwidth_1m_avg_of_now - $bandwidth_1m_avg_of_previous_1m) * 100 / $bandwidth_1m_avg_of_previous_1m", 5.0, EVAL_ERROR_OK, true},
1067
+ {"abs($offset)", 5.0, EVAL_ERROR_OK, true},
1068
+ {"abs($sent) * 100 / abs($buffered)", 200.0, EVAL_ERROR_OK, true},
1069
+
1070
+ // Label syntax
1071
+ {"(${label:host} != \"wg-manage-lte\") AND ($this > $green OR $this > $red)", 0.0, EVAL_ERROR_REMAINING_GARBAGE, false},
1072
+
1073
+ // Used with pidmax (common system metrics pattern)
1074
+ {"$active * 100 / $pidmax", 4.577637, EVAL_ERROR_OK, true},
1075
+ {"$arrays * 100 / $ipc.semaphores.arrays.max", 50.0, EVAL_ERROR_OK, true},
1076
+ {"$arrays * 100 / $ipc_semaphores_arrays_max", 50.0, EVAL_ERROR_OK, true},
1077
+
1078
+ // Basic patterns with lowercase logical operators
1079
+ {"$netdata.uptime.uptime > 30 AND $this > 0 and $this < 24", 0.0, EVAL_ERROR_OK, true},
1080
+ {"($this > $green OR $var1 > $red) and $this > 2", 1.0, EVAL_ERROR_OK, true},
1081
+
1082
+ // Expressions with word operators and comparisons
1083
+ {"$var1 > 40 AND $var2 < 30", 1.0, EVAL_ERROR_OK, true},
1084
+ {"$var1 > 40 OR $var2 < 30", 1.0, EVAL_ERROR_OK, true},
1085
+ {"NOT($var1 < 40 AND $var2 > 20)", 1.0, EVAL_ERROR_OK, true},
1086
+
1087
+ // Mixed symbol and word operators
1088
+ {"$var1 > 40 AND ($var2 < 30 || $this > 45)", 1.0, EVAL_ERROR_OK, true},
1089
+ {"($var1 > 30 && $var2 < 30) OR $this > 45", 1.0, EVAL_ERROR_OK, true},
1090
+};
1091
+
1092
+// Define the test groups
1093
+static TestGroup test_groups[] = {
1094
+ {"Arithmetic Tests", arithmetic_tests, ARRAY_SIZE(arithmetic_tests)},
1095
+ {"Comparison Tests", comparison_tests, ARRAY_SIZE(comparison_tests)},
1096
+ {"Logical Tests", logical_tests, ARRAY_SIZE(logical_tests)},
1097
+ {"Variable Tests", variable_tests, ARRAY_SIZE(variable_tests)},
1098
+ {"Variable Space Tests", variable_space_tests, ARRAY_SIZE(variable_space_tests)},
1099
+ {"Function Tests", function_tests, ARRAY_SIZE(function_tests)},
1100
+ {"Special Value Tests", special_value_tests, ARRAY_SIZE(special_value_tests)},
1101
+ {"Complex Expression Tests", complex_tests, ARRAY_SIZE(complex_tests)},
1102
+ {"Edge Case Tests", edge_case_tests, ARRAY_SIZE(edge_case_tests)},
1103
+ {"Operator Precedence Tests", precedence_tests, ARRAY_SIZE(precedence_tests)},
1104
+ {"Parentheses Tests", parentheses_tests, ARRAY_SIZE(parentheses_tests)},
1105
+ {"Nested Unary Tests", nested_unary_tests, ARRAY_SIZE(nested_unary_tests)},
1106
+ {"Real-World Expression Tests", real_world_tests, ARRAY_SIZE(real_world_tests)},
1107
+ {"API Function Tests", api_function_tests, ARRAY_SIZE(api_function_tests)},
1108
+ {"Number Overflow Tests", overflow_tests, ARRAY_SIZE(overflow_tests)},
1109
+ {"Combined Complex Expressions", combined_tests, ARRAY_SIZE(combined_tests)},
1110
+ {"Crash Tests", crash_tests, ARRAY_SIZE(crash_tests)},
1111
+};
1112
+
1113
+int eval_unittest(void) {
1114
+ // Test cases for basic arithmetic operations
1115
+
1116
+ // Run all test groups
1117
+ int total_passed = 0;
1118
+ int total_failed = 0;
1119
+ int total_tests = 0;
1120
+
1121
+#ifdef USE_RE2C_LEMON_PARSER
1122
+ printf("Starting comprehensive evaluation tests using RE2C/LEMON PARSER\n");
1123
+#else
1124
+ printf("Starting comprehensive evaluation tests using RECURSIVE DESCENT PARSER\n");
1125
+#endif
1126
+
1127
+ for (size_t i = 0; i < ARRAY_SIZE(test_groups); i++) {
1128
+ run_test_group(&test_groups[i]);
1129
+
1130
+ int group_tests = test_groups[i].test_count;
1131
+ int group_passed = 0;
1132
+ int group_failed = 0;
1133
+
1134
+ for (int j = 0; j < group_tests; j++) {
1135
+ TestCase *tc = &test_groups[i].test_cases[j];
1136
+ const char *failed_at = NULL;
1137
+ int error = 0;
1138
+
1139
+ // Try to parse the expression
1140
+ EVAL_EXPRESSION *exp = expression_parse(tc->expression, &failed_at, &error);
1141
+
1142
+ // Check if parsing succeeded as expected
1143
+ bool test_failed = false;
1144
+
1145
+ if (tc->should_parse && !exp) {
1146
+ test_failed = true;
1147
+ }
1148
+ else if (!tc->should_parse && exp) {
1149
+ test_failed = true;
1150
+ }
1151
+
1152
+ // If the expression parsed successfully, evaluate it
1153
+ if (exp) {
1154
+ // Set up the variable lookup callback
1155
+ expression_set_variable_lookup_callback(exp, test_variable_lookup, NULL);
1156
+
1157
+ // Evaluate the expression
1158
+ expression_evaluate(exp);
1159
+
1160
+ // Check if there was an error during evaluation
1161
+ if (tc->expected_error != EVAL_ERROR_OK && exp->error == EVAL_ERROR_OK) {
1162
+ test_failed = true;
1163
+ }
1164
+ else if (tc->expected_error == EVAL_ERROR_OK && exp->error != EVAL_ERROR_OK) {
1165
+ test_failed = true;
1166
+ }
1167
+ else if (tc->expected_error != EVAL_ERROR_OK && exp->error != tc->expected_error) {
1168
+ test_failed = true;
1169
+ }
1170
+
1171
+ // Check the evaluation result
1172
+ if (tc->expected_error == EVAL_ERROR_OK) {
1173
+ if (isnan(tc->expected_result) && !isnan(exp->result)) {
1174
+ test_failed = true;
1175
+ }
1176
+ else if (isinf(tc->expected_result) && !isinf(exp->result)) {
1177
+ test_failed = true;
1178
+ }
1179
+ else if (!isnan(tc->expected_result) && !isinf(tc->expected_result) &&
1180
+ !isnan(exp->result) && !isinf(exp->result) &&
1181
+ fabs(tc->expected_result - exp->result) > 0.000001) {
1182
+ test_failed = true;
1183
+ }
1184
+ }
1185
+
1186
+ // Clean up
1187
+ expression_free(exp);
1188
+ }
1189
+
1190
+ if (test_failed) {
1191
+ group_failed++;
1192
+ } else {
1193
+ group_passed++;
1194
+ }
1195
+ }
1196
+
1197
+ total_passed += group_passed;
1198
+ total_failed += group_failed;
1199
+ total_tests += group_tests;
1200
+ }
1201
+
1202
+ printf("\n========== OVERALL TEST SUMMARY ==========\n");
1203
+ printf("Total tests: %d\n", total_tests);
1204
+ printf("Passed: %d (%.1f%%)\n", total_passed, (float)total_passed / total_tests * 100);
1205
+ printf("Failed: %d (%.1f%%)\n", total_failed, (float)total_failed / total_tests * 100);
1206
+
1207
+ return total_failed > 0 ? 1 : 0;
1208
+}
src/libnetdata/eval/eval-utils.c
new
+365
@@ -0,0 +1,365 @@
1
+// SPDX-License-Identifier: GPL-3.0-or-later
2
+
3
+#include "../libnetdata.h"
4
+#include "eval-internal.h"
5
+
6
+// ----------------------------------------------------------------------------
7
+// memory management
8
+
9
+EVAL_NODE *eval_node_alloc(int count) {
10
+ static int id = 1;
11
+
12
+ EVAL_NODE *op = callocz(1, sizeof(EVAL_NODE) + (sizeof(EVAL_VALUE) * count));
13
+
14
+ op->id = id++;
15
+ op->operator = EVAL_OPERATOR_NOP;
16
+ op->precedence = 0; // Will be set based on the operator
17
+ op->count = count;
18
+ return op;
19
+}
20
+
21
+void eval_node_set_value_to_node(EVAL_NODE *op, int pos, EVAL_NODE *value) {
22
+ if(pos >= op->count)
23
+ fatal("Invalid request to set position %d of OPERAND that has only %d values", pos + 1, op->count + 1);
24
+
25
+ op->ops[pos].type = EVAL_VALUE_EXPRESSION;
26
+ op->ops[pos].expression = value;
27
+}
28
+
29
+void eval_node_set_value_to_constant(EVAL_NODE *op, int pos, NETDATA_DOUBLE value) {
30
+ if(pos >= op->count)
31
+ fatal("Invalid request to set position %d of OPERAND that has only %d values", pos + 1, op->count + 1);
32
+
33
+ op->ops[pos].type = EVAL_VALUE_NUMBER;
34
+ op->ops[pos].number = value;
35
+}
36
+
37
+void eval_node_set_value_to_variable(EVAL_NODE *op, int pos, const char *variable) {
38
+ if(pos >= op->count)
39
+ fatal("Invalid request to set position %d of OPERAND that has only %d values", pos + 1, op->count + 1);
40
+
41
+ op->ops[pos].type = EVAL_VALUE_VARIABLE;
42
+ op->ops[pos].variable = callocz(1, sizeof(EVAL_VARIABLE));
43
+ op->ops[pos].variable->name = string_strdupz(variable);
44
+}
45
+
46
+void eval_variable_free(EVAL_VARIABLE *v) {
47
+ string_freez(v->name);
48
+ freez(v);
49
+}
50
+
51
+void eval_value_free(EVAL_VALUE *v) {
52
+ switch(v->type) {
53
+ case EVAL_VALUE_EXPRESSION:
54
+ eval_node_free(v->expression);
55
+ break;
56
+
57
+ case EVAL_VALUE_VARIABLE:
58
+ eval_variable_free(v->variable);
59
+ break;
60
+
61
+ default:
62
+ break;
63
+ }
64
+}
65
+
66
+void eval_node_free(EVAL_NODE *op) {
67
+ if(!op) return;
68
+
69
+ if(op->count) {
70
+ int i;
71
+ for(i = op->count - 1; i >= 0 ;i--)
72
+ eval_value_free(&op->ops[i]);
73
+ }
74
+
75
+ freez(op);
76
+}
77
+
78
+// ----------------------------------------------------------------------------
79
+// parsed-as generation
80
+
81
+void print_parsed_as_variable(BUFFER *out, EVAL_VARIABLE *v, int *error __maybe_unused) {
82
+ buffer_sprintf(out, "${%s}", string2str(v->name));
83
+}
84
+
85
+void print_parsed_as_constant(BUFFER *out, NETDATA_DOUBLE n) {
86
+ if(unlikely(isnan(n))) {
87
+ buffer_strcat(out, "nan");
88
+ return;
89
+ }
90
+
91
+ if(unlikely(isinf(n))) {
92
+ buffer_strcat(out, "inf");
93
+ return;
94
+ }
95
+
96
+ char b[100+1], *s;
97
+ snprintfz(b, sizeof(b) - 1, NETDATA_DOUBLE_FORMAT, n);
98
+
99
+ s = &b[strlen(b) - 1];
100
+ while(s > b && *s == '0') {
101
+ *s ='\0';
102
+ s--;
103
+ }
104
+
105
+ if(s > b && *s == '.')
106
+ *s = '\0';
107
+
108
+ buffer_strcat(out, b);
109
+}
110
+
111
+void print_parsed_as_value(BUFFER *out, EVAL_VALUE *v, int *error) {
112
+ switch(v->type) {
113
+ case EVAL_VALUE_EXPRESSION:
114
+ print_parsed_as_node(out, v->expression, error);
115
+ break;
116
+
117
+ case EVAL_VALUE_NUMBER:
118
+ print_parsed_as_constant(out, v->number);
119
+ break;
120
+
121
+ case EVAL_VALUE_VARIABLE:
122
+ print_parsed_as_variable(out, v->variable, error);
123
+ break;
124
+
125
+ default:
126
+ *error = EVAL_ERROR_INVALID_VALUE;
127
+ break;
128
+ }
129
+}
130
+
131
+void print_parsed_as_node(BUFFER *out, EVAL_NODE *op, int *error) {
132
+ extern struct operator operators[];
133
+
134
+ if(unlikely(!op)) {
135
+ buffer_strcat(out, "NULL");
136
+ *error = EVAL_ERROR_INVALID_VALUE;
137
+ return;
138
+ }
139
+
140
+ if(unlikely(op->count != operators[op->operator].parameters)) {
141
+ buffer_sprintf(out, "INVALID PARAMETERS (operator requires %d, but node has %d)", operators[op->operator].parameters, op->count);
142
+ *error = EVAL_ERROR_INVALID_NUMBER_OF_OPERANDS;
143
+ return;
144
+ }
145
+
146
+ if(operators[op->operator].isfunction) {
147
+ buffer_strcat(out, operators[op->operator].print_as);
148
+ buffer_strcat(out, "(");
149
+ print_parsed_as_value(out, &op->ops[0], error);
150
+ buffer_strcat(out, ")");
151
+ return;
152
+ }
153
+
154
+ if(op->operator == EVAL_OPERATOR_NOP) {
155
+ print_parsed_as_value(out, &op->ops[0], error);
156
+ return;
157
+ }
158
+
159
+ if(op->operator == EVAL_OPERATOR_IF_THEN_ELSE) {
160
+ print_parsed_as_value(out, &op->ops[0], error);
161
+ buffer_strcat(out, " ? ");
162
+ print_parsed_as_value(out, &op->ops[1], error);
163
+ buffer_strcat(out, " : ");
164
+ print_parsed_as_value(out, &op->ops[2], error);
165
+ return;
166
+ }
167
+
168
+ if(op->count == 1) {
169
+ buffer_strcat(out, operators[op->operator].print_as);
170
+ buffer_strcat(out, "(");
171
+ print_parsed_as_value(out, &op->ops[0], error);
172
+ buffer_strcat(out, ")");
173
+ return;
174
+ }
175
+
176
+ buffer_strcat(out, "(");
177
+ print_parsed_as_value(out, &op->ops[0], error);
178
+ buffer_strcat(out, " ");
179
+ buffer_strcat(out, operators[op->operator].print_as);
180
+ buffer_strcat(out, " ");
181
+ print_parsed_as_value(out, &op->ops[1], error);
182
+ buffer_strcat(out, ")");
183
+}
184
+
185
+// ----------------------------------------------------------------------------
186
+// public API utility functions
187
+
188
+const char *expression_strerror(int error) {
189
+ switch(error) {
190
+ case EVAL_ERROR_OK:
191
+ return "success";
192
+
193
+ case EVAL_ERROR_MISSING_CLOSE_SUBEXPRESSION:
194
+ return "missing closing parenthesis";
195
+
196
+ case EVAL_ERROR_UNKNOWN_OPERAND:
197
+ return "unknown operand";
198
+
199
+ case EVAL_ERROR_MISSING_OPERAND:
200
+ return "expected operand";
201
+
202
+ case EVAL_ERROR_MISSING_OPERATOR:
203
+ return "expected operator";
204
+
205
+ case EVAL_ERROR_REMAINING_GARBAGE:
206
+ return "remaining characters after expression";
207
+
208
+ case EVAL_ERROR_INVALID_VALUE:
209
+ return "invalid value structure - internal error";
210
+
211
+ case EVAL_ERROR_INVALID_NUMBER_OF_OPERANDS:
212
+ return "wrong number of operands for operation - internal error";
213
+
214
+ case EVAL_ERROR_VALUE_IS_NAN:
215
+ return "value is unset";
216
+
217
+ case EVAL_ERROR_VALUE_IS_INFINITE:
218
+ return "computed value is infinite";
219
+
220
+ case EVAL_ERROR_UNKNOWN_VARIABLE:
221
+ return "undefined variable";
222
+
223
+ case EVAL_ERROR_IF_THEN_ELSE_MISSING_ELSE:
224
+ return "missing second sub-expression of inline conditional";
225
+
226
+ default:
227
+ return "unknown error";
228
+ }
229
+}
230
+
231
+const char *expression_source(EVAL_EXPRESSION *expression) {
232
+ if(!expression)
233
+ return string2str(NULL);
234
+
235
+ return string2str(expression->source);
236
+}
237
+
238
+const char *expression_parsed_as(EVAL_EXPRESSION *expression) {
239
+ if(!expression)
240
+ return string2str(NULL);
241
+
242
+ return string2str(expression->parsed_as);
243
+}
244
+
245
+const char *expression_error_msg(EVAL_EXPRESSION *expression) {
246
+ if(!expression || !expression->error_msg)
247
+ return "";
248
+
249
+ return buffer_tostring(expression->error_msg);
250
+}
251
+
252
+NETDATA_DOUBLE expression_result(EVAL_EXPRESSION *expression) {
253
+ if(!expression)
254
+ return NAN;
255
+
256
+ return expression->result;
257
+}
258
+
259
+void expression_set_variable_lookup_callback(EVAL_EXPRESSION *expression, eval_expression_variable_lookup_t cb, void *data) {
260
+ if(!expression)
261
+ return;
262
+
263
+ expression->variable_lookup_cb = cb;
264
+ expression->variable_lookup_cb_data = data;
265
+}
266
+
267
+static size_t expression_hardcode_node_variable(EVAL_NODE *node, STRING *variable, NETDATA_DOUBLE value) {
268
+ size_t matches = 0;
269
+
270
+ for(int i = 0; i < node->count; i++) {
271
+ switch(node->ops[i].type) {
272
+ case EVAL_VALUE_NUMBER:
273
+ case EVAL_VALUE_INVALID:
274
+ break;
275
+
276
+ case EVAL_VALUE_VARIABLE:
277
+ if(node->ops[i].variable->name == variable) {
278
+ string_freez(node->ops[i].variable->name);
279
+ freez(node->ops[i].variable);
280
+ node->ops[i].type = EVAL_VALUE_NUMBER;
281
+ node->ops[i].number = value;
282
+ matches++;
283
+ }
284
+ break;
285
+
286
+ case EVAL_VALUE_EXPRESSION:
287
+ matches += expression_hardcode_node_variable(node->ops[i].expression, variable, value);
288
+ break;
289
+ }
290
+ }
291
+
292
+ return matches;
293
+}
294
+
295
+void expression_hardcode_variable(EVAL_EXPRESSION *expression, STRING *variable, NETDATA_DOUBLE value) {
296
+ if (!expression || !variable || isnan(value))
297
+ return;
298
+
299
+ size_t matches = expression_hardcode_node_variable(expression->nodes, variable, value);
300
+ if (matches) {
301
+ char replace[1024];
302
+ snprintfz(replace, sizeof(replace), NETDATA_DOUBLE_FORMAT_AUTO, value);
303
+ size_t replace_len = strlen(replace);
304
+
305
+ size_t source_len = string_strlen(expression->source);
306
+ const char *source_str = string2str(expression->source);
307
+
308
+ // Allocate enough space to accommodate all replacements.
309
+ char buf[source_len + 1 + matches * (replace_len + 1)];
310
+
311
+ char find1[string_strlen(variable) + 1 + 1];
312
+ snprintfz(find1, sizeof(find1), "$%s", string2str(variable));
313
+ size_t find1_len = strlen(find1);
314
+
315
+ char find2[string_strlen(variable) + 1 + 3];
316
+ snprintfz(find2, sizeof(find2), "${%s}", string2str(variable));
317
+ size_t find2_len = strlen(find2);
318
+
319
+ size_t found = 0; (void)found;
320
+ char *buf_ptr = buf;
321
+ const char *source_ptr = source_str;
322
+
323
+ while (*source_ptr) {
324
+ char *s1 = strstr(source_ptr, find1);
325
+ char *s2 = strstr(source_ptr, find2);
326
+
327
+ char *s = s1;
328
+ size_t len = find1_len;
329
+ if (s2 && (!s1 || s2 < s1)) {
330
+ s = s2;
331
+ len = find2_len;
332
+ }
333
+
334
+ if (s) {
335
+ // Skip this check since the function has been moved to eval-parser.c
336
+ if (s == s1) {
337
+ // Move past the variable if it's part of a larger word.
338
+ source_ptr = s + len;
339
+ continue;
340
+ }
341
+
342
+ // Copy the part before the variable.
343
+ memcpy(buf_ptr, source_ptr, s - source_ptr);
344
+ buf_ptr += (s - source_ptr);
345
+
346
+ // Copy the replacement.
347
+ memcpy(buf_ptr, replace, replace_len);
348
+ buf_ptr += replace_len;
349
+ *buf_ptr = '\0';
350
+
351
+ // Move the source pointer past the replaced variable.
352
+ source_ptr = s + len;
353
+ found++;
354
+ } else {
355
+ // Copy the rest of the string if no more variables are found.
356
+ strcpy(buf_ptr, source_ptr);
357
+ break;
358
+ }
359
+ }
360
+
361
+ // Update the expression source with the new string.
362
+ string_freez(expression->source);
363
+ expression->source = string_strdupz(buf);
364
+ }
365
+}
src/libnetdata/eval/eval.c
+11
-1284
@@ -1,1287 +1,14 @@
1
// SPDX-License-Identifier: GPL-3.0-or-later
2
3
-#include "../libnetdata.h"
4
-
5
-typedef enum __attribute__((packed)) {
6
- EVAL_VALUE_INVALID = 0,
7
- EVAL_VALUE_NUMBER,
8
- EVAL_VALUE_VARIABLE,
9
- EVAL_VALUE_EXPRESSION
10
-} EVAL_VALUE_TYPE;
11
-
12
-// ----------------------------------------------------------------------------
13
-// data structures for storing the parsed expression in memory
14
-
15
-typedef struct eval_variable {
16
- STRING *name;
17
- struct eval_variable *next;
18
-} EVAL_VARIABLE;
19
-
20
-typedef struct eval_value {
21
- EVAL_VALUE_TYPE type;
22
-
23
- union {
24
- NETDATA_DOUBLE number;
25
- EVAL_VARIABLE *variable;
26
- struct eval_node *expression;
27
- };
28
-} EVAL_VALUE;
29
-
30
-typedef struct eval_node {
31
- int id;
32
- unsigned char operator;
33
- int precedence;
34
-
35
- int count;
36
- EVAL_VALUE ops[];
37
-} EVAL_NODE;
38
-
39
-struct eval_expression {
40
- STRING *source;
41
- STRING *parsed_as;
42
-
43
- NETDATA_DOUBLE result;
44
-
45
- int error;
46
- BUFFER *error_msg;
47
-
48
- EVAL_NODE *nodes;
49
-
50
- void *variable_lookup_cb_data;
51
- eval_expression_variable_lookup_t variable_lookup_cb;
52
-};
53
-
54
-// these are used for EVAL_NODE.operator
55
-// they are used as internal IDs to identify an operator
56
-// THEY ARE NOT USED FOR PARSING OPERATORS LIKE THAT
57
-#define EVAL_OPERATOR_NOP '\0'
58
-#define EVAL_OPERATOR_EXPRESSION_OPEN '('
59
-#define EVAL_OPERATOR_EXPRESSION_CLOSE ')'
60
-#define EVAL_OPERATOR_NOT '!'
61
-#define EVAL_OPERATOR_PLUS '+'
62
-#define EVAL_OPERATOR_MINUS '-'
63
-#define EVAL_OPERATOR_AND '&'
64
-#define EVAL_OPERATOR_OR '|'
65
-#define EVAL_OPERATOR_GREATER_THAN_OR_EQUAL 'G'
66
-#define EVAL_OPERATOR_LESS_THAN_OR_EQUAL 'L'
67
-#define EVAL_OPERATOR_NOT_EQUAL '~'
68
-#define EVAL_OPERATOR_EQUAL '='
69
-#define EVAL_OPERATOR_LESS '<'
70
-#define EVAL_OPERATOR_GREATER '>'
71
-#define EVAL_OPERATOR_MULTIPLY '*'
72
-#define EVAL_OPERATOR_DIVIDE '/'
73
-#define EVAL_OPERATOR_SIGN_PLUS 'P'
74
-#define EVAL_OPERATOR_SIGN_MINUS 'M'
75
-#define EVAL_OPERATOR_ABS 'A'
76
-#define EVAL_OPERATOR_IF_THEN_ELSE '?'
77
-
78
-// ----------------------------------------------------------------------------
79
-// forward function definitions
80
-
81
-static inline void eval_node_free(EVAL_NODE *op);
82
-static inline EVAL_NODE *parse_full_expression(const char **string, int *error);
83
-static inline EVAL_NODE *parse_one_full_operand(const char **string, int *error);
84
-static inline NETDATA_DOUBLE eval_node(EVAL_EXPRESSION *exp, EVAL_NODE *op, int *error);
85
-static inline void print_parsed_as_node(BUFFER *out, EVAL_NODE *op, int *error);
86
-static inline void print_parsed_as_constant(BUFFER *out, NETDATA_DOUBLE n);
87
-
88
-// ----------------------------------------------------------------------------
89
-// evaluation of expressions
90
-
91
-static inline NETDATA_DOUBLE eval_variable(EVAL_EXPRESSION *exp, EVAL_VARIABLE *v, int *error) {
92
- NETDATA_DOUBLE n;
93
-
94
- if(exp->variable_lookup_cb && exp->variable_lookup_cb(v->name, exp->variable_lookup_cb_data, &n)) {
95
- buffer_sprintf(exp->error_msg, "[ ${%s} = ", string2str(v->name));
96
- print_parsed_as_constant(exp->error_msg, n);
97
- buffer_strcat(exp->error_msg, " ] ");
98
- return n;
99
- }
100
-
101
- *error = EVAL_ERROR_UNKNOWN_VARIABLE;
102
- buffer_sprintf(exp->error_msg, "[ undefined variable '%s' ] ", string2str(v->name));
103
- return NAN;
104
-}
105
-
106
-static inline NETDATA_DOUBLE eval_value(EVAL_EXPRESSION *exp, EVAL_VALUE *v, int *error) {
107
- NETDATA_DOUBLE n;
108
-
109
- switch(v->type) {
110
- case EVAL_VALUE_EXPRESSION:
111
- n = eval_node(exp, v->expression, error);
112
- break;
113
-
114
- case EVAL_VALUE_NUMBER:
115
- n = v->number;
116
- break;
117
-
118
- case EVAL_VALUE_VARIABLE:
119
- n = eval_variable(exp, v->variable, error);
120
- break;
121
-
122
- default:
123
- *error = EVAL_ERROR_INVALID_VALUE;
124
- n = 0;
125
- break;
126
- }
127
-
128
- return n;
129
-}
130
-
131
-static inline int is_true(NETDATA_DOUBLE n) {
132
- if(isnan(n)) return 0;
133
- if(isinf(n)) return 1;
134
- if(n == 0) return 0;
135
- return 1;
136
-}
137
-
138
-NETDATA_DOUBLE eval_and(EVAL_EXPRESSION *exp, EVAL_NODE *op, int *error) {
139
- return is_true(eval_value(exp, &op->ops[0], error)) && is_true(eval_value(exp, &op->ops[1], error));
140
-}
141
-NETDATA_DOUBLE eval_or(EVAL_EXPRESSION *exp, EVAL_NODE *op, int *error) {
142
- return is_true(eval_value(exp, &op->ops[0], error)) || is_true(eval_value(exp, &op->ops[1], error));
143
-}
144
-NETDATA_DOUBLE eval_greater_than_or_equal(EVAL_EXPRESSION *exp, EVAL_NODE *op, int *error) {
145
- NETDATA_DOUBLE n1 = eval_value(exp, &op->ops[0], error);
146
- NETDATA_DOUBLE n2 = eval_value(exp, &op->ops[1], error);
147
- return isgreaterequal(n1, n2);
148
-}
149
-NETDATA_DOUBLE eval_less_than_or_equal(EVAL_EXPRESSION *exp, EVAL_NODE *op, int *error) {
150
- NETDATA_DOUBLE n1 = eval_value(exp, &op->ops[0], error);
151
- NETDATA_DOUBLE n2 = eval_value(exp, &op->ops[1], error);
152
- return islessequal(n1, n2);
153
-}
154
-NETDATA_DOUBLE eval_equal(EVAL_EXPRESSION *exp, EVAL_NODE *op, int *error) {
155
- NETDATA_DOUBLE n1 = eval_value(exp, &op->ops[0], error);
156
- NETDATA_DOUBLE n2 = eval_value(exp, &op->ops[1], error);
157
- if(isnan(n1) && isnan(n2)) return 1;
158
- if(isinf(n1) && isinf(n2)) return 1;
159
- if(isnan(n1) || isnan(n2)) return 0;
160
- if(isinf(n1) || isinf(n2)) return 0;
161
- return considered_equal_ndd(n1, n2);
162
-}
163
-NETDATA_DOUBLE eval_not_equal(EVAL_EXPRESSION *exp, EVAL_NODE *op, int *error) {
164
- return !eval_equal(exp, op, error);
165
-}
166
-NETDATA_DOUBLE eval_less(EVAL_EXPRESSION *exp, EVAL_NODE *op, int *error) {
167
- NETDATA_DOUBLE n1 = eval_value(exp, &op->ops[0], error);
168
- NETDATA_DOUBLE n2 = eval_value(exp, &op->ops[1], error);
169
- return isless(n1, n2);
170
-}
171
-NETDATA_DOUBLE eval_greater(EVAL_EXPRESSION *exp, EVAL_NODE *op, int *error) {
172
- NETDATA_DOUBLE n1 = eval_value(exp, &op->ops[0], error);
173
- NETDATA_DOUBLE n2 = eval_value(exp, &op->ops[1], error);
174
- return isgreater(n1, n2);
175
-}
176
-NETDATA_DOUBLE eval_plus(EVAL_EXPRESSION *exp, EVAL_NODE *op, int *error) {
177
- NETDATA_DOUBLE n1 = eval_value(exp, &op->ops[0], error);
178
- NETDATA_DOUBLE n2 = eval_value(exp, &op->ops[1], error);
179
- if(isnan(n1) || isnan(n2)) return NAN;
180
- if(isinf(n1) || isinf(n2)) return INFINITY;
181
- return n1 + n2;
182
-}
183
-NETDATA_DOUBLE eval_minus(EVAL_EXPRESSION *exp, EVAL_NODE *op, int *error) {
184
- NETDATA_DOUBLE n1 = eval_value(exp, &op->ops[0], error);
185
- NETDATA_DOUBLE n2 = eval_value(exp, &op->ops[1], error);
186
- if(isnan(n1) || isnan(n2)) return NAN;
187
- if(isinf(n1) || isinf(n2)) return INFINITY;
188
- return n1 - n2;
189
-}
190
-NETDATA_DOUBLE eval_multiply(EVAL_EXPRESSION *exp, EVAL_NODE *op, int *error) {
191
- NETDATA_DOUBLE n1 = eval_value(exp, &op->ops[0], error);
192
- NETDATA_DOUBLE n2 = eval_value(exp, &op->ops[1], error);
193
- if(isnan(n1) || isnan(n2)) return NAN;
194
- if(isinf(n1) || isinf(n2)) return INFINITY;
195
- return n1 * n2;
196
-}
197
-NETDATA_DOUBLE eval_divide(EVAL_EXPRESSION *exp, EVAL_NODE *op, int *error) {
198
- NETDATA_DOUBLE n1 = eval_value(exp, &op->ops[0], error);
199
- NETDATA_DOUBLE n2 = eval_value(exp, &op->ops[1], error);
200
- if(isnan(n1) || isnan(n2)) return NAN;
201
- if(isinf(n1) || isinf(n2)) return INFINITY;
202
- return n1 / n2;
203
-}
204
-NETDATA_DOUBLE eval_nop(EVAL_EXPRESSION *exp, EVAL_NODE *op, int *error) {
205
- return eval_value(exp, &op->ops[0], error);
206
-}
207
-NETDATA_DOUBLE eval_not(EVAL_EXPRESSION *exp, EVAL_NODE *op, int *error) {
208
- return !is_true(eval_value(exp, &op->ops[0], error));
209
-}
210
-NETDATA_DOUBLE eval_sign_plus(EVAL_EXPRESSION *exp, EVAL_NODE *op, int *error) {
211
- return eval_value(exp, &op->ops[0], error);
212
-}
213
-NETDATA_DOUBLE eval_sign_minus(EVAL_EXPRESSION *exp, EVAL_NODE *op, int *error) {
214
- NETDATA_DOUBLE n1 = eval_value(exp, &op->ops[0], error);
215
- if(isnan(n1)) return NAN;
216
- if(isinf(n1)) return INFINITY;
217
- return -n1;
218
-}
219
-NETDATA_DOUBLE eval_abs(EVAL_EXPRESSION *exp, EVAL_NODE *op, int *error) {
220
- NETDATA_DOUBLE n1 = eval_value(exp, &op->ops[0], error);
221
- if(isnan(n1)) return NAN;
222
- if(isinf(n1)) return INFINITY;
223
- return ABS(n1);
224
-}
225
-NETDATA_DOUBLE eval_if_then_else(EVAL_EXPRESSION *exp, EVAL_NODE *op, int *error) {
226
- if(is_true(eval_value(exp, &op->ops[0], error)))
227
- return eval_value(exp, &op->ops[1], error);
228
- else
229
- return eval_value(exp, &op->ops[2], error);
230
-}
231
-
232
-static struct operator {
233
- const char *print_as;
234
- char precedence;
235
- char parameters;
236
- char isfunction;
237
- NETDATA_DOUBLE (*eval)(EVAL_EXPRESSION *exp, EVAL_NODE *op, int *error);
238
-} operators[256] = {
239
- // this is a random access array
240
- // we always access it with a known EVAL_OPERATOR_X
241
-
242
- [EVAL_OPERATOR_AND] = { "&&", 2, 2, 0, eval_and },
243
- [EVAL_OPERATOR_OR] = { "||", 2, 2, 0, eval_or },
244
- [EVAL_OPERATOR_GREATER_THAN_OR_EQUAL] = { ">=", 3, 2, 0, eval_greater_than_or_equal },
245
- [EVAL_OPERATOR_LESS_THAN_OR_EQUAL] = { "<=", 3, 2, 0, eval_less_than_or_equal },
246
- [EVAL_OPERATOR_NOT_EQUAL] = { "!=", 3, 2, 0, eval_not_equal },
247
- [EVAL_OPERATOR_EQUAL] = { "==", 3, 2, 0, eval_equal },
248
- [EVAL_OPERATOR_LESS] = { "<", 3, 2, 0, eval_less },
249
- [EVAL_OPERATOR_GREATER] = { ">", 3, 2, 0, eval_greater },
250
- [EVAL_OPERATOR_PLUS] = { "+", 4, 2, 0, eval_plus },
251
- [EVAL_OPERATOR_MINUS] = { "-", 4, 2, 0, eval_minus },
252
- [EVAL_OPERATOR_MULTIPLY] = { "*", 5, 2, 0, eval_multiply },
253
- [EVAL_OPERATOR_DIVIDE] = { "/", 5, 2, 0, eval_divide },
254
- [EVAL_OPERATOR_NOT] = { "!", 6, 1, 0, eval_not },
255
- [EVAL_OPERATOR_SIGN_PLUS] = { "+", 6, 1, 0, eval_sign_plus },
256
- [EVAL_OPERATOR_SIGN_MINUS] = { "-", 6, 1, 0, eval_sign_minus },
257
- [EVAL_OPERATOR_ABS] = { "abs(",6,1, 1, eval_abs },
258
- [EVAL_OPERATOR_IF_THEN_ELSE] = { "?", 7, 3, 0, eval_if_then_else },
259
- [EVAL_OPERATOR_NOP] = { NULL, 8, 1, 0, eval_nop },
260
- [EVAL_OPERATOR_EXPRESSION_OPEN] = { NULL, 8, 1, 0, eval_nop },
261
-
262
- // this should exist in our evaluation list
263
- [EVAL_OPERATOR_EXPRESSION_CLOSE] = { NULL, 99, 1, 0, eval_nop }
264
-};
265
-
266
-#define eval_precedence(operator) (operators[(unsigned char)(operator)].precedence)
267
-
268
-static inline NETDATA_DOUBLE eval_node(EVAL_EXPRESSION *exp, EVAL_NODE *op, int *error) {
269
- if(unlikely(op->count != operators[op->operator].parameters)) {
270
- *error = EVAL_ERROR_INVALID_NUMBER_OF_OPERANDS;
271
- return 0;
272
- }
273
-
274
- NETDATA_DOUBLE n = operators[op->operator].eval(exp, op, error);
275
-
276
- return n;
277
-}
278
-
279
-// ----------------------------------------------------------------------------
280
-// parsed-as generation
281
-
282
-static inline void print_parsed_as_variable(BUFFER *out, EVAL_VARIABLE *v, int *error) {
283
- (void)error;
284
- buffer_sprintf(out, "${%s}", string2str(v->name));
285
-}
286
-
287
-static inline void print_parsed_as_constant(BUFFER *out, NETDATA_DOUBLE n) {
288
- if(unlikely(isnan(n))) {
289
- buffer_strcat(out, "nan");
290
- return;
291
- }
292
-
293
- if(unlikely(isinf(n))) {
294
- buffer_strcat(out, "inf");
295
- return;
296
- }
297
-
298
- char b[100+1], *s;
299
- snprintfz(b, sizeof(b) - 1, NETDATA_DOUBLE_FORMAT, n);
300
-
301
- s = &b[strlen(b) - 1];
302
- while(s > b && *s == '0') {
303
- *s ='\0';
304
- s--;
305
- }
306
-
307
- if(s > b && *s == '.')
308
- *s = '\0';
309
-
310
- buffer_strcat(out, b);
311
-}
312
-
313
-static inline void print_parsed_as_value(BUFFER *out, EVAL_VALUE *v, int *error) {
314
- switch(v->type) {
315
- case EVAL_VALUE_EXPRESSION:
316
- print_parsed_as_node(out, v->expression, error);
317
- break;
318
-
319
- case EVAL_VALUE_NUMBER:
320
- print_parsed_as_constant(out, v->number);
321
- break;
322
-
323
- case EVAL_VALUE_VARIABLE:
324
- print_parsed_as_variable(out, v->variable, error);
325
- break;
326
-
327
- default:
328
- *error = EVAL_ERROR_INVALID_VALUE;
329
- break;
330
- }
331
-}
332
-
333
-static inline void print_parsed_as_node(BUFFER *out, EVAL_NODE *op, int *error) {
334
- if(unlikely(op->count != operators[op->operator].parameters)) {
335
- *error = EVAL_ERROR_INVALID_NUMBER_OF_OPERANDS;
336
- return;
337
- }
338
-
339
- if(operators[op->operator].parameters == 1) {
340
-
341
- if(operators[op->operator].print_as)
342
- buffer_sprintf(out, "%s", operators[op->operator].print_as);
343
-
344
- //if(op->operator == EVAL_OPERATOR_EXPRESSION_OPEN)
345
- // buffer_strcat(out, "(");
346
-
347
- print_parsed_as_value(out, &op->ops[0], error);
348
-
349
- //if(op->operator == EVAL_OPERATOR_EXPRESSION_OPEN)
350
- // buffer_strcat(out, ")");
351
- }
352
-
353
- else if(operators[op->operator].parameters == 2) {
354
- buffer_strcat(out, "(");
355
- print_parsed_as_value(out, &op->ops[0], error);
356
-
357
- if(operators[op->operator].print_as)
358
- buffer_sprintf(out, " %s ", operators[op->operator].print_as);
359
-
360
- print_parsed_as_value(out, &op->ops[1], error);
361
- buffer_strcat(out, ")");
362
- }
363
- else if(op->operator == EVAL_OPERATOR_IF_THEN_ELSE && operators[op->operator].parameters == 3) {
364
- buffer_strcat(out, "(");
365
- print_parsed_as_value(out, &op->ops[0], error);
366
-
367
- if(operators[op->operator].print_as)
368
- buffer_sprintf(out, " %s ", operators[op->operator].print_as);
369
-
370
- print_parsed_as_value(out, &op->ops[1], error);
371
- buffer_strcat(out, " : ");
372
- print_parsed_as_value(out, &op->ops[2], error);
373
- buffer_strcat(out, ")");
374
- }
375
-
376
- if(operators[op->operator].isfunction)
377
- buffer_strcat(out, ")");
378
-}
379
-
380
-// ----------------------------------------------------------------------------
381
-// parsing expressions
382
-
383
-// skip spaces
384
-static inline void skip_spaces(const char **string) {
385
- const char *s = *string;
386
- while(isspace((uint8_t)*s)) s++;
387
- *string = s;
388
-}
389
-
390
-//static inline int old_isoperatorterm_word(const char s) {
391
-// if (isspace(s) || s == '(' || s == '$' || s == '!' || s == '-' || s == '+' || isdigit(s) || !s)
392
-// return 1;
393
-// return 0;
394
-//}
395
-//
396
-//static inline int old_isoperatorterm_symbol(const char s) {
397
-// if (old_isoperatorterm_word(s) || isalpha(s))
398
-// return 1;
399
-// return 0;
400
-//}
401
-//
402
-//// return 1 if the character should never appear in a variable
403
-//static inline int old_isvariableterm(const char s) {
404
-// if (isalnum(s) || s == '.' || s == '_')
405
-// return 0;
406
-// return 1;
407
-//}
408
-
409
-static inline bool is_operator_first_symbol_or_space(const char s) {
410
- return (
411
- isspace((uint8_t)s) || !s ||
412
- s == '&' || s == '|' || s == '!' || s == '>' || s == '<' ||
413
- s == '=' || s == '+' || s == '-' || s == '*' || s == '/' || s == '?');
414
-}
415
-
416
-// what character can appear just after the operators: NOT, AND, OR
417
-static inline bool is_valid_after_operator_word(const char s) {
418
- bool rc = isspace((uint8_t)s) || s == '(' || s == '$' || s == '!' || s == '-' || s == '+' || isdigit((uint8_t)s) || !s;
419
-// bool old = old_isoperatorterm_word(s);
420
-// if(rc != old) {
421
-// int x = 0;
422
-// x++;
423
-// }
424
- return rc;
425
-}
426
-
427
-// what character can appear just after an operator symbol?
428
-static inline bool is_valid_after_operator_symbol(const char s) {
429
- bool rc = is_valid_after_operator_word(s) || is_operator_first_symbol_or_space(s);
430
-// bool old = old_isoperatorterm_symbol(s);
431
-// if(rc != old) {
432
-// int x = 0;
433
-// x++;
434
-// }
435
- return rc;
436
-}
437
-
438
-// return true if the character may appear in a variable name
439
-static inline bool is_valid_variable_character(const char s) {
440
- bool rc = !is_operator_first_symbol_or_space(s) && s != ')' && s != '}';
441
-// bool old = !old_isvariableterm(s);
442
-// if(rc != old) {
443
-// int x = 0;
444
-// x++;
445
-// }
446
- return rc;
447
-}
448
-
449
-// ----------------------------------------------------------------------------
450
-// parse operators
451
-
452
-static inline int parse_and(const char **string) {
453
- const char *s = *string;
454
-
455
- // AND
456
- if((s[0] == 'A' || s[0] == 'a') && (s[1] == 'N' || s[1] == 'n') && (s[2] == 'D' || s[2] == 'd') &&
457
- is_valid_after_operator_word(s[3])) {
458
- *string = &s[4];
459
- return 1;
460
- }
461
-
462
- // &&
463
- if(s[0] == '&' && s[1] == '&' && is_valid_after_operator_symbol(s[2])) {
464
- *string = &s[2];
465
- return 1;
466
- }
467
-
468
- return 0;
469
-}
470
-
471
-static inline int parse_or(const char **string) {
472
- const char *s = *string;
473
-
474
- // OR
475
- if((s[0] == 'O' || s[0] == 'o') && (s[1] == 'R' || s[1] == 'r') && is_valid_after_operator_word(s[2])) {
476
- *string = &s[3];
477
- return 1;
478
- }
479
-
480
- // ||
481
- if(s[0] == '|' && s[1] == '|' && is_valid_after_operator_symbol(s[2])) {
482
- *string = &s[2];
483
- return 1;
484
- }
485
-
486
- return 0;
487
-}
488
-
489
-static inline int parse_greater_than_or_equal(const char **string) {
490
- const char *s = *string;
491
-
492
- // >=
493
- if(s[0] == '>' && s[1] == '=' && is_valid_after_operator_symbol(s[2])) {
494
- *string = &s[2];
495
- return 1;
496
- }
497
-
498
- return 0;
499
-}
500
-
501
-static inline int parse_less_than_or_equal(const char **string) {
502
- const char *s = *string;
503
-
504
- // <=
505
- if (s[0] == '<' && s[1] == '=' && is_valid_after_operator_symbol(s[2])) {
506
- *string = &s[2];
507
- return 1;
508
- }
509
-
510
- return 0;
511
-}
512
-
513
-static inline int parse_greater(const char **string) {
514
- const char *s = *string;
515
-
516
- // >
517
- if(s[0] == '>' && is_valid_after_operator_symbol(s[1])) {
518
- *string = &s[1];
519
- return 1;
520
- }
521
-
522
- return 0;
523
-}
524
-
525
-static inline int parse_less(const char **string) {
526
- const char *s = *string;
527
-
528
- // <
529
- if(s[0] == '<' && is_valid_after_operator_symbol(s[1])) {
530
- *string = &s[1];
531
- return 1;
532
- }
533
-
534
- return 0;
535
-}
536
-
537
-static inline int parse_equal(const char **string) {
538
- const char *s = *string;
539
-
540
- // ==
541
- if(s[0] == '=' && s[1] == '=' && is_valid_after_operator_symbol(s[2])) {
542
- *string = &s[2];
543
- return 1;
544
- }
545
-
546
- // =
547
- if(s[0] == '=' && is_valid_after_operator_symbol(s[1])) {
548
- *string = &s[1];
549
- return 1;
550
- }
551
-
552
- return 0;
553
-}
554
-
555
-static inline int parse_not_equal(const char **string) {
556
- const char *s = *string;
557
-
558
- // !=
559
- if(s[0] == '!' && s[1] == '=' && is_valid_after_operator_symbol(s[2])) {
560
- *string = &s[2];
561
- return 1;
562
- }
563
-
564
- // <>
565
- if(s[0] == '<' && s[1] == '>' && is_valid_after_operator_symbol(s[2])) {
566
- *string = &s[2];
567
- }
568
-
569
- return 0;
570
-}
571
-
572
-static inline int parse_not(const char **string) {
573
- const char *s = *string;
574
-
575
- // NOT
576
- if((s[0] == 'N' || s[0] == 'n') && (s[1] == 'O' || s[1] == 'o') && (s[2] == 'T' || s[2] == 't') &&
577
- is_valid_after_operator_word(s[3])) {
578
- *string = &s[3];
579
- return 1;
580
- }
581
-
582
- if(s[0] == '!') {
583
- *string = &s[1];
584
- return 1;
585
- }
586
-
587
- return 0;
588
-}
589
-
590
-static inline int parse_multiply(const char **string) {
591
- const char *s = *string;
592
-
593
- // *
594
- if(s[0] == '*' && is_valid_after_operator_symbol(s[1])) {
595
- *string = &s[1];
596
- return 1;
597
- }
598
-
599
- return 0;
600
-}
601
-
602
-static inline int parse_divide(const char **string) {
603
- const char *s = *string;
604
-
605
- // /
606
- if(s[0] == '/' && is_valid_after_operator_symbol(s[1])) {
607
- *string = &s[1];
608
- return 1;
609
- }
610
-
611
- return 0;
612
-}
613
-
614
-static inline int parse_minus(const char **string) {
615
- const char *s = *string;
616
-
617
- // -
618
- if(s[0] == '-' && is_valid_after_operator_symbol(s[1])) {
619
- *string = &s[1];
620
- return 1;
621
- }
622
-
623
- return 0;
624
-}
625
-
626
-static inline int parse_plus(const char **string) {
627
- const char *s = *string;
628
-
629
- // +
630
- if(s[0] == '+' && is_valid_after_operator_symbol(s[1])) {
631
- *string = &s[1];
632
- return 1;
633
- }
634
-
635
- return 0;
636
-}
637
-
638
-static inline int parse_open_subexpression(const char **string) {
639
- const char *s = *string;
640
-
641
- // (
642
- if(s[0] == '(') {
643
- *string = &s[1];
644
- return 1;
645
- }
646
-
647
- return 0;
648
-}
649
-
650
-#define parse_close_function(x) parse_close_subexpression(x)
651
-
652
-static inline int parse_close_subexpression(const char **string) {
653
- const char *s = *string;
654
-
655
- // )
656
- if(s[0] == ')') {
657
- *string = &s[1];
658
- return 1;
659
- }
660
-
661
- return 0;
662
-}
663
-
664
-static inline int parse_variable(const char **string, char *buffer, size_t len) {
665
- const char *s = *string;
666
-
667
- // $
668
- if(*s == '$') {
669
- size_t i = 0;
670
- s++;
671
-
672
- if(*s == '{') {
673
- // ${variable_name}
674
-
675
- s++;
676
- while (*s && *s != '}' && i < len)
677
- buffer[i++] = *s++;
3
+/*
4
+ * This file has been split into three parts:
5
+ * - eval-parser.c: Parser implementation
6
+ * - eval-execute.c: Execution/evaluation logic
7
+ * - eval-utils.c: Common utilities
8
+ *
9
+ * Any new changes should be made to those files instead of this one.
10
+ * This file is now just a stub for backward compatibility.
11
+ */
12
679
- if(*s == '}')
680
- s++;
681
- }
682
- else {
683
- // $variable_name
684
-
685
- while (*s && is_valid_variable_character(*s) && i < len)
686
- buffer[i++] = *s++;
687
- }
688
-
689
- buffer[i] = '\0';
690
-
691
- if (buffer[0]) {
692
- *string = s;
693
- return 1;
694
- }
695
- }
696
-
697
- return 0;
698
-}
699
-
700
-static inline int parse_constant(const char **string, NETDATA_DOUBLE *number) {
701
- char *end = NULL;
702
- NETDATA_DOUBLE n = str2ndd(*string, &end);
703
- if(unlikely(!end || *string == end)) {
704
- *number = 0;
705
- return 0;
706
- }
707
- *number = n;
708
- *string = end;
709
- return 1;
710
-}
711
-
712
-static inline int parse_abs(const char **string) {
713
- const char *s = *string;
714
-
715
- // ABS
716
- if((s[0] == 'A' || s[0] == 'a') && (s[1] == 'B' || s[1] == 'b') && (s[2] == 'S' || s[2] == 's') && s[3] == '(') {
717
- *string = &s[3];
718
- return 1;
719
- }
720
-
721
- return 0;
722
-}
723
-
724
-static inline int parse_if_then_else(const char **string) {
725
- const char *s = *string;
726
-
727
- // ?
728
- if(s[0] == '?') {
729
- *string = &s[1];
730
- return 1;
731
- }
732
-
733
- return 0;
734
-}
735
-
736
-static struct operator_parser {
737
- unsigned char id;
738
- int (*parse)(const char **);
739
-} operator_parsers[] = {
740
- // the order in this list is important!
741
- // the first matching will be used
742
- // so place the longer of overlapping ones
743
- // at the top
744
-
745
- { EVAL_OPERATOR_AND, parse_and },
746
- { EVAL_OPERATOR_OR, parse_or },
747
- { EVAL_OPERATOR_GREATER_THAN_OR_EQUAL, parse_greater_than_or_equal },
748
- { EVAL_OPERATOR_LESS_THAN_OR_EQUAL, parse_less_than_or_equal },
749
- { EVAL_OPERATOR_NOT_EQUAL, parse_not_equal },
750
- { EVAL_OPERATOR_EQUAL, parse_equal },
751
- { EVAL_OPERATOR_LESS, parse_less },
752
- { EVAL_OPERATOR_GREATER, parse_greater },
753
- { EVAL_OPERATOR_PLUS, parse_plus },
754
- { EVAL_OPERATOR_MINUS, parse_minus },
755
- { EVAL_OPERATOR_MULTIPLY, parse_multiply },
756
- { EVAL_OPERATOR_DIVIDE, parse_divide },
757
- { EVAL_OPERATOR_IF_THEN_ELSE, parse_if_then_else },
758
-
759
- /* we should not put in this list the following:
760
- *
761
- * - NOT
762
- * - (
763
- * - )
764
- *
765
- * these are handled in code
766
- */
767
-
768
- // termination
769
- { EVAL_OPERATOR_NOP, NULL }
770
-};
771
-
772
-static inline unsigned char parse_operator(const char **string, int *precedence) {
773
- skip_spaces(string);
774
-
775
- int i;
776
- for(i = 0 ; operator_parsers[i].parse != NULL ; i++)
777
- if(operator_parsers[i].parse(string)) {
778
- if(precedence) *precedence = eval_precedence(operator_parsers[i].id);
779
- return operator_parsers[i].id;
780
- }
781
-
782
- return EVAL_OPERATOR_NOP;
783
-}
784
-
785
-// ----------------------------------------------------------------------------
786
-// memory management
787
-
788
-static inline EVAL_NODE *eval_node_alloc(int count) {
789
- static int id = 1;
790
-
791
- EVAL_NODE *op = callocz(1, sizeof(EVAL_NODE) + (sizeof(EVAL_VALUE) * count));
792
-
793
- op->id = id++;
794
- op->operator = EVAL_OPERATOR_NOP;
795
- op->precedence = eval_precedence(EVAL_OPERATOR_NOP);
796
- op->count = count;
797
- return op;
798
-}
799
-
800
-static inline void eval_node_set_value_to_node(EVAL_NODE *op, int pos, EVAL_NODE *value) {
801
- if(pos >= op->count)
802
- fatal("Invalid request to set position %d of OPERAND that has only %d values", pos + 1, op->count + 1);
803
-
804
- op->ops[pos].type = EVAL_VALUE_EXPRESSION;
805
- op->ops[pos].expression = value;
806
-}
807
-
808
-static inline void eval_node_set_value_to_constant(EVAL_NODE *op, int pos, NETDATA_DOUBLE value) {
809
- if(pos >= op->count)
810
- fatal("Invalid request to set position %d of OPERAND that has only %d values", pos + 1, op->count + 1);
811
-
812
- op->ops[pos].type = EVAL_VALUE_NUMBER;
813
- op->ops[pos].number = value;
814
-}
815
-
816
-static inline void eval_node_set_value_to_variable(EVAL_NODE *op, int pos, const char *variable) {
817
- if(pos >= op->count)
818
- fatal("Invalid request to set position %d of OPERAND that has only %d values", pos + 1, op->count + 1);
819
-
820
- op->ops[pos].type = EVAL_VALUE_VARIABLE;
821
- op->ops[pos].variable = callocz(1, sizeof(EVAL_VARIABLE));
822
- op->ops[pos].variable->name = string_strdupz(variable);
823
-}
824
-
825
-static inline void eval_variable_free(EVAL_VARIABLE *v) {
826
- string_freez(v->name);
827
- freez(v);
828
-}
829
-
830
-static inline void eval_value_free(EVAL_VALUE *v) {
831
- switch(v->type) {
832
- case EVAL_VALUE_EXPRESSION:
833
- eval_node_free(v->expression);
834
- break;
835
-
836
- case EVAL_VALUE_VARIABLE:
837
- eval_variable_free(v->variable);
838
- break;
839
-
840
- default:
841
- break;
842
- }
843
-}
844
-
845
-static inline void eval_node_free(EVAL_NODE *op) {
846
- if(op->count) {
847
- int i;
848
- for(i = op->count - 1; i >= 0 ;i--)
849
- eval_value_free(&op->ops[i]);
850
- }
851
-
852
- freez(op);
853
-}
854
-
855
-// ----------------------------------------------------------------------------
856
-// the parsing logic
857
-
858
-// helper function to avoid allocations all over the place
859
-static inline EVAL_NODE *parse_next_operand_given_its_operator(const char **string, unsigned char operator_type, int *error) {
860
- EVAL_NODE *sub = parse_one_full_operand(string, error);
861
- if(!sub) return NULL;
862
-
863
- EVAL_NODE *op = eval_node_alloc(1);
864
- op->operator = operator_type;
865
- eval_node_set_value_to_node(op, 0, sub);
866
- return op;
867
-}
868
-
869
-// parse a full operand, including its sign or other associative operator (e.g. NOT)
870
-static inline EVAL_NODE *parse_one_full_operand(const char **string, int *error) {
871
- char variable_buffer[EVAL_MAX_VARIABLE_NAME_LENGTH + 1];
872
- EVAL_NODE *op1 = NULL;
873
- NETDATA_DOUBLE number;
874
-
875
- *error = EVAL_ERROR_OK;
876
-
877
- skip_spaces(string);
878
- if(!(**string)) {
879
- *error = EVAL_ERROR_MISSING_OPERAND;
880
- return NULL;
881
- }
882
-
883
- if(parse_not(string)) {
884
- op1 = parse_next_operand_given_its_operator(string, EVAL_OPERATOR_NOT, error);
885
- op1->precedence = eval_precedence(EVAL_OPERATOR_NOT);
886
- }
887
- else if(parse_plus(string)) {
888
- op1 = parse_next_operand_given_its_operator(string, EVAL_OPERATOR_SIGN_PLUS, error);
889
- op1->precedence = eval_precedence(EVAL_OPERATOR_SIGN_PLUS);
890
- }
891
- else if(parse_minus(string)) {
892
- op1 = parse_next_operand_given_its_operator(string, EVAL_OPERATOR_SIGN_MINUS, error);
893
- op1->precedence = eval_precedence(EVAL_OPERATOR_SIGN_MINUS);
894
- }
895
- else if(parse_abs(string)) {
896
- op1 = parse_next_operand_given_its_operator(string, EVAL_OPERATOR_ABS, error);
897
- op1->precedence = eval_precedence(EVAL_OPERATOR_ABS);
898
- }
899
- else if(parse_open_subexpression(string)) {
900
- EVAL_NODE *sub = parse_full_expression(string, error);
901
- if(sub) {
902
- op1 = eval_node_alloc(1);
903
- op1->operator = EVAL_OPERATOR_EXPRESSION_OPEN;
904
- op1->precedence = eval_precedence(EVAL_OPERATOR_EXPRESSION_OPEN);
905
- eval_node_set_value_to_node(op1, 0, sub);
906
- if(!parse_close_subexpression(string)) {
907
- *error = EVAL_ERROR_MISSING_CLOSE_SUBEXPRESSION;
908
- eval_node_free(op1);
909
- return NULL;
910
- }
911
- }
912
- }
913
- else if(parse_variable(string, variable_buffer, EVAL_MAX_VARIABLE_NAME_LENGTH)) {
914
- op1 = eval_node_alloc(1);
915
- op1->operator = EVAL_OPERATOR_NOP;
916
- eval_node_set_value_to_variable(op1, 0, variable_buffer);
917
- }
918
- else if(parse_constant(string, &number)) {
919
- op1 = eval_node_alloc(1);
920
- op1->operator = EVAL_OPERATOR_NOP;
921
- eval_node_set_value_to_constant(op1, 0, number);
922
- }
923
- else if(**string)
924
- *error = EVAL_ERROR_UNKNOWN_OPERAND;
925
- else
926
- *error = EVAL_ERROR_MISSING_OPERAND;
927
-
928
- return op1;
929
-}
930
-
931
-// parse an operator and the rest of the expression
932
-// precedence processing is handled here
933
-static inline EVAL_NODE *parse_rest_of_expression(const char **string, int *error, EVAL_NODE *op1) {
934
- EVAL_NODE *op2 = NULL;
935
- unsigned char operator;
936
- int precedence;
937
-
938
- operator = parse_operator(string, &precedence);
939
- skip_spaces(string);
940
-
941
- if(operator != EVAL_OPERATOR_NOP) {
942
- op2 = parse_one_full_operand(string, error);
943
- if(!op2) {
944
- // error is already reported
945
- eval_node_free(op1);
946
- return NULL;
947
- }
948
-
949
- EVAL_NODE *op = eval_node_alloc(operators[operator].parameters);
950
- op->operator = operator;
951
- op->precedence = precedence;
952
-
953
- if(operator == EVAL_OPERATOR_IF_THEN_ELSE && op->count == 3) {
954
- skip_spaces(string);
955
-
956
- if(**string != ':') {
957
- eval_node_free(op);
958
- eval_node_free(op1);
959
- eval_node_free(op2);
960
- *error = EVAL_ERROR_IF_THEN_ELSE_MISSING_ELSE;
961
- return NULL;
962
- }
963
- (*string)++;
964
-
965
- skip_spaces(string);
966
-
967
- EVAL_NODE *op3 = parse_one_full_operand(string, error);
968
- if(!op3) {
969
- eval_node_free(op);
970
- eval_node_free(op1);
971
- eval_node_free(op2);
972
- // error is already reported
973
- return NULL;
974
- }
975
-
976
- eval_node_set_value_to_node(op, 2, op3);
977
- }
978
-
979
- eval_node_set_value_to_node(op, 1, op2);
980
-
981
- // precedence processing
982
- // if this operator has a higher precedence compared to its next
983
- // put the next operator on top of us (top = evaluated later)
984
- // function recursion does the rest...
985
- if(op->precedence > op1->precedence && op1->count == 2 && op1->operator != '(' && op1->ops[1].type == EVAL_VALUE_EXPRESSION) {
986
- eval_node_set_value_to_node(op, 0, op1->ops[1].expression);
987
- op1->ops[1].expression = op;
988
- op = op1;
989
- }
990
- else
991
- eval_node_set_value_to_node(op, 0, op1);
992
-
993
- return parse_rest_of_expression(string, error, op);
994
- }
995
- else if(**string == ')') {
996
- ;
997
- }
998
- else if(**string) {
999
- eval_node_free(op1);
1000
- op1 = NULL;
1001
- *error = EVAL_ERROR_MISSING_OPERATOR;
1002
- }
1003
-
1004
- return op1;
1005
-}
1006
-
1007
-// high level function to parse an expression or a sub-expression
1008
-static inline EVAL_NODE *parse_full_expression(const char **string, int *error) {
1009
- EVAL_NODE *op1 = parse_one_full_operand(string, error);
1010
- if(!op1) {
1011
- *error = EVAL_ERROR_MISSING_OPERAND;
1012
- return NULL;
1013
- }
1014
-
1015
- return parse_rest_of_expression(string, error, op1);
1016
-}
1017
-
1018
-// ----------------------------------------------------------------------------
1019
-// public API
1020
-
1021
-int expression_evaluate(EVAL_EXPRESSION *expression) {
1022
- expression->error = EVAL_ERROR_OK;
1023
-
1024
- buffer_reset(expression->error_msg);
1025
- expression->result = eval_node(expression, expression->nodes, &expression->error);
1026
-
1027
- if(unlikely(isnan(expression->result))) {
1028
- if(expression->error == EVAL_ERROR_OK)
1029
- expression->error = EVAL_ERROR_VALUE_IS_NAN;
1030
- }
1031
- else if(unlikely(isinf(expression->result))) {
1032
- if(expression->error == EVAL_ERROR_OK)
1033
- expression->error = EVAL_ERROR_VALUE_IS_INFINITE;
1034
- }
1035
- else if(unlikely(expression->error == EVAL_ERROR_UNKNOWN_VARIABLE)) {
1036
- // although there is an unknown variable
1037
- // the expression was evaluated successfully
1038
- expression->error = EVAL_ERROR_OK;
1039
- }
1040
-
1041
- if(expression->error != EVAL_ERROR_OK) {
1042
- expression->result = NAN;
1043
-
1044
- if(buffer_strlen(expression->error_msg))
1045
- buffer_strcat(expression->error_msg, "; ");
1046
-
1047
- buffer_sprintf(expression->error_msg, "failed to evaluate expression with error %d (%s)", expression->error, expression_strerror(expression->error));
1048
- return 0;
1049
- }
1050
-
1051
- return 1;
1052
-}
1053
-
1054
-EVAL_EXPRESSION *expression_parse(const char *string, const char **failed_at, int *error) {
1055
- if(!string || !*string)
1056
- return NULL;
1057
-
1058
- const char *s = string;
1059
- int err = EVAL_ERROR_OK;
1060
-
1061
- EVAL_NODE *op = parse_full_expression(&s, &err);
1062
-
1063
- if(*s) {
1064
- if(op) {
1065
- eval_node_free(op);
1066
- op = NULL;
1067
- }
1068
- err = EVAL_ERROR_REMAINING_GARBAGE;
1069
- }
1070
-
1071
- if (failed_at) *failed_at = s;
1072
- if (error) *error = err;
1073
-
1074
- if(!op) {
1075
- unsigned long pos = s - string + 1;
1076
- netdata_log_error("failed to parse expression '%s': %s at character %lu (i.e.: '%s').", string, expression_strerror(err), pos, s);
1077
- return NULL;
1078
- }
1079
-
1080
- BUFFER *out = buffer_create(1024, NULL);
1081
- print_parsed_as_node(out, op, &err);
1082
- if(err != EVAL_ERROR_OK) {
1083
- netdata_log_error("failed to re-generate expression '%s' with reason: %s", string, expression_strerror(err));
1084
- eval_node_free(op);
1085
- buffer_free(out);
1086
- return NULL;
1087
- }
1088
-
1089
- EVAL_EXPRESSION *exp = callocz(1, sizeof(EVAL_EXPRESSION));
1090
-
1091
- exp->source = string_strdupz(string);
1092
- exp->parsed_as = string_strdupz(buffer_tostring(out));
1093
- buffer_free(out);
1094
-
1095
- exp->error_msg = buffer_create(100, NULL);
1096
- exp->nodes = op;
1097
-
1098
- return exp;
1099
-}
1100
-
1101
-void expression_free(EVAL_EXPRESSION *expression) {
1102
- if(!expression) return;
1103
-
1104
- if(expression->nodes) eval_node_free(expression->nodes);
1105
- string_freez((void *)expression->source);
1106
- string_freez((void *)expression->parsed_as);
1107
- buffer_free(expression->error_msg);
1108
- freez(expression);
1109
-}
1110
-
1111
-const char *expression_strerror(int error) {
1112
- switch(error) {
1113
- case EVAL_ERROR_OK:
1114
- return "success";
1115
-
1116
- case EVAL_ERROR_MISSING_CLOSE_SUBEXPRESSION:
1117
- return "missing closing parenthesis";
1118
-
1119
- case EVAL_ERROR_UNKNOWN_OPERAND:
1120
- return "unknown operand";
1121
-
1122
- case EVAL_ERROR_MISSING_OPERAND:
1123
- return "expected operand";
1124
-
1125
- case EVAL_ERROR_MISSING_OPERATOR:
1126
- return "expected operator";
1127
-
1128
- case EVAL_ERROR_REMAINING_GARBAGE:
1129
- return "remaining characters after expression";
1130
-
1131
- case EVAL_ERROR_INVALID_VALUE:
1132
- return "invalid value structure - internal error";
1133
-
1134
- case EVAL_ERROR_INVALID_NUMBER_OF_OPERANDS:
1135
- return "wrong number of operands for operation - internal error";
1136
-
1137
- case EVAL_ERROR_VALUE_IS_NAN:
1138
- return "value is unset";
1139
-
1140
- case EVAL_ERROR_VALUE_IS_INFINITE:
1141
- return "computed value is infinite";
1142
-
1143
- case EVAL_ERROR_UNKNOWN_VARIABLE:
1144
- return "undefined variable";
1145
-
1146
- case EVAL_ERROR_IF_THEN_ELSE_MISSING_ELSE:
1147
- return "missing second sub-expression of inline conditional";
1148
-
1149
- default:
1150
- return "unknown error";
1151
- }
1152
-}
1153
-
1154
-const char *expression_source(EVAL_EXPRESSION *expression) {
1155
- if(!expression)
1156
- return string2str(NULL);
1157
-
1158
- return string2str(expression->source);
1159
-}
1160
-
1161
-const char *expression_parsed_as(EVAL_EXPRESSION *expression) {
1162
- if(!expression)
1163
- return string2str(NULL);
1164
-
1165
- return string2str(expression->parsed_as);
1166
-}
1167
-
1168
-const char *expression_error_msg(EVAL_EXPRESSION *expression) {
1169
- if(!expression || !expression->error_msg)
1170
- return "";
1171
-
1172
- return buffer_tostring(expression->error_msg);
1173
-}
1174
-
1175
-NETDATA_DOUBLE expression_result(EVAL_EXPRESSION *expression) {
1176
- if(!expression)
1177
- return NAN;
1178
-
1179
- return expression->result;
1180
-}
1181
-
1182
-void expression_set_variable_lookup_callback(EVAL_EXPRESSION *expression, eval_expression_variable_lookup_t cb, void *data) {
1183
- if(!expression)
1184
- return;
1185
-
1186
- expression->variable_lookup_cb = cb;
1187
- expression->variable_lookup_cb_data = data;
1188
-}
1189
-
1190
-static size_t expression_hardcode_node_variable(EVAL_NODE *node, STRING *variable, NETDATA_DOUBLE value) {
1191
- size_t matches = 0;
1192
-
1193
- for(int i = 0; i < node->count; i++) {
1194
- switch(node->ops[i].type) {
1195
- case EVAL_VALUE_NUMBER:
1196
- case EVAL_VALUE_INVALID:
1197
- break;
1198
-
1199
- case EVAL_VALUE_VARIABLE:
1200
- if(node->ops[i].variable->name == variable) {
1201
- string_freez(node->ops[i].variable->name);
1202
- freez(node->ops[i].variable);
1203
- node->ops[i].type = EVAL_VALUE_NUMBER;
1204
- node->ops[i].number = value;
1205
- matches++;
1206
- }
1207
- break;
1208
-
1209
- case EVAL_VALUE_EXPRESSION:
1210
- matches += expression_hardcode_node_variable(node->ops[i].expression, variable, value);
1211
- break;
1212
- }
1213
- }
1214
-
1215
- return matches;
1216
-}
1217
-
1218
-void expression_hardcode_variable(EVAL_EXPRESSION *expression, STRING *variable, NETDATA_DOUBLE value) {
1219
- if (!expression || !variable || isnan(value))
1220
- return;
1221
-
1222
- size_t matches = expression_hardcode_node_variable(expression->nodes, variable, value);
1223
- if (matches) {
1224
- char replace[1024];
1225
- snprintfz(replace, sizeof(replace), NETDATA_DOUBLE_FORMAT_AUTO, value);
1226
- size_t replace_len = strlen(replace);
1227
-
1228
- size_t source_len = string_strlen(expression->source);
1229
- const char *source_str = string2str(expression->source);
1230
-
1231
- // Allocate enough space to accommodate all replacements.
1232
- char buf[source_len + 1 + matches * (replace_len + 1)];
1233
-
1234
- char find1[string_strlen(variable) + 1 + 1];
1235
- snprintfz(find1, sizeof(find1), "$%s", string2str(variable));
1236
- size_t find1_len = strlen(find1);
1237
-
1238
- char find2[string_strlen(variable) + 1 + 3];
1239
- snprintfz(find2, sizeof(find2), "${%s}", string2str(variable));
1240
- size_t find2_len = strlen(find2);
1241
-
1242
- size_t found = 0;
1243
- char *buf_ptr = buf;
1244
- const char *source_ptr = source_str;
1245
-
1246
- while (*source_ptr) {
1247
- char *s1 = strstr(source_ptr, find1);
1248
- char *s2 = strstr(source_ptr, find2);
1249
-
1250
- char *s = s1;
1251
- size_t len = find1_len;
1252
- if (s2 && (!s1 || s2 < s1)) {
1253
- s = s2;
1254
- len = find2_len;
1255
- }
1256
-
1257
- if (s) {
1258
- if (s == s1 && is_valid_variable_character(s[len])) {
1259
- // Move past the variable if it's part of a larger word.
1260
- source_ptr = s + len;
1261
- continue;
1262
- }
1263
-
1264
- // Copy the part before the variable.
1265
- memcpy(buf_ptr, source_ptr, s - source_ptr);
1266
- buf_ptr += (s - source_ptr);
1267
-
1268
- // Copy the replacement.
1269
- memcpy(buf_ptr, replace, replace_len);
1270
- buf_ptr += replace_len;
1271
- *buf_ptr = '\0';
1272
-
1273
- // Move the source pointer past the replaced variable.
1274
- source_ptr = s + len;
1275
- found++;
1276
- } else {
1277
- // Copy the rest of the string if no more variables are found.
1278
- strcpy(buf_ptr, source_ptr);
1279
- break;
1280
- }
1281
- }
1282
-
1283
- // Update the expression source with the new string.
1284
- string_freez(expression->source);
1285
- expression->source = string_strdupz(buf);
1286
- }
1287
-}
13
+#include "../libnetdata.h"
14
+#include "eval-internal.h"
\ No newline at end of file
src/libnetdata/eval/re2c_lemon/Makefile
new
+52
@@ -0,0 +1,52 @@
1
+# Makefile for re2c/lemon parser generator
2
+
3
+# Check if we have re2c and lemon in the system
4
+HAVE_RE2C := $(shell command -v re2c 2>/dev/null)
5
+HAVE_LEMON := $(shell command -v lemon 2>/dev/null)
6
+
7
+# Variables for lemon if we need to download it
8
+LEMON_C = lemon.c
9
+LEMPAR_C = lempar.c
10
+LEMON_URL = https://raw.githubusercontent.com/sqlite/sqlite/master/tool/lemon.c
11
+LEMPAR_URL = https://raw.githubusercontent.com/sqlite/sqlite/master/tool/lempar.c
12
+
13
+all: lexer.c parser.c parser.h
14
+
15
+# Generate lexer from re2c file
16
+lexer.c: lexer.re
17
+ifndef HAVE_RE2C
18
+ $(error re2c is not installed. Please install re2c to build this project)
19
+else
20
+ re2c -o $@ $<
21
+endif
22
+
23
+# Generate lemon parser from grammar
24
+parser.c parser.h: parser.y $(LEMPAR_C)
25
+ifdef HAVE_LEMON
26
+ lemon -s -T$(LEMPAR_C) $<
27
+else
28
+ $(CC) -o lemon $(LEMON_C)
29
+ ./lemon -s -T$(LEMPAR_C) $<
30
+endif
31
+ @if [ ! -f parser.c ]; then \
32
+ echo "Error: parser.c was not generated"; \
33
+ exit 1; \
34
+ fi
35
+
36
+# Download lemon sources if needed
37
+$(LEMON_C):
38
+ifndef HAVE_LEMON
39
+ @echo "Downloading lemon source..."
40
+ curl -s -o $(LEMON_C) $(LEMON_URL)
41
+endif
42
+
43
+# Download lempar.c template
44
+$(LEMPAR_C):
45
+ @echo "Downloading lempar.c template..."
46
+ curl -s -o $(LEMPAR_C) $(LEMPAR_URL)
47
+
48
+# Clean rule
49
+clean:
50
+ rm -f lexer.c parser.c parser.h parser.out lemon $(LEMON_C) $(LEMPAR_C)
51
+
52
+.PHONY: all clean
src/libnetdata/eval/re2c_lemon/README.md
new
+87
@@ -0,0 +1,87 @@
1
+# re2c/Lemon Parser for Netdata Expression Evaluator
2
+
3
+This directory contains a parser implementation for Netdata's expression evaluator using re2c for lexical analysis and Lemon for syntax parsing.
4
+
5
+## Overview
6
+
7
+The re2c/Lemon-based parser is designed to be more efficient and maintainable compared to the handwritten recursive descent parser in the parent directory. It achieves full compatibility with the original parser while providing better performance, especially for complex expressions with nested operations.
8
+
9
+## Components
10
+
11
+- **lexer.re** - The re2c-based lexical analyzer source file
12
+- **lexer.c** - The generated lexer code (generated from lexer.re)
13
+- **parser.y** - The Lemon-based grammar definition
14
+- **parser.c** - The generated parser code (generated from parser.y)
15
+- **parser.h** - The generated parser header
16
+- **parser_internal.h** - Internal definitions shared between the lexer and parser
17
+- **parser_wrapper.c** - Integration wrapper for the Netdata build system
18
+- **Makefile** - Build instructions for regenerating the parser
19
+
20
+## Technology Stack
21
+
22
+### re2c
23
+
24
+[re2c](https://re2c.org/) is a lexer generator that translates regular expressions into deterministic finite automata (DFA) and produces efficient C code. The lexer implemented in `lexer.re` tokenizes the input string according to the expression language grammar, handling:
25
+
26
+- Special literals (nan, inf)
27
+- Numbers
28
+- Variable names
29
+- Operators
30
+- Function names
31
+- Whitespace
32
+
33
+### Lemon
34
+
35
+[Lemon](https://www.sqlite.org/lemon.html) is a parser generator similar to YACC/Bison but with a different parsing technique and better thread safety. The grammar in `parser.y` defines the expression language syntax, operator precedence, and associativity rules.
36
+
37
+## Integration
38
+
39
+This parser implementation can be selected by defining `USE_RE2C_LEMON_PARSER` in `eval-internal.h`. When enabled, the function `parse_expression_with_re2c_lemon()` is used instead of the original recursive descent parser.
40
+
41
+## Key Features
42
+
43
+1. **Parser Generator Approach**: Using specialized tools (re2c and Lemon) for lexical analysis and parsing rather than handwritten code.
44
+
45
+2. **Full Compatibility**: Maintains 100% compatibility with the original parser, ensuring all test cases pass with identical results.
46
+
47
+3. **Proper Operator Precedence**: Handles operator precedence correctly, especially for complex cases like nested ternary operators.
48
+
49
+4. **Flexible Variable Names**: Supports the same variable naming rules as the original parser, including braced variables with spaces.
50
+
51
+5. **Special Literal Handling**: Properly processes special literals like NaN and Infinity in various capitalizations.
52
+
53
+6. **Case-Insensitive Keywords**: Handles logical operators (AND, OR, NOT) case-insensitively, just like the original parser.
54
+
55
+7. **Reduced Memory Leaks**: Carefully manages memory allocations to prevent leaks, especially in error conditions.
56
+
57
+## Rebuilding the Parser
58
+
59
+If you need to modify the lexer or parser definitions, you can rebuild the generated files using:
60
+
61
+```bash
62
+make -C src/libnetdata/eval/re2c_lemon
63
+```
64
+
65
+This requires re2c and lemon to be installed on your system.
66
+
67
+## Technical Details
68
+
69
+### Parser Notes
70
+
71
+- The parser builds an abstract syntax tree (AST) using the `EVAL_NODE` structure.
72
+- Operator precedence is carefully defined to match C-like languages.
73
+- The ternary operator (`?:`) is properly implemented as right-associative.
74
+- Error handling includes meaningful error messages and reporting of error locations.
75
+
76
+### Lexer Notes
77
+
78
+- The re2c lexer is implemented as a scanner that tokenizes the input string one token at a time.
79
+- It handles variable names with both simple syntax (`$var`) and braced syntax (`${complex var}`).
80
+- Special care is taken for case-insensitive handling of keywords and special literals.
81
+- The lexer automatically skips whitespace and handles end-of-input conditions.
82
+
83
+### Memory Management
84
+
85
+- Uses Netdata's memory allocation patterns (`mallocz`, `freez`, etc.).
86
+- Carefully tracks and frees memory in error conditions.
87
+- Ensures proper cleanup on parse failure.
\ No newline at end of file
src/libnetdata/eval/re2c_lemon/lempar.c
new
+1086
@@ -0,0 +1,1086 @@
1
+/*
2
+** 2000-05-29
3
+**
4
+** The author disclaims copyright to this source code. In place of
5
+** a legal notice, here is a blessing:
6
+**
7
+** May you do good and not evil.
8
+** May you find forgiveness for yourself and forgive others.
9
+** May you share freely, never taking more than you give.
10
+**
11
+*************************************************************************
12
+** Driver template for the LEMON parser generator.
13
+**
14
+** The "lemon" program processes an LALR(1) input grammar file, then uses
15
+** this template to construct a parser. The "lemon" program inserts text
16
+** at each "%%" line. Also, any "P-a-r-s-e" identifier prefix (without the
17
+** interstitial "-" characters) contained in this template is changed into
18
+** the value of the %name directive from the grammar. Otherwise, the content
19
+** of this template is copied straight through into the generate parser
20
+** source file.
21
+**
22
+** The following is the concatenation of all %include directives from the
23
+** input grammar file:
24
+*/
25
+/************ Begin %include sections from the grammar ************************/
26
+%%
27
+/**************** End of %include directives **********************************/
28
+/* These constants specify the various numeric values for terminal symbols.
29
+***************** Begin token definitions *************************************/
30
+%%
31
+/**************** End token definitions ***************************************/
32
+
33
+/* The next sections is a series of control #defines.
34
+** various aspects of the generated parser.
35
+** YYCODETYPE is the data type used to store the integer codes
36
+** that represent terminal and non-terminal symbols.
37
+** "unsigned char" is used if there are fewer than
38
+** 256 symbols. Larger types otherwise.
39
+** YYNOCODE is a number of type YYCODETYPE that is not used for
40
+** any terminal or nonterminal symbol.
41
+** YYFALLBACK If defined, this indicates that one or more tokens
42
+** (also known as: "terminal symbols") have fall-back
43
+** values which should be used if the original symbol
44
+** would not parse. This permits keywords to sometimes
45
+** be used as identifiers, for example.
46
+** YYACTIONTYPE is the data type used for "action codes" - numbers
47
+** that indicate what to do in response to the next
48
+** token.
49
+** ParseTOKENTYPE is the data type used for minor type for terminal
50
+** symbols. Background: A "minor type" is a semantic
51
+** value associated with a terminal or non-terminal
52
+** symbols. For example, for an "ID" terminal symbol,
53
+** the minor type might be the name of the identifier.
54
+** Each non-terminal can have a different minor type.
55
+** Terminal symbols all have the same minor type, though.
56
+** This macros defines the minor type for terminal
57
+** symbols.
58
+** YYMINORTYPE is the data type used for all minor types.
59
+** This is typically a union of many types, one of
60
+** which is ParseTOKENTYPE. The entry in the union
61
+** for terminal symbols is called "yy0".
62
+** YYSTACKDEPTH is the maximum depth of the parser's stack. If
63
+** zero the stack is dynamically sized using realloc()
64
+** ParseARG_SDECL A static variable declaration for the %extra_argument
65
+** ParseARG_PDECL A parameter declaration for the %extra_argument
66
+** ParseARG_PARAM Code to pass %extra_argument as a subroutine parameter
67
+** ParseARG_STORE Code to store %extra_argument into yypParser
68
+** ParseARG_FETCH Code to extract %extra_argument from yypParser
69
+** ParseCTX_* As ParseARG_ except for %extra_context
70
+** YYREALLOC Name of the realloc() function to use
71
+** YYFREE Name of the free() function to use
72
+** YYDYNSTACK True if stack space should be extended on heap
73
+** YYERRORSYMBOL is the code number of the error symbol. If not
74
+** defined, then do no error processing.
75
+** YYNSTATE the combined number of states.
76
+** YYNRULE the number of rules in the grammar
77
+** YYNTOKEN Number of terminal symbols
78
+** YY_MAX_SHIFT Maximum value for shift actions
79
+** YY_MIN_SHIFTREDUCE Minimum value for shift-reduce actions
80
+** YY_MAX_SHIFTREDUCE Maximum value for shift-reduce actions
81
+** YY_ERROR_ACTION The yy_action[] code for syntax error
82
+** YY_ACCEPT_ACTION The yy_action[] code for accept
83
+** YY_NO_ACTION The yy_action[] code for no-op
84
+** YY_MIN_REDUCE Minimum value for reduce actions
85
+** YY_MAX_REDUCE Maximum value for reduce actions
86
+** YY_MIN_DSTRCTR Minimum symbol value that has a destructor
87
+** YY_MAX_DSTRCTR Maximum symbol value that has a destructor
88
+*/
89
+#ifndef INTERFACE
90
+# define INTERFACE 1
91
+#endif
92
+/************* Begin control #defines *****************************************/
93
+%%
94
+/************* End control #defines *******************************************/
95
+#define YY_NLOOKAHEAD ((int)(sizeof(yy_lookahead)/sizeof(yy_lookahead[0])))
96
+
97
+/* Define the yytestcase() macro to be a no-op if is not already defined
98
+** otherwise.
99
+**
100
+** Applications can choose to define yytestcase() in the %include section
101
+** to a macro that can assist in verifying code coverage. For production
102
+** code the yytestcase() macro should be turned off. But it is useful
103
+** for testing.
104
+*/
105
+#ifndef yytestcase
106
+# define yytestcase(X)
107
+#endif
108
+
109
+/* Macro to determine if stack space has the ability to grow using
110
+** heap memory.
111
+*/
112
+#if YYSTACKDEPTH<=0 || YYDYNSTACK
113
+# define YYGROWABLESTACK 1
114
+#else
115
+# define YYGROWABLESTACK 0
116
+#endif
117
+
118
+/* Guarantee a minimum number of initial stack slots.
119
+*/
120
+#if YYSTACKDEPTH<=0
121
+# undef YYSTACKDEPTH
122
+# define YYSTACKDEPTH 2 /* Need a minimum stack size */
123
+#endif
124
+
125
+
126
+/* Next are the tables used to determine what action to take based on the
127
+** current state and lookahead token. These tables are used to implement
128
+** functions that take a state number and lookahead value and return an
129
+** action integer.
130
+**
131
+** Suppose the action integer is N. Then the action is determined as
132
+** follows
133
+**
134
+** 0 <= N <= YY_MAX_SHIFT Shift N. That is, push the lookahead
135
+** token onto the stack and goto state N.
136
+**
137
+** N between YY_MIN_SHIFTREDUCE Shift to an arbitrary state then
138
+** and YY_MAX_SHIFTREDUCE reduce by rule N-YY_MIN_SHIFTREDUCE.
139
+**
140
+** N == YY_ERROR_ACTION A syntax error has occurred.
141
+**
142
+** N == YY_ACCEPT_ACTION The parser accepts its input.
143
+**
144
+** N == YY_NO_ACTION No such action. Denotes unused
145
+** slots in the yy_action[] table.
146
+**
147
+** N between YY_MIN_REDUCE Reduce by rule N-YY_MIN_REDUCE
148
+** and YY_MAX_REDUCE
149
+**
150
+** The action table is constructed as a single large table named yy_action[].
151
+** Given state S and lookahead X, the action is computed as either:
152
+**
153
+** (A) N = yy_action[ yy_shift_ofst[S] + X ]
154
+** (B) N = yy_default[S]
155
+**
156
+** The (A) formula is preferred. The B formula is used instead if
157
+** yy_lookahead[yy_shift_ofst[S]+X] is not equal to X.
158
+**
159
+** The formulas above are for computing the action when the lookahead is
160
+** a terminal symbol. If the lookahead is a non-terminal (as occurs after
161
+** a reduce action) then the yy_reduce_ofst[] array is used in place of
162
+** the yy_shift_ofst[] array.
163
+**
164
+** The following are the tables generated in this section:
165
+**
166
+** yy_action[] A single table containing all actions.
167
+** yy_lookahead[] A table containing the lookahead for each entry in
168
+** yy_action. Used to detect hash collisions.
169
+** yy_shift_ofst[] For each state, the offset into yy_action for
170
+** shifting terminals.
171
+** yy_reduce_ofst[] For each state, the offset into yy_action for
172
+** shifting non-terminals after a reduce.
173
+** yy_default[] Default action for each state.
174
+**
175
+*********** Begin parsing tables **********************************************/
176
+%%
177
+/********** End of lemon-generated parsing tables *****************************/
178
+
179
+/* The next table maps tokens (terminal symbols) into fallback tokens.
180
+** If a construct like the following:
181
+**
182
+** %fallback ID X Y Z.
183
+**
184
+** appears in the grammar, then ID becomes a fallback token for X, Y,
185
+** and Z. Whenever one of the tokens X, Y, or Z is input to the parser
186
+** but it does not parse, the type of the token is changed to ID and
187
+** the parse is retried before an error is thrown.
188
+**
189
+** This feature can be used, for example, to cause some keywords in a language
190
+** to revert to identifiers if they keyword does not apply in the context where
191
+** it appears.
192
+*/
193
+#ifdef YYFALLBACK
194
+static const YYCODETYPE yyFallback[] = {
195
+%%
196
+};
197
+#endif /* YYFALLBACK */
198
+
199
+/* The following structure represents a single element of the
200
+** parser's stack. Information stored includes:
201
+**
202
+** + The state number for the parser at this level of the stack.
203
+**
204
+** + The value of the token stored at this level of the stack.
205
+** (In other words, the "major" token.)
206
+**
207
+** + The semantic value stored at this level of the stack. This is
208
+** the information used by the action routines in the grammar.
209
+** It is sometimes called the "minor" token.
210
+**
211
+** After the "shift" half of a SHIFTREDUCE action, the stateno field
212
+** actually contains the reduce action for the second half of the
213
+** SHIFTREDUCE.
214
+*/
215
+struct yyStackEntry {
216
+ YYACTIONTYPE stateno; /* The state-number, or reduce action in SHIFTREDUCE */
217
+ YYCODETYPE major; /* The major token value. This is the code
218
+ ** number for the token at this stack level */
219
+ YYMINORTYPE minor; /* The user-supplied minor token value. This
220
+ ** is the value of the token */
221
+};
222
+typedef struct yyStackEntry yyStackEntry;
223
+
224
+/* The state of the parser is completely contained in an instance of
225
+** the following structure */
226
+struct yyParser {
227
+ yyStackEntry *yytos; /* Pointer to top element of the stack */
228
+#ifdef YYTRACKMAXSTACKDEPTH
229
+ int yyhwm; /* High-water mark of the stack */
230
+#endif
231
+#ifndef YYNOERRORRECOVERY
232
+ int yyerrcnt; /* Shifts left before out of the error */
233
+#endif
234
+ ParseARG_SDECL /* A place to hold %extra_argument */
235
+ ParseCTX_SDECL /* A place to hold %extra_context */
236
+ yyStackEntry *yystackEnd; /* Last entry in the stack */
237
+ yyStackEntry *yystack; /* The parser stack */
238
+ yyStackEntry yystk0[YYSTACKDEPTH]; /* Initial stack space */
239
+};
240
+typedef struct yyParser yyParser;
241
+
242
+#include <assert.h>
243
+#ifndef NDEBUG
244
+#include <stdio.h>
245
+static FILE *yyTraceFILE = 0;
246
+static char *yyTracePrompt = 0;
247
+#endif /* NDEBUG */
248
+
249
+#ifndef NDEBUG
250
+/*
251
+** Turn parser tracing on by giving a stream to which to write the trace
252
+** and a prompt to preface each trace message. Tracing is turned off
253
+** by making either argument NULL
254
+**
255
+** Inputs:
256
+** <ul>
257
+** <li> A FILE* to which trace output should be written.
258
+** If NULL, then tracing is turned off.
259
+** <li> A prefix string written at the beginning of every
260
+** line of trace output. If NULL, then tracing is
261
+** turned off.
262
+** </ul>
263
+**
264
+** Outputs:
265
+** None.
266
+*/
267
+void ParseTrace(FILE *TraceFILE, char *zTracePrompt){
268
+ yyTraceFILE = TraceFILE;
269
+ yyTracePrompt = zTracePrompt;
270
+ if( yyTraceFILE==0 ) yyTracePrompt = 0;
271
+ else if( yyTracePrompt==0 ) yyTraceFILE = 0;
272
+}
273
+#endif /* NDEBUG */
274
+
275
+#if defined(YYCOVERAGE) || !defined(NDEBUG)
276
+/* For tracing shifts, the names of all terminals and nonterminals
277
+** are required. The following table supplies these names */
278
+static const char *const yyTokenName[] = {
279
+%%
280
+};
281
+#endif /* defined(YYCOVERAGE) || !defined(NDEBUG) */
282
+
283
+#ifndef NDEBUG
284
+/* For tracing reduce actions, the names of all rules are required.
285
+*/
286
+static const char *const yyRuleName[] = {
287
+%%
288
+};
289
+#endif /* NDEBUG */
290
+
291
+
292
+#if YYGROWABLESTACK
293
+/*
294
+** Try to increase the size of the parser stack. Return the number
295
+** of errors. Return 0 on success.
296
+*/
297
+static int yyGrowStack(yyParser *p){
298
+ int oldSize = 1 + (int)(p->yystackEnd - p->yystack);
299
+ int newSize;
300
+ int idx;
301
+ yyStackEntry *pNew;
302
+
303
+ newSize = oldSize*2 + 100;
304
+ idx = (int)(p->yytos - p->yystack);
305
+ if( p->yystack==p->yystk0 ){
306
+ pNew = YYREALLOC(0, newSize*sizeof(pNew[0]));
307
+ if( pNew==0 ) return 1;
308
+ memcpy(pNew, p->yystack, oldSize*sizeof(pNew[0]));
309
+ }else{
310
+ pNew = YYREALLOC(p->yystack, newSize*sizeof(pNew[0]));
311
+ if( pNew==0 ) return 1;
312
+ }
313
+ p->yystack = pNew;
314
+ p->yytos = &p->yystack[idx];
315
+#ifndef NDEBUG
316
+ if( yyTraceFILE ){
317
+ fprintf(yyTraceFILE,"%sStack grows from %d to %d entries.\n",
318
+ yyTracePrompt, oldSize, newSize);
319
+ }
320
+#endif
321
+ p->yystackEnd = &p->yystack[newSize-1];
322
+ return 0;
323
+}
324
+#endif /* YYGROWABLESTACK */
325
+
326
+#if !YYGROWABLESTACK
327
+/* For builds that do no have a growable stack, yyGrowStack always
328
+** returns an error.
329
+*/
330
+# define yyGrowStack(X) 1
331
+#endif
332
+
333
+/* Datatype of the argument to the memory allocated passed as the
334
+** second argument to ParseAlloc() below. This can be changed by
335
+** putting an appropriate #define in the %include section of the input
336
+** grammar.
337
+*/
338
+#ifndef YYMALLOCARGTYPE
339
+# define YYMALLOCARGTYPE size_t
340
+#endif
341
+
342
+/* Initialize a new parser that has already been allocated.
343
+*/
344
+void ParseInit(void *yypRawParser ParseCTX_PDECL){
345
+ yyParser *yypParser = (yyParser*)yypRawParser;
346
+ ParseCTX_STORE
347
+#ifdef YYTRACKMAXSTACKDEPTH
348
+ yypParser->yyhwm = 0;
349
+#endif
350
+ yypParser->yystack = yypParser->yystk0;
351
+ yypParser->yystackEnd = &yypParser->yystack[YYSTACKDEPTH-1];
352
+#ifndef YYNOERRORRECOVERY
353
+ yypParser->yyerrcnt = -1;
354
+#endif
355
+ yypParser->yytos = yypParser->yystack;
356
+ yypParser->yystack[0].stateno = 0;
357
+ yypParser->yystack[0].major = 0;
358
+}
359
+
360
+#ifndef Parse_ENGINEALWAYSONSTACK
361
+/*
362
+** This function allocates a new parser.
363
+** The only argument is a pointer to a function which works like
364
+** malloc.
365
+**
366
+** Inputs:
367
+** A pointer to the function used to allocate memory.
368
+**
369
+** Outputs:
370
+** A pointer to a parser. This pointer is used in subsequent calls
371
+** to Parse and ParseFree.
372
+*/
373
+void *ParseAlloc(void *(*mallocProc)(YYMALLOCARGTYPE) ParseCTX_PDECL){
374
+ yyParser *yypParser;
375
+ yypParser = (yyParser*)(*mallocProc)( (YYMALLOCARGTYPE)sizeof(yyParser) );
376
+ if( yypParser ){
377
+ ParseCTX_STORE
378
+ ParseInit(yypParser ParseCTX_PARAM);
379
+ }
380
+ return (void*)yypParser;
381
+}
382
+#endif /* Parse_ENGINEALWAYSONSTACK */
383
+
384
+
385
+/* The following function deletes the "minor type" or semantic value
386
+** associated with a symbol. The symbol can be either a terminal
387
+** or nonterminal. "yymajor" is the symbol code, and "yypminor" is
388
+** a pointer to the value to be deleted. The code used to do the
389
+** deletions is derived from the %destructor and/or %token_destructor
390
+** directives of the input grammar.
391
+*/
392
+static void yy_destructor(
393
+ yyParser *yypParser, /* The parser */
394
+ YYCODETYPE yymajor, /* Type code for object to destroy */
395
+ YYMINORTYPE *yypminor /* The object to be destroyed */
396
+){
397
+ ParseARG_FETCH
398
+ ParseCTX_FETCH
399
+ switch( yymajor ){
400
+ /* Here is inserted the actions which take place when a
401
+ ** terminal or non-terminal is destroyed. This can happen
402
+ ** when the symbol is popped from the stack during a
403
+ ** reduce or during error processing or when a parser is
404
+ ** being destroyed before it is finished parsing.
405
+ **
406
+ ** Note: during a reduce, the only symbols destroyed are those
407
+ ** which appear on the RHS of the rule, but which are *not* used
408
+ ** inside the C code.
409
+ */
410
+/********* Begin destructor definitions ***************************************/
411
+%%
412
+/********* End destructor definitions *****************************************/
413
+ default: break; /* If no destructor action specified: do nothing */
414
+ }
415
+}
416
+
417
+/*
418
+** Pop the parser's stack once.
419
+**
420
+** If there is a destructor routine associated with the token which
421
+** is popped from the stack, then call it.
422
+*/
423
+static void yy_pop_parser_stack(yyParser *pParser){
424
+ yyStackEntry *yytos;
425
+ assert( pParser->yytos!=0 );
426
+ assert( pParser->yytos > pParser->yystack );
427
+ yytos = pParser->yytos--;
428
+#ifndef NDEBUG
429
+ if( yyTraceFILE ){
430
+ fprintf(yyTraceFILE,"%sPopping %s\n",
431
+ yyTracePrompt,
432
+ yyTokenName[yytos->major]);
433
+ }
434
+#endif
435
+ yy_destructor(pParser, yytos->major, &yytos->minor);
436
+}
437
+
438
+/*
439
+** Clear all secondary memory allocations from the parser
440
+*/
441
+void ParseFinalize(void *p){
442
+ yyParser *pParser = (yyParser*)p;
443
+
444
+ /* In-lined version of calling yy_pop_parser_stack() for each
445
+ ** element left in the stack */
446
+ yyStackEntry *yytos = pParser->yytos;
447
+ while( yytos>pParser->yystack ){
448
+#ifndef NDEBUG
449
+ if( yyTraceFILE ){
450
+ fprintf(yyTraceFILE,"%sPopping %s\n",
451
+ yyTracePrompt,
452
+ yyTokenName[yytos->major]);
453
+ }
454
+#endif
455
+ if( yytos->major>=YY_MIN_DSTRCTR ){
456
+ yy_destructor(pParser, yytos->major, &yytos->minor);
457
+ }
458
+ yytos--;
459
+ }
460
+
461
+#if YYGROWABLESTACK
462
+ if( pParser->yystack!=pParser->yystk0 ) YYFREE(pParser->yystack);
463
+#endif
464
+}
465
+
466
+#ifndef Parse_ENGINEALWAYSONSTACK
467
+/*
468
+** Deallocate and destroy a parser. Destructors are called for
469
+** all stack elements before shutting the parser down.
470
+**
471
+** If the YYPARSEFREENEVERNULL macro exists (for example because it
472
+** is defined in a %include section of the input grammar) then it is
473
+** assumed that the input pointer is never NULL.
474
+*/
475
+void ParseFree(
476
+ void *p, /* The parser to be deleted */
477
+ void (*freeProc)(void*) /* Function used to reclaim memory */
478
+){
479
+#ifndef YYPARSEFREENEVERNULL
480
+ if( p==0 ) return;
481
+#endif
482
+ ParseFinalize(p);
483
+ (*freeProc)(p);
484
+}
485
+#endif /* Parse_ENGINEALWAYSONSTACK */
486
+
487
+/*
488
+** Return the peak depth of the stack for a parser.
489
+*/
490
+#ifdef YYTRACKMAXSTACKDEPTH
491
+int ParseStackPeak(void *p){
492
+ yyParser *pParser = (yyParser*)p;
493
+ return pParser->yyhwm;
494
+}
495
+#endif
496
+
497
+/* This array of booleans keeps track of the parser statement
498
+** coverage. The element yycoverage[X][Y] is set when the parser
499
+** is in state X and has a lookahead token Y. In a well-tested
500
+** systems, every element of this matrix should end up being set.
501
+*/
502
+#if defined(YYCOVERAGE)
503
+static unsigned char yycoverage[YYNSTATE][YYNTOKEN];
504
+#endif
505
+
506
+/*
507
+** Write into out a description of every state/lookahead combination that
508
+**
509
+** (1) has not been used by the parser, and
510
+** (2) is not a syntax error.
511
+**
512
+** Return the number of missed state/lookahead combinations.
513
+*/
514
+#if defined(YYCOVERAGE)
515
+int ParseCoverage(FILE *out){
516
+ int stateno, iLookAhead, i;
517
+ int nMissed = 0;
518
+ for(stateno=0; stateno<YYNSTATE; stateno++){
519
+ i = yy_shift_ofst[stateno];
520
+ for(iLookAhead=0; iLookAhead<YYNTOKEN; iLookAhead++){
521
+ if( yy_lookahead[i+iLookAhead]!=iLookAhead ) continue;
522
+ if( yycoverage[stateno][iLookAhead]==0 ) nMissed++;
523
+ if( out ){
524
+ fprintf(out,"State %d lookahead %s %s\n", stateno,
525
+ yyTokenName[iLookAhead],
526
+ yycoverage[stateno][iLookAhead] ? "ok" : "missed");
527
+ }
528
+ }
529
+ }
530
+ return nMissed;
531
+}
532
+#endif
533
+
534
+/*
535
+** Find the appropriate action for a parser given the terminal
536
+** look-ahead token iLookAhead.
537
+*/
538
+static YYACTIONTYPE yy_find_shift_action(
539
+ YYCODETYPE iLookAhead, /* The look-ahead token */
540
+ YYACTIONTYPE stateno /* Current state number */
541
+){
542
+ int i;
543
+
544
+ if( stateno>YY_MAX_SHIFT ) return stateno;
545
+ assert( stateno <= YY_SHIFT_COUNT );
546
+#if defined(YYCOVERAGE)
547
+ yycoverage[stateno][iLookAhead] = 1;
548
+#endif
549
+ do{
550
+ i = yy_shift_ofst[stateno];
551
+ assert( i>=0 );
552
+ assert( i<=YY_ACTTAB_COUNT );
553
+ assert( i+YYNTOKEN<=(int)YY_NLOOKAHEAD );
554
+ assert( iLookAhead!=YYNOCODE );
555
+ assert( iLookAhead < YYNTOKEN );
556
+ i += iLookAhead;
557
+ assert( i<(int)YY_NLOOKAHEAD );
558
+ if( yy_lookahead[i]!=iLookAhead ){
559
+#ifdef YYFALLBACK
560
+ YYCODETYPE iFallback; /* Fallback token */
561
+ assert( iLookAhead<sizeof(yyFallback)/sizeof(yyFallback[0]) );
562
+ iFallback = yyFallback[iLookAhead];
563
+ if( iFallback!=0 ){
564
+#ifndef NDEBUG
565
+ if( yyTraceFILE ){
566
+ fprintf(yyTraceFILE, "%sFALLBACK %s => %s\n",
567
+ yyTracePrompt, yyTokenName[iLookAhead], yyTokenName[iFallback]);
568
+ }
569
+#endif
570
+ assert( yyFallback[iFallback]==0 ); /* Fallback loop must terminate */
571
+ iLookAhead = iFallback;
572
+ continue;
573
+ }
574
+#endif
575
+#ifdef YYWILDCARD
576
+ {
577
+ int j = i - iLookAhead + YYWILDCARD;
578
+ assert( j<(int)(sizeof(yy_lookahead)/sizeof(yy_lookahead[0])) );
579
+ if( yy_lookahead[j]==YYWILDCARD && iLookAhead>0 ){
580
+#ifndef NDEBUG
581
+ if( yyTraceFILE ){
582
+ fprintf(yyTraceFILE, "%sWILDCARD %s => %s\n",
583
+ yyTracePrompt, yyTokenName[iLookAhead],
584
+ yyTokenName[YYWILDCARD]);
585
+ }
586
+#endif /* NDEBUG */
587
+ return yy_action[j];
588
+ }
589
+ }
590
+#endif /* YYWILDCARD */
591
+ return yy_default[stateno];
592
+ }else{
593
+ assert( i>=0 && i<(int)(sizeof(yy_action)/sizeof(yy_action[0])) );
594
+ return yy_action[i];
595
+ }
596
+ }while(1);
597
+}
598
+
599
+/*
600
+** Find the appropriate action for a parser given the non-terminal
601
+** look-ahead token iLookAhead.
602
+*/
603
+static YYACTIONTYPE yy_find_reduce_action(
604
+ YYACTIONTYPE stateno, /* Current state number */
605
+ YYCODETYPE iLookAhead /* The look-ahead token */
606
+){
607
+ int i;
608
+#ifdef YYERRORSYMBOL
609
+ if( stateno>YY_REDUCE_COUNT ){
610
+ return yy_default[stateno];
611
+ }
612
+#else
613
+ assert( stateno<=YY_REDUCE_COUNT );
614
+#endif
615
+ i = yy_reduce_ofst[stateno];
616
+ assert( iLookAhead!=YYNOCODE );
617
+ i += iLookAhead;
618
+#ifdef YYERRORSYMBOL
619
+ if( i<0 || i>=YY_ACTTAB_COUNT || yy_lookahead[i]!=iLookAhead ){
620
+ return yy_default[stateno];
621
+ }
622
+#else
623
+ assert( i>=0 && i<YY_ACTTAB_COUNT );
624
+ assert( yy_lookahead[i]==iLookAhead );
625
+#endif
626
+ return yy_action[i];
627
+}
628
+
629
+/*
630
+** The following routine is called if the stack overflows.
631
+*/
632
+static void yyStackOverflow(yyParser *yypParser){
633
+ ParseARG_FETCH
634
+ ParseCTX_FETCH
635
+#ifndef NDEBUG
636
+ if( yyTraceFILE ){
637
+ fprintf(yyTraceFILE,"%sStack Overflow!\n",yyTracePrompt);
638
+ }
639
+#endif
640
+ while( yypParser->yytos>yypParser->yystack ) yy_pop_parser_stack(yypParser);
641
+ /* Here code is inserted which will execute if the parser
642
+ ** stack every overflows */
643
+/******** Begin %stack_overflow code ******************************************/
644
+%%
645
+/******** End %stack_overflow code ********************************************/
646
+ ParseARG_STORE /* Suppress warning about unused %extra_argument var */
647
+ ParseCTX_STORE
648
+}
649
+
650
+/*
651
+** Print tracing information for a SHIFT action
652
+*/
653
+#ifndef NDEBUG
654
+static void yyTraceShift(yyParser *yypParser, int yyNewState, const char *zTag){
655
+ if( yyTraceFILE ){
656
+ if( yyNewState<YYNSTATE ){
657
+ fprintf(yyTraceFILE,"%s%s '%s', go to state %d\n",
658
+ yyTracePrompt, zTag, yyTokenName[yypParser->yytos->major],
659
+ yyNewState);
660
+ }else{
661
+ fprintf(yyTraceFILE,"%s%s '%s', pending reduce %d\n",
662
+ yyTracePrompt, zTag, yyTokenName[yypParser->yytos->major],
663
+ yyNewState - YY_MIN_REDUCE);
664
+ }
665
+ }
666
+}
667
+#else
668
+# define yyTraceShift(X,Y,Z)
669
+#endif
670
+
671
+/*
672
+** Perform a shift action.
673
+*/
674
+static void yy_shift(
675
+ yyParser *yypParser, /* The parser to be shifted */
676
+ YYACTIONTYPE yyNewState, /* The new state to shift in */
677
+ YYCODETYPE yyMajor, /* The major token to shift in */
678
+ ParseTOKENTYPE yyMinor /* The minor token to shift in */
679
+){
680
+ yyStackEntry *yytos;
681
+ yypParser->yytos++;
682
+#ifdef YYTRACKMAXSTACKDEPTH
683
+ if( (int)(yypParser->yytos - yypParser->yystack)>yypParser->yyhwm ){
684
+ yypParser->yyhwm++;
685
+ assert( yypParser->yyhwm == (int)(yypParser->yytos - yypParser->yystack) );
686
+ }
687
+#endif
688
+ yytos = yypParser->yytos;
689
+ if( yytos>yypParser->yystackEnd ){
690
+ if( yyGrowStack(yypParser) ){
691
+ yypParser->yytos--;
692
+ yyStackOverflow(yypParser);
693
+ return;
694
+ }
695
+ yytos = yypParser->yytos;
696
+ assert( yytos <= yypParser->yystackEnd );
697
+ }
698
+ if( yyNewState > YY_MAX_SHIFT ){
699
+ yyNewState += YY_MIN_REDUCE - YY_MIN_SHIFTREDUCE;
700
+ }
701
+ yytos->stateno = yyNewState;
702
+ yytos->major = yyMajor;
703
+ yytos->minor.yy0 = yyMinor;
704
+ yyTraceShift(yypParser, yyNewState, "Shift");
705
+}
706
+
707
+/* For rule J, yyRuleInfoLhs[J] contains the symbol on the left-hand side
708
+** of that rule */
709
+static const YYCODETYPE yyRuleInfoLhs[] = {
710
+%%
711
+};
712
+
713
+/* For rule J, yyRuleInfoNRhs[J] contains the negative of the number
714
+** of symbols on the right-hand side of that rule. */
715
+static const signed char yyRuleInfoNRhs[] = {
716
+%%
717
+};
718
+
719
+static void yy_accept(yyParser*); /* Forward Declaration */
720
+
721
+/*
722
+** Perform a reduce action and the shift that must immediately
723
+** follow the reduce.
724
+**
725
+** The yyLookahead and yyLookaheadToken parameters provide reduce actions
726
+** access to the lookahead token (if any). The yyLookahead will be YYNOCODE
727
+** if the lookahead token has already been consumed. As this procedure is
728
+** only called from one place, optimizing compilers will in-line it, which
729
+** means that the extra parameters have no performance impact.
730
+*/
731
+static YYACTIONTYPE yy_reduce(
732
+ yyParser *yypParser, /* The parser */
733
+ unsigned int yyruleno, /* Number of the rule by which to reduce */
734
+ int yyLookahead, /* Lookahead token, or YYNOCODE if none */
735
+ ParseTOKENTYPE yyLookaheadToken /* Value of the lookahead token */
736
+ ParseCTX_PDECL /* %extra_context */
737
+){
738
+ int yygoto; /* The next state */
739
+ YYACTIONTYPE yyact; /* The next action */
740
+ yyStackEntry *yymsp; /* The top of the parser's stack */
741
+ int yysize; /* Amount to pop the stack */
742
+ ParseARG_FETCH
743
+ (void)yyLookahead;
744
+ (void)yyLookaheadToken;
745
+ yymsp = yypParser->yytos;
746
+
747
+ switch( yyruleno ){
748
+ /* Beginning here are the reduction cases. A typical example
749
+ ** follows:
750
+ ** case 0:
751
+ ** #line <lineno> <grammarfile>
752
+ ** { ... } // User supplied code
753
+ ** #line <lineno> <thisfile>
754
+ ** break;
755
+ */
756
+/********** Begin reduce actions **********************************************/
757
+%%
758
+/********** End reduce actions ************************************************/
759
+ };
760
+ assert( yyruleno<sizeof(yyRuleInfoLhs)/sizeof(yyRuleInfoLhs[0]) );
761
+ yygoto = yyRuleInfoLhs[yyruleno];
762
+ yysize = yyRuleInfoNRhs[yyruleno];
763
+ yyact = yy_find_reduce_action(yymsp[yysize].stateno,(YYCODETYPE)yygoto);
764
+
765
+ /* There are no SHIFTREDUCE actions on nonterminals because the table
766
+ ** generator has simplified them to pure REDUCE actions. */
767
+ assert( !(yyact>YY_MAX_SHIFT && yyact<=YY_MAX_SHIFTREDUCE) );
768
+
769
+ /* It is not possible for a REDUCE to be followed by an error */
770
+ assert( yyact!=YY_ERROR_ACTION );
771
+
772
+ yymsp += yysize+1;
773
+ yypParser->yytos = yymsp;
774
+ yymsp->stateno = (YYACTIONTYPE)yyact;
775
+ yymsp->major = (YYCODETYPE)yygoto;
776
+ yyTraceShift(yypParser, yyact, "... then shift");
777
+ return yyact;
778
+}
779
+
780
+/*
781
+** The following code executes when the parse fails
782
+*/
783
+#ifndef YYNOERRORRECOVERY
784
+static void yy_parse_failed(
785
+ yyParser *yypParser /* The parser */
786
+){
787
+ ParseARG_FETCH
788
+ ParseCTX_FETCH
789
+#ifndef NDEBUG
790
+ if( yyTraceFILE ){
791
+ fprintf(yyTraceFILE,"%sFail!\n",yyTracePrompt);
792
+ }
793
+#endif
794
+ while( yypParser->yytos>yypParser->yystack ) yy_pop_parser_stack(yypParser);
795
+ /* Here code is inserted which will be executed whenever the
796
+ ** parser fails */
797
+/************ Begin %parse_failure code ***************************************/
798
+%%
799
+/************ End %parse_failure code *****************************************/
800
+ ParseARG_STORE /* Suppress warning about unused %extra_argument variable */
801
+ ParseCTX_STORE
802
+}
803
+#endif /* YYNOERRORRECOVERY */
804
+
805
+/*
806
+** The following code executes when a syntax error first occurs.
807
+*/
808
+static void yy_syntax_error(
809
+ yyParser *yypParser, /* The parser */
810
+ int yymajor, /* The major type of the error token */
811
+ ParseTOKENTYPE yyminor /* The minor type of the error token */
812
+){
813
+ ParseARG_FETCH
814
+ ParseCTX_FETCH
815
+#define TOKEN yyminor
816
+/************ Begin %syntax_error code ****************************************/
817
+%%
818
+/************ End %syntax_error code ******************************************/
819
+ ParseARG_STORE /* Suppress warning about unused %extra_argument variable */
820
+ ParseCTX_STORE
821
+}
822
+
823
+/*
824
+** The following is executed when the parser accepts
825
+*/
826
+static void yy_accept(
827
+ yyParser *yypParser /* The parser */
828
+){
829
+ ParseARG_FETCH
830
+ ParseCTX_FETCH
831
+#ifndef NDEBUG
832
+ if( yyTraceFILE ){
833
+ fprintf(yyTraceFILE,"%sAccept!\n",yyTracePrompt);
834
+ }
835
+#endif
836
+#ifndef YYNOERRORRECOVERY
837
+ yypParser->yyerrcnt = -1;
838
+#endif
839
+ assert( yypParser->yytos==yypParser->yystack );
840
+ /* Here code is inserted which will be executed whenever the
841
+ ** parser accepts */
842
+/*********** Begin %parse_accept code *****************************************/
843
+%%
844
+/*********** End %parse_accept code *******************************************/
845
+ ParseARG_STORE /* Suppress warning about unused %extra_argument variable */
846
+ ParseCTX_STORE
847
+}
848
+
849
+/* The main parser program.
850
+** The first argument is a pointer to a structure obtained from
851
+** "ParseAlloc" which describes the current state of the parser.
852
+** The second argument is the major token number. The third is
853
+** the minor token. The fourth optional argument is whatever the
854
+** user wants (and specified in the grammar) and is available for
855
+** use by the action routines.
856
+**
857
+** Inputs:
858
+** <ul>
859
+** <li> A pointer to the parser (an opaque structure.)
860
+** <li> The major token number.
861
+** <li> The minor token number.
862
+** <li> An option argument of a grammar-specified type.
863
+** </ul>
864
+**
865
+** Outputs:
866
+** None.
867
+*/
868
+void Parse(
869
+ void *yyp, /* The parser */
870
+ int yymajor, /* The major token code number */
871
+ ParseTOKENTYPE yyminor /* The value for the token */
872
+ ParseARG_PDECL /* Optional %extra_argument parameter */
873
+){
874
+ YYMINORTYPE yyminorunion;
875
+ YYACTIONTYPE yyact; /* The parser action. */
876
+#if !defined(YYERRORSYMBOL) && !defined(YYNOERRORRECOVERY)
877
+ int yyendofinput; /* True if we are at the end of input */
878
+#endif
879
+#ifdef YYERRORSYMBOL
880
+ int yyerrorhit = 0; /* True if yymajor has invoked an error */
881
+#endif
882
+ yyParser *yypParser = (yyParser*)yyp; /* The parser */
883
+ ParseCTX_FETCH
884
+ ParseARG_STORE
885
+
886
+ assert( yypParser->yytos!=0 );
887
+#if !defined(YYERRORSYMBOL) && !defined(YYNOERRORRECOVERY)
888
+ yyendofinput = (yymajor==0);
889
+#endif
890
+
891
+ yyact = yypParser->yytos->stateno;
892
+#ifndef NDEBUG
893
+ if( yyTraceFILE ){
894
+ if( yyact < YY_MIN_REDUCE ){
895
+ fprintf(yyTraceFILE,"%sInput '%s' in state %d\n",
896
+ yyTracePrompt,yyTokenName[yymajor],yyact);
897
+ }else{
898
+ fprintf(yyTraceFILE,"%sInput '%s' with pending reduce %d\n",
899
+ yyTracePrompt,yyTokenName[yymajor],yyact-YY_MIN_REDUCE);
900
+ }
901
+ }
902
+#endif
903
+
904
+ while(1){ /* Exit by "break" */
905
+ assert( yypParser->yytos>=yypParser->yystack );
906
+ assert( yyact==yypParser->yytos->stateno );
907
+ yyact = yy_find_shift_action((YYCODETYPE)yymajor,yyact);
908
+ if( yyact >= YY_MIN_REDUCE ){
909
+ unsigned int yyruleno = yyact - YY_MIN_REDUCE; /* Reduce by this rule */
910
+#ifndef NDEBUG
911
+ assert( yyruleno<(int)(sizeof(yyRuleName)/sizeof(yyRuleName[0])) );
912
+ if( yyTraceFILE ){
913
+ int yysize = yyRuleInfoNRhs[yyruleno];
914
+ if( yysize ){
915
+ fprintf(yyTraceFILE, "%sReduce %d [%s]%s, pop back to state %d.\n",
916
+ yyTracePrompt,
917
+ yyruleno, yyRuleName[yyruleno],
918
+ yyruleno<YYNRULE_WITH_ACTION ? "" : " without external action",
919
+ yypParser->yytos[yysize].stateno);
920
+ }else{
921
+ fprintf(yyTraceFILE, "%sReduce %d [%s]%s.\n",
922
+ yyTracePrompt, yyruleno, yyRuleName[yyruleno],
923
+ yyruleno<YYNRULE_WITH_ACTION ? "" : " without external action");
924
+ }
925
+ }
926
+#endif /* NDEBUG */
927
+
928
+ /* Check that the stack is large enough to grow by a single entry
929
+ ** if the RHS of the rule is empty. This ensures that there is room
930
+ ** enough on the stack to push the LHS value */
931
+ if( yyRuleInfoNRhs[yyruleno]==0 ){
932
+#ifdef YYTRACKMAXSTACKDEPTH
933
+ if( (int)(yypParser->yytos - yypParser->yystack)>yypParser->yyhwm ){
934
+ yypParser->yyhwm++;
935
+ assert( yypParser->yyhwm ==
936
+ (int)(yypParser->yytos - yypParser->yystack));
937
+ }
938
+#endif
939
+ if( yypParser->yytos>=yypParser->yystackEnd ){
940
+ if( yyGrowStack(yypParser) ){
941
+ yyStackOverflow(yypParser);
942
+ break;
943
+ }
944
+ }
945
+ }
946
+ yyact = yy_reduce(yypParser,yyruleno,yymajor,yyminor ParseCTX_PARAM);
947
+ }else if( yyact <= YY_MAX_SHIFTREDUCE ){
948
+ yy_shift(yypParser,yyact,(YYCODETYPE)yymajor,yyminor);
949
+#ifndef YYNOERRORRECOVERY
950
+ yypParser->yyerrcnt--;
951
+#endif
952
+ break;
953
+ }else if( yyact==YY_ACCEPT_ACTION ){
954
+ yypParser->yytos--;
955
+ yy_accept(yypParser);
956
+ return;
957
+ }else{
958
+ assert( yyact == YY_ERROR_ACTION );
959
+ yyminorunion.yy0 = yyminor;
960
+#ifdef YYERRORSYMBOL
961
+ int yymx;
962
+#endif
963
+#ifndef NDEBUG
964
+ if( yyTraceFILE ){
965
+ fprintf(yyTraceFILE,"%sSyntax Error!\n",yyTracePrompt);
966
+ }
967
+#endif
968
+#ifdef YYERRORSYMBOL
969
+ /* A syntax error has occurred.
970
+ ** The response to an error depends upon whether or not the
971
+ ** grammar defines an error token "ERROR".
972
+ **
973
+ ** This is what we do if the grammar does define ERROR:
974
+ **
975
+ ** * Call the %syntax_error function.
976
+ **
977
+ ** * Begin popping the stack until we enter a state where
978
+ ** it is legal to shift the error symbol, then shift
979
+ ** the error symbol.
980
+ **
981
+ ** * Set the error count to three.
982
+ **
983
+ ** * Begin accepting and shifting new tokens. No new error
984
+ ** processing will occur until three tokens have been
985
+ ** shifted successfully.
986
+ **
987
+ */
988
+ if( yypParser->yyerrcnt<0 ){
989
+ yy_syntax_error(yypParser,yymajor,yyminor);
990
+ }
991
+ yymx = yypParser->yytos->major;
992
+ if( yymx==YYERRORSYMBOL || yyerrorhit ){
993
+#ifndef NDEBUG
994
+ if( yyTraceFILE ){
995
+ fprintf(yyTraceFILE,"%sDiscard input token %s\n",
996
+ yyTracePrompt,yyTokenName[yymajor]);
997
+ }
998
+#endif
999
+ yy_destructor(yypParser, (YYCODETYPE)yymajor, &yyminorunion);
1000
+ yymajor = YYNOCODE;
1001
+ }else{
1002
+ while( yypParser->yytos > yypParser->yystack ){
1003
+ yyact = yy_find_reduce_action(yypParser->yytos->stateno,
1004
+ YYERRORSYMBOL);
1005
+ if( yyact<=YY_MAX_SHIFTREDUCE ) break;
1006
+ yy_pop_parser_stack(yypParser);
1007
+ }
1008
+ if( yypParser->yytos <= yypParser->yystack || yymajor==0 ){
1009
+ yy_destructor(yypParser,(YYCODETYPE)yymajor,&yyminorunion);
1010
+ yy_parse_failed(yypParser);
1011
+#ifndef YYNOERRORRECOVERY
1012
+ yypParser->yyerrcnt = -1;
1013
+#endif
1014
+ yymajor = YYNOCODE;
1015
+ }else if( yymx!=YYERRORSYMBOL ){
1016
+ yy_shift(yypParser,yyact,YYERRORSYMBOL,yyminor);
1017
+ }
1018
+ }
1019
+ yypParser->yyerrcnt = 3;
1020
+ yyerrorhit = 1;
1021
+ if( yymajor==YYNOCODE ) break;
1022
+ yyact = yypParser->yytos->stateno;
1023
+#elif defined(YYNOERRORRECOVERY)
1024
+ /* If the YYNOERRORRECOVERY macro is defined, then do not attempt to
1025
+ ** do any kind of error recovery. Instead, simply invoke the syntax
1026
+ ** error routine and continue going as if nothing had happened.
1027
+ **
1028
+ ** Applications can set this macro (for example inside %include) if
1029
+ ** they intend to abandon the parse upon the first syntax error seen.
1030
+ */
1031
+ yy_syntax_error(yypParser,yymajor, yyminor);
1032
+ yy_destructor(yypParser,(YYCODETYPE)yymajor,&yyminorunion);
1033
+ break;
1034
+#else /* YYERRORSYMBOL is not defined */
1035
+ /* This is what we do if the grammar does not define ERROR:
1036
+ **
1037
+ ** * Report an error message, and throw away the input token.
1038
+ **
1039
+ ** * If the input token is $, then fail the parse.
1040
+ **
1041
+ ** As before, subsequent error messages are suppressed until
1042
+ ** three input tokens have been successfully shifted.
1043
+ */
1044
+ if( yypParser->yyerrcnt<=0 ){
1045
+ yy_syntax_error(yypParser,yymajor, yyminor);
1046
+ }
1047
+ yypParser->yyerrcnt = 3;
1048
+ yy_destructor(yypParser,(YYCODETYPE)yymajor,&yyminorunion);
1049
+ if( yyendofinput ){
1050
+ yy_parse_failed(yypParser);
1051
+#ifndef YYNOERRORRECOVERY
1052
+ yypParser->yyerrcnt = -1;
1053
+#endif
1054
+ }
1055
+ break;
1056
+#endif
1057
+ }
1058
+ }
1059
+#ifndef NDEBUG
1060
+ if( yyTraceFILE ){
1061
+ yyStackEntry *i;
1062
+ char cDiv = '[';
1063
+ fprintf(yyTraceFILE,"%sReturn. Stack=",yyTracePrompt);
1064
+ for(i=&yypParser->yystack[1]; i<=yypParser->yytos; i++){
1065
+ fprintf(yyTraceFILE,"%c%s", cDiv, yyTokenName[i->major]);
1066
+ cDiv = ' ';
1067
+ }
1068
+ fprintf(yyTraceFILE,"]\n");
1069
+ }
1070
+#endif
1071
+ return;
1072
+}
1073
+
1074
+/*
1075
+** Return the fallback token corresponding to canonical token iToken, or
1076
+** 0 if iToken has no fallback.
1077
+*/
1078
+int ParseFallback(int iToken){
1079
+#ifdef YYFALLBACK
1080
+ assert( iToken<(int)(sizeof(yyFallback)/sizeof(yyFallback[0])) );
1081
+ return yyFallback[iToken];
1082
+#else
1083
+ (void)iToken;
1084
+ return 0;
1085
+#endif
1086
+}
src/libnetdata/eval/re2c_lemon/lexer.c
new
+731
@@ -0,0 +1,731 @@
1
+/* Generated by re2c 4.1 on Wed Apr 16 21:13:47 2025 */
2
+#line 1 "lexer.re"
3
+/**
4
+ * re2c lexer for Netdata's expression evaluator
5
+ *
6
+ * This implementation uses re2c for lexical analysis and lemon for parsing.
7
+ * It is fully integrated with Netdata's existing EVAL_NODE structure.
8
+ */
9
+
10
+#include "../eval-internal.h"
11
+#include "parser_internal.h"
12
+
13
+// Scanner functions implementation
14
+void scanner_init(Scanner *s, const char *input) {
15
+ if (!input) {
16
+ // Handle NULL input safely
17
+ s->cursor = "";
18
+ s->marker = s->cursor;
19
+ s->token = s->cursor;
20
+ s->limit = s->cursor;
21
+ s->line = 1;
22
+ s->error = 1; // Set error flag for NULL input
23
+ return;
24
+ }
25
+
26
+ s->cursor = input;
27
+ s->marker = s->cursor;
28
+ s->token = s->cursor;
29
+ s->limit = s->cursor + strlen(s->cursor);
30
+ s->line = 1;
31
+ s->error = 0; // Initialize error flag
32
+}
33
+
34
+int scan(Scanner *s, YYSTYPE *lval) {
35
+ const char *YYMARKER;
36
+ const char *YYCURSOR = s->cursor;
37
+ char variable_buffer[EVAL_MAX_VARIABLE_NAME_LENGTH + 1] = {0};
38
+
39
+ // Skip whitespace
40
+ while (1) {
41
+ s->token = YYCURSOR;
42
+
43
+
44
+#line 45 "lexer.c"
45
+{
46
+ char yych;
47
+ unsigned int yyaccept = 0;
48
+ yych = *YYCURSOR;
49
+ switch (yych) {
50
+ case 0x00: goto yy1;
51
+ case '\t':
52
+ case '\n':
53
+ case '\r':
54
+ case ' ': goto yy4;
55
+ case '!': goto yy6;
56
+ case '$': goto yy8;
57
+ case '%': goto yy9;
58
+ case '&': goto yy10;
59
+ case '(': goto yy11;
60
+ case ')': goto yy12;
61
+ case '*': goto yy13;
62
+ case '+': goto yy14;
63
+ case '-': goto yy15;
64
+ case '.': goto yy16;
65
+ case '/': goto yy17;
66
+ case '0':
67
+ case '1':
68
+ case '2':
69
+ case '3':
70
+ case '4':
71
+ case '5':
72
+ case '6':
73
+ case '7':
74
+ case '8':
75
+ case '9': goto yy18;
76
+ case ':': goto yy20;
77
+ case '<': goto yy21;
78
+ case '=': goto yy23;
79
+ case '>': goto yy25;
80
+ case '?': goto yy27;
81
+ case 'A':
82
+ case 'a': goto yy28;
83
+ case 'I':
84
+ case 'i': goto yy29;
85
+ case 'N':
86
+ case 'n': goto yy30;
87
+ case 'O':
88
+ case 'o': goto yy31;
89
+ case '|': goto yy32;
90
+ default: goto yy2;
91
+ }
92
+yy1:
93
+ ++YYCURSOR;
94
+#line 158 "lexer.re"
95
+ { s->cursor = YYCURSOR; return 0; }
96
+#line 97 "lexer.c"
97
+yy2:
98
+ ++YYCURSOR;
99
+yy3:
100
+#line 161 "lexer.re"
101
+ {
102
+ s->cursor = YYCURSOR;
103
+ s->error = 1; // Set error flag
104
+ return 0; // Return 0 to stop parsing
105
+ }
106
+#line 107 "lexer.c"
107
+yy4:
108
+ yych = *++YYCURSOR;
109
+ switch (yych) {
110
+ case '\t':
111
+ case '\n':
112
+ case '\r':
113
+ case ' ': goto yy4;
114
+ default: goto yy5;
115
+ }
116
+yy5:
117
+#line 46 "lexer.re"
118
+ { continue; }
119
+#line 120 "lexer.c"
120
+yy6:
121
+ yych = *++YYCURSOR;
122
+ switch (yych) {
123
+ case '=': goto yy33;
124
+ default: goto yy7;
125
+ }
126
+yy7:
127
+#line 129 "lexer.re"
128
+ {
129
+ s->cursor = YYCURSOR;
130
+ return TOK_NOT;
131
+ }
132
+#line 133 "lexer.c"
133
+yy8:
134
+ yych = *++YYCURSOR;
135
+ switch (yych) {
136
+ case 0x00:
137
+ case '\t':
138
+ case '\n':
139
+ case '\r':
140
+ case ' ':
141
+ case '!':
142
+ case '%':
143
+ case '&':
144
+ case '(':
145
+ case ')':
146
+ case '*':
147
+ case '+':
148
+ case '-':
149
+ case '/':
150
+ case '<':
151
+ case '=':
152
+ case '>':
153
+ case '?':
154
+ case '|':
155
+ case '}': goto yy3;
156
+ case '{': goto yy36;
157
+ default: goto yy34;
158
+ }
159
+yy9:
160
+ ++YYCURSOR;
161
+#line 115 "lexer.re"
162
+ { s->cursor = YYCURSOR; return TOK_MODULO; }
163
+#line 164 "lexer.c"
164
+yy10:
165
+ yych = *++YYCURSOR;
166
+ switch (yych) {
167
+ case '&': goto yy38;
168
+ default: goto yy3;
169
+ }
170
+yy11:
171
+ ++YYCURSOR;
172
+#line 147 "lexer.re"
173
+ { s->cursor = YYCURSOR; return TOK_LPAREN; }
174
+#line 175 "lexer.c"
175
+yy12:
176
+ ++YYCURSOR;
177
+#line 148 "lexer.re"
178
+ { s->cursor = YYCURSOR; return TOK_RPAREN; }
179
+#line 180 "lexer.c"
180
+yy13:
181
+ ++YYCURSOR;
182
+#line 113 "lexer.re"
183
+ { s->cursor = YYCURSOR; return TOK_MULTIPLY; }
184
+#line 185 "lexer.c"
185
+yy14:
186
+ ++YYCURSOR;
187
+#line 111 "lexer.re"
188
+ { s->cursor = YYCURSOR; return TOK_PLUS; }
189
+#line 190 "lexer.c"
190
+yy15:
191
+ ++YYCURSOR;
192
+#line 112 "lexer.re"
193
+ { s->cursor = YYCURSOR; return TOK_MINUS; }
194
+#line 195 "lexer.c"
195
+yy16:
196
+ yych = *++YYCURSOR;
197
+ switch (yych) {
198
+ case '0':
199
+ case '1':
200
+ case '2':
201
+ case '3':
202
+ case '4':
203
+ case '5':
204
+ case '6':
205
+ case '7':
206
+ case '8':
207
+ case '9': goto yy39;
208
+ default: goto yy3;
209
+ }
210
+yy17:
211
+ ++YYCURSOR;
212
+#line 114 "lexer.re"
213
+ { s->cursor = YYCURSOR; return TOK_DIVIDE; }
214
+#line 215 "lexer.c"
215
+yy18:
216
+ yyaccept = 0;
217
+ yych = *(YYMARKER = ++YYCURSOR);
218
+ switch (yych) {
219
+ case '.': goto yy39;
220
+ case '0':
221
+ case '1':
222
+ case '2':
223
+ case '3':
224
+ case '4':
225
+ case '5':
226
+ case '6':
227
+ case '7':
228
+ case '8':
229
+ case '9': goto yy18;
230
+ case 'E':
231
+ case 'e': goto yy40;
232
+ default: goto yy19;
233
+ }
234
+yy19:
235
+#line 71 "lexer.re"
236
+ {
237
+ char *endptr;
238
+ lval->dval = str2ndd(s->token, &endptr);
239
+ s->cursor = YYCURSOR;
240
+ return TOK_NUMBER;
241
+ }
242
+#line 243 "lexer.c"
243
+yy20:
244
+ ++YYCURSOR;
245
+#line 144 "lexer.re"
246
+ { s->cursor = YYCURSOR; return TOK_COLON; }
247
+#line 248 "lexer.c"
248
+yy21:
249
+ yych = *++YYCURSOR;
250
+ switch (yych) {
251
+ case '=': goto yy42;
252
+ case '>': goto yy33;
253
+ default: goto yy22;
254
+ }
255
+yy22:
256
+#line 137 "lexer.re"
257
+ { s->cursor = YYCURSOR; return TOK_LT; }
258
+#line 259 "lexer.c"
259
+yy23:
260
+ yych = *++YYCURSOR;
261
+ switch (yych) {
262
+ case '=': goto yy43;
263
+ default: goto yy24;
264
+ }
265
+yy24:
266
+#line 135 "lexer.re"
267
+ { s->cursor = YYCURSOR; return TOK_EQ; }
268
+#line 269 "lexer.c"
269
+yy25:
270
+ yych = *++YYCURSOR;
271
+ switch (yych) {
272
+ case '=': goto yy44;
273
+ default: goto yy26;
274
+ }
275
+yy26:
276
+#line 139 "lexer.re"
277
+ { s->cursor = YYCURSOR; return TOK_GT; }
278
+#line 279 "lexer.c"
279
+yy27:
280
+ ++YYCURSOR;
281
+#line 143 "lexer.re"
282
+ { s->cursor = YYCURSOR; return TOK_QMARK; }
283
+#line 284 "lexer.c"
284
+yy28:
285
+ yyaccept = 1;
286
+ yych = *(YYMARKER = ++YYCURSOR);
287
+ switch (yych) {
288
+ case 'B':
289
+ case 'b': goto yy45;
290
+ case 'N':
291
+ case 'n': goto yy46;
292
+ default: goto yy3;
293
+ }
294
+yy29:
295
+ yyaccept = 1;
296
+ yych = *(YYMARKER = ++YYCURSOR);
297
+ switch (yych) {
298
+ case 'N':
299
+ case 'n': goto yy47;
300
+ default: goto yy3;
301
+ }
302
+yy30:
303
+ yyaccept = 1;
304
+ yych = *(YYMARKER = ++YYCURSOR);
305
+ switch (yych) {
306
+ case 'A':
307
+ case 'a': goto yy48;
308
+ case 'O':
309
+ case 'o': goto yy49;
310
+ case 'U':
311
+ case 'u': goto yy50;
312
+ default: goto yy3;
313
+ }
314
+yy31:
315
+ yych = *++YYCURSOR;
316
+ switch (yych) {
317
+ case 'R':
318
+ case 'r': goto yy51;
319
+ default: goto yy3;
320
+ }
321
+yy32:
322
+ yych = *++YYCURSOR;
323
+ switch (yych) {
324
+ case '|': goto yy51;
325
+ default: goto yy3;
326
+ }
327
+yy33:
328
+ ++YYCURSOR;
329
+#line 136 "lexer.re"
330
+ { s->cursor = YYCURSOR; return TOK_NE; }
331
+#line 332 "lexer.c"
332
+yy34:
333
+ yych = *++YYCURSOR;
334
+ switch (yych) {
335
+ case 0x00:
336
+ case '\t':
337
+ case '\n':
338
+ case '\r':
339
+ case ' ':
340
+ case '!':
341
+ case '%':
342
+ case '&':
343
+ case '(':
344
+ case ')':
345
+ case '*':
346
+ case '+':
347
+ case '-':
348
+ case '/':
349
+ case '<':
350
+ case '=':
351
+ case '>':
352
+ case '?':
353
+ case '{':
354
+ case '|':
355
+ case '}': goto yy35;
356
+ default: goto yy34;
357
+ }
358
+yy35:
359
+#line 81 "lexer.re"
360
+ {
361
+ size_t len = YYCURSOR - s->token - 1; // -1 to skip the $
362
+ if (len >= EVAL_MAX_VARIABLE_NAME_LENGTH) {
363
+ len = EVAL_MAX_VARIABLE_NAME_LENGTH - 1;
364
+ }
365
+ memcpy(variable_buffer, s->token + 1, len);
366
+ variable_buffer[len] = '\0';
367
+ lval->strval = strdupz(variable_buffer);
368
+ s->cursor = YYCURSOR;
369
+ return TOK_VARIABLE;
370
+ }
371
+#line 372 "lexer.c"
372
+yy36:
373
+ yyaccept = 2;
374
+ yych = *(YYMARKER = ++YYCURSOR);
375
+ switch (yych) {
376
+ case 0x00: goto yy37;
377
+ case '}': goto yy53;
378
+ default: goto yy52;
379
+ }
380
+yy37:
381
+#line 155 "lexer.re"
382
+ { s->cursor = YYCURSOR; s->error = 1; return 0; }
383
+#line 384 "lexer.c"
384
+yy38:
385
+ ++YYCURSOR;
386
+#line 119 "lexer.re"
387
+ {
388
+ s->cursor = YYCURSOR;
389
+ return TOK_AND;
390
+ }
391
+#line 392 "lexer.c"
392
+yy39:
393
+ yyaccept = 0;
394
+ yych = *(YYMARKER = ++YYCURSOR);
395
+ switch (yych) {
396
+ case '0':
397
+ case '1':
398
+ case '2':
399
+ case '3':
400
+ case '4':
401
+ case '5':
402
+ case '6':
403
+ case '7':
404
+ case '8':
405
+ case '9': goto yy39;
406
+ case 'E':
407
+ case 'e': goto yy40;
408
+ default: goto yy19;
409
+ }
410
+yy40:
411
+ yych = *++YYCURSOR;
412
+ switch (yych) {
413
+ case '+':
414
+ case '-': goto yy54;
415
+ case '0':
416
+ case '1':
417
+ case '2':
418
+ case '3':
419
+ case '4':
420
+ case '5':
421
+ case '6':
422
+ case '7':
423
+ case '8':
424
+ case '9': goto yy55;
425
+ default: goto yy41;
426
+ }
427
+yy41:
428
+ YYCURSOR = YYMARKER;
429
+ switch (yyaccept) {
430
+ case 0: goto yy19;
431
+ case 1: goto yy3;
432
+ case 2: goto yy37;
433
+ default: goto yy58;
434
+ }
435
+yy42:
436
+ ++YYCURSOR;
437
+#line 138 "lexer.re"
438
+ { s->cursor = YYCURSOR; return TOK_LE; }
439
+#line 440 "lexer.c"
440
+yy43:
441
+ ++YYCURSOR;
442
+ goto yy24;
443
+yy44:
444
+ ++YYCURSOR;
445
+#line 140 "lexer.re"
446
+ { s->cursor = YYCURSOR; return TOK_GE; }
447
+#line 448 "lexer.c"
448
+yy45:
449
+ yych = *++YYCURSOR;
450
+ switch (yych) {
451
+ case 'S':
452
+ case 's': goto yy56;
453
+ default: goto yy41;
454
+ }
455
+yy46:
456
+ yych = *++YYCURSOR;
457
+ switch (yych) {
458
+ case 'D':
459
+ case 'd': goto yy38;
460
+ default: goto yy41;
461
+ }
462
+yy47:
463
+ yych = *++YYCURSOR;
464
+ switch (yych) {
465
+ case 'F':
466
+ case 'f': goto yy57;
467
+ default: goto yy41;
468
+ }
469
+yy48:
470
+ yych = *++YYCURSOR;
471
+ switch (yych) {
472
+ case 'N':
473
+ case 'n': goto yy59;
474
+ default: goto yy41;
475
+ }
476
+yy49:
477
+ yych = *++YYCURSOR;
478
+ switch (yych) {
479
+ case 'T':
480
+ case 't': goto yy60;
481
+ default: goto yy41;
482
+ }
483
+yy50:
484
+ yych = *++YYCURSOR;
485
+ switch (yych) {
486
+ case 'L':
487
+ case 'l': goto yy61;
488
+ default: goto yy41;
489
+ }
490
+yy51:
491
+ ++YYCURSOR;
492
+#line 124 "lexer.re"
493
+ {
494
+ s->cursor = YYCURSOR;
495
+ return TOK_OR;
496
+ }
497
+#line 498 "lexer.c"
498
+yy52:
499
+ yych = *++YYCURSOR;
500
+ switch (yych) {
501
+ case 0x00: goto yy41;
502
+ case '}': goto yy62;
503
+ default: goto yy52;
504
+ }
505
+yy53:
506
+ ++YYCURSOR;
507
+#line 94 "lexer.re"
508
+ { s->cursor = YYCURSOR; s->error = 1; return 0; }
509
+#line 510 "lexer.c"
510
+yy54:
511
+ yych = *++YYCURSOR;
512
+ switch (yych) {
513
+ case '0':
514
+ case '1':
515
+ case '2':
516
+ case '3':
517
+ case '4':
518
+ case '5':
519
+ case '6':
520
+ case '7':
521
+ case '8':
522
+ case '9': goto yy55;
523
+ default: goto yy41;
524
+ }
525
+yy55:
526
+ yych = *++YYCURSOR;
527
+ switch (yych) {
528
+ case '0':
529
+ case '1':
530
+ case '2':
531
+ case '3':
532
+ case '4':
533
+ case '5':
534
+ case '6':
535
+ case '7':
536
+ case '8':
537
+ case '9': goto yy55;
538
+ default: goto yy19;
539
+ }
540
+yy56:
541
+ ++YYCURSOR;
542
+#line 152 "lexer.re"
543
+ { s->cursor = YYCURSOR; return TOK_FUNCTION_ABS; }
544
+#line 545 "lexer.c"
545
+yy57:
546
+ yyaccept = 3;
547
+ yych = *(YYMARKER = ++YYCURSOR);
548
+ switch (yych) {
549
+ case 'I':
550
+ case 'i': goto yy63;
551
+ default: goto yy58;
552
+ }
553
+yy58:
554
+#line 59 "lexer.re"
555
+ {
556
+ lval->dval = INFINITY;
557
+ s->cursor = YYCURSOR;
558
+ return TOK_NUMBER;
559
+ }
560
+#line 561 "lexer.c"
561
+yy59:
562
+ ++YYCURSOR;
563
+#line 51 "lexer.re"
564
+ {
565
+ lval->dval = NAN;
566
+ s->cursor = YYCURSOR;
567
+ return TOK_NUMBER;
568
+ }
569
+#line 570 "lexer.c"
570
+yy60:
571
+ ++YYCURSOR;
572
+ goto yy7;
573
+yy61:
574
+ yych = *++YYCURSOR;
575
+ switch (yych) {
576
+ case 'L':
577
+ case 'l': goto yy59;
578
+ default: goto yy41;
579
+ }
580
+yy62:
581
+ ++YYCURSOR;
582
+#line 97 "lexer.re"
583
+ {
584
+ // Calculate length, excluding the ${ prefix and the } suffix
585
+ size_t len = YYCURSOR - s->token - 3; // -3 to skip ${ and }
586
+ if (len >= EVAL_MAX_VARIABLE_NAME_LENGTH) {
587
+ len = EVAL_MAX_VARIABLE_NAME_LENGTH - 1;
588
+ }
589
+ memcpy(variable_buffer, s->token + 2, len);
590
+ variable_buffer[len] = '\0';
591
+ lval->strval = strdupz(variable_buffer);
592
+ s->cursor = YYCURSOR;
593
+ return TOK_VARIABLE;
594
+ }
595
+#line 596 "lexer.c"
596
+yy63:
597
+ yych = *++YYCURSOR;
598
+ switch (yych) {
599
+ case 'N':
600
+ case 'n': goto yy64;
601
+ default: goto yy41;
602
+ }
603
+yy64:
604
+ yych = *++YYCURSOR;
605
+ switch (yych) {
606
+ case 'I':
607
+ case 'i': goto yy65;
608
+ default: goto yy41;
609
+ }
610
+yy65:
611
+ yych = *++YYCURSOR;
612
+ switch (yych) {
613
+ case 'T':
614
+ case 't': goto yy66;
615
+ default: goto yy41;
616
+ }
617
+yy66:
618
+ yych = *++YYCURSOR;
619
+ switch (yych) {
620
+ case 'Y':
621
+ case 'y': goto yy67;
622
+ default: goto yy41;
623
+ }
624
+yy67:
625
+ ++YYCURSOR;
626
+ goto yy58;
627
+}
628
+#line 166 "lexer.re"
629
+
630
+ }
631
+}
632
+
633
+// Function to parse an expression with re2c/lemon
634
+EVAL_NODE *parse_expression_with_re2c_lemon(const char *string, const char **failed_at, int *error) {
635
+ Scanner scanner;
636
+ scanner_init(&scanner, string);
637
+
638
+ if(failed_at)
639
+ *failed_at = NULL;
640
+
641
+ // Use ParseAlloc with mallocz instead of malloc - mallocz will handle allocation failures
642
+ void *parser = ParseAlloc(mallocz);
643
+
644
+ EVAL_NODE *result = NULL;
645
+
646
+ YYSTYPE token_value;
647
+ int token_type;
648
+
649
+ // Initialize error code
650
+ if (error) *error = EVAL_ERROR_OK;
651
+
652
+ // Save the token start position for error reporting
653
+ const char *error_pos = scanner.cursor;
654
+
655
+ // Variable to track if we need to free token_value.strval
656
+ int free_strval = 0;
657
+
658
+ while ((token_type = scan(&scanner, &token_value)) > 0) {
659
+ // If the token is a variable, remember to free it if there's an error
660
+ free_strval = (token_type == TOK_VARIABLE);
661
+
662
+ Parse(parser, token_type, token_value, &result);
663
+
664
+ // Save position before potential error
665
+ error_pos = scanner.token;
666
+
667
+ // Check for syntax errors after each token
668
+ if (result && result->operator == EVAL_OPERATOR_NOP && result->count == 0) {
669
+ // This is an error marker
670
+ if (error) *error = EVAL_ERROR_SYNTAX;
671
+ if (failed_at) {
672
+ *failed_at = error_pos;
673
+ }
674
+
675
+ // Clean up
676
+ eval_node_free(result);
677
+ ParseFree(parser, freez);
678
+
679
+ // If we just scanned a variable, free its strval
680
+ if (free_strval && token_value.strval) {
681
+ freez(token_value.strval);
682
+ }
683
+
684
+ return NULL;
685
+ }
686
+
687
+ // Reset free_strval since the parser has taken ownership of the string
688
+ free_strval = 0;
689
+ }
690
+
691
+ // If the last token was a variable and scanning stopped due to an error,
692
+ // we need to free the token_value.strval
693
+ if (free_strval && token_value.strval) {
694
+ freez(token_value.strval);
695
+ token_value.strval = NULL;
696
+ }
697
+
698
+ // Finish parsing
699
+ Parse(parser, 0, token_value, &result);
700
+
701
+ // Clean up the parser
702
+ ParseFree(parser, freez);
703
+
704
+ // Check for lexer errors
705
+ if (scanner.error) {
706
+ if (error) *error = EVAL_ERROR_UNKNOWN_OPERAND;
707
+ if (failed_at) {
708
+ *failed_at = error_pos;
709
+ }
710
+
711
+ // Clean up result if it was created
712
+ if (result) {
713
+ eval_node_free(result);
714
+ }
715
+
716
+ return NULL;
717
+ }
718
+
719
+ if (!result) {
720
+ if (error) *error = EVAL_ERROR_SYNTAX;
721
+ if (failed_at) {
722
+ *failed_at = error_pos;
723
+ }
724
+ return NULL;
725
+ }
726
+
727
+ if (failed_at)
728
+ *failed_at = NULL;
729
+
730
+ return result;
731
+}
\ No newline at end of file
src/libnetdata/eval/re2c_lemon/lexer.re
new
+268
@@ -0,0 +1,268 @@
1
+/**
2
+ * re2c lexer for Netdata's expression evaluator
3
+ *
4
+ * This implementation uses re2c for lexical analysis and lemon for parsing.
5
+ * It is fully integrated with Netdata's existing EVAL_NODE structure.
6
+ */
7
+
8
+#include "../eval-internal.h"
9
+#include "parser_internal.h"
10
+
11
+// Scanner functions implementation
12
+void scanner_init(Scanner *s, const char *input) {
13
+ if (!input) {
14
+ // Handle NULL input safely
15
+ s->cursor = "";
16
+ s->marker = s->cursor;
17
+ s->token = s->cursor;
18
+ s->limit = s->cursor;
19
+ s->line = 1;
20
+ s->error = 1; // Set error flag for NULL input
21
+ return;
22
+ }
23
+
24
+ s->cursor = input;
25
+ s->marker = s->cursor;
26
+ s->token = s->cursor;
27
+ s->limit = s->cursor + strlen(s->cursor);
28
+ s->line = 1;
29
+ s->error = 0; // Initialize error flag
30
+}
31
+
32
+int scan(Scanner *s, YYSTYPE *lval) {
33
+ const char *YYMARKER;
34
+ const char *YYCURSOR = s->cursor;
35
+ char variable_buffer[EVAL_MAX_VARIABLE_NAME_LENGTH + 1] = {0};
36
+
37
+ // Skip whitespace
38
+ while (1) {
39
+ s->token = YYCURSOR;
40
+
41
+/*!re2c
42
+ re2c:define:YYCTYPE = char;
43
+ re2c:yyfill:enable = 0;
44
+
45
+ // Skip whitespace
46
+ [ \t\r\n]+ { continue; }
47
+
48
+ // Special numeric literals - more comprehensive handling for various capitalizations
49
+ // Support NaN with all case variations
50
+ // Matching str2ndd behavior in inlined.h which accepts "nan" (any case) and also "null"
51
+ [nN][aA][nN] | [nN][uU][lL][lL] {
52
+ lval->dval = NAN;
53
+ s->cursor = YYCURSOR;
54
+ return TOK_NUMBER;
55
+ }
56
+
57
+ // Support Infinity with all case variations
58
+ // Matching str2ndd behavior in inlined.h which accepts "inf" in any case
59
+ [iI][nN][fF]([iI][nN][iI][tT][yY])? {
60
+ lval->dval = INFINITY;
61
+ s->cursor = YYCURSOR;
62
+ return TOK_NUMBER;
63
+ }
64
+
65
+ // Numbers
66
+ [0-9]+ |
67
+ [0-9]+"."[0-9]* |
68
+ "."[0-9]+ |
69
+ [0-9]+[eE][+-]?[0-9]+ |
70
+ [0-9]+"."[0-9]*[eE][+-]?[0-9]+ |
71
+ "."[0-9]+[eE][+-]?[0-9]+ {
72
+ char *endptr;
73
+ lval->dval = str2ndd(s->token, &endptr);
74
+ s->cursor = YYCURSOR;
75
+ return TOK_NUMBER;
76
+ }
77
+
78
+ // Variables - can contain any characters that aren't operators or closing brackets
79
+ // The original parser allows any character that passes !is_operator_first_symbol_or_space(s) && s != ')' && s != '}'
80
+ // Note that % is not explicitly excluded by is_operator_first_symbol_or_space in the original parser
81
+ "$"[^\000 \t\r\n&|!><=%+\-*/?()}{]+ {
82
+ size_t len = YYCURSOR - s->token - 1; // -1 to skip the $
83
+ if (len >= EVAL_MAX_VARIABLE_NAME_LENGTH) {
84
+ len = EVAL_MAX_VARIABLE_NAME_LENGTH - 1;
85
+ }
86
+ memcpy(variable_buffer, s->token + 1, len);
87
+ variable_buffer[len] = '\0';
88
+ lval->strval = strdupz(variable_buffer);
89
+ s->cursor = YYCURSOR;
90
+ return TOK_VARIABLE;
91
+ }
92
+
93
+ // Empty variable with braces - treat as error
94
+ "${}" { s->cursor = YYCURSOR; s->error = 1; return 0; }
95
+
96
+ // Variables with braces - can contain any character except } and \0
97
+ "${" [^}\000]* "}" {
98
+ // Calculate length, excluding the ${ prefix and the } suffix
99
+ size_t len = YYCURSOR - s->token - 3; // -3 to skip ${ and }
100
+ if (len >= EVAL_MAX_VARIABLE_NAME_LENGTH) {
101
+ len = EVAL_MAX_VARIABLE_NAME_LENGTH - 1;
102
+ }
103
+ memcpy(variable_buffer, s->token + 2, len);
104
+ variable_buffer[len] = '\0';
105
+ lval->strval = strdupz(variable_buffer);
106
+ s->cursor = YYCURSOR;
107
+ return TOK_VARIABLE;
108
+ }
109
+
110
+ // Operators
111
+ "+" { s->cursor = YYCURSOR; return TOK_PLUS; }
112
+ "-" { s->cursor = YYCURSOR; return TOK_MINUS; }
113
+ "*" { s->cursor = YYCURSOR; return TOK_MULTIPLY; }
114
+ "/" { s->cursor = YYCURSOR; return TOK_DIVIDE; }
115
+ "%" { s->cursor = YYCURSOR; return TOK_MODULO; }
116
+
117
+ // Logical operators - full case-insensitive handling for AND, OR, NOT
118
+ // Exactly matching the original parser's behavior from parse_and, parse_or, and parse_not
119
+ "&&" | [aA][nN][dD] {
120
+ s->cursor = YYCURSOR;
121
+ return TOK_AND;
122
+ }
123
+
124
+ "||" | [oO][rR] {
125
+ s->cursor = YYCURSOR;
126
+ return TOK_OR;
127
+ }
128
+
129
+ "!" | [nN][oO][tT] {
130
+ s->cursor = YYCURSOR;
131
+ return TOK_NOT;
132
+ }
133
+
134
+ // Comparison operators
135
+ "==" | "=" { s->cursor = YYCURSOR; return TOK_EQ; }
136
+ "!=" | "<>" { s->cursor = YYCURSOR; return TOK_NE; }
137
+ "<" { s->cursor = YYCURSOR; return TOK_LT; }
138
+ "<=" { s->cursor = YYCURSOR; return TOK_LE; }
139
+ ">" { s->cursor = YYCURSOR; return TOK_GT; }
140
+ ">=" { s->cursor = YYCURSOR; return TOK_GE; }
141
+
142
+ // Ternary operator
143
+ "?" { s->cursor = YYCURSOR; return TOK_QMARK; }
144
+ ":" { s->cursor = YYCURSOR; return TOK_COLON; }
145
+
146
+ // Parentheses
147
+ "(" { s->cursor = YYCURSOR; return TOK_LPAREN; }
148
+ ")" { s->cursor = YYCURSOR; return TOK_RPAREN; }
149
+
150
+ // Function names - case-insensitive support
151
+ // Exactly matching the original parser's behavior from parse_function
152
+ [aA][bB][sS] { s->cursor = YYCURSOR; return TOK_FUNCTION_ABS; }
153
+
154
+ // Empty variable placeholders - these should be errors
155
+ "${" { s->cursor = YYCURSOR; s->error = 1; return 0; }
156
+
157
+ // End of input
158
+ "\000" { s->cursor = YYCURSOR; return 0; }
159
+
160
+ // Any other character is an error - set error flag and return 0 to stop parsing
161
+ . {
162
+ s->cursor = YYCURSOR;
163
+ s->error = 1; // Set error flag
164
+ return 0; // Return 0 to stop parsing
165
+ }
166
+*/
167
+ }
168
+}
169
+
170
+// Function to parse an expression with re2c/lemon
171
+EVAL_NODE *parse_expression_with_re2c_lemon(const char *string, const char **failed_at, int *error) {
172
+ Scanner scanner;
173
+ scanner_init(&scanner, string);
174
+
175
+ if(failed_at)
176
+ *failed_at = NULL;
177
+
178
+ // Use ParseAlloc with mallocz instead of malloc - mallocz will handle allocation failures
179
+ void *parser = ParseAlloc(mallocz);
180
+
181
+ EVAL_NODE *result = NULL;
182
+
183
+ YYSTYPE token_value;
184
+ int token_type;
185
+
186
+ // Initialize error code
187
+ if (error) *error = EVAL_ERROR_OK;
188
+
189
+ // Save the token start position for error reporting
190
+ const char *error_pos = scanner.cursor;
191
+
192
+ // Variable to track if we need to free token_value.strval
193
+ int free_strval = 0;
194
+
195
+ while ((token_type = scan(&scanner, &token_value)) > 0) {
196
+ // If the token is a variable, remember to free it if there's an error
197
+ free_strval = (token_type == TOK_VARIABLE);
198
+
199
+ Parse(parser, token_type, token_value, &result);
200
+
201
+ // Save position before potential error
202
+ error_pos = scanner.token;
203
+
204
+ // Check for syntax errors after each token
205
+ if (result && result->operator == EVAL_OPERATOR_NOP && result->count == 0) {
206
+ // This is an error marker
207
+ if (error) *error = EVAL_ERROR_SYNTAX;
208
+ if (failed_at) {
209
+ *failed_at = error_pos;
210
+ }
211
+
212
+ // Clean up
213
+ eval_node_free(result);
214
+ ParseFree(parser, freez);
215
+
216
+ // If we just scanned a variable, free its strval
217
+ if (free_strval && token_value.strval) {
218
+ freez(token_value.strval);
219
+ }
220
+
221
+ return NULL;
222
+ }
223
+
224
+ // Reset free_strval since the parser has taken ownership of the string
225
+ free_strval = 0;
226
+ }
227
+
228
+ // If the last token was a variable and scanning stopped due to an error,
229
+ // we need to free the token_value.strval
230
+ if (free_strval && token_value.strval) {
231
+ freez(token_value.strval);
232
+ token_value.strval = NULL;
233
+ }
234
+
235
+ // Finish parsing
236
+ Parse(parser, 0, token_value, &result);
237
+
238
+ // Clean up the parser
239
+ ParseFree(parser, freez);
240
+
241
+ // Check for lexer errors
242
+ if (scanner.error) {
243
+ if (error) *error = EVAL_ERROR_UNKNOWN_OPERAND;
244
+ if (failed_at) {
245
+ *failed_at = error_pos;
246
+ }
247
+
248
+ // Clean up result if it was created
249
+ if (result) {
250
+ eval_node_free(result);
251
+ }
252
+
253
+ return NULL;
254
+ }
255
+
256
+ if (!result) {
257
+ if (error) *error = EVAL_ERROR_SYNTAX;
258
+ if (failed_at) {
259
+ *failed_at = error_pos;
260
+ }
261
+ return NULL;
262
+ }
263
+
264
+ if (failed_at)
265
+ *failed_at = NULL;
266
+
267
+ return result;
268
+}
\ No newline at end of file
src/libnetdata/eval/re2c_lemon/parser.c
new
+1575
@@ -0,0 +1,1575 @@
1
+/* This file is automatically generated by Lemon from input grammar
2
+** source file "parser.y".
3
+*/
4
+/*
5
+** 2000-05-29
6
+**
7
+** The author disclaims copyright to this source code. In place of
8
+** a legal notice, here is a blessing:
9
+**
10
+** May you do good and not evil.
11
+** May you find forgiveness for yourself and forgive others.
12
+** May you share freely, never taking more than you give.
13
+**
14
+*************************************************************************
15
+** Driver template for the LEMON parser generator.
16
+**
17
+** The "lemon" program processes an LALR(1) input grammar file, then uses
18
+** this template to construct a parser. The "lemon" program inserts text
19
+** at each "%%" line. Also, any "P-a-r-s-e" identifier prefix (without the
20
+** interstitial "-" characters) contained in this template is changed into
21
+** the value of the %name directive from the grammar. Otherwise, the content
22
+** of this template is copied straight through into the generate parser
23
+** source file.
24
+**
25
+** The following is the concatenation of all %include directives from the
26
+** input grammar file:
27
+*/
28
+/************ Begin %include sections from the grammar ************************/
29
+#include "../eval-internal.h"
30
+#include "parser_internal.h"
31
+#include <assert.h>
32
+#line 33 "parser.c"
33
+/**************** End of %include directives **********************************/
34
+/* These constants specify the various numeric values for terminal symbols.
35
+***************** Begin token definitions *************************************/
36
+#ifndef TOK_NUMBER
37
+#define TOK_NUMBER 1
38
+#define TOK_VARIABLE 2
39
+#define TOK_LPAREN 3
40
+#define TOK_RPAREN 4
41
+#define TOK_PLUS 5
42
+#define TOK_UPLUS 6
43
+#define TOK_MINUS 7
44
+#define TOK_UMINUS 8
45
+#define TOK_NOT 9
46
+#define TOK_FUNCTION_ABS 10
47
+#define TOK_MULTIPLY 11
48
+#define TOK_DIVIDE 12
49
+#define TOK_MODULO 13
50
+#define TOK_AND 14
51
+#define TOK_OR 15
52
+#define TOK_EQ 16
53
+#define TOK_NE 17
54
+#define TOK_LT 18
55
+#define TOK_LE 19
56
+#define TOK_GT 20
57
+#define TOK_GE 21
58
+#define TOK_QMARK 22
59
+#define TOK_COLON 23
60
+#endif
61
+/**************** End token definitions ***************************************/
62
+
63
+/* The next sections is a series of control #defines.
64
+** various aspects of the generated parser.
65
+** YYCODETYPE is the data type used to store the integer codes
66
+** that represent terminal and non-terminal symbols.
67
+** "unsigned char" is used if there are fewer than
68
+** 256 symbols. Larger types otherwise.
69
+** YYNOCODE is a number of type YYCODETYPE that is not used for
70
+** any terminal or nonterminal symbol.
71
+** YYFALLBACK If defined, this indicates that one or more tokens
72
+** (also known as: "terminal symbols") have fall-back
73
+** values which should be used if the original symbol
74
+** would not parse. This permits keywords to sometimes
75
+** be used as identifiers, for example.
76
+** YYACTIONTYPE is the data type used for "action codes" - numbers
77
+** that indicate what to do in response to the next
78
+** token.
79
+** ParseTOKENTYPE is the data type used for minor type for terminal
80
+** symbols. Background: A "minor type" is a semantic
81
+** value associated with a terminal or non-terminal
82
+** symbols. For example, for an "ID" terminal symbol,
83
+** the minor type might be the name of the identifier.
84
+** Each non-terminal can have a different minor type.
85
+** Terminal symbols all have the same minor type, though.
86
+** This macros defines the minor type for terminal
87
+** symbols.
88
+** YYMINORTYPE is the data type used for all minor types.
89
+** This is typically a union of many types, one of
90
+** which is ParseTOKENTYPE. The entry in the union
91
+** for terminal symbols is called "yy0".
92
+** YYSTACKDEPTH is the maximum depth of the parser's stack. If
93
+** zero the stack is dynamically sized using realloc()
94
+** ParseARG_SDECL A static variable declaration for the %extra_argument
95
+** ParseARG_PDECL A parameter declaration for the %extra_argument
96
+** ParseARG_PARAM Code to pass %extra_argument as a subroutine parameter
97
+** ParseARG_STORE Code to store %extra_argument into yypParser
98
+** ParseARG_FETCH Code to extract %extra_argument from yypParser
99
+** ParseCTX_* As ParseARG_ except for %extra_context
100
+** YYREALLOC Name of the realloc() function to use
101
+** YYFREE Name of the free() function to use
102
+** YYDYNSTACK True if stack space should be extended on heap
103
+** YYERRORSYMBOL is the code number of the error symbol. If not
104
+** defined, then do no error processing.
105
+** YYNSTATE the combined number of states.
106
+** YYNRULE the number of rules in the grammar
107
+** YYNTOKEN Number of terminal symbols
108
+** YY_MAX_SHIFT Maximum value for shift actions
109
+** YY_MIN_SHIFTREDUCE Minimum value for shift-reduce actions
110
+** YY_MAX_SHIFTREDUCE Maximum value for shift-reduce actions
111
+** YY_ERROR_ACTION The yy_action[] code for syntax error
112
+** YY_ACCEPT_ACTION The yy_action[] code for accept
113
+** YY_NO_ACTION The yy_action[] code for no-op
114
+** YY_MIN_REDUCE Minimum value for reduce actions
115
+** YY_MAX_REDUCE Maximum value for reduce actions
116
+** YY_MIN_DSTRCTR Minimum symbol value that has a destructor
117
+** YY_MAX_DSTRCTR Maximum symbol value that has a destructor
118
+*/
119
+#ifndef INTERFACE
120
+# define INTERFACE 1
121
+#endif
122
+/************* Begin control #defines *****************************************/
123
+#define YYCODETYPE unsigned char
124
+#define YYNOCODE 26
125
+#define YYACTIONTYPE unsigned char
126
+#define ParseTOKENTYPE YYSTYPE
127
+typedef union {
128
+ int yyinit;
129
+ ParseTOKENTYPE yy0;
130
+ EVAL_NODE* yy48;
131
+} YYMINORTYPE;
132
+#ifndef YYSTACKDEPTH
133
+#define YYSTACKDEPTH 100
134
+#endif
135
+#define ParseARG_SDECL EVAL_NODE **result;
136
+#define ParseARG_PDECL ,EVAL_NODE **result
137
+#define ParseARG_PARAM ,result
138
+#define ParseARG_FETCH EVAL_NODE **result=yypParser->result;
139
+#define ParseARG_STORE yypParser->result=result;
140
+#define YYREALLOC realloc
141
+#define YYFREE free
142
+#define YYDYNSTACK 0
143
+#define ParseCTX_SDECL
144
+#define ParseCTX_PDECL
145
+#define ParseCTX_PARAM
146
+#define ParseCTX_FETCH
147
+#define ParseCTX_STORE
148
+#define YYNSTATE 37
149
+#define YYNRULE 22
150
+#define YYNRULE_WITH_ACTION 22
151
+#define YYNTOKEN 24
152
+#define YY_MAX_SHIFT 36
153
+#define YY_MIN_SHIFTREDUCE 47
154
+#define YY_MAX_SHIFTREDUCE 68
155
+#define YY_ERROR_ACTION 69
156
+#define YY_ACCEPT_ACTION 70
157
+#define YY_NO_ACTION 71
158
+#define YY_MIN_REDUCE 72
159
+#define YY_MAX_REDUCE 93
160
+#define YY_MIN_DSTRCTR 24
161
+#define YY_MAX_DSTRCTR 24
162
+/************* End control #defines *******************************************/
163
+#define YY_NLOOKAHEAD ((int)(sizeof(yy_lookahead)/sizeof(yy_lookahead[0])))
164
+
165
+/* Define the yytestcase() macro to be a no-op if is not already defined
166
+** otherwise.
167
+**
168
+** Applications can choose to define yytestcase() in the %include section
169
+** to a macro that can assist in verifying code coverage. For production
170
+** code the yytestcase() macro should be turned off. But it is useful
171
+** for testing.
172
+*/
173
+#ifndef yytestcase
174
+# define yytestcase(X)
175
+#endif
176
+
177
+/* Macro to determine if stack space has the ability to grow using
178
+** heap memory.
179
+*/
180
+#if YYSTACKDEPTH<=0 || YYDYNSTACK
181
+# define YYGROWABLESTACK 1
182
+#else
183
+# define YYGROWABLESTACK 0
184
+#endif
185
+
186
+/* Guarantee a minimum number of initial stack slots.
187
+*/
188
+#if YYSTACKDEPTH<=0
189
+# undef YYSTACKDEPTH
190
+# define YYSTACKDEPTH 2 /* Need a minimum stack size */
191
+#endif
192
+
193
+
194
+/* Next are the tables used to determine what action to take based on the
195
+** current state and lookahead token. These tables are used to implement
196
+** functions that take a state number and lookahead value and return an
197
+** action integer.
198
+**
199
+** Suppose the action integer is N. Then the action is determined as
200
+** follows
201
+**
202
+** 0 <= N <= YY_MAX_SHIFT Shift N. That is, push the lookahead
203
+** token onto the stack and goto state N.
204
+**
205
+** N between YY_MIN_SHIFTREDUCE Shift to an arbitrary state then
206
+** and YY_MAX_SHIFTREDUCE reduce by rule N-YY_MIN_SHIFTREDUCE.
207
+**
208
+** N == YY_ERROR_ACTION A syntax error has occurred.
209
+**
210
+** N == YY_ACCEPT_ACTION The parser accepts its input.
211
+**
212
+** N == YY_NO_ACTION No such action. Denotes unused
213
+** slots in the yy_action[] table.
214
+**
215
+** N between YY_MIN_REDUCE Reduce by rule N-YY_MIN_REDUCE
216
+** and YY_MAX_REDUCE
217
+**
218
+** The action table is constructed as a single large table named yy_action[].
219
+** Given state S and lookahead X, the action is computed as either:
220
+**
221
+** (A) N = yy_action[ yy_shift_ofst[S] + X ]
222
+** (B) N = yy_default[S]
223
+**
224
+** The (A) formula is preferred. The B formula is used instead if
225
+** yy_lookahead[yy_shift_ofst[S]+X] is not equal to X.
226
+**
227
+** The formulas above are for computing the action when the lookahead is
228
+** a terminal symbol. If the lookahead is a non-terminal (as occurs after
229
+** a reduce action) then the yy_reduce_ofst[] array is used in place of
230
+** the yy_shift_ofst[] array.
231
+**
232
+** The following are the tables generated in this section:
233
+**
234
+** yy_action[] A single table containing all actions.
235
+** yy_lookahead[] A table containing the lookahead for each entry in
236
+** yy_action. Used to detect hash collisions.
237
+** yy_shift_ofst[] For each state, the offset into yy_action for
238
+** shifting terminals.
239
+** yy_reduce_ofst[] For each state, the offset into yy_action for
240
+** shifting non-terminals after a reduce.
241
+** yy_default[] Default action for each state.
242
+**
243
+*********** Begin parsing tables **********************************************/
244
+#define YY_ACTTAB_COUNT (147)
245
+static const YYACTIONTYPE yy_action[] = {
246
+ /* 0 */ 24, 70, 25, 21, 30, 20, 31, 18, 32, 33,
247
+ /* 10 */ 28, 16, 14, 12, 10, 9, 8, 7, 6, 5,
248
+ /* 20 */ 4, 3, 2, 1, 54, 20, 29, 18, 16, 14,
249
+ /* 30 */ 12, 16, 14, 12, 10, 9, 8, 7, 6, 5,
250
+ /* 40 */ 4, 3, 2, 50, 20, 26, 18, 27, 22, 84,
251
+ /* 50 */ 16, 14, 12, 10, 9, 8, 7, 6, 5, 4,
252
+ /* 60 */ 3, 2, 72, 11, 20, 78, 18, 20, 83, 18,
253
+ /* 70 */ 16, 14, 12, 16, 14, 12, 10, 9, 8, 7,
254
+ /* 80 */ 6, 5, 4, 3, 2, 20, 71, 18, 71, 77,
255
+ /* 90 */ 82, 16, 14, 12, 10, 9, 8, 7, 6, 5,
256
+ /* 100 */ 4, 3, 2, 20, 71, 18, 76, 35, 23, 16,
257
+ /* 110 */ 14, 12, 34, 71, 8, 7, 6, 5, 4, 3,
258
+ /* 120 */ 20, 71, 18, 71, 71, 71, 16, 14, 12, 71,
259
+ /* 130 */ 71, 71, 71, 6, 5, 4, 3, 48, 49, 19,
260
+ /* 140 */ 71, 17, 71, 15, 71, 13, 36,
261
+};
262
+static const YYCODETYPE yy_lookahead[] = {
263
+ /* 0 */ 24, 25, 24, 24, 24, 5, 24, 7, 24, 24,
264
+ /* 10 */ 24, 11, 12, 13, 14, 15, 16, 17, 18, 19,
265
+ /* 20 */ 20, 21, 22, 23, 4, 5, 24, 7, 11, 12,
266
+ /* 30 */ 13, 11, 12, 13, 14, 15, 16, 17, 18, 19,
267
+ /* 40 */ 20, 21, 22, 4, 5, 24, 7, 24, 24, 24,
268
+ /* 50 */ 11, 12, 13, 14, 15, 16, 17, 18, 19, 20,
269
+ /* 60 */ 21, 22, 0, 3, 5, 24, 7, 5, 24, 7,
270
+ /* 70 */ 11, 12, 13, 11, 12, 13, 14, 15, 16, 17,
271
+ /* 80 */ 18, 19, 20, 21, 22, 5, 26, 7, 26, 24,
272
+ /* 90 */ 24, 11, 12, 13, 14, 15, 16, 17, 18, 19,
273
+ /* 100 */ 20, 21, 22, 5, 26, 7, 24, 24, 24, 11,
274
+ /* 110 */ 12, 13, 24, 26, 16, 17, 18, 19, 20, 21,
275
+ /* 120 */ 5, 26, 7, 26, 26, 26, 11, 12, 13, 26,
276
+ /* 130 */ 26, 26, 26, 18, 19, 20, 21, 1, 2, 3,
277
+ /* 140 */ 26, 5, 26, 7, 26, 9, 10, 26, 26, 26,
278
+ /* 150 */ 26, 26, 26, 26, 26, 26, 26, 26, 26, 26,
279
+ /* 160 */ 26, 26, 24, 24, 24, 24, 24, 24, 24, 24,
280
+ /* 170 */ 24,
281
+};
282
+#define YY_SHIFT_COUNT (36)
283
+#define YY_SHIFT_MIN (0)
284
+#define YY_SHIFT_MAX (136)
285
+static const unsigned char yy_shift_ofst[] = {
286
+ /* 0 */ 136, 136, 136, 136, 136, 136, 136, 136, 136, 136,
287
+ /* 10 */ 136, 136, 136, 136, 136, 136, 136, 136, 136, 136,
288
+ /* 20 */ 136, 0, 20, 39, 62, 80, 98, 98, 115, 115,
289
+ /* 30 */ 59, 59, 59, 59, 17, 17, 60,
290
+};
291
+#define YY_REDUCE_COUNT (20)
292
+#define YY_REDUCE_MIN (-24)
293
+#define YY_REDUCE_MAX (88)
294
+static const signed char yy_reduce_ofst[] = {
295
+ /* 0 */ -24, -22, -21, -20, -18, -16, -15, -14, 2, 21,
296
+ /* 10 */ 23, 24, 25, 41, 44, 65, 66, 82, 83, 84,
297
+ /* 20 */ 88,
298
+};
299
+static const YYACTIONTYPE yy_default[] = {
300
+ /* 0 */ 69, 69, 69, 69, 69, 69, 69, 69, 69, 69,
301
+ /* 10 */ 69, 69, 69, 69, 69, 69, 69, 69, 69, 69,
302
+ /* 20 */ 69, 69, 69, 69, 69, 93, 86, 85, 88, 87,
303
+ /* 30 */ 92, 91, 90, 89, 80, 81, 69,
304
+};
305
+/********** End of lemon-generated parsing tables *****************************/
306
+
307
+/* The next table maps tokens (terminal symbols) into fallback tokens.
308
+** If a construct like the following:
309
+**
310
+** %fallback ID X Y Z.
311
+**
312
+** appears in the grammar, then ID becomes a fallback token for X, Y,
313
+** and Z. Whenever one of the tokens X, Y, or Z is input to the parser
314
+** but it does not parse, the type of the token is changed to ID and
315
+** the parse is retried before an error is thrown.
316
+**
317
+** This feature can be used, for example, to cause some keywords in a language
318
+** to revert to identifiers if they keyword does not apply in the context where
319
+** it appears.
320
+*/
321
+#ifdef YYFALLBACK
322
+static const YYCODETYPE yyFallback[] = {
323
+};
324
+#endif /* YYFALLBACK */
325
+
326
+/* The following structure represents a single element of the
327
+** parser's stack. Information stored includes:
328
+**
329
+** + The state number for the parser at this level of the stack.
330
+**
331
+** + The value of the token stored at this level of the stack.
332
+** (In other words, the "major" token.)
333
+**
334
+** + The semantic value stored at this level of the stack. This is
335
+** the information used by the action routines in the grammar.
336
+** It is sometimes called the "minor" token.
337
+**
338
+** After the "shift" half of a SHIFTREDUCE action, the stateno field
339
+** actually contains the reduce action for the second half of the
340
+** SHIFTREDUCE.
341
+*/
342
+struct yyStackEntry {
343
+ YYACTIONTYPE stateno; /* The state-number, or reduce action in SHIFTREDUCE */
344
+ YYCODETYPE major; /* The major token value. This is the code
345
+ ** number for the token at this stack level */
346
+ YYMINORTYPE minor; /* The user-supplied minor token value. This
347
+ ** is the value of the token */
348
+};
349
+typedef struct yyStackEntry yyStackEntry;
350
+
351
+/* The state of the parser is completely contained in an instance of
352
+** the following structure */
353
+struct yyParser {
354
+ yyStackEntry *yytos; /* Pointer to top element of the stack */
355
+#ifdef YYTRACKMAXSTACKDEPTH
356
+ int yyhwm; /* High-water mark of the stack */
357
+#endif
358
+#ifndef YYNOERRORRECOVERY
359
+ int yyerrcnt; /* Shifts left before out of the error */
360
+#endif
361
+ ParseARG_SDECL /* A place to hold %extra_argument */
362
+ ParseCTX_SDECL /* A place to hold %extra_context */
363
+ yyStackEntry *yystackEnd; /* Last entry in the stack */
364
+ yyStackEntry *yystack; /* The parser stack */
365
+ yyStackEntry yystk0[YYSTACKDEPTH]; /* Initial stack space */
366
+};
367
+typedef struct yyParser yyParser;
368
+
369
+#include <assert.h>
370
+#ifndef NDEBUG
371
+#include <stdio.h>
372
+static FILE *yyTraceFILE = 0;
373
+static char *yyTracePrompt = 0;
374
+#endif /* NDEBUG */
375
+
376
+#ifndef NDEBUG
377
+/*
378
+** Turn parser tracing on by giving a stream to which to write the trace
379
+** and a prompt to preface each trace message. Tracing is turned off
380
+** by making either argument NULL
381
+**
382
+** Inputs:
383
+** <ul>
384
+** <li> A FILE* to which trace output should be written.
385
+** If NULL, then tracing is turned off.
386
+** <li> A prefix string written at the beginning of every
387
+** line of trace output. If NULL, then tracing is
388
+** turned off.
389
+** </ul>
390
+**
391
+** Outputs:
392
+** None.
393
+*/
394
+void ParseTrace(FILE *TraceFILE, char *zTracePrompt){
395
+ yyTraceFILE = TraceFILE;
396
+ yyTracePrompt = zTracePrompt;
397
+ if( yyTraceFILE==0 ) yyTracePrompt = 0;
398
+ else if( yyTracePrompt==0 ) yyTraceFILE = 0;
399
+}
400
+#endif /* NDEBUG */
401
+
402
+#if defined(YYCOVERAGE) || !defined(NDEBUG)
403
+/* For tracing shifts, the names of all terminals and nonterminals
404
+** are required. The following table supplies these names */
405
+static const char *const yyTokenName[] = {
406
+ /* 0 */ "$",
407
+ /* 1 */ "NUMBER",
408
+ /* 2 */ "VARIABLE",
409
+ /* 3 */ "LPAREN",
410
+ /* 4 */ "RPAREN",
411
+ /* 5 */ "PLUS",
412
+ /* 6 */ "UPLUS",
413
+ /* 7 */ "MINUS",
414
+ /* 8 */ "UMINUS",
415
+ /* 9 */ "NOT",
416
+ /* 10 */ "FUNCTION_ABS",
417
+ /* 11 */ "MULTIPLY",
418
+ /* 12 */ "DIVIDE",
419
+ /* 13 */ "MODULO",
420
+ /* 14 */ "AND",
421
+ /* 15 */ "OR",
422
+ /* 16 */ "EQ",
423
+ /* 17 */ "NE",
424
+ /* 18 */ "LT",
425
+ /* 19 */ "LE",
426
+ /* 20 */ "GT",
427
+ /* 21 */ "GE",
428
+ /* 22 */ "QMARK",
429
+ /* 23 */ "COLON",
430
+ /* 24 */ "expr",
431
+ /* 25 */ "program",
432
+};
433
+#endif /* defined(YYCOVERAGE) || !defined(NDEBUG) */
434
+
435
+#ifndef NDEBUG
436
+/* For tracing reduce actions, the names of all rules are required.
437
+*/
438
+static const char *const yyRuleName[] = {
439
+ /* 0 */ "program ::= expr",
440
+ /* 1 */ "expr ::= NUMBER",
441
+ /* 2 */ "expr ::= VARIABLE",
442
+ /* 3 */ "expr ::= LPAREN expr RPAREN",
443
+ /* 4 */ "expr ::= PLUS expr",
444
+ /* 5 */ "expr ::= MINUS expr",
445
+ /* 6 */ "expr ::= NOT expr",
446
+ /* 7 */ "expr ::= FUNCTION_ABS LPAREN expr RPAREN",
447
+ /* 8 */ "expr ::= expr PLUS expr",
448
+ /* 9 */ "expr ::= expr MINUS expr",
449
+ /* 10 */ "expr ::= expr MULTIPLY expr",
450
+ /* 11 */ "expr ::= expr DIVIDE expr",
451
+ /* 12 */ "expr ::= expr MODULO expr",
452
+ /* 13 */ "expr ::= expr AND expr",
453
+ /* 14 */ "expr ::= expr OR expr",
454
+ /* 15 */ "expr ::= expr EQ expr",
455
+ /* 16 */ "expr ::= expr NE expr",
456
+ /* 17 */ "expr ::= expr LT expr",
457
+ /* 18 */ "expr ::= expr LE expr",
458
+ /* 19 */ "expr ::= expr GT expr",
459
+ /* 20 */ "expr ::= expr GE expr",
460
+ /* 21 */ "expr ::= expr QMARK expr COLON expr",
461
+};
462
+#endif /* NDEBUG */
463
+
464
+
465
+#if YYGROWABLESTACK
466
+/*
467
+** Try to increase the size of the parser stack. Return the number
468
+** of errors. Return 0 on success.
469
+*/
470
+static int yyGrowStack(yyParser *p){
471
+ int oldSize = 1 + (int)(p->yystackEnd - p->yystack);
472
+ int newSize;
473
+ int idx;
474
+ yyStackEntry *pNew;
475
+
476
+ newSize = oldSize*2 + 100;
477
+ idx = (int)(p->yytos - p->yystack);
478
+ if( p->yystack==p->yystk0 ){
479
+ pNew = YYREALLOC(0, newSize*sizeof(pNew[0]));
480
+ if( pNew==0 ) return 1;
481
+ memcpy(pNew, p->yystack, oldSize*sizeof(pNew[0]));
482
+ }else{
483
+ pNew = YYREALLOC(p->yystack, newSize*sizeof(pNew[0]));
484
+ if( pNew==0 ) return 1;
485
+ }
486
+ p->yystack = pNew;
487
+ p->yytos = &p->yystack[idx];
488
+#ifndef NDEBUG
489
+ if( yyTraceFILE ){
490
+ fprintf(yyTraceFILE,"%sStack grows from %d to %d entries.\n",
491
+ yyTracePrompt, oldSize, newSize);
492
+ }
493
+#endif
494
+ p->yystackEnd = &p->yystack[newSize-1];
495
+ return 0;
496
+}
497
+#endif /* YYGROWABLESTACK */
498
+
499
+#if !YYGROWABLESTACK
500
+/* For builds that do no have a growable stack, yyGrowStack always
501
+** returns an error.
502
+*/
503
+# define yyGrowStack(X) 1
504
+#endif
505
+
506
+/* Datatype of the argument to the memory allocated passed as the
507
+** second argument to ParseAlloc() below. This can be changed by
508
+** putting an appropriate #define in the %include section of the input
509
+** grammar.
510
+*/
511
+#ifndef YYMALLOCARGTYPE
512
+# define YYMALLOCARGTYPE size_t
513
+#endif
514
+
515
+/* Initialize a new parser that has already been allocated.
516
+*/
517
+void ParseInit(void *yypRawParser ParseCTX_PDECL){
518
+ yyParser *yypParser = (yyParser*)yypRawParser;
519
+ ParseCTX_STORE
520
+#ifdef YYTRACKMAXSTACKDEPTH
521
+ yypParser->yyhwm = 0;
522
+#endif
523
+ yypParser->yystack = yypParser->yystk0;
524
+ yypParser->yystackEnd = &yypParser->yystack[YYSTACKDEPTH-1];
525
+#ifndef YYNOERRORRECOVERY
526
+ yypParser->yyerrcnt = -1;
527
+#endif
528
+ yypParser->yytos = yypParser->yystack;
529
+ yypParser->yystack[0].stateno = 0;
530
+ yypParser->yystack[0].major = 0;
531
+}
532
+
533
+#ifndef Parse_ENGINEALWAYSONSTACK
534
+/*
535
+** This function allocates a new parser.
536
+** The only argument is a pointer to a function which works like
537
+** malloc.
538
+**
539
+** Inputs:
540
+** A pointer to the function used to allocate memory.
541
+**
542
+** Outputs:
543
+** A pointer to a parser. This pointer is used in subsequent calls
544
+** to Parse and ParseFree.
545
+*/
546
+void *ParseAlloc(void *(*mallocProc)(YYMALLOCARGTYPE) ParseCTX_PDECL){
547
+ yyParser *yypParser;
548
+ yypParser = (yyParser*)(*mallocProc)( (YYMALLOCARGTYPE)sizeof(yyParser) );
549
+ if( yypParser ){
550
+ ParseCTX_STORE
551
+ ParseInit(yypParser ParseCTX_PARAM);
552
+ }
553
+ return (void*)yypParser;
554
+}
555
+#endif /* Parse_ENGINEALWAYSONSTACK */
556
+
557
+
558
+/* The following function deletes the "minor type" or semantic value
559
+** associated with a symbol. The symbol can be either a terminal
560
+** or nonterminal. "yymajor" is the symbol code, and "yypminor" is
561
+** a pointer to the value to be deleted. The code used to do the
562
+** deletions is derived from the %destructor and/or %token_destructor
563
+** directives of the input grammar.
564
+*/
565
+static void yy_destructor(
566
+ yyParser *yypParser, /* The parser */
567
+ YYCODETYPE yymajor, /* Type code for object to destroy */
568
+ YYMINORTYPE *yypminor /* The object to be destroyed */
569
+){
570
+ ParseARG_FETCH
571
+ ParseCTX_FETCH
572
+ switch( yymajor ){
573
+ /* Here is inserted the actions which take place when a
574
+ ** terminal or non-terminal is destroyed. This can happen
575
+ ** when the symbol is popped from the stack during a
576
+ ** reduce or during error processing or when a parser is
577
+ ** being destroyed before it is finished parsing.
578
+ **
579
+ ** Note: during a reduce, the only symbols destroyed are those
580
+ ** which appear on the RHS of the rule, but which are *not* used
581
+ ** inside the C code.
582
+ */
583
+/********* Begin destructor definitions ***************************************/
584
+ case 24: /* expr */
585
+{
586
+#line 34 "parser.y"
587
+
588
+ if ((yypminor->yy48)) {
589
+ eval_node_free((yypminor->yy48));
590
+ }
591
+
592
+#line 592 "parser.c"
593
+}
594
+ break;
595
+/********* End destructor definitions *****************************************/
596
+ default: break; /* If no destructor action specified: do nothing */
597
+ }
598
+}
599
+
600
+/*
601
+** Pop the parser's stack once.
602
+**
603
+** If there is a destructor routine associated with the token which
604
+** is popped from the stack, then call it.
605
+*/
606
+static void yy_pop_parser_stack(yyParser *pParser){
607
+ yyStackEntry *yytos;
608
+ assert( pParser->yytos!=0 );
609
+ assert( pParser->yytos > pParser->yystack );
610
+ yytos = pParser->yytos--;
611
+#ifndef NDEBUG
612
+ if( yyTraceFILE ){
613
+ fprintf(yyTraceFILE,"%sPopping %s\n",
614
+ yyTracePrompt,
615
+ yyTokenName[yytos->major]);
616
+ }
617
+#endif
618
+ yy_destructor(pParser, yytos->major, &yytos->minor);
619
+}
620
+
621
+/*
622
+** Clear all secondary memory allocations from the parser
623
+*/
624
+void ParseFinalize(void *p){
625
+ yyParser *pParser = (yyParser*)p;
626
+
627
+ /* In-lined version of calling yy_pop_parser_stack() for each
628
+ ** element left in the stack */
629
+ yyStackEntry *yytos = pParser->yytos;
630
+ while( yytos>pParser->yystack ){
631
+#ifndef NDEBUG
632
+ if( yyTraceFILE ){
633
+ fprintf(yyTraceFILE,"%sPopping %s\n",
634
+ yyTracePrompt,
635
+ yyTokenName[yytos->major]);
636
+ }
637
+#endif
638
+ if( yytos->major>=YY_MIN_DSTRCTR ){
639
+ yy_destructor(pParser, yytos->major, &yytos->minor);
640
+ }
641
+ yytos--;
642
+ }
643
+
644
+#if YYGROWABLESTACK
645
+ if( pParser->yystack!=pParser->yystk0 ) YYFREE(pParser->yystack);
646
+#endif
647
+}
648
+
649
+#ifndef Parse_ENGINEALWAYSONSTACK
650
+/*
651
+** Deallocate and destroy a parser. Destructors are called for
652
+** all stack elements before shutting the parser down.
653
+**
654
+** If the YYPARSEFREENEVERNULL macro exists (for example because it
655
+** is defined in a %include section of the input grammar) then it is
656
+** assumed that the input pointer is never NULL.
657
+*/
658
+void ParseFree(
659
+ void *p, /* The parser to be deleted */
660
+ void (*freeProc)(void*) /* Function used to reclaim memory */
661
+){
662
+#ifndef YYPARSEFREENEVERNULL
663
+ if( p==0 ) return;
664
+#endif
665
+ ParseFinalize(p);
666
+ (*freeProc)(p);
667
+}
668
+#endif /* Parse_ENGINEALWAYSONSTACK */
669
+
670
+/*
671
+** Return the peak depth of the stack for a parser.
672
+*/
673
+#ifdef YYTRACKMAXSTACKDEPTH
674
+int ParseStackPeak(void *p){
675
+ yyParser *pParser = (yyParser*)p;
676
+ return pParser->yyhwm;
677
+}
678
+#endif
679
+
680
+/* This array of booleans keeps track of the parser statement
681
+** coverage. The element yycoverage[X][Y] is set when the parser
682
+** is in state X and has a lookahead token Y. In a well-tested
683
+** systems, every element of this matrix should end up being set.
684
+*/
685
+#if defined(YYCOVERAGE)
686
+static unsigned char yycoverage[YYNSTATE][YYNTOKEN];
687
+#endif
688
+
689
+/*
690
+** Write into out a description of every state/lookahead combination that
691
+**
692
+** (1) has not been used by the parser, and
693
+** (2) is not a syntax error.
694
+**
695
+** Return the number of missed state/lookahead combinations.
696
+*/
697
+#if defined(YYCOVERAGE)
698
+int ParseCoverage(FILE *out){
699
+ int stateno, iLookAhead, i;
700
+ int nMissed = 0;
701
+ for(stateno=0; stateno<YYNSTATE; stateno++){
702
+ i = yy_shift_ofst[stateno];
703
+ for(iLookAhead=0; iLookAhead<YYNTOKEN; iLookAhead++){
704
+ if( yy_lookahead[i+iLookAhead]!=iLookAhead ) continue;
705
+ if( yycoverage[stateno][iLookAhead]==0 ) nMissed++;
706
+ if( out ){
707
+ fprintf(out,"State %d lookahead %s %s\n", stateno,
708
+ yyTokenName[iLookAhead],
709
+ yycoverage[stateno][iLookAhead] ? "ok" : "missed");
710
+ }
711
+ }
712
+ }
713
+ return nMissed;
714
+}
715
+#endif
716
+
717
+/*
718
+** Find the appropriate action for a parser given the terminal
719
+** look-ahead token iLookAhead.
720
+*/
721
+static YYACTIONTYPE yy_find_shift_action(
722
+ YYCODETYPE iLookAhead, /* The look-ahead token */
723
+ YYACTIONTYPE stateno /* Current state number */
724
+){
725
+ int i;
726
+
727
+ if( stateno>YY_MAX_SHIFT ) return stateno;
728
+ assert( stateno <= YY_SHIFT_COUNT );
729
+#if defined(YYCOVERAGE)
730
+ yycoverage[stateno][iLookAhead] = 1;
731
+#endif
732
+ do{
733
+ i = yy_shift_ofst[stateno];
734
+ assert( i>=0 );
735
+ assert( i<=YY_ACTTAB_COUNT );
736
+ assert( i+YYNTOKEN<=(int)YY_NLOOKAHEAD );
737
+ assert( iLookAhead!=YYNOCODE );
738
+ assert( iLookAhead < YYNTOKEN );
739
+ i += iLookAhead;
740
+ assert( i<(int)YY_NLOOKAHEAD );
741
+ if( yy_lookahead[i]!=iLookAhead ){
742
+#ifdef YYFALLBACK
743
+ YYCODETYPE iFallback; /* Fallback token */
744
+ assert( iLookAhead<sizeof(yyFallback)/sizeof(yyFallback[0]) );
745
+ iFallback = yyFallback[iLookAhead];
746
+ if( iFallback!=0 ){
747
+#ifndef NDEBUG
748
+ if( yyTraceFILE ){
749
+ fprintf(yyTraceFILE, "%sFALLBACK %s => %s\n",
750
+ yyTracePrompt, yyTokenName[iLookAhead], yyTokenName[iFallback]);
751
+ }
752
+#endif
753
+ assert( yyFallback[iFallback]==0 ); /* Fallback loop must terminate */
754
+ iLookAhead = iFallback;
755
+ continue;
756
+ }
757
+#endif
758
+#ifdef YYWILDCARD
759
+ {
760
+ int j = i - iLookAhead + YYWILDCARD;
761
+ assert( j<(int)(sizeof(yy_lookahead)/sizeof(yy_lookahead[0])) );
762
+ if( yy_lookahead[j]==YYWILDCARD && iLookAhead>0 ){
763
+#ifndef NDEBUG
764
+ if( yyTraceFILE ){
765
+ fprintf(yyTraceFILE, "%sWILDCARD %s => %s\n",
766
+ yyTracePrompt, yyTokenName[iLookAhead],
767
+ yyTokenName[YYWILDCARD]);
768
+ }
769
+#endif /* NDEBUG */
770
+ return yy_action[j];
771
+ }
772
+ }
773
+#endif /* YYWILDCARD */
774
+ return yy_default[stateno];
775
+ }else{
776
+ assert( i>=0 && i<(int)(sizeof(yy_action)/sizeof(yy_action[0])) );
777
+ return yy_action[i];
778
+ }
779
+ }while(1);
780
+}
781
+
782
+/*
783
+** Find the appropriate action for a parser given the non-terminal
784
+** look-ahead token iLookAhead.
785
+*/
786
+static YYACTIONTYPE yy_find_reduce_action(
787
+ YYACTIONTYPE stateno, /* Current state number */
788
+ YYCODETYPE iLookAhead /* The look-ahead token */
789
+){
790
+ int i;
791
+#ifdef YYERRORSYMBOL
792
+ if( stateno>YY_REDUCE_COUNT ){
793
+ return yy_default[stateno];
794
+ }
795
+#else
796
+ assert( stateno<=YY_REDUCE_COUNT );
797
+#endif
798
+ i = yy_reduce_ofst[stateno];
799
+ assert( iLookAhead!=YYNOCODE );
800
+ i += iLookAhead;
801
+#ifdef YYERRORSYMBOL
802
+ if( i<0 || i>=YY_ACTTAB_COUNT || yy_lookahead[i]!=iLookAhead ){
803
+ return yy_default[stateno];
804
+ }
805
+#else
806
+ assert( i>=0 && i<YY_ACTTAB_COUNT );
807
+ assert( yy_lookahead[i]==iLookAhead );
808
+#endif
809
+ return yy_action[i];
810
+}
811
+
812
+/*
813
+** The following routine is called if the stack overflows.
814
+*/
815
+static void yyStackOverflow(yyParser *yypParser){
816
+ ParseARG_FETCH
817
+ ParseCTX_FETCH
818
+#ifndef NDEBUG
819
+ if( yyTraceFILE ){
820
+ fprintf(yyTraceFILE,"%sStack Overflow!\n",yyTracePrompt);
821
+ }
822
+#endif
823
+ while( yypParser->yytos>yypParser->yystack ) yy_pop_parser_stack(yypParser);
824
+ /* Here code is inserted which will execute if the parser
825
+ ** stack every overflows */
826
+/******** Begin %stack_overflow code ******************************************/
827
+/******** End %stack_overflow code ********************************************/
828
+ ParseARG_STORE /* Suppress warning about unused %extra_argument var */
829
+ ParseCTX_STORE
830
+}
831
+
832
+/*
833
+** Print tracing information for a SHIFT action
834
+*/
835
+#ifndef NDEBUG
836
+static void yyTraceShift(yyParser *yypParser, int yyNewState, const char *zTag){
837
+ if( yyTraceFILE ){
838
+ if( yyNewState<YYNSTATE ){
839
+ fprintf(yyTraceFILE,"%s%s '%s', go to state %d\n",
840
+ yyTracePrompt, zTag, yyTokenName[yypParser->yytos->major],
841
+ yyNewState);
842
+ }else{
843
+ fprintf(yyTraceFILE,"%s%s '%s', pending reduce %d\n",
844
+ yyTracePrompt, zTag, yyTokenName[yypParser->yytos->major],
845
+ yyNewState - YY_MIN_REDUCE);
846
+ }
847
+ }
848
+}
849
+#else
850
+# define yyTraceShift(X,Y,Z)
851
+#endif
852
+
853
+/*
854
+** Perform a shift action.
855
+*/
856
+static void yy_shift(
857
+ yyParser *yypParser, /* The parser to be shifted */
858
+ YYACTIONTYPE yyNewState, /* The new state to shift in */
859
+ YYCODETYPE yyMajor, /* The major token to shift in */
860
+ ParseTOKENTYPE yyMinor /* The minor token to shift in */
861
+){
862
+ yyStackEntry *yytos;
863
+ yypParser->yytos++;
864
+#ifdef YYTRACKMAXSTACKDEPTH
865
+ if( (int)(yypParser->yytos - yypParser->yystack)>yypParser->yyhwm ){
866
+ yypParser->yyhwm++;
867
+ assert( yypParser->yyhwm == (int)(yypParser->yytos - yypParser->yystack) );
868
+ }
869
+#endif
870
+ yytos = yypParser->yytos;
871
+ if( yytos>yypParser->yystackEnd ){
872
+ if( yyGrowStack(yypParser) ){
873
+ yypParser->yytos--;
874
+ yyStackOverflow(yypParser);
875
+ return;
876
+ }
877
+ yytos = yypParser->yytos;
878
+ assert( yytos <= yypParser->yystackEnd );
879
+ }
880
+ if( yyNewState > YY_MAX_SHIFT ){
881
+ yyNewState += YY_MIN_REDUCE - YY_MIN_SHIFTREDUCE;
882
+ }
883
+ yytos->stateno = yyNewState;
884
+ yytos->major = yyMajor;
885
+ yytos->minor.yy0 = yyMinor;
886
+ yyTraceShift(yypParser, yyNewState, "Shift");
887
+}
888
+
889
+/* For rule J, yyRuleInfoLhs[J] contains the symbol on the left-hand side
890
+** of that rule */
891
+static const YYCODETYPE yyRuleInfoLhs[] = {
892
+ 25, /* (0) program ::= expr */
893
+ 24, /* (1) expr ::= NUMBER */
894
+ 24, /* (2) expr ::= VARIABLE */
895
+ 24, /* (3) expr ::= LPAREN expr RPAREN */
896
+ 24, /* (4) expr ::= PLUS expr */
897
+ 24, /* (5) expr ::= MINUS expr */
898
+ 24, /* (6) expr ::= NOT expr */
899
+ 24, /* (7) expr ::= FUNCTION_ABS LPAREN expr RPAREN */
900
+ 24, /* (8) expr ::= expr PLUS expr */
901
+ 24, /* (9) expr ::= expr MINUS expr */
902
+ 24, /* (10) expr ::= expr MULTIPLY expr */
903
+ 24, /* (11) expr ::= expr DIVIDE expr */
904
+ 24, /* (12) expr ::= expr MODULO expr */
905
+ 24, /* (13) expr ::= expr AND expr */
906
+ 24, /* (14) expr ::= expr OR expr */
907
+ 24, /* (15) expr ::= expr EQ expr */
908
+ 24, /* (16) expr ::= expr NE expr */
909
+ 24, /* (17) expr ::= expr LT expr */
910
+ 24, /* (18) expr ::= expr LE expr */
911
+ 24, /* (19) expr ::= expr GT expr */
912
+ 24, /* (20) expr ::= expr GE expr */
913
+ 24, /* (21) expr ::= expr QMARK expr COLON expr */
914
+};
915
+
916
+/* For rule J, yyRuleInfoNRhs[J] contains the negative of the number
917
+** of symbols on the right-hand side of that rule. */
918
+static const signed char yyRuleInfoNRhs[] = {
919
+ -1, /* (0) program ::= expr */
920
+ -1, /* (1) expr ::= NUMBER */
921
+ -1, /* (2) expr ::= VARIABLE */
922
+ -3, /* (3) expr ::= LPAREN expr RPAREN */
923
+ -2, /* (4) expr ::= PLUS expr */
924
+ -2, /* (5) expr ::= MINUS expr */
925
+ -2, /* (6) expr ::= NOT expr */
926
+ -4, /* (7) expr ::= FUNCTION_ABS LPAREN expr RPAREN */
927
+ -3, /* (8) expr ::= expr PLUS expr */
928
+ -3, /* (9) expr ::= expr MINUS expr */
929
+ -3, /* (10) expr ::= expr MULTIPLY expr */
930
+ -3, /* (11) expr ::= expr DIVIDE expr */
931
+ -3, /* (12) expr ::= expr MODULO expr */
932
+ -3, /* (13) expr ::= expr AND expr */
933
+ -3, /* (14) expr ::= expr OR expr */
934
+ -3, /* (15) expr ::= expr EQ expr */
935
+ -3, /* (16) expr ::= expr NE expr */
936
+ -3, /* (17) expr ::= expr LT expr */
937
+ -3, /* (18) expr ::= expr LE expr */
938
+ -3, /* (19) expr ::= expr GT expr */
939
+ -3, /* (20) expr ::= expr GE expr */
940
+ -5, /* (21) expr ::= expr QMARK expr COLON expr */
941
+};
942
+
943
+static void yy_accept(yyParser*); /* Forward Declaration */
944
+
945
+/*
946
+** Perform a reduce action and the shift that must immediately
947
+** follow the reduce.
948
+**
949
+** The yyLookahead and yyLookaheadToken parameters provide reduce actions
950
+** access to the lookahead token (if any). The yyLookahead will be YYNOCODE
951
+** if the lookahead token has already been consumed. As this procedure is
952
+** only called from one place, optimizing compilers will in-line it, which
953
+** means that the extra parameters have no performance impact.
954
+*/
955
+static YYACTIONTYPE yy_reduce(
956
+ yyParser *yypParser, /* The parser */
957
+ unsigned int yyruleno, /* Number of the rule by which to reduce */
958
+ int yyLookahead, /* Lookahead token, or YYNOCODE if none */
959
+ ParseTOKENTYPE yyLookaheadToken /* Value of the lookahead token */
960
+ ParseCTX_PDECL /* %extra_context */
961
+){
962
+ int yygoto; /* The next state */
963
+ YYACTIONTYPE yyact; /* The next action */
964
+ yyStackEntry *yymsp; /* The top of the parser's stack */
965
+ int yysize; /* Amount to pop the stack */
966
+ ParseARG_FETCH
967
+ (void)yyLookahead;
968
+ (void)yyLookaheadToken;
969
+ yymsp = yypParser->yytos;
970
+
971
+ switch( yyruleno ){
972
+ /* Beginning here are the reduction cases. A typical example
973
+ ** follows:
974
+ ** case 0:
975
+ ** #line <lineno> <grammarfile>
976
+ ** { ... } // User supplied code
977
+ ** #line <lineno> <thisfile>
978
+ ** break;
979
+ */
980
+/********** Begin reduce actions **********************************************/
981
+ YYMINORTYPE yylhsminor;
982
+ case 0: /* program ::= expr */
983
+#line 41 "parser.y"
984
+{
985
+ *result = yymsp[0].minor.yy48;
986
+}
987
+#line 987 "parser.c"
988
+ break;
989
+ case 1: /* expr ::= NUMBER */
990
+#line 46 "parser.y"
991
+{
992
+ yylhsminor.yy48 = eval_node_alloc(1);
993
+ yylhsminor.yy48->operator = EVAL_OPERATOR_NOP;
994
+ eval_node_set_value_to_constant(yylhsminor.yy48, 0, yymsp[0].minor.yy0.dval);
995
+}
996
+#line 996 "parser.c"
997
+ yymsp[0].minor.yy48 = yylhsminor.yy48;
998
+ break;
999
+ case 2: /* expr ::= VARIABLE */
1000
+#line 52 "parser.y"
1001
+{
1002
+ yylhsminor.yy48 = eval_node_alloc(1);
1003
+ yylhsminor.yy48->operator = EVAL_OPERATOR_NOP;
1004
+ eval_node_set_value_to_variable(yylhsminor.yy48, 0, yymsp[0].minor.yy0.strval);
1005
+ freez(yymsp[0].minor.yy0.strval); // Free the strdup'd string
1006
+}
1007
+#line 1007 "parser.c"
1008
+ yymsp[0].minor.yy48 = yylhsminor.yy48;
1009
+ break;
1010
+ case 3: /* expr ::= LPAREN expr RPAREN */
1011
+#line 60 "parser.y"
1012
+{
1013
+ yymsp[-2].minor.yy48 = eval_node_alloc(1);
1014
+ yymsp[-2].minor.yy48->operator = EVAL_OPERATOR_EXPRESSION_OPEN;
1015
+ yymsp[-2].minor.yy48->precedence = eval_precedence(EVAL_OPERATOR_EXPRESSION_OPEN);
1016
+ eval_node_set_value_to_node(yymsp[-2].minor.yy48, 0, yymsp[-1].minor.yy48);
1017
+}
1018
+#line 1018 "parser.c"
1019
+ break;
1020
+ case 4: /* expr ::= PLUS expr */
1021
+#line 68 "parser.y"
1022
+{
1023
+ yymsp[-1].minor.yy48 = eval_node_alloc(1);
1024
+ yymsp[-1].minor.yy48->operator = EVAL_OPERATOR_SIGN_PLUS;
1025
+ yymsp[-1].minor.yy48->precedence = eval_precedence(EVAL_OPERATOR_SIGN_PLUS);
1026
+ eval_node_set_value_to_node(yymsp[-1].minor.yy48, 0, yymsp[0].minor.yy48);
1027
+}
1028
+#line 1028 "parser.c"
1029
+ break;
1030
+ case 5: /* expr ::= MINUS expr */
1031
+#line 75 "parser.y"
1032
+{
1033
+ yymsp[-1].minor.yy48 = eval_node_alloc(1);
1034
+ yymsp[-1].minor.yy48->operator = EVAL_OPERATOR_SIGN_MINUS;
1035
+ yymsp[-1].minor.yy48->precedence = eval_precedence(EVAL_OPERATOR_SIGN_MINUS);
1036
+ eval_node_set_value_to_node(yymsp[-1].minor.yy48, 0, yymsp[0].minor.yy48);
1037
+}
1038
+#line 1038 "parser.c"
1039
+ break;
1040
+ case 6: /* expr ::= NOT expr */
1041
+#line 82 "parser.y"
1042
+{
1043
+ yymsp[-1].minor.yy48 = eval_node_alloc(1);
1044
+ yymsp[-1].minor.yy48->operator = EVAL_OPERATOR_NOT;
1045
+ yymsp[-1].minor.yy48->precedence = eval_precedence(EVAL_OPERATOR_NOT);
1046
+ eval_node_set_value_to_node(yymsp[-1].minor.yy48, 0, yymsp[0].minor.yy48);
1047
+}
1048
+#line 1048 "parser.c"
1049
+ break;
1050
+ case 7: /* expr ::= FUNCTION_ABS LPAREN expr RPAREN */
1051
+#line 90 "parser.y"
1052
+{
1053
+ yymsp[-3].minor.yy48 = eval_node_alloc(1);
1054
+ yymsp[-3].minor.yy48->operator = EVAL_OPERATOR_ABS;
1055
+ yymsp[-3].minor.yy48->precedence = eval_precedence(EVAL_OPERATOR_ABS);
1056
+ eval_node_set_value_to_node(yymsp[-3].minor.yy48, 0, yymsp[-1].minor.yy48);
1057
+}
1058
+#line 1058 "parser.c"
1059
+ break;
1060
+ case 8: /* expr ::= expr PLUS expr */
1061
+#line 98 "parser.y"
1062
+{
1063
+ yylhsminor.yy48 = eval_node_alloc(2);
1064
+ yylhsminor.yy48->operator = EVAL_OPERATOR_PLUS;
1065
+ yylhsminor.yy48->precedence = eval_precedence(EVAL_OPERATOR_PLUS);
1066
+ eval_node_set_value_to_node(yylhsminor.yy48, 0, yymsp[-2].minor.yy48);
1067
+ eval_node_set_value_to_node(yylhsminor.yy48, 1, yymsp[0].minor.yy48);
1068
+}
1069
+#line 1069 "parser.c"
1070
+ yymsp[-2].minor.yy48 = yylhsminor.yy48;
1071
+ break;
1072
+ case 9: /* expr ::= expr MINUS expr */
1073
+#line 106 "parser.y"
1074
+{
1075
+ yylhsminor.yy48 = eval_node_alloc(2);
1076
+ yylhsminor.yy48->operator = EVAL_OPERATOR_MINUS;
1077
+ yylhsminor.yy48->precedence = eval_precedence(EVAL_OPERATOR_MINUS);
1078
+ eval_node_set_value_to_node(yylhsminor.yy48, 0, yymsp[-2].minor.yy48);
1079
+ eval_node_set_value_to_node(yylhsminor.yy48, 1, yymsp[0].minor.yy48);
1080
+}
1081
+#line 1081 "parser.c"
1082
+ yymsp[-2].minor.yy48 = yylhsminor.yy48;
1083
+ break;
1084
+ case 10: /* expr ::= expr MULTIPLY expr */
1085
+#line 114 "parser.y"
1086
+{
1087
+ yylhsminor.yy48 = eval_node_alloc(2);
1088
+ yylhsminor.yy48->operator = EVAL_OPERATOR_MULTIPLY;
1089
+ yylhsminor.yy48->precedence = eval_precedence(EVAL_OPERATOR_MULTIPLY);
1090
+ eval_node_set_value_to_node(yylhsminor.yy48, 0, yymsp[-2].minor.yy48);
1091
+ eval_node_set_value_to_node(yylhsminor.yy48, 1, yymsp[0].minor.yy48);
1092
+}
1093
+#line 1093 "parser.c"
1094
+ yymsp[-2].minor.yy48 = yylhsminor.yy48;
1095
+ break;
1096
+ case 11: /* expr ::= expr DIVIDE expr */
1097
+#line 122 "parser.y"
1098
+{
1099
+ yylhsminor.yy48 = eval_node_alloc(2);
1100
+ yylhsminor.yy48->operator = EVAL_OPERATOR_DIVIDE;
1101
+ yylhsminor.yy48->precedence = eval_precedence(EVAL_OPERATOR_DIVIDE);
1102
+ eval_node_set_value_to_node(yylhsminor.yy48, 0, yymsp[-2].minor.yy48);
1103
+ eval_node_set_value_to_node(yylhsminor.yy48, 1, yymsp[0].minor.yy48);
1104
+}
1105
+#line 1105 "parser.c"
1106
+ yymsp[-2].minor.yy48 = yylhsminor.yy48;
1107
+ break;
1108
+ case 12: /* expr ::= expr MODULO expr */
1109
+#line 130 "parser.y"
1110
+{
1111
+ yylhsminor.yy48 = eval_node_alloc(2);
1112
+ yylhsminor.yy48->operator = EVAL_OPERATOR_MODULO;
1113
+ yylhsminor.yy48->precedence = eval_precedence(EVAL_OPERATOR_MODULO);
1114
+ eval_node_set_value_to_node(yylhsminor.yy48, 0, yymsp[-2].minor.yy48);
1115
+ eval_node_set_value_to_node(yylhsminor.yy48, 1, yymsp[0].minor.yy48);
1116
+}
1117
+#line 1117 "parser.c"
1118
+ yymsp[-2].minor.yy48 = yylhsminor.yy48;
1119
+ break;
1120
+ case 13: /* expr ::= expr AND expr */
1121
+#line 138 "parser.y"
1122
+{
1123
+ yylhsminor.yy48 = eval_node_alloc(2);
1124
+ yylhsminor.yy48->operator = EVAL_OPERATOR_AND;
1125
+ yylhsminor.yy48->precedence = eval_precedence(EVAL_OPERATOR_AND);
1126
+ eval_node_set_value_to_node(yylhsminor.yy48, 0, yymsp[-2].minor.yy48);
1127
+ eval_node_set_value_to_node(yylhsminor.yy48, 1, yymsp[0].minor.yy48);
1128
+}
1129
+#line 1129 "parser.c"
1130
+ yymsp[-2].minor.yy48 = yylhsminor.yy48;
1131
+ break;
1132
+ case 14: /* expr ::= expr OR expr */
1133
+#line 146 "parser.y"
1134
+{
1135
+ yylhsminor.yy48 = eval_node_alloc(2);
1136
+ yylhsminor.yy48->operator = EVAL_OPERATOR_OR;
1137
+ yylhsminor.yy48->precedence = eval_precedence(EVAL_OPERATOR_OR);
1138
+ eval_node_set_value_to_node(yylhsminor.yy48, 0, yymsp[-2].minor.yy48);
1139
+ eval_node_set_value_to_node(yylhsminor.yy48, 1, yymsp[0].minor.yy48);
1140
+}
1141
+#line 1141 "parser.c"
1142
+ yymsp[-2].minor.yy48 = yylhsminor.yy48;
1143
+ break;
1144
+ case 15: /* expr ::= expr EQ expr */
1145
+#line 154 "parser.y"
1146
+{
1147
+ yylhsminor.yy48 = eval_node_alloc(2);
1148
+ yylhsminor.yy48->operator = EVAL_OPERATOR_EQUAL;
1149
+ yylhsminor.yy48->precedence = eval_precedence(EVAL_OPERATOR_EQUAL);
1150
+ eval_node_set_value_to_node(yylhsminor.yy48, 0, yymsp[-2].minor.yy48);
1151
+ eval_node_set_value_to_node(yylhsminor.yy48, 1, yymsp[0].minor.yy48);
1152
+}
1153
+#line 1153 "parser.c"
1154
+ yymsp[-2].minor.yy48 = yylhsminor.yy48;
1155
+ break;
1156
+ case 16: /* expr ::= expr NE expr */
1157
+#line 162 "parser.y"
1158
+{
1159
+ yylhsminor.yy48 = eval_node_alloc(2);
1160
+ yylhsminor.yy48->operator = EVAL_OPERATOR_NOT_EQUAL;
1161
+ yylhsminor.yy48->precedence = eval_precedence(EVAL_OPERATOR_NOT_EQUAL);
1162
+ eval_node_set_value_to_node(yylhsminor.yy48, 0, yymsp[-2].minor.yy48);
1163
+ eval_node_set_value_to_node(yylhsminor.yy48, 1, yymsp[0].minor.yy48);
1164
+}
1165
+#line 1165 "parser.c"
1166
+ yymsp[-2].minor.yy48 = yylhsminor.yy48;
1167
+ break;
1168
+ case 17: /* expr ::= expr LT expr */
1169
+#line 170 "parser.y"
1170
+{
1171
+ yylhsminor.yy48 = eval_node_alloc(2);
1172
+ yylhsminor.yy48->operator = EVAL_OPERATOR_LESS;
1173
+ yylhsminor.yy48->precedence = eval_precedence(EVAL_OPERATOR_LESS);
1174
+ eval_node_set_value_to_node(yylhsminor.yy48, 0, yymsp[-2].minor.yy48);
1175
+ eval_node_set_value_to_node(yylhsminor.yy48, 1, yymsp[0].minor.yy48);
1176
+}
1177
+#line 1177 "parser.c"
1178
+ yymsp[-2].minor.yy48 = yylhsminor.yy48;
1179
+ break;
1180
+ case 18: /* expr ::= expr LE expr */
1181
+#line 178 "parser.y"
1182
+{
1183
+ yylhsminor.yy48 = eval_node_alloc(2);
1184
+ yylhsminor.yy48->operator = EVAL_OPERATOR_LESS_THAN_OR_EQUAL;
1185
+ yylhsminor.yy48->precedence = eval_precedence(EVAL_OPERATOR_LESS_THAN_OR_EQUAL);
1186
+ eval_node_set_value_to_node(yylhsminor.yy48, 0, yymsp[-2].minor.yy48);
1187
+ eval_node_set_value_to_node(yylhsminor.yy48, 1, yymsp[0].minor.yy48);
1188
+}
1189
+#line 1189 "parser.c"
1190
+ yymsp[-2].minor.yy48 = yylhsminor.yy48;
1191
+ break;
1192
+ case 19: /* expr ::= expr GT expr */
1193
+#line 186 "parser.y"
1194
+{
1195
+ yylhsminor.yy48 = eval_node_alloc(2);
1196
+ yylhsminor.yy48->operator = EVAL_OPERATOR_GREATER;
1197
+ yylhsminor.yy48->precedence = eval_precedence(EVAL_OPERATOR_GREATER);
1198
+ eval_node_set_value_to_node(yylhsminor.yy48, 0, yymsp[-2].minor.yy48);
1199
+ eval_node_set_value_to_node(yylhsminor.yy48, 1, yymsp[0].minor.yy48);
1200
+}
1201
+#line 1201 "parser.c"
1202
+ yymsp[-2].minor.yy48 = yylhsminor.yy48;
1203
+ break;
1204
+ case 20: /* expr ::= expr GE expr */
1205
+#line 194 "parser.y"
1206
+{
1207
+ yylhsminor.yy48 = eval_node_alloc(2);
1208
+ yylhsminor.yy48->operator = EVAL_OPERATOR_GREATER_THAN_OR_EQUAL;
1209
+ yylhsminor.yy48->precedence = eval_precedence(EVAL_OPERATOR_GREATER_THAN_OR_EQUAL);
1210
+ eval_node_set_value_to_node(yylhsminor.yy48, 0, yymsp[-2].minor.yy48);
1211
+ eval_node_set_value_to_node(yylhsminor.yy48, 1, yymsp[0].minor.yy48);
1212
+}
1213
+#line 1213 "parser.c"
1214
+ yymsp[-2].minor.yy48 = yylhsminor.yy48;
1215
+ break;
1216
+ case 21: /* expr ::= expr QMARK expr COLON expr */
1217
+#line 205 "parser.y"
1218
+{
1219
+ yylhsminor.yy48 = eval_node_alloc(3);
1220
+ yylhsminor.yy48->operator = EVAL_OPERATOR_IF_THEN_ELSE;
1221
+ yylhsminor.yy48->precedence = eval_precedence(EVAL_OPERATOR_IF_THEN_ELSE);
1222
+ eval_node_set_value_to_node(yylhsminor.yy48, 0, yymsp[-4].minor.yy48);
1223
+ eval_node_set_value_to_node(yylhsminor.yy48, 1, yymsp[-2].minor.yy48);
1224
+ eval_node_set_value_to_node(yylhsminor.yy48, 2, yymsp[0].minor.yy48);
1225
+}
1226
+#line 1226 "parser.c"
1227
+ yymsp[-4].minor.yy48 = yylhsminor.yy48;
1228
+ break;
1229
+ default:
1230
+ break;
1231
+/********** End reduce actions ************************************************/
1232
+ };
1233
+ assert( yyruleno<sizeof(yyRuleInfoLhs)/sizeof(yyRuleInfoLhs[0]) );
1234
+ yygoto = yyRuleInfoLhs[yyruleno];
1235
+ yysize = yyRuleInfoNRhs[yyruleno];
1236
+ yyact = yy_find_reduce_action(yymsp[yysize].stateno,(YYCODETYPE)yygoto);
1237
+
1238
+ /* There are no SHIFTREDUCE actions on nonterminals because the table
1239
+ ** generator has simplified them to pure REDUCE actions. */
1240
+ assert( !(yyact>YY_MAX_SHIFT && yyact<=YY_MAX_SHIFTREDUCE) );
1241
+
1242
+ /* It is not possible for a REDUCE to be followed by an error */
1243
+ assert( yyact!=YY_ERROR_ACTION );
1244
+
1245
+ yymsp += yysize+1;
1246
+ yypParser->yytos = yymsp;
1247
+ yymsp->stateno = (YYACTIONTYPE)yyact;
1248
+ yymsp->major = (YYCODETYPE)yygoto;
1249
+ yyTraceShift(yypParser, yyact, "... then shift");
1250
+ return yyact;
1251
+}
1252
+
1253
+/*
1254
+** The following code executes when the parse fails
1255
+*/
1256
+#ifndef YYNOERRORRECOVERY
1257
+static void yy_parse_failed(
1258
+ yyParser *yypParser /* The parser */
1259
+){
1260
+ ParseARG_FETCH
1261
+ ParseCTX_FETCH
1262
+#ifndef NDEBUG
1263
+ if( yyTraceFILE ){
1264
+ fprintf(yyTraceFILE,"%sFail!\n",yyTracePrompt);
1265
+ }
1266
+#endif
1267
+ while( yypParser->yytos>yypParser->yystack ) yy_pop_parser_stack(yypParser);
1268
+ /* Here code is inserted which will be executed whenever the
1269
+ ** parser fails */
1270
+/************ Begin %parse_failure code ***************************************/
1271
+#line 24 "parser.y"
1272
+
1273
+ // Failed to parse the expression
1274
+ if (*result) {
1275
+ eval_node_free(*result);
1276
+ *result = NULL;
1277
+ }
1278
+#line 1278 "parser.c"
1279
+/************ End %parse_failure code *****************************************/
1280
+ ParseARG_STORE /* Suppress warning about unused %extra_argument variable */
1281
+ ParseCTX_STORE
1282
+}
1283
+#endif /* YYNOERRORRECOVERY */
1284
+
1285
+/*
1286
+** The following code executes when a syntax error first occurs.
1287
+*/
1288
+static void yy_syntax_error(
1289
+ yyParser *yypParser, /* The parser */
1290
+ int yymajor, /* The major type of the error token */
1291
+ ParseTOKENTYPE yyminor /* The minor type of the error token */
1292
+){
1293
+ ParseARG_FETCH
1294
+ ParseCTX_FETCH
1295
+#define TOKEN yyminor
1296
+/************ Begin %syntax_error code ****************************************/
1297
+#line 13 "parser.y"
1298
+
1299
+ // Create a NOP node with count=0 as an error marker
1300
+ EVAL_NODE *error_node = eval_node_alloc(0);
1301
+ error_node->operator = EVAL_OPERATOR_NOP;
1302
+ *result = error_node;
1303
+#line 1303 "parser.c"
1304
+/************ End %syntax_error code ******************************************/
1305
+ ParseARG_STORE /* Suppress warning about unused %extra_argument variable */
1306
+ ParseCTX_STORE
1307
+}
1308
+
1309
+/*
1310
+** The following is executed when the parser accepts
1311
+*/
1312
+static void yy_accept(
1313
+ yyParser *yypParser /* The parser */
1314
+){
1315
+ ParseARG_FETCH
1316
+ ParseCTX_FETCH
1317
+#ifndef NDEBUG
1318
+ if( yyTraceFILE ){
1319
+ fprintf(yyTraceFILE,"%sAccept!\n",yyTracePrompt);
1320
+ }
1321
+#endif
1322
+#ifndef YYNOERRORRECOVERY
1323
+ yypParser->yyerrcnt = -1;
1324
+#endif
1325
+ assert( yypParser->yytos==yypParser->yystack );
1326
+ /* Here code is inserted which will be executed whenever the
1327
+ ** parser accepts */
1328
+/*********** Begin %parse_accept code *****************************************/
1329
+#line 20 "parser.y"
1330
+
1331
+ // Successfully parsed the expression
1332
+#line 1332 "parser.c"
1333
+/*********** End %parse_accept code *******************************************/
1334
+ ParseARG_STORE /* Suppress warning about unused %extra_argument variable */
1335
+ ParseCTX_STORE
1336
+}
1337
+
1338
+/* The main parser program.
1339
+** The first argument is a pointer to a structure obtained from
1340
+** "ParseAlloc" which describes the current state of the parser.
1341
+** The second argument is the major token number. The third is
1342
+** the minor token. The fourth optional argument is whatever the
1343
+** user wants (and specified in the grammar) and is available for
1344
+** use by the action routines.
1345
+**
1346
+** Inputs:
1347
+** <ul>
1348
+** <li> A pointer to the parser (an opaque structure.)
1349
+** <li> The major token number.
1350
+** <li> The minor token number.
1351
+** <li> An option argument of a grammar-specified type.
1352
+** </ul>
1353
+**
1354
+** Outputs:
1355
+** None.
1356
+*/
1357
+void Parse(
1358
+ void *yyp, /* The parser */
1359
+ int yymajor, /* The major token code number */
1360
+ ParseTOKENTYPE yyminor /* The value for the token */
1361
+ ParseARG_PDECL /* Optional %extra_argument parameter */
1362
+){
1363
+ YYMINORTYPE yyminorunion;
1364
+ YYACTIONTYPE yyact; /* The parser action. */
1365
+#if !defined(YYERRORSYMBOL) && !defined(YYNOERRORRECOVERY)
1366
+ int yyendofinput; /* True if we are at the end of input */
1367
+#endif
1368
+#ifdef YYERRORSYMBOL
1369
+ int yyerrorhit = 0; /* True if yymajor has invoked an error */
1370
+#endif
1371
+ yyParser *yypParser = (yyParser*)yyp; /* The parser */
1372
+ ParseCTX_FETCH
1373
+ ParseARG_STORE
1374
+
1375
+ assert( yypParser->yytos!=0 );
1376
+#if !defined(YYERRORSYMBOL) && !defined(YYNOERRORRECOVERY)
1377
+ yyendofinput = (yymajor==0);
1378
+#endif
1379
+
1380
+ yyact = yypParser->yytos->stateno;
1381
+#ifndef NDEBUG
1382
+ if( yyTraceFILE ){
1383
+ if( yyact < YY_MIN_REDUCE ){
1384
+ fprintf(yyTraceFILE,"%sInput '%s' in state %d\n",
1385
+ yyTracePrompt,yyTokenName[yymajor],yyact);
1386
+ }else{
1387
+ fprintf(yyTraceFILE,"%sInput '%s' with pending reduce %d\n",
1388
+ yyTracePrompt,yyTokenName[yymajor],yyact-YY_MIN_REDUCE);
1389
+ }
1390
+ }
1391
+#endif
1392
+
1393
+ while(1){ /* Exit by "break" */
1394
+ assert( yypParser->yytos>=yypParser->yystack );
1395
+ assert( yyact==yypParser->yytos->stateno );
1396
+ yyact = yy_find_shift_action((YYCODETYPE)yymajor,yyact);
1397
+ if( yyact >= YY_MIN_REDUCE ){
1398
+ unsigned int yyruleno = yyact - YY_MIN_REDUCE; /* Reduce by this rule */
1399
+#ifndef NDEBUG
1400
+ assert( yyruleno<(int)(sizeof(yyRuleName)/sizeof(yyRuleName[0])) );
1401
+ if( yyTraceFILE ){
1402
+ int yysize = yyRuleInfoNRhs[yyruleno];
1403
+ if( yysize ){
1404
+ fprintf(yyTraceFILE, "%sReduce %d [%s]%s, pop back to state %d.\n",
1405
+ yyTracePrompt,
1406
+ yyruleno, yyRuleName[yyruleno],
1407
+ yyruleno<YYNRULE_WITH_ACTION ? "" : " without external action",
1408
+ yypParser->yytos[yysize].stateno);
1409
+ }else{
1410
+ fprintf(yyTraceFILE, "%sReduce %d [%s]%s.\n",
1411
+ yyTracePrompt, yyruleno, yyRuleName[yyruleno],
1412
+ yyruleno<YYNRULE_WITH_ACTION ? "" : " without external action");
1413
+ }
1414
+ }
1415
+#endif /* NDEBUG */
1416
+
1417
+ /* Check that the stack is large enough to grow by a single entry
1418
+ ** if the RHS of the rule is empty. This ensures that there is room
1419
+ ** enough on the stack to push the LHS value */
1420
+ if( yyRuleInfoNRhs[yyruleno]==0 ){
1421
+#ifdef YYTRACKMAXSTACKDEPTH
1422
+ if( (int)(yypParser->yytos - yypParser->yystack)>yypParser->yyhwm ){
1423
+ yypParser->yyhwm++;
1424
+ assert( yypParser->yyhwm ==
1425
+ (int)(yypParser->yytos - yypParser->yystack));
1426
+ }
1427
+#endif
1428
+ if( yypParser->yytos>=yypParser->yystackEnd ){
1429
+ if( yyGrowStack(yypParser) ){
1430
+ yyStackOverflow(yypParser);
1431
+ break;
1432
+ }
1433
+ }
1434
+ }
1435
+ yyact = yy_reduce(yypParser,yyruleno,yymajor,yyminor ParseCTX_PARAM);
1436
+ }else if( yyact <= YY_MAX_SHIFTREDUCE ){
1437
+ yy_shift(yypParser,yyact,(YYCODETYPE)yymajor,yyminor);
1438
+#ifndef YYNOERRORRECOVERY
1439
+ yypParser->yyerrcnt--;
1440
+#endif
1441
+ break;
1442
+ }else if( yyact==YY_ACCEPT_ACTION ){
1443
+ yypParser->yytos--;
1444
+ yy_accept(yypParser);
1445
+ return;
1446
+ }else{
1447
+ assert( yyact == YY_ERROR_ACTION );
1448
+ yyminorunion.yy0 = yyminor;
1449
+#ifdef YYERRORSYMBOL
1450
+ int yymx;
1451
+#endif
1452
+#ifndef NDEBUG
1453
+ if( yyTraceFILE ){
1454
+ fprintf(yyTraceFILE,"%sSyntax Error!\n",yyTracePrompt);
1455
+ }
1456
+#endif
1457
+#ifdef YYERRORSYMBOL
1458
+ /* A syntax error has occurred.
1459
+ ** The response to an error depends upon whether or not the
1460
+ ** grammar defines an error token "ERROR".
1461
+ **
1462
+ ** This is what we do if the grammar does define ERROR:
1463
+ **
1464
+ ** * Call the %syntax_error function.
1465
+ **
1466
+ ** * Begin popping the stack until we enter a state where
1467
+ ** it is legal to shift the error symbol, then shift
1468
+ ** the error symbol.
1469
+ **
1470
+ ** * Set the error count to three.
1471
+ **
1472
+ ** * Begin accepting and shifting new tokens. No new error
1473
+ ** processing will occur until three tokens have been
1474
+ ** shifted successfully.
1475
+ **
1476
+ */
1477
+ if( yypParser->yyerrcnt<0 ){
1478
+ yy_syntax_error(yypParser,yymajor,yyminor);
1479
+ }
1480
+ yymx = yypParser->yytos->major;
1481
+ if( yymx==YYERRORSYMBOL || yyerrorhit ){
1482
+#ifndef NDEBUG
1483
+ if( yyTraceFILE ){
1484
+ fprintf(yyTraceFILE,"%sDiscard input token %s\n",
1485
+ yyTracePrompt,yyTokenName[yymajor]);
1486
+ }
1487
+#endif
1488
+ yy_destructor(yypParser, (YYCODETYPE)yymajor, &yyminorunion);
1489
+ yymajor = YYNOCODE;
1490
+ }else{
1491
+ while( yypParser->yytos > yypParser->yystack ){
1492
+ yyact = yy_find_reduce_action(yypParser->yytos->stateno,
1493
+ YYERRORSYMBOL);
1494
+ if( yyact<=YY_MAX_SHIFTREDUCE ) break;
1495
+ yy_pop_parser_stack(yypParser);
1496
+ }
1497
+ if( yypParser->yytos <= yypParser->yystack || yymajor==0 ){
1498
+ yy_destructor(yypParser,(YYCODETYPE)yymajor,&yyminorunion);
1499
+ yy_parse_failed(yypParser);
1500
+#ifndef YYNOERRORRECOVERY
1501
+ yypParser->yyerrcnt = -1;
1502
+#endif
1503
+ yymajor = YYNOCODE;
1504
+ }else if( yymx!=YYERRORSYMBOL ){
1505
+ yy_shift(yypParser,yyact,YYERRORSYMBOL,yyminor);
1506
+ }
1507
+ }
1508
+ yypParser->yyerrcnt = 3;
1509
+ yyerrorhit = 1;
1510
+ if( yymajor==YYNOCODE ) break;
1511
+ yyact = yypParser->yytos->stateno;
1512
+#elif defined(YYNOERRORRECOVERY)
1513
+ /* If the YYNOERRORRECOVERY macro is defined, then do not attempt to
1514
+ ** do any kind of error recovery. Instead, simply invoke the syntax
1515
+ ** error routine and continue going as if nothing had happened.
1516
+ **
1517
+ ** Applications can set this macro (for example inside %include) if
1518
+ ** they intend to abandon the parse upon the first syntax error seen.
1519
+ */
1520
+ yy_syntax_error(yypParser,yymajor, yyminor);
1521
+ yy_destructor(yypParser,(YYCODETYPE)yymajor,&yyminorunion);
1522
+ break;
1523
+#else /* YYERRORSYMBOL is not defined */
1524
+ /* This is what we do if the grammar does not define ERROR:
1525
+ **
1526
+ ** * Report an error message, and throw away the input token.
1527
+ **
1528
+ ** * If the input token is $, then fail the parse.
1529
+ **
1530
+ ** As before, subsequent error messages are suppressed until
1531
+ ** three input tokens have been successfully shifted.
1532
+ */
1533
+ if( yypParser->yyerrcnt<=0 ){
1534
+ yy_syntax_error(yypParser,yymajor, yyminor);
1535
+ }
1536
+ yypParser->yyerrcnt = 3;
1537
+ yy_destructor(yypParser,(YYCODETYPE)yymajor,&yyminorunion);
1538
+ if( yyendofinput ){
1539
+ yy_parse_failed(yypParser);
1540
+#ifndef YYNOERRORRECOVERY
1541
+ yypParser->yyerrcnt = -1;
1542
+#endif
1543
+ }
1544
+ break;
1545
+#endif
1546
+ }
1547
+ }
1548
+#ifndef NDEBUG
1549
+ if( yyTraceFILE ){
1550
+ yyStackEntry *i;
1551
+ char cDiv = '[';
1552
+ fprintf(yyTraceFILE,"%sReturn. Stack=",yyTracePrompt);
1553
+ for(i=&yypParser->yystack[1]; i<=yypParser->yytos; i++){
1554
+ fprintf(yyTraceFILE,"%c%s", cDiv, yyTokenName[i->major]);
1555
+ cDiv = ' ';
1556
+ }
1557
+ fprintf(yyTraceFILE,"]\n");
1558
+ }
1559
+#endif
1560
+ return;
1561
+}
1562
+
1563
+/*
1564
+** Return the fallback token corresponding to canonical token iToken, or
1565
+** 0 if iToken has no fallback.
1566
+*/
1567
+int ParseFallback(int iToken){
1568
+#ifdef YYFALLBACK
1569
+ assert( iToken<(int)(sizeof(yyFallback)/sizeof(yyFallback[0])) );
1570
+ return yyFallback[iToken];
1571
+#else
1572
+ (void)iToken;
1573
+ return 0;
1574
+#endif
1575
+}
src/libnetdata/eval/re2c_lemon/parser.h
new
+23
@@ -0,0 +1,23 @@
1
+#define TOK_NUMBER 1
2
+#define TOK_VARIABLE 2
3
+#define TOK_LPAREN 3
4
+#define TOK_RPAREN 4
5
+#define TOK_PLUS 5
6
+#define TOK_UPLUS 6
7
+#define TOK_MINUS 7
8
+#define TOK_UMINUS 8
9
+#define TOK_NOT 9
10
+#define TOK_FUNCTION_ABS 10
11
+#define TOK_MULTIPLY 11
12
+#define TOK_DIVIDE 12
13
+#define TOK_MODULO 13
14
+#define TOK_AND 14
15
+#define TOK_OR 15
16
+#define TOK_EQ 16
17
+#define TOK_NE 17
18
+#define TOK_LT 18
19
+#define TOK_LE 19
20
+#define TOK_GT 20
21
+#define TOK_GE 21
22
+#define TOK_QMARK 22
23
+#define TOK_COLON 23
src/libnetdata/eval/re2c_lemon/parser.out
new
+1180
@@ -0,0 +1,1180 @@
1
+State 0:
2
+ program ::= * expr
3
+ expr ::= * NUMBER
4
+ expr ::= * VARIABLE
5
+ expr ::= * LPAREN expr RPAREN
6
+ expr ::= * PLUS expr
7
+ expr ::= * MINUS expr
8
+ expr ::= * NOT expr
9
+ expr ::= * FUNCTION_ABS LPAREN expr RPAREN
10
+ expr ::= * expr PLUS expr
11
+ expr ::= * expr MINUS expr
12
+ expr ::= * expr MULTIPLY expr
13
+ expr ::= * expr DIVIDE expr
14
+ expr ::= * expr MODULO expr
15
+ expr ::= * expr AND expr
16
+ expr ::= * expr OR expr
17
+ expr ::= * expr EQ expr
18
+ expr ::= * expr NE expr
19
+ expr ::= * expr LT expr
20
+ expr ::= * expr LE expr
21
+ expr ::= * expr GT expr
22
+ expr ::= * expr GE expr
23
+ expr ::= * expr QMARK expr COLON expr
24
+
25
+ NUMBER shift-reduce 1 expr ::= NUMBER
26
+ VARIABLE shift-reduce 2 expr ::= VARIABLE
27
+ LPAREN shift 19
28
+ PLUS shift 17
29
+ MINUS shift 15
30
+ NOT shift 13
31
+ FUNCTION_ABS shift 36
32
+ expr shift 24
33
+ program accept
34
+
35
+State 1:
36
+ expr ::= * NUMBER
37
+ expr ::= * VARIABLE
38
+ expr ::= * LPAREN expr RPAREN
39
+ expr ::= * PLUS expr
40
+ expr ::= * MINUS expr
41
+ expr ::= * NOT expr
42
+ expr ::= * FUNCTION_ABS LPAREN expr RPAREN
43
+ expr ::= * expr PLUS expr
44
+ expr ::= * expr MINUS expr
45
+ expr ::= * expr MULTIPLY expr
46
+ expr ::= * expr DIVIDE expr
47
+ expr ::= * expr MODULO expr
48
+ expr ::= * expr AND expr
49
+ expr ::= * expr OR expr
50
+ expr ::= * expr EQ expr
51
+ expr ::= * expr NE expr
52
+ expr ::= * expr LT expr
53
+ expr ::= * expr LE expr
54
+ expr ::= * expr GT expr
55
+ expr ::= * expr GE expr
56
+ expr ::= * expr QMARK expr COLON expr
57
+ expr ::= expr QMARK expr COLON * expr
58
+
59
+ NUMBER shift-reduce 1 expr ::= NUMBER
60
+ VARIABLE shift-reduce 2 expr ::= VARIABLE
61
+ LPAREN shift 19
62
+ PLUS shift 17
63
+ MINUS shift 15
64
+ NOT shift 13
65
+ FUNCTION_ABS shift 36
66
+ expr shift 25
67
+
68
+State 2:
69
+ expr ::= * NUMBER
70
+ expr ::= * VARIABLE
71
+ expr ::= * LPAREN expr RPAREN
72
+ expr ::= * PLUS expr
73
+ expr ::= * MINUS expr
74
+ expr ::= * NOT expr
75
+ expr ::= * FUNCTION_ABS LPAREN expr RPAREN
76
+ expr ::= * expr PLUS expr
77
+ expr ::= * expr MINUS expr
78
+ expr ::= * expr MULTIPLY expr
79
+ expr ::= * expr DIVIDE expr
80
+ expr ::= * expr MODULO expr
81
+ expr ::= * expr AND expr
82
+ expr ::= * expr OR expr
83
+ expr ::= * expr EQ expr
84
+ expr ::= * expr NE expr
85
+ expr ::= * expr LT expr
86
+ expr ::= * expr LE expr
87
+ expr ::= * expr GT expr
88
+ expr ::= * expr GE expr
89
+ expr ::= * expr QMARK expr COLON expr
90
+ expr ::= expr QMARK * expr COLON expr
91
+
92
+ NUMBER shift-reduce 1 expr ::= NUMBER
93
+ VARIABLE shift-reduce 2 expr ::= VARIABLE
94
+ LPAREN shift 19
95
+ PLUS shift 17
96
+ MINUS shift 15
97
+ NOT shift 13
98
+ FUNCTION_ABS shift 36
99
+ expr shift 21
100
+
101
+State 3:
102
+ expr ::= * NUMBER
103
+ expr ::= * VARIABLE
104
+ expr ::= * LPAREN expr RPAREN
105
+ expr ::= * PLUS expr
106
+ expr ::= * MINUS expr
107
+ expr ::= * NOT expr
108
+ expr ::= * FUNCTION_ABS LPAREN expr RPAREN
109
+ expr ::= * expr PLUS expr
110
+ expr ::= * expr MINUS expr
111
+ expr ::= * expr MULTIPLY expr
112
+ expr ::= * expr DIVIDE expr
113
+ expr ::= * expr MODULO expr
114
+ expr ::= * expr AND expr
115
+ expr ::= * expr OR expr
116
+ expr ::= * expr EQ expr
117
+ expr ::= * expr NE expr
118
+ expr ::= * expr LT expr
119
+ expr ::= * expr LE expr
120
+ expr ::= * expr GT expr
121
+ expr ::= * expr GE expr
122
+ expr ::= expr GE * expr
123
+ expr ::= * expr QMARK expr COLON expr
124
+
125
+ NUMBER shift-reduce 1 expr ::= NUMBER
126
+ VARIABLE shift-reduce 2 expr ::= VARIABLE
127
+ LPAREN shift 19
128
+ PLUS shift 17
129
+ MINUS shift 15
130
+ NOT shift 13
131
+ FUNCTION_ABS shift 36
132
+ expr shift 30
133
+
134
+State 4:
135
+ expr ::= * NUMBER
136
+ expr ::= * VARIABLE
137
+ expr ::= * LPAREN expr RPAREN
138
+ expr ::= * PLUS expr
139
+ expr ::= * MINUS expr
140
+ expr ::= * NOT expr
141
+ expr ::= * FUNCTION_ABS LPAREN expr RPAREN
142
+ expr ::= * expr PLUS expr
143
+ expr ::= * expr MINUS expr
144
+ expr ::= * expr MULTIPLY expr
145
+ expr ::= * expr DIVIDE expr
146
+ expr ::= * expr MODULO expr
147
+ expr ::= * expr AND expr
148
+ expr ::= * expr OR expr
149
+ expr ::= * expr EQ expr
150
+ expr ::= * expr NE expr
151
+ expr ::= * expr LT expr
152
+ expr ::= * expr LE expr
153
+ expr ::= * expr GT expr
154
+ expr ::= expr GT * expr
155
+ expr ::= * expr GE expr
156
+ expr ::= * expr QMARK expr COLON expr
157
+
158
+ NUMBER shift-reduce 1 expr ::= NUMBER
159
+ VARIABLE shift-reduce 2 expr ::= VARIABLE
160
+ LPAREN shift 19
161
+ PLUS shift 17
162
+ MINUS shift 15
163
+ NOT shift 13
164
+ FUNCTION_ABS shift 36
165
+ expr shift 31
166
+
167
+State 5:
168
+ expr ::= * NUMBER
169
+ expr ::= * VARIABLE
170
+ expr ::= * LPAREN expr RPAREN
171
+ expr ::= * PLUS expr
172
+ expr ::= * MINUS expr
173
+ expr ::= * NOT expr
174
+ expr ::= * FUNCTION_ABS LPAREN expr RPAREN
175
+ expr ::= * expr PLUS expr
176
+ expr ::= * expr MINUS expr
177
+ expr ::= * expr MULTIPLY expr
178
+ expr ::= * expr DIVIDE expr
179
+ expr ::= * expr MODULO expr
180
+ expr ::= * expr AND expr
181
+ expr ::= * expr OR expr
182
+ expr ::= * expr EQ expr
183
+ expr ::= * expr NE expr
184
+ expr ::= * expr LT expr
185
+ expr ::= * expr LE expr
186
+ expr ::= expr LE * expr
187
+ expr ::= * expr GT expr
188
+ expr ::= * expr GE expr
189
+ expr ::= * expr QMARK expr COLON expr
190
+
191
+ NUMBER shift-reduce 1 expr ::= NUMBER
192
+ VARIABLE shift-reduce 2 expr ::= VARIABLE
193
+ LPAREN shift 19
194
+ PLUS shift 17
195
+ MINUS shift 15
196
+ NOT shift 13
197
+ FUNCTION_ABS shift 36
198
+ expr shift 32
199
+
200
+State 6:
201
+ expr ::= * NUMBER
202
+ expr ::= * VARIABLE
203
+ expr ::= * LPAREN expr RPAREN
204
+ expr ::= * PLUS expr
205
+ expr ::= * MINUS expr
206
+ expr ::= * NOT expr
207
+ expr ::= * FUNCTION_ABS LPAREN expr RPAREN
208
+ expr ::= * expr PLUS expr
209
+ expr ::= * expr MINUS expr
210
+ expr ::= * expr MULTIPLY expr
211
+ expr ::= * expr DIVIDE expr
212
+ expr ::= * expr MODULO expr
213
+ expr ::= * expr AND expr
214
+ expr ::= * expr OR expr
215
+ expr ::= * expr EQ expr
216
+ expr ::= * expr NE expr
217
+ expr ::= * expr LT expr
218
+ expr ::= expr LT * expr
219
+ expr ::= * expr LE expr
220
+ expr ::= * expr GT expr
221
+ expr ::= * expr GE expr
222
+ expr ::= * expr QMARK expr COLON expr
223
+
224
+ NUMBER shift-reduce 1 expr ::= NUMBER
225
+ VARIABLE shift-reduce 2 expr ::= VARIABLE
226
+ LPAREN shift 19
227
+ PLUS shift 17
228
+ MINUS shift 15
229
+ NOT shift 13
230
+ FUNCTION_ABS shift 36
231
+ expr shift 33
232
+
233
+State 7:
234
+ expr ::= * NUMBER
235
+ expr ::= * VARIABLE
236
+ expr ::= * LPAREN expr RPAREN
237
+ expr ::= * PLUS expr
238
+ expr ::= * MINUS expr
239
+ expr ::= * NOT expr
240
+ expr ::= * FUNCTION_ABS LPAREN expr RPAREN
241
+ expr ::= * expr PLUS expr
242
+ expr ::= * expr MINUS expr
243
+ expr ::= * expr MULTIPLY expr
244
+ expr ::= * expr DIVIDE expr
245
+ expr ::= * expr MODULO expr
246
+ expr ::= * expr AND expr
247
+ expr ::= * expr OR expr
248
+ expr ::= * expr EQ expr
249
+ expr ::= * expr NE expr
250
+ expr ::= expr NE * expr
251
+ expr ::= * expr LT expr
252
+ expr ::= * expr LE expr
253
+ expr ::= * expr GT expr
254
+ expr ::= * expr GE expr
255
+ expr ::= * expr QMARK expr COLON expr
256
+
257
+ NUMBER shift-reduce 1 expr ::= NUMBER
258
+ VARIABLE shift-reduce 2 expr ::= VARIABLE
259
+ LPAREN shift 19
260
+ PLUS shift 17
261
+ MINUS shift 15
262
+ NOT shift 13
263
+ FUNCTION_ABS shift 36
264
+ expr shift 28
265
+
266
+State 8:
267
+ expr ::= * NUMBER
268
+ expr ::= * VARIABLE
269
+ expr ::= * LPAREN expr RPAREN
270
+ expr ::= * PLUS expr
271
+ expr ::= * MINUS expr
272
+ expr ::= * NOT expr
273
+ expr ::= * FUNCTION_ABS LPAREN expr RPAREN
274
+ expr ::= * expr PLUS expr
275
+ expr ::= * expr MINUS expr
276
+ expr ::= * expr MULTIPLY expr
277
+ expr ::= * expr DIVIDE expr
278
+ expr ::= * expr MODULO expr
279
+ expr ::= * expr AND expr
280
+ expr ::= * expr OR expr
281
+ expr ::= * expr EQ expr
282
+ expr ::= expr EQ * expr
283
+ expr ::= * expr NE expr
284
+ expr ::= * expr LT expr
285
+ expr ::= * expr LE expr
286
+ expr ::= * expr GT expr
287
+ expr ::= * expr GE expr
288
+ expr ::= * expr QMARK expr COLON expr
289
+
290
+ NUMBER shift-reduce 1 expr ::= NUMBER
291
+ VARIABLE shift-reduce 2 expr ::= VARIABLE
292
+ LPAREN shift 19
293
+ PLUS shift 17
294
+ MINUS shift 15
295
+ NOT shift 13
296
+ FUNCTION_ABS shift 36
297
+ expr shift 29
298
+
299
+State 9:
300
+ expr ::= * NUMBER
301
+ expr ::= * VARIABLE
302
+ expr ::= * LPAREN expr RPAREN
303
+ expr ::= * PLUS expr
304
+ expr ::= * MINUS expr
305
+ expr ::= * NOT expr
306
+ expr ::= * FUNCTION_ABS LPAREN expr RPAREN
307
+ expr ::= * expr PLUS expr
308
+ expr ::= * expr MINUS expr
309
+ expr ::= * expr MULTIPLY expr
310
+ expr ::= * expr DIVIDE expr
311
+ expr ::= * expr MODULO expr
312
+ expr ::= * expr AND expr
313
+ expr ::= * expr OR expr
314
+ expr ::= expr OR * expr
315
+ expr ::= * expr EQ expr
316
+ expr ::= * expr NE expr
317
+ expr ::= * expr LT expr
318
+ expr ::= * expr LE expr
319
+ expr ::= * expr GT expr
320
+ expr ::= * expr GE expr
321
+ expr ::= * expr QMARK expr COLON expr
322
+
323
+ NUMBER shift-reduce 1 expr ::= NUMBER
324
+ VARIABLE shift-reduce 2 expr ::= VARIABLE
325
+ LPAREN shift 19
326
+ PLUS shift 17
327
+ MINUS shift 15
328
+ NOT shift 13
329
+ FUNCTION_ABS shift 36
330
+ expr shift 26
331
+
332
+State 10:
333
+ expr ::= * NUMBER
334
+ expr ::= * VARIABLE
335
+ expr ::= * LPAREN expr RPAREN
336
+ expr ::= * PLUS expr
337
+ expr ::= * MINUS expr
338
+ expr ::= * NOT expr
339
+ expr ::= * FUNCTION_ABS LPAREN expr RPAREN
340
+ expr ::= * expr PLUS expr
341
+ expr ::= * expr MINUS expr
342
+ expr ::= * expr MULTIPLY expr
343
+ expr ::= * expr DIVIDE expr
344
+ expr ::= * expr MODULO expr
345
+ expr ::= * expr AND expr
346
+ expr ::= expr AND * expr
347
+ expr ::= * expr OR expr
348
+ expr ::= * expr EQ expr
349
+ expr ::= * expr NE expr
350
+ expr ::= * expr LT expr
351
+ expr ::= * expr LE expr
352
+ expr ::= * expr GT expr
353
+ expr ::= * expr GE expr
354
+ expr ::= * expr QMARK expr COLON expr
355
+
356
+ NUMBER shift-reduce 1 expr ::= NUMBER
357
+ VARIABLE shift-reduce 2 expr ::= VARIABLE
358
+ LPAREN shift 19
359
+ PLUS shift 17
360
+ MINUS shift 15
361
+ NOT shift 13
362
+ FUNCTION_ABS shift 36
363
+ expr shift 27
364
+
365
+State 11:
366
+ expr ::= * NUMBER
367
+ expr ::= * VARIABLE
368
+ expr ::= * LPAREN expr RPAREN
369
+ expr ::= * PLUS expr
370
+ expr ::= * MINUS expr
371
+ expr ::= * NOT expr
372
+ expr ::= * FUNCTION_ABS LPAREN expr RPAREN
373
+ expr ::= FUNCTION_ABS LPAREN * expr RPAREN
374
+ expr ::= * expr PLUS expr
375
+ expr ::= * expr MINUS expr
376
+ expr ::= * expr MULTIPLY expr
377
+ expr ::= * expr DIVIDE expr
378
+ expr ::= * expr MODULO expr
379
+ expr ::= * expr AND expr
380
+ expr ::= * expr OR expr
381
+ expr ::= * expr EQ expr
382
+ expr ::= * expr NE expr
383
+ expr ::= * expr LT expr
384
+ expr ::= * expr LE expr
385
+ expr ::= * expr GT expr
386
+ expr ::= * expr GE expr
387
+ expr ::= * expr QMARK expr COLON expr
388
+
389
+ NUMBER shift-reduce 1 expr ::= NUMBER
390
+ VARIABLE shift-reduce 2 expr ::= VARIABLE
391
+ LPAREN shift 19
392
+ PLUS shift 17
393
+ MINUS shift 15
394
+ NOT shift 13
395
+ FUNCTION_ABS shift 36
396
+ expr shift 22
397
+
398
+State 12:
399
+ expr ::= * NUMBER
400
+ expr ::= * VARIABLE
401
+ expr ::= * LPAREN expr RPAREN
402
+ expr ::= * PLUS expr
403
+ expr ::= * MINUS expr
404
+ expr ::= * NOT expr
405
+ expr ::= * FUNCTION_ABS LPAREN expr RPAREN
406
+ expr ::= * expr PLUS expr
407
+ expr ::= * expr MINUS expr
408
+ expr ::= * expr MULTIPLY expr
409
+ expr ::= * expr DIVIDE expr
410
+ expr ::= * expr MODULO expr
411
+ expr ::= expr MODULO * expr
412
+ expr ::= * expr AND expr
413
+ expr ::= * expr OR expr
414
+ expr ::= * expr EQ expr
415
+ expr ::= * expr NE expr
416
+ expr ::= * expr LT expr
417
+ expr ::= * expr LE expr
418
+ expr ::= * expr GT expr
419
+ expr ::= * expr GE expr
420
+ expr ::= * expr QMARK expr COLON expr
421
+
422
+ NUMBER shift-reduce 1 expr ::= NUMBER
423
+ VARIABLE shift-reduce 2 expr ::= VARIABLE
424
+ LPAREN shift 19
425
+ PLUS shift 17
426
+ MINUS shift 15
427
+ NOT shift 13
428
+ FUNCTION_ABS shift 36
429
+ expr shift-reduce 12 expr ::= expr MODULO expr
430
+
431
+State 13:
432
+ expr ::= * NUMBER
433
+ expr ::= * VARIABLE
434
+ expr ::= * LPAREN expr RPAREN
435
+ expr ::= * PLUS expr
436
+ expr ::= * MINUS expr
437
+ expr ::= * NOT expr
438
+ expr ::= NOT * expr
439
+ expr ::= * FUNCTION_ABS LPAREN expr RPAREN
440
+ expr ::= * expr PLUS expr
441
+ expr ::= * expr MINUS expr
442
+ expr ::= * expr MULTIPLY expr
443
+ expr ::= * expr DIVIDE expr
444
+ expr ::= * expr MODULO expr
445
+ expr ::= * expr AND expr
446
+ expr ::= * expr OR expr
447
+ expr ::= * expr EQ expr
448
+ expr ::= * expr NE expr
449
+ expr ::= * expr LT expr
450
+ expr ::= * expr LE expr
451
+ expr ::= * expr GT expr
452
+ expr ::= * expr GE expr
453
+ expr ::= * expr QMARK expr COLON expr
454
+
455
+ NUMBER shift-reduce 1 expr ::= NUMBER
456
+ VARIABLE shift-reduce 2 expr ::= VARIABLE
457
+ LPAREN shift 19
458
+ PLUS shift 17
459
+ MINUS shift 15
460
+ NOT shift 13
461
+ FUNCTION_ABS shift 36
462
+ expr shift-reduce 6 expr ::= NOT expr
463
+
464
+State 14:
465
+ expr ::= * NUMBER
466
+ expr ::= * VARIABLE
467
+ expr ::= * LPAREN expr RPAREN
468
+ expr ::= * PLUS expr
469
+ expr ::= * MINUS expr
470
+ expr ::= * NOT expr
471
+ expr ::= * FUNCTION_ABS LPAREN expr RPAREN
472
+ expr ::= * expr PLUS expr
473
+ expr ::= * expr MINUS expr
474
+ expr ::= * expr MULTIPLY expr
475
+ expr ::= * expr DIVIDE expr
476
+ expr ::= expr DIVIDE * expr
477
+ expr ::= * expr MODULO expr
478
+ expr ::= * expr AND expr
479
+ expr ::= * expr OR expr
480
+ expr ::= * expr EQ expr
481
+ expr ::= * expr NE expr
482
+ expr ::= * expr LT expr
483
+ expr ::= * expr LE expr
484
+ expr ::= * expr GT expr
485
+ expr ::= * expr GE expr
486
+ expr ::= * expr QMARK expr COLON expr
487
+
488
+ NUMBER shift-reduce 1 expr ::= NUMBER
489
+ VARIABLE shift-reduce 2 expr ::= VARIABLE
490
+ LPAREN shift 19
491
+ PLUS shift 17
492
+ MINUS shift 15
493
+ NOT shift 13
494
+ FUNCTION_ABS shift 36
495
+ expr shift-reduce 11 expr ::= expr DIVIDE expr
496
+
497
+State 15:
498
+ expr ::= * NUMBER
499
+ expr ::= * VARIABLE
500
+ expr ::= * LPAREN expr RPAREN
501
+ expr ::= * PLUS expr
502
+ expr ::= * MINUS expr
503
+ expr ::= MINUS * expr
504
+ expr ::= * NOT expr
505
+ expr ::= * FUNCTION_ABS LPAREN expr RPAREN
506
+ expr ::= * expr PLUS expr
507
+ expr ::= * expr MINUS expr
508
+ expr ::= * expr MULTIPLY expr
509
+ expr ::= * expr DIVIDE expr
510
+ expr ::= * expr MODULO expr
511
+ expr ::= * expr AND expr
512
+ expr ::= * expr OR expr
513
+ expr ::= * expr EQ expr
514
+ expr ::= * expr NE expr
515
+ expr ::= * expr LT expr
516
+ expr ::= * expr LE expr
517
+ expr ::= * expr GT expr
518
+ expr ::= * expr GE expr
519
+ expr ::= * expr QMARK expr COLON expr
520
+
521
+ NUMBER shift-reduce 1 expr ::= NUMBER
522
+ VARIABLE shift-reduce 2 expr ::= VARIABLE
523
+ LPAREN shift 19
524
+ PLUS shift 17
525
+ MINUS shift 15
526
+ NOT shift 13
527
+ FUNCTION_ABS shift 36
528
+ expr shift-reduce 5 expr ::= MINUS expr
529
+
530
+State 16:
531
+ expr ::= * NUMBER
532
+ expr ::= * VARIABLE
533
+ expr ::= * LPAREN expr RPAREN
534
+ expr ::= * PLUS expr
535
+ expr ::= * MINUS expr
536
+ expr ::= * NOT expr
537
+ expr ::= * FUNCTION_ABS LPAREN expr RPAREN
538
+ expr ::= * expr PLUS expr
539
+ expr ::= * expr MINUS expr
540
+ expr ::= * expr MULTIPLY expr
541
+ expr ::= expr MULTIPLY * expr
542
+ expr ::= * expr DIVIDE expr
543
+ expr ::= * expr MODULO expr
544
+ expr ::= * expr AND expr
545
+ expr ::= * expr OR expr
546
+ expr ::= * expr EQ expr
547
+ expr ::= * expr NE expr
548
+ expr ::= * expr LT expr
549
+ expr ::= * expr LE expr
550
+ expr ::= * expr GT expr
551
+ expr ::= * expr GE expr
552
+ expr ::= * expr QMARK expr COLON expr
553
+
554
+ NUMBER shift-reduce 1 expr ::= NUMBER
555
+ VARIABLE shift-reduce 2 expr ::= VARIABLE
556
+ LPAREN shift 19
557
+ PLUS shift 17
558
+ MINUS shift 15
559
+ NOT shift 13
560
+ FUNCTION_ABS shift 36
561
+ expr shift-reduce 10 expr ::= expr MULTIPLY expr
562
+
563
+State 17:
564
+ expr ::= * NUMBER
565
+ expr ::= * VARIABLE
566
+ expr ::= * LPAREN expr RPAREN
567
+ expr ::= * PLUS expr
568
+ expr ::= PLUS * expr
569
+ expr ::= * MINUS expr
570
+ expr ::= * NOT expr
571
+ expr ::= * FUNCTION_ABS LPAREN expr RPAREN
572
+ expr ::= * expr PLUS expr
573
+ expr ::= * expr MINUS expr
574
+ expr ::= * expr MULTIPLY expr
575
+ expr ::= * expr DIVIDE expr
576
+ expr ::= * expr MODULO expr
577
+ expr ::= * expr AND expr
578
+ expr ::= * expr OR expr
579
+ expr ::= * expr EQ expr
580
+ expr ::= * expr NE expr
581
+ expr ::= * expr LT expr
582
+ expr ::= * expr LE expr
583
+ expr ::= * expr GT expr
584
+ expr ::= * expr GE expr
585
+ expr ::= * expr QMARK expr COLON expr
586
+
587
+ NUMBER shift-reduce 1 expr ::= NUMBER
588
+ VARIABLE shift-reduce 2 expr ::= VARIABLE
589
+ LPAREN shift 19
590
+ PLUS shift 17
591
+ MINUS shift 15
592
+ NOT shift 13
593
+ FUNCTION_ABS shift 36
594
+ expr shift-reduce 4 expr ::= PLUS expr
595
+
596
+State 18:
597
+ expr ::= * NUMBER
598
+ expr ::= * VARIABLE
599
+ expr ::= * LPAREN expr RPAREN
600
+ expr ::= * PLUS expr
601
+ expr ::= * MINUS expr
602
+ expr ::= * NOT expr
603
+ expr ::= * FUNCTION_ABS LPAREN expr RPAREN
604
+ expr ::= * expr PLUS expr
605
+ expr ::= * expr MINUS expr
606
+ expr ::= expr MINUS * expr
607
+ expr ::= * expr MULTIPLY expr
608
+ expr ::= * expr DIVIDE expr
609
+ expr ::= * expr MODULO expr
610
+ expr ::= * expr AND expr
611
+ expr ::= * expr OR expr
612
+ expr ::= * expr EQ expr
613
+ expr ::= * expr NE expr
614
+ expr ::= * expr LT expr
615
+ expr ::= * expr LE expr
616
+ expr ::= * expr GT expr
617
+ expr ::= * expr GE expr
618
+ expr ::= * expr QMARK expr COLON expr
619
+
620
+ NUMBER shift-reduce 1 expr ::= NUMBER
621
+ VARIABLE shift-reduce 2 expr ::= VARIABLE
622
+ LPAREN shift 19
623
+ PLUS shift 17
624
+ MINUS shift 15
625
+ NOT shift 13
626
+ FUNCTION_ABS shift 36
627
+ expr shift 35
628
+
629
+State 19:
630
+ expr ::= * NUMBER
631
+ expr ::= * VARIABLE
632
+ expr ::= * LPAREN expr RPAREN
633
+ expr ::= LPAREN * expr RPAREN
634
+ expr ::= * PLUS expr
635
+ expr ::= * MINUS expr
636
+ expr ::= * NOT expr
637
+ expr ::= * FUNCTION_ABS LPAREN expr RPAREN
638
+ expr ::= * expr PLUS expr
639
+ expr ::= * expr MINUS expr
640
+ expr ::= * expr MULTIPLY expr
641
+ expr ::= * expr DIVIDE expr
642
+ expr ::= * expr MODULO expr
643
+ expr ::= * expr AND expr
644
+ expr ::= * expr OR expr
645
+ expr ::= * expr EQ expr
646
+ expr ::= * expr NE expr
647
+ expr ::= * expr LT expr
648
+ expr ::= * expr LE expr
649
+ expr ::= * expr GT expr
650
+ expr ::= * expr GE expr
651
+ expr ::= * expr QMARK expr COLON expr
652
+
653
+ NUMBER shift-reduce 1 expr ::= NUMBER
654
+ VARIABLE shift-reduce 2 expr ::= VARIABLE
655
+ LPAREN shift 19
656
+ PLUS shift 17
657
+ MINUS shift 15
658
+ NOT shift 13
659
+ FUNCTION_ABS shift 36
660
+ expr shift 23
661
+
662
+State 20:
663
+ expr ::= * NUMBER
664
+ expr ::= * VARIABLE
665
+ expr ::= * LPAREN expr RPAREN
666
+ expr ::= * PLUS expr
667
+ expr ::= * MINUS expr
668
+ expr ::= * NOT expr
669
+ expr ::= * FUNCTION_ABS LPAREN expr RPAREN
670
+ expr ::= * expr PLUS expr
671
+ expr ::= expr PLUS * expr
672
+ expr ::= * expr MINUS expr
673
+ expr ::= * expr MULTIPLY expr
674
+ expr ::= * expr DIVIDE expr
675
+ expr ::= * expr MODULO expr
676
+ expr ::= * expr AND expr
677
+ expr ::= * expr OR expr
678
+ expr ::= * expr EQ expr
679
+ expr ::= * expr NE expr
680
+ expr ::= * expr LT expr
681
+ expr ::= * expr LE expr
682
+ expr ::= * expr GT expr
683
+ expr ::= * expr GE expr
684
+ expr ::= * expr QMARK expr COLON expr
685
+
686
+ NUMBER shift-reduce 1 expr ::= NUMBER
687
+ VARIABLE shift-reduce 2 expr ::= VARIABLE
688
+ LPAREN shift 19
689
+ PLUS shift 17
690
+ MINUS shift 15
691
+ NOT shift 13
692
+ FUNCTION_ABS shift 36
693
+ expr shift 34
694
+
695
+State 21:
696
+ expr ::= expr * PLUS expr
697
+ expr ::= expr * MINUS expr
698
+ expr ::= expr * MULTIPLY expr
699
+ expr ::= expr * DIVIDE expr
700
+ expr ::= expr * MODULO expr
701
+ expr ::= expr * AND expr
702
+ expr ::= expr * OR expr
703
+ expr ::= expr * EQ expr
704
+ expr ::= expr * NE expr
705
+ expr ::= expr * LT expr
706
+ expr ::= expr * LE expr
707
+ expr ::= expr * GT expr
708
+ expr ::= expr * GE expr
709
+ expr ::= expr * QMARK expr COLON expr
710
+ expr ::= expr QMARK expr * COLON expr
711
+
712
+ PLUS shift 20
713
+ MINUS shift 18
714
+ MULTIPLY shift 16
715
+ DIVIDE shift 14
716
+ MODULO shift 12
717
+ AND shift 10
718
+ OR shift 9
719
+ EQ shift 8
720
+ NE shift 7
721
+ LT shift 6
722
+ LE shift 5
723
+ GT shift 4
724
+ GE shift 3
725
+ QMARK shift 2
726
+ COLON shift 1
727
+
728
+State 22:
729
+ expr ::= FUNCTION_ABS LPAREN expr * RPAREN
730
+ expr ::= expr * PLUS expr
731
+ expr ::= expr * MINUS expr
732
+ expr ::= expr * MULTIPLY expr
733
+ expr ::= expr * DIVIDE expr
734
+ expr ::= expr * MODULO expr
735
+ expr ::= expr * AND expr
736
+ expr ::= expr * OR expr
737
+ expr ::= expr * EQ expr
738
+ expr ::= expr * NE expr
739
+ expr ::= expr * LT expr
740
+ expr ::= expr * LE expr
741
+ expr ::= expr * GT expr
742
+ expr ::= expr * GE expr
743
+ expr ::= expr * QMARK expr COLON expr
744
+
745
+ RPAREN shift-reduce 7 expr ::= FUNCTION_ABS LPAREN expr RPAREN
746
+ PLUS shift 20
747
+ MINUS shift 18
748
+ MULTIPLY shift 16
749
+ DIVIDE shift 14
750
+ MODULO shift 12
751
+ AND shift 10
752
+ OR shift 9
753
+ EQ shift 8
754
+ NE shift 7
755
+ LT shift 6
756
+ LE shift 5
757
+ GT shift 4
758
+ GE shift 3
759
+ QMARK shift 2
760
+
761
+State 23:
762
+ expr ::= LPAREN expr * RPAREN
763
+ expr ::= expr * PLUS expr
764
+ expr ::= expr * MINUS expr
765
+ expr ::= expr * MULTIPLY expr
766
+ expr ::= expr * DIVIDE expr
767
+ expr ::= expr * MODULO expr
768
+ expr ::= expr * AND expr
769
+ expr ::= expr * OR expr
770
+ expr ::= expr * EQ expr
771
+ expr ::= expr * NE expr
772
+ expr ::= expr * LT expr
773
+ expr ::= expr * LE expr
774
+ expr ::= expr * GT expr
775
+ expr ::= expr * GE expr
776
+ expr ::= expr * QMARK expr COLON expr
777
+
778
+ RPAREN shift-reduce 3 expr ::= LPAREN expr RPAREN
779
+ PLUS shift 20
780
+ MINUS shift 18
781
+ MULTIPLY shift 16
782
+ DIVIDE shift 14
783
+ MODULO shift 12
784
+ AND shift 10
785
+ OR shift 9
786
+ EQ shift 8
787
+ NE shift 7
788
+ LT shift 6
789
+ LE shift 5
790
+ GT shift 4
791
+ GE shift 3
792
+ QMARK shift 2
793
+
794
+State 24:
795
+ (0) program ::= expr *
796
+ expr ::= expr * PLUS expr
797
+ expr ::= expr * MINUS expr
798
+ expr ::= expr * MULTIPLY expr
799
+ expr ::= expr * DIVIDE expr
800
+ expr ::= expr * MODULO expr
801
+ expr ::= expr * AND expr
802
+ expr ::= expr * OR expr
803
+ expr ::= expr * EQ expr
804
+ expr ::= expr * NE expr
805
+ expr ::= expr * LT expr
806
+ expr ::= expr * LE expr
807
+ expr ::= expr * GT expr
808
+ expr ::= expr * GE expr
809
+ expr ::= expr * QMARK expr COLON expr
810
+
811
+ $ reduce 0 program ::= expr
812
+ PLUS shift 20
813
+ MINUS shift 18
814
+ MULTIPLY shift 16
815
+ DIVIDE shift 14
816
+ MODULO shift 12
817
+ AND shift 10
818
+ OR shift 9
819
+ EQ shift 8
820
+ NE shift 7
821
+ LT shift 6
822
+ LE shift 5
823
+ GT shift 4
824
+ GE shift 3
825
+ QMARK shift 2
826
+
827
+State 25:
828
+ expr ::= expr * PLUS expr
829
+ expr ::= expr * MINUS expr
830
+ expr ::= expr * MULTIPLY expr
831
+ expr ::= expr * DIVIDE expr
832
+ expr ::= expr * MODULO expr
833
+ expr ::= expr * AND expr
834
+ expr ::= expr * OR expr
835
+ expr ::= expr * EQ expr
836
+ expr ::= expr * NE expr
837
+ expr ::= expr * LT expr
838
+ expr ::= expr * LE expr
839
+ expr ::= expr * GT expr
840
+ expr ::= expr * GE expr
841
+ expr ::= expr * QMARK expr COLON expr
842
+ (21) expr ::= expr QMARK expr COLON expr *
843
+
844
+ PLUS shift 20
845
+ MINUS shift 18
846
+ MULTIPLY shift 16
847
+ DIVIDE shift 14
848
+ MODULO shift 12
849
+ AND shift 10
850
+ OR shift 9
851
+ EQ shift 8
852
+ NE shift 7
853
+ LT shift 6
854
+ LE shift 5
855
+ GT shift 4
856
+ GE shift 3
857
+ QMARK shift 2
858
+ {default} reduce 21 expr ::= expr QMARK expr COLON expr
859
+
860
+State 26:
861
+ expr ::= expr * PLUS expr
862
+ expr ::= expr * MINUS expr
863
+ expr ::= expr * MULTIPLY expr
864
+ expr ::= expr * DIVIDE expr
865
+ expr ::= expr * MODULO expr
866
+ expr ::= expr * AND expr
867
+ expr ::= expr * OR expr
868
+ (14) expr ::= expr OR expr *
869
+ expr ::= expr * EQ expr
870
+ expr ::= expr * NE expr
871
+ expr ::= expr * LT expr
872
+ expr ::= expr * LE expr
873
+ expr ::= expr * GT expr
874
+ expr ::= expr * GE expr
875
+ expr ::= expr * QMARK expr COLON expr
876
+
877
+ PLUS shift 20
878
+ MINUS shift 18
879
+ MULTIPLY shift 16
880
+ DIVIDE shift 14
881
+ MODULO shift 12
882
+ EQ shift 8
883
+ NE shift 7
884
+ LT shift 6
885
+ LE shift 5
886
+ GT shift 4
887
+ GE shift 3
888
+ {default} reduce 14 expr ::= expr OR expr
889
+
890
+State 27:
891
+ expr ::= expr * PLUS expr
892
+ expr ::= expr * MINUS expr
893
+ expr ::= expr * MULTIPLY expr
894
+ expr ::= expr * DIVIDE expr
895
+ expr ::= expr * MODULO expr
896
+ expr ::= expr * AND expr
897
+ (13) expr ::= expr AND expr *
898
+ expr ::= expr * OR expr
899
+ expr ::= expr * EQ expr
900
+ expr ::= expr * NE expr
901
+ expr ::= expr * LT expr
902
+ expr ::= expr * LE expr
903
+ expr ::= expr * GT expr
904
+ expr ::= expr * GE expr
905
+ expr ::= expr * QMARK expr COLON expr
906
+
907
+ PLUS shift 20
908
+ MINUS shift 18
909
+ MULTIPLY shift 16
910
+ DIVIDE shift 14
911
+ MODULO shift 12
912
+ EQ shift 8
913
+ NE shift 7
914
+ LT shift 6
915
+ LE shift 5
916
+ GT shift 4
917
+ GE shift 3
918
+ {default} reduce 13 expr ::= expr AND expr
919
+
920
+State 28:
921
+ expr ::= expr * PLUS expr
922
+ expr ::= expr * MINUS expr
923
+ expr ::= expr * MULTIPLY expr
924
+ expr ::= expr * DIVIDE expr
925
+ expr ::= expr * MODULO expr
926
+ expr ::= expr * AND expr
927
+ expr ::= expr * OR expr
928
+ expr ::= expr * EQ expr
929
+ expr ::= expr * NE expr
930
+ (16) expr ::= expr NE expr *
931
+ expr ::= expr * LT expr
932
+ expr ::= expr * LE expr
933
+ expr ::= expr * GT expr
934
+ expr ::= expr * GE expr
935
+ expr ::= expr * QMARK expr COLON expr
936
+
937
+ PLUS shift 20
938
+ MINUS shift 18
939
+ MULTIPLY shift 16
940
+ DIVIDE shift 14
941
+ MODULO shift 12
942
+ LT shift 6
943
+ LE shift 5
944
+ GT shift 4
945
+ GE shift 3
946
+ {default} reduce 16 expr ::= expr NE expr
947
+
948
+State 29:
949
+ expr ::= expr * PLUS expr
950
+ expr ::= expr * MINUS expr
951
+ expr ::= expr * MULTIPLY expr
952
+ expr ::= expr * DIVIDE expr
953
+ expr ::= expr * MODULO expr
954
+ expr ::= expr * AND expr
955
+ expr ::= expr * OR expr
956
+ expr ::= expr * EQ expr
957
+ (15) expr ::= expr EQ expr *
958
+ expr ::= expr * NE expr
959
+ expr ::= expr * LT expr
960
+ expr ::= expr * LE expr
961
+ expr ::= expr * GT expr
962
+ expr ::= expr * GE expr
963
+ expr ::= expr * QMARK expr COLON expr
964
+
965
+ PLUS shift 20
966
+ MINUS shift 18
967
+ MULTIPLY shift 16
968
+ DIVIDE shift 14
969
+ MODULO shift 12
970
+ LT shift 6
971
+ LE shift 5
972
+ GT shift 4
973
+ GE shift 3
974
+ {default} reduce 15 expr ::= expr EQ expr
975
+
976
+State 30:
977
+ expr ::= expr * PLUS expr
978
+ expr ::= expr * MINUS expr
979
+ expr ::= expr * MULTIPLY expr
980
+ expr ::= expr * DIVIDE expr
981
+ expr ::= expr * MODULO expr
982
+ expr ::= expr * AND expr
983
+ expr ::= expr * OR expr
984
+ expr ::= expr * EQ expr
985
+ expr ::= expr * NE expr
986
+ expr ::= expr * LT expr
987
+ expr ::= expr * LE expr
988
+ expr ::= expr * GT expr
989
+ expr ::= expr * GE expr
990
+ (20) expr ::= expr GE expr *
991
+ expr ::= expr * QMARK expr COLON expr
992
+
993
+ PLUS shift 20
994
+ MINUS shift 18
995
+ MULTIPLY shift 16
996
+ DIVIDE shift 14
997
+ MODULO shift 12
998
+ {default} reduce 20 expr ::= expr GE expr
999
+
1000
+State 31:
1001
+ expr ::= expr * PLUS expr
1002
+ expr ::= expr * MINUS expr
1003
+ expr ::= expr * MULTIPLY expr
1004
+ expr ::= expr * DIVIDE expr
1005
+ expr ::= expr * MODULO expr
1006
+ expr ::= expr * AND expr
1007
+ expr ::= expr * OR expr
1008
+ expr ::= expr * EQ expr
1009
+ expr ::= expr * NE expr
1010
+ expr ::= expr * LT expr
1011
+ expr ::= expr * LE expr
1012
+ expr ::= expr * GT expr
1013
+ (19) expr ::= expr GT expr *
1014
+ expr ::= expr * GE expr
1015
+ expr ::= expr * QMARK expr COLON expr
1016
+
1017
+ PLUS shift 20
1018
+ MINUS shift 18
1019
+ MULTIPLY shift 16
1020
+ DIVIDE shift 14
1021
+ MODULO shift 12
1022
+ {default} reduce 19 expr ::= expr GT expr
1023
+
1024
+State 32:
1025
+ expr ::= expr * PLUS expr
1026
+ expr ::= expr * MINUS expr
1027
+ expr ::= expr * MULTIPLY expr
1028
+ expr ::= expr * DIVIDE expr
1029
+ expr ::= expr * MODULO expr
1030
+ expr ::= expr * AND expr
1031
+ expr ::= expr * OR expr
1032
+ expr ::= expr * EQ expr
1033
+ expr ::= expr * NE expr
1034
+ expr ::= expr * LT expr
1035
+ expr ::= expr * LE expr
1036
+ (18) expr ::= expr LE expr *
1037
+ expr ::= expr * GT expr
1038
+ expr ::= expr * GE expr
1039
+ expr ::= expr * QMARK expr COLON expr
1040
+
1041
+ PLUS shift 20
1042
+ MINUS shift 18
1043
+ MULTIPLY shift 16
1044
+ DIVIDE shift 14
1045
+ MODULO shift 12
1046
+ {default} reduce 18 expr ::= expr LE expr
1047
+
1048
+State 33:
1049
+ expr ::= expr * PLUS expr
1050
+ expr ::= expr * MINUS expr
1051
+ expr ::= expr * MULTIPLY expr
1052
+ expr ::= expr * DIVIDE expr
1053
+ expr ::= expr * MODULO expr
1054
+ expr ::= expr * AND expr
1055
+ expr ::= expr * OR expr
1056
+ expr ::= expr * EQ expr
1057
+ expr ::= expr * NE expr
1058
+ expr ::= expr * LT expr
1059
+ (17) expr ::= expr LT expr *
1060
+ expr ::= expr * LE expr
1061
+ expr ::= expr * GT expr
1062
+ expr ::= expr * GE expr
1063
+ expr ::= expr * QMARK expr COLON expr
1064
+
1065
+ PLUS shift 20
1066
+ MINUS shift 18
1067
+ MULTIPLY shift 16
1068
+ DIVIDE shift 14
1069
+ MODULO shift 12
1070
+ {default} reduce 17 expr ::= expr LT expr
1071
+
1072
+State 34:
1073
+ expr ::= expr * PLUS expr
1074
+ (8) expr ::= expr PLUS expr *
1075
+ expr ::= expr * MINUS expr
1076
+ expr ::= expr * MULTIPLY expr
1077
+ expr ::= expr * DIVIDE expr
1078
+ expr ::= expr * MODULO expr
1079
+ expr ::= expr * AND expr
1080
+ expr ::= expr * OR expr
1081
+ expr ::= expr * EQ expr
1082
+ expr ::= expr * NE expr
1083
+ expr ::= expr * LT expr
1084
+ expr ::= expr * LE expr
1085
+ expr ::= expr * GT expr
1086
+ expr ::= expr * GE expr
1087
+ expr ::= expr * QMARK expr COLON expr
1088
+
1089
+ MULTIPLY shift 16
1090
+ DIVIDE shift 14
1091
+ MODULO shift 12
1092
+ {default} reduce 8 expr ::= expr PLUS expr
1093
+
1094
+State 35:
1095
+ expr ::= expr * PLUS expr
1096
+ expr ::= expr * MINUS expr
1097
+ (9) expr ::= expr MINUS expr *
1098
+ expr ::= expr * MULTIPLY expr
1099
+ expr ::= expr * DIVIDE expr
1100
+ expr ::= expr * MODULO expr
1101
+ expr ::= expr * AND expr
1102
+ expr ::= expr * OR expr
1103
+ expr ::= expr * EQ expr
1104
+ expr ::= expr * NE expr
1105
+ expr ::= expr * LT expr
1106
+ expr ::= expr * LE expr
1107
+ expr ::= expr * GT expr
1108
+ expr ::= expr * GE expr
1109
+ expr ::= expr * QMARK expr COLON expr
1110
+
1111
+ MULTIPLY shift 16
1112
+ DIVIDE shift 14
1113
+ MODULO shift 12
1114
+ {default} reduce 9 expr ::= expr MINUS expr
1115
+
1116
+State 36:
1117
+ expr ::= FUNCTION_ABS * LPAREN expr RPAREN
1118
+
1119
+ LPAREN shift 11
1120
+
1121
+----------------------------------------------------
1122
+Symbols:
1123
+The first-set of non-terminals is shown after the name.
1124
+
1125
+ 0: $:
1126
+ 1: NUMBER
1127
+ 2: VARIABLE
1128
+ 3: LPAREN
1129
+ 4: RPAREN
1130
+ 5: PLUS (precedence=5)
1131
+ 6: UPLUS (precedence=7)
1132
+ 7: MINUS (precedence=5)
1133
+ 8: UMINUS (precedence=7)
1134
+ 9: NOT (precedence=7)
1135
+ 10: FUNCTION_ABS
1136
+ 11: MULTIPLY (precedence=6)
1137
+ 12: DIVIDE (precedence=6)
1138
+ 13: MODULO (precedence=6)
1139
+ 14: AND (precedence=2)
1140
+ 15: OR (precedence=2)
1141
+ 16: EQ (precedence=3)
1142
+ 17: NE (precedence=3)
1143
+ 18: LT (precedence=4)
1144
+ 19: LE (precedence=4)
1145
+ 20: GT (precedence=4)
1146
+ 21: GE (precedence=4)
1147
+ 22: QMARK (precedence=1)
1148
+ 23: COLON (precedence=1)
1149
+ 24: expr: NUMBER VARIABLE LPAREN PLUS MINUS NOT FUNCTION_ABS
1150
+ 25: program: NUMBER VARIABLE LPAREN PLUS MINUS NOT FUNCTION_ABS
1151
+----------------------------------------------------
1152
+Syntax-only Symbols:
1153
+The following symbols never carry semantic content.
1154
+
1155
+$ LPAREN RPAREN PLUS UPLUS MINUS UMINUS NOT FUNCTION_ABS MULTIPLY DIVIDE
1156
+MODULO AND OR EQ NE LT LE GT GE QMARK COLON program
1157
+----------------------------------------------------
1158
+Rules:
1159
+ 0: program ::= expr.
1160
+ 1: expr ::= NUMBER.
1161
+ 2: expr ::= VARIABLE.
1162
+ 3: expr ::= LPAREN expr RPAREN.
1163
+ 4: expr ::= PLUS expr. [UPLUS precedence=7]
1164
+ 5: expr ::= MINUS expr. [UMINUS precedence=7]
1165
+ 6: expr ::= NOT expr. [NOT precedence=7]
1166
+ 7: expr ::= FUNCTION_ABS LPAREN expr RPAREN.
1167
+ 8: expr ::= expr PLUS expr. [PLUS precedence=5]
1168
+ 9: expr ::= expr MINUS expr. [MINUS precedence=5]
1169
+ 10: expr ::= expr MULTIPLY expr. [MULTIPLY precedence=6]
1170
+ 11: expr ::= expr DIVIDE expr. [DIVIDE precedence=6]
1171
+ 12: expr ::= expr MODULO expr. [MODULO precedence=6]
1172
+ 13: expr ::= expr AND expr. [AND precedence=2]
1173
+ 14: expr ::= expr OR expr. [OR precedence=2]
1174
+ 15: expr ::= expr EQ expr. [EQ precedence=3]
1175
+ 16: expr ::= expr NE expr. [NE precedence=3]
1176
+ 17: expr ::= expr LT expr. [LT precedence=4]
1177
+ 18: expr ::= expr LE expr. [LE precedence=4]
1178
+ 19: expr ::= expr GT expr. [GT precedence=4]
1179
+ 20: expr ::= expr GE expr. [GE precedence=4]
1180
+ 21: expr ::= expr QMARK expr COLON expr. [QMARK precedence=1]
src/libnetdata/eval/re2c_lemon/parser.y
new
+235
@@ -0,0 +1,235 @@
1
+%include {
2
+#include "../eval-internal.h"
3
+#include "parser_internal.h"
4
+#include <assert.h>
5
+}
6
+
7
+%token_type {YYSTYPE}
8
+%token_prefix TOK_
9
+
10
+%type expr {EVAL_NODE*}
11
+%type program {EVAL_NODE*}
12
+
13
+%syntax_error {
14
+ // Create a NOP node with count=0 as an error marker
15
+ EVAL_NODE *error_node = eval_node_alloc(0);
16
+ error_node->operator = EVAL_OPERATOR_NOP;
17
+ *result = error_node;
18
+}
19
+
20
+%parse_accept {
21
+ // Successfully parsed the expression
22
+}
23
+
24
+%parse_failure {
25
+ // Failed to parse the expression
26
+ if (*result) {
27
+ eval_node_free(*result);
28
+ *result = NULL;
29
+ }
30
+}
31
+
32
+%extra_argument {EVAL_NODE **result}
33
+
34
+%destructor expr {
35
+ if ($$) {
36
+ eval_node_free($$);
37
+ }
38
+}
39
+
40
+// Start symbol
41
+program ::= expr(E). {
42
+ *result = E;
43
+}
44
+
45
+// Basic expressions
46
+expr(A) ::= NUMBER(B). {
47
+ A = eval_node_alloc(1);
48
+ A->operator = EVAL_OPERATOR_NOP;
49
+ eval_node_set_value_to_constant(A, 0, B.dval);
50
+}
51
+
52
+expr(A) ::= VARIABLE(B). {
53
+ A = eval_node_alloc(1);
54
+ A->operator = EVAL_OPERATOR_NOP;
55
+ eval_node_set_value_to_variable(A, 0, B.strval);
56
+ freez(B.strval); // Free the strdup'd string
57
+}
58
+
59
+// Parenthesized expressions
60
+expr(A) ::= LPAREN expr(B) RPAREN. {
61
+ A = eval_node_alloc(1);
62
+ A->operator = EVAL_OPERATOR_EXPRESSION_OPEN;
63
+ A->precedence = eval_precedence(EVAL_OPERATOR_EXPRESSION_OPEN);
64
+ eval_node_set_value_to_node(A, 0, B);
65
+}
66
+
67
+// Unary operators
68
+expr(A) ::= PLUS expr(B). [UPLUS] {
69
+ A = eval_node_alloc(1);
70
+ A->operator = EVAL_OPERATOR_SIGN_PLUS;
71
+ A->precedence = eval_precedence(EVAL_OPERATOR_SIGN_PLUS);
72
+ eval_node_set_value_to_node(A, 0, B);
73
+}
74
+
75
+expr(A) ::= MINUS expr(B). [UMINUS] {
76
+ A = eval_node_alloc(1);
77
+ A->operator = EVAL_OPERATOR_SIGN_MINUS;
78
+ A->precedence = eval_precedence(EVAL_OPERATOR_SIGN_MINUS);
79
+ eval_node_set_value_to_node(A, 0, B);
80
+}
81
+
82
+expr(A) ::= NOT expr(B). {
83
+ A = eval_node_alloc(1);
84
+ A->operator = EVAL_OPERATOR_NOT;
85
+ A->precedence = eval_precedence(EVAL_OPERATOR_NOT);
86
+ eval_node_set_value_to_node(A, 0, B);
87
+}
88
+
89
+// Function calls
90
+expr(A) ::= FUNCTION_ABS LPAREN expr(B) RPAREN. {
91
+ A = eval_node_alloc(1);
92
+ A->operator = EVAL_OPERATOR_ABS;
93
+ A->precedence = eval_precedence(EVAL_OPERATOR_ABS);
94
+ eval_node_set_value_to_node(A, 0, B);
95
+}
96
+
97
+// Binary operators
98
+expr(A) ::= expr(B) PLUS expr(C). {
99
+ A = eval_node_alloc(2);
100
+ A->operator = EVAL_OPERATOR_PLUS;
101
+ A->precedence = eval_precedence(EVAL_OPERATOR_PLUS);
102
+ eval_node_set_value_to_node(A, 0, B);
103
+ eval_node_set_value_to_node(A, 1, C);
104
+}
105
+
106
+expr(A) ::= expr(B) MINUS expr(C). {
107
+ A = eval_node_alloc(2);
108
+ A->operator = EVAL_OPERATOR_MINUS;
109
+ A->precedence = eval_precedence(EVAL_OPERATOR_MINUS);
110
+ eval_node_set_value_to_node(A, 0, B);
111
+ eval_node_set_value_to_node(A, 1, C);
112
+}
113
+
114
+expr(A) ::= expr(B) MULTIPLY expr(C). {
115
+ A = eval_node_alloc(2);
116
+ A->operator = EVAL_OPERATOR_MULTIPLY;
117
+ A->precedence = eval_precedence(EVAL_OPERATOR_MULTIPLY);
118
+ eval_node_set_value_to_node(A, 0, B);
119
+ eval_node_set_value_to_node(A, 1, C);
120
+}
121
+
122
+expr(A) ::= expr(B) DIVIDE expr(C). {
123
+ A = eval_node_alloc(2);
124
+ A->operator = EVAL_OPERATOR_DIVIDE;
125
+ A->precedence = eval_precedence(EVAL_OPERATOR_DIVIDE);
126
+ eval_node_set_value_to_node(A, 0, B);
127
+ eval_node_set_value_to_node(A, 1, C);
128
+}
129
+
130
+expr(A) ::= expr(B) MODULO expr(C). {
131
+ A = eval_node_alloc(2);
132
+ A->operator = EVAL_OPERATOR_MODULO;
133
+ A->precedence = eval_precedence(EVAL_OPERATOR_MODULO);
134
+ eval_node_set_value_to_node(A, 0, B);
135
+ eval_node_set_value_to_node(A, 1, C);
136
+}
137
+
138
+expr(A) ::= expr(B) AND expr(C). {
139
+ A = eval_node_alloc(2);
140
+ A->operator = EVAL_OPERATOR_AND;
141
+ A->precedence = eval_precedence(EVAL_OPERATOR_AND);
142
+ eval_node_set_value_to_node(A, 0, B);
143
+ eval_node_set_value_to_node(A, 1, C);
144
+}
145
+
146
+expr(A) ::= expr(B) OR expr(C). {
147
+ A = eval_node_alloc(2);
148
+ A->operator = EVAL_OPERATOR_OR;
149
+ A->precedence = eval_precedence(EVAL_OPERATOR_OR);
150
+ eval_node_set_value_to_node(A, 0, B);
151
+ eval_node_set_value_to_node(A, 1, C);
152
+}
153
+
154
+expr(A) ::= expr(B) EQ expr(C). {
155
+ A = eval_node_alloc(2);
156
+ A->operator = EVAL_OPERATOR_EQUAL;
157
+ A->precedence = eval_precedence(EVAL_OPERATOR_EQUAL);
158
+ eval_node_set_value_to_node(A, 0, B);
159
+ eval_node_set_value_to_node(A, 1, C);
160
+}
161
+
162
+expr(A) ::= expr(B) NE expr(C). {
163
+ A = eval_node_alloc(2);
164
+ A->operator = EVAL_OPERATOR_NOT_EQUAL;
165
+ A->precedence = eval_precedence(EVAL_OPERATOR_NOT_EQUAL);
166
+ eval_node_set_value_to_node(A, 0, B);
167
+ eval_node_set_value_to_node(A, 1, C);
168
+}
169
+
170
+expr(A) ::= expr(B) LT expr(C). {
171
+ A = eval_node_alloc(2);
172
+ A->operator = EVAL_OPERATOR_LESS;
173
+ A->precedence = eval_precedence(EVAL_OPERATOR_LESS);
174
+ eval_node_set_value_to_node(A, 0, B);
175
+ eval_node_set_value_to_node(A, 1, C);
176
+}
177
+
178
+expr(A) ::= expr(B) LE expr(C). {
179
+ A = eval_node_alloc(2);
180
+ A->operator = EVAL_OPERATOR_LESS_THAN_OR_EQUAL;
181
+ A->precedence = eval_precedence(EVAL_OPERATOR_LESS_THAN_OR_EQUAL);
182
+ eval_node_set_value_to_node(A, 0, B);
183
+ eval_node_set_value_to_node(A, 1, C);
184
+}
185
+
186
+expr(A) ::= expr(B) GT expr(C). {
187
+ A = eval_node_alloc(2);
188
+ A->operator = EVAL_OPERATOR_GREATER;
189
+ A->precedence = eval_precedence(EVAL_OPERATOR_GREATER);
190
+ eval_node_set_value_to_node(A, 0, B);
191
+ eval_node_set_value_to_node(A, 1, C);
192
+}
193
+
194
+expr(A) ::= expr(B) GE expr(C). {
195
+ A = eval_node_alloc(2);
196
+ A->operator = EVAL_OPERATOR_GREATER_THAN_OR_EQUAL;
197
+ A->precedence = eval_precedence(EVAL_OPERATOR_GREATER_THAN_OR_EQUAL);
198
+ eval_node_set_value_to_node(A, 0, B);
199
+ eval_node_set_value_to_node(A, 1, C);
200
+}
201
+
202
+// Ternary operator with proper precedence and associativity
203
+// This rule should ensure that ternary operators are right-associative
204
+// and have lower precedence than comparison operators
205
+expr(A) ::= expr(B) QMARK expr(C) COLON expr(D). {
206
+ A = eval_node_alloc(3);
207
+ A->operator = EVAL_OPERATOR_IF_THEN_ELSE;
208
+ A->precedence = eval_precedence(EVAL_OPERATOR_IF_THEN_ELSE);
209
+ eval_node_set_value_to_node(A, 0, B);
210
+ eval_node_set_value_to_node(A, 1, C);
211
+ eval_node_set_value_to_node(A, 2, D);
212
+}
213
+
214
+// Operator precedence declarations - LOWEST to HIGHEST
215
+// In Lemon (like yacc/bison), precedence increases as you go down the list
216
+//
217
+// This means:
218
+// 1. Ternary operator (?:) has the lowest precedence (will be evaluated last)
219
+// 2. Logical operators (AND, OR) have the next lowest precedence
220
+// 3. Comparison operators (EQ, NE, LT, etc.) are next
221
+// 4. Addition and subtraction come next
222
+// 5. Multiplication, division, and modulo have higher precedence
223
+// 6. Unary operators (-, +, !) have the highest precedence (will be evaluated first)
224
+
225
+// The %left and %right directives specify associativity:
226
+// - %left: left-associative (a + b + c is parsed as (a + b) + c)
227
+// - %right: right-associative (a = b = c is parsed as a = (b = c))
228
+
229
+%right COLON QMARK. // Ternary operator (right-associative)
230
+%left OR AND. // Logical operators
231
+%left EQ NE. // Equality operators
232
+%left LT LE GT GE. // Comparison operators
233
+%left PLUS MINUS. // Addition and subtraction
234
+%left MULTIPLY DIVIDE MODULO. // Multiplication, division, and modulo
235
+%right UMINUS UPLUS NOT. // Unary operators (highest precedence)
\ No newline at end of file
src/libnetdata/eval/re2c_lemon/parser_internal.h
new
+35
@@ -0,0 +1,35 @@
1
+#ifndef EVAL_RE2C_LEMON_INTERNAL_H
2
+#define EVAL_RE2C_LEMON_INTERNAL_H
3
+
4
+#include "../eval-internal.h"
5
+#include "parser.h" // This has the token definitions (TOK_*)
6
+
7
+// Token values for re2c lexer
8
+typedef union {
9
+ NETDATA_DOUBLE dval;
10
+ char *strval;
11
+} YYSTYPE;
12
+
13
+// Scanner structure definition
14
+typedef struct {
15
+ const char *cursor;
16
+ const char *marker;
17
+ const char *token;
18
+ const char *limit;
19
+ int line;
20
+ int error; // Flag to indicate a lexer error occurred
21
+} Scanner;
22
+
23
+// Function declarations for the scanner
24
+void scanner_init(Scanner *s, const char *input);
25
+int scan(Scanner *s, YYSTYPE *lval);
26
+
27
+// Function declarations for the parser
28
+void *ParseAlloc(void *(*mallocProc)(size_t));
29
+void ParseFree(void *p, void (*freeProc)(void*));
30
+void Parse(void *yyp, int yymajor, YYSTYPE yyminor, EVAL_NODE **result);
31
+
32
+// Additional error code
33
+#define EVAL_ERROR_SYNTAX EVAL_ERROR_UNKNOWN_OPERAND
34
+
35
+#endif // EVAL_RE2C_LEMON_INTERNAL_H
\ No newline at end of file
src/libnetdata/eval/re2c_lemon/parser_wrapper.c
new
+15
@@ -0,0 +1,15 @@
1
+/**
2
+ * Integration wrapper for using the re2c/lemon parser with Netdata
3
+ */
4
+
5
+#include "../eval-internal.h"
6
+#include "parser_internal.h"
7
+
8
+// This file exists to allow the integration of the re2c/lemon parser
9
+// with Netdata's build system. The actual implementation is in lexer.c
10
+// which is generated from lexer.re.
11
+
12
+// We're just re-exporting the interface here, so the functions can be
13
+// properly found by the linker.
14
+
15
+// See lexer.c and parser.c for the real implementations.
\ No newline at end of file