2 * \file build/expression.c
3 * Simple logical expression parser.
4 * This module implements a basic expression parser with support for
5 * integer and string datatypes. For ease of programming, we use the
6 * top-down "recursive descent" method of parsing. While a
7 * table-driven bottom-up parser might be faster, it does not really
8 * matter for the expressions we will be parsing.
10 * Copyright (C) 1998 Tom Dyas <tdyas@eden.rutgers.edu>
11 * This work is provided under the GPL or LGPL at your choice.
16 #include <rpm/rpmlog.h>
17 #include "build/rpmbuild_internal.h"
20 /* #define DEBUG_PARSER 1 */
24 #define DEBUG(x) do { x ; } while (0)
35 * Encapsulation of a "value"
37 typedef struct _value {
47 static Value valueMakeInteger(int i)
51 v = (Value) xmalloc(sizeof(*v));
52 v->type = VALUE_TYPE_INTEGER;
59 static Value valueMakeString(char *s)
63 v = (Value) xmalloc(sizeof(*v));
64 v->type = VALUE_TYPE_STRING;
71 static void valueFree( Value v)
74 if (v->type == VALUE_TYPE_STRING)
75 v->data.s = _free(v->data.s);
81 static void valueDump(const char *msg, Value v, FILE *fp)
84 fprintf(fp, "%s ", msg);
86 if (v->type == VALUE_TYPE_INTEGER)
87 fprintf(fp, "INTEGER %d\n", v->data.i);
89 fprintf(fp, "STRING '%s'\n", v->data.s);
91 fprintf(fp, "NULL\n");
95 #define valueIsInteger(v) ((v)->type == VALUE_TYPE_INTEGER)
96 #define valueIsString(v) ((v)->type == VALUE_TYPE_STRING)
97 #define valueSameType(v1,v2) ((v1)->type == (v2)->type)
103 typedef struct _parseState {
104 char *str; /*!< expression string */
105 char *p; /*!< current position in expression string */
106 int nextToken; /*!< current lookahead token */
107 Value tokenValue; /*!< valid when TOK_INTEGER or TOK_STRING */
112 * \name Parser tokens
115 #define TOK_INTEGER 2
117 #define TOK_IDENTIFIER 4
120 #define TOK_MULTIPLY 7
123 #define TOK_CLOSE_P 10
131 #define TOK_LOGICAL_AND 18
132 #define TOK_LOGICAL_OR 19
134 #if defined(DEBUG_PARSER)
135 typedef struct exprTokTableEntry {
140 ETTE_t exprTokTable[] = {
142 { "I", TOK_INTEGER },
144 { "ID", TOK_IDENTIFIER },
147 { "*", TOK_MULTIPLY },
149 { "( ", TOK_OPEN_P },
150 { " )", TOK_CLOSE_P },
158 { "&&", TOK_LOGICAL_AND },
159 { "||", TOK_LOGICAL_OR },
163 static const char *prToken(int val)
167 for (et = exprTokTable; et->name != NULL; et++) {
173 #endif /* DEBUG_PARSER */
176 * @param state expression parser state
178 static int rdToken(ParseState state)
184 /* Skip whitespace before the next token. */
185 while (*p && risspace(*p)) p++;
199 token = TOK_MULTIPLY;
215 rpmlog(RPMLOG_ERR, _("syntax error while parsing ==\n"));
242 token = TOK_LOGICAL_AND;
245 rpmlog(RPMLOG_ERR, _("syntax error while parsing &&\n"));
251 token = TOK_LOGICAL_OR;
254 rpmlog(RPMLOG_ERR, _("syntax error while parsing ||\n"));
264 for (ts=1; p[ts] && risdigit(p[ts]); ts++);
265 temp = xmalloc(ts+1);
271 v = valueMakeInteger(atoi(temp));
274 } else if (risalpha(*p)) {
278 for (ts=1; p[ts] && (risalnum(p[ts]) || p[ts] == '_'); ts++);
279 temp = xmalloc(ts+1);
284 token = TOK_IDENTIFIER;
285 v = valueMakeString(temp);
287 } else if (*p == '\"') {
292 for (ts=0; p[ts] && p[ts] != '\"'; ts++);
293 temp = xmalloc(ts+1);
300 v = valueMakeString( rpmExpand(temp, NULL) );
304 rpmlog(RPMLOG_ERR, _("parse error in expression\n"));
310 state->nextToken = token;
311 state->tokenValue = v;
313 DEBUG(printf("rdToken: \"%s\" (%d)\n", prToken(token), token));
314 DEBUG(valueDump("rdToken:", state->tokenValue, stdout));
319 static Value doLogical(ParseState state);
322 * @param state expression parser state
324 static Value doPrimary(ParseState state)
328 DEBUG(printf("doPrimary()\n"));
330 switch (state->nextToken) {
334 v = doLogical(state);
335 if (state->nextToken != TOK_CLOSE_P) {
336 rpmlog(RPMLOG_ERR, _("unmatched (\n"));
345 v = state->tokenValue;
350 case TOK_IDENTIFIER: {
351 const char *name = state->tokenValue->data.s;
353 v = valueMakeString( rpmExpand(name, NULL) );
363 v = doPrimary(state);
367 if (! valueIsInteger(v)) {
368 rpmlog(RPMLOG_ERR, _("- only on numbers\n"));
372 v = valueMakeInteger(- v->data.i);
379 v = doPrimary(state);
383 if (! valueIsInteger(v)) {
384 rpmlog(RPMLOG_ERR, _("! only on numbers\n"));
388 v = valueMakeInteger(! v->data.i);
395 DEBUG(valueDump("doPrimary:", v, stdout));
400 * @param state expression parser state
402 static Value doMultiplyDivide(ParseState state)
406 DEBUG(printf("doMultiplyDivide()\n"));
408 v1 = doPrimary(state);
412 while (state->nextToken == TOK_MULTIPLY
413 || state->nextToken == TOK_DIVIDE) {
414 int op = state->nextToken;
419 if (v2) valueFree(v2);
421 v2 = doPrimary(state);
425 if (! valueSameType(v1, v2)) {
426 rpmlog(RPMLOG_ERR, _("types must match\n"));
430 if (valueIsInteger(v1)) {
431 int i1 = v1->data.i, i2 = v2->data.i;
434 if (op == TOK_MULTIPLY)
435 v1 = valueMakeInteger(i1 * i2);
437 v1 = valueMakeInteger(i1 / i2);
439 rpmlog(RPMLOG_ERR, _("* / not suported for strings\n"));
444 if (v2) valueFree(v2);
449 * @param state expression parser state
451 static Value doAddSubtract(ParseState state)
455 DEBUG(printf("doAddSubtract()\n"));
457 v1 = doMultiplyDivide(state);
461 while (state->nextToken == TOK_ADD || state->nextToken == TOK_MINUS) {
462 int op = state->nextToken;
467 if (v2) valueFree(v2);
469 v2 = doMultiplyDivide(state);
473 if (! valueSameType(v1, v2)) {
474 rpmlog(RPMLOG_ERR, _("types must match\n"));
478 if (valueIsInteger(v1)) {
479 int i1 = v1->data.i, i2 = v2->data.i;
483 v1 = valueMakeInteger(i1 + i2);
485 v1 = valueMakeInteger(i1 - i2);
489 if (op == TOK_MINUS) {
490 rpmlog(RPMLOG_ERR, _("- not suported for strings\n"));
494 copy = xmalloc(strlen(v1->data.s) + strlen(v2->data.s) + 1);
495 (void) stpcpy( stpcpy(copy, v1->data.s), v2->data.s);
498 v1 = valueMakeString(copy);
502 if (v2) valueFree(v2);
507 * @param state expression parser state
509 static Value doRelational(ParseState state)
513 DEBUG(printf("doRelational()\n"));
515 v1 = doAddSubtract(state);
519 while (state->nextToken >= TOK_EQ && state->nextToken <= TOK_GE) {
520 int op = state->nextToken;
525 if (v2) valueFree(v2);
527 v2 = doAddSubtract(state);
531 if (! valueSameType(v1, v2)) {
532 rpmlog(RPMLOG_ERR, _("types must match\n"));
536 if (valueIsInteger(v1)) {
537 int i1 = v1->data.i, i2 = v2->data.i, r = 0;
561 v1 = valueMakeInteger(r);
563 const char * s1 = v1->data.s;
564 const char * s2 = v2->data.s;
568 r = (strcmp(s1,s2) == 0);
571 r = (strcmp(s1,s2) != 0);
574 r = (strcmp(s1,s2) < 0);
577 r = (strcmp(s1,s2) <= 0);
580 r = (strcmp(s1,s2) > 0);
583 r = (strcmp(s1,s2) >= 0);
589 v1 = valueMakeInteger(r);
593 if (v2) valueFree(v2);
598 * @param state expression parser state
600 static Value doLogical(ParseState state)
604 DEBUG(printf("doLogical()\n"));
606 v1 = doRelational(state);
610 while (state->nextToken == TOK_LOGICAL_AND
611 || state->nextToken == TOK_LOGICAL_OR) {
612 int op = state->nextToken;
617 if (v2) valueFree(v2);
619 v2 = doRelational(state);
623 if (! valueSameType(v1, v2)) {
624 rpmlog(RPMLOG_ERR, _("types must match\n"));
628 if (valueIsInteger(v1)) {
629 int i1 = v1->data.i, i2 = v2->data.i;
632 if (op == TOK_LOGICAL_AND)
633 v1 = valueMakeInteger(i1 && i2);
635 v1 = valueMakeInteger(i1 || i2);
637 rpmlog(RPMLOG_ERR, _("&& and || not suported for strings\n"));
642 if (v2) valueFree(v2);
646 int parseExpressionBoolean(const char *expr)
648 struct _parseState state;
652 DEBUG(printf("parseExprBoolean(?, '%s')\n", expr));
654 /* Initialize the expression parser state. */
655 state.p = state.str = xstrdup(expr);
657 state.tokenValue = NULL;
658 (void) rdToken(&state);
660 /* Parse the expression. */
661 v = doLogical(&state);
663 state.str = _free(state.str);
667 /* If the next token is not TOK_EOF, we have a syntax error. */
668 if (state.nextToken != TOK_EOF) {
669 rpmlog(RPMLOG_ERR, _("syntax error in expression\n"));
670 state.str = _free(state.str);
674 DEBUG(valueDump("parseExprBoolean:", v, stdout));
677 case VALUE_TYPE_INTEGER:
678 result = v->data.i != 0;
680 case VALUE_TYPE_STRING:
681 result = v->data.s[0] != '\0';
687 state.str = _free(state.str);