master
re 268 lines 8.27 KB
Raw
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 }