| 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 | } |