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/rpmbuild.h>
17 #include <rpm/rpmlog.h>
21 /* #define DEBUG_PARSER 1 */
25 #define DEBUG(x) do { x ; } while (0)
31 * Encapsulation of a "value"
33 typedef struct _value {
34 enum { VALUE_TYPE_INTEGER, VALUE_TYPE_STRING } type;
43 static Value valueMakeInteger(int i)
47 v = (Value) xmalloc(sizeof(*v));
48 v->type = VALUE_TYPE_INTEGER;
55 static Value valueMakeString(char *s)
59 v = (Value) xmalloc(sizeof(*v));
60 v->type = VALUE_TYPE_STRING;
67 static void valueFree( Value v)
70 if (v->type == VALUE_TYPE_STRING)
71 v->data.s = _free(v->data.s);
77 static void valueDump(const char *msg, Value v, FILE *fp)
80 fprintf(fp, "%s ", msg);
82 if (v->type == VALUE_TYPE_INTEGER)
83 fprintf(fp, "INTEGER %d\n", v->data.i);
85 fprintf(fp, "STRING '%s'\n", v->data.s);
87 fprintf(fp, "NULL\n");
91 #define valueIsInteger(v) ((v)->type == VALUE_TYPE_INTEGER)
92 #define valueIsString(v) ((v)->type == VALUE_TYPE_STRING)
93 #define valueSameType(v1,v2) ((v1)->type == (v2)->type)
99 typedef struct _parseState {
100 char *str; /*!< expression string */
101 char *p; /*!< current position in expression string */
102 int nextToken; /*!< current lookahead token */
103 Value tokenValue; /*!< valid when TOK_INTEGER or TOK_STRING */
104 rpmSpec spec; /*!< spec file that we are parsing inside of */
109 * \name Parser tokens
112 #define TOK_INTEGER 2
114 #define TOK_IDENTIFIER 4
117 #define TOK_MULTIPLY 7
120 #define TOK_CLOSE_P 10
128 #define TOK_LOGICAL_AND 18
129 #define TOK_LOGICAL_OR 19
131 #if defined(DEBUG_PARSER)
132 typedef struct exprTokTableEntry {
137 ETTE_t exprTokTable[] = {
139 { "I", TOK_INTEGER },
141 { "ID", TOK_IDENTIFIER },
144 { "*", TOK_MULTIPLY },
146 { "( ", TOK_OPEN_P },
147 { " )", TOK_CLOSE_P },
155 { "&&", TOK_LOGICAL_AND },
156 { "||", TOK_LOGICAL_OR },
160 static const char *prToken(int val)
164 for (et = exprTokTable; et->name != NULL; et++) {
170 #endif /* DEBUG_PARSER */
173 * @param state expression parser state
175 static int rdToken(ParseState state)
181 /* Skip whitespace before the next token. */
182 while (*p && risspace(*p)) p++;
196 token = TOK_MULTIPLY;
212 rpmlog(RPMLOG_ERR, _("syntax error while parsing ==\n"));
239 token = TOK_LOGICAL_AND;
242 rpmlog(RPMLOG_ERR, _("syntax error while parsing &&\n"));
248 token = TOK_LOGICAL_OR;
251 rpmlog(RPMLOG_ERR, _("syntax error while parsing ||\n"));
261 for (ts=1; p[ts] && risdigit(p[ts]); ts++);
262 temp = xmalloc(ts+1);
268 v = valueMakeInteger(atoi(temp));
271 } else if (risalpha(*p)) {
275 for (ts=1; p[ts] && (risalnum(p[ts]) || p[ts] == '_'); ts++);
276 temp = xmalloc(ts+1);
281 token = TOK_IDENTIFIER;
282 v = valueMakeString(temp);
284 } else if (*p == '\"') {
289 for (ts=0; p[ts] && p[ts] != '\"'; ts++);
290 temp = xmalloc(ts+1);
297 v = valueMakeString( rpmExpand(temp, NULL) );
301 rpmlog(RPMLOG_ERR, _("parse error in expression\n"));
307 state->nextToken = token;
308 state->tokenValue = v;
310 DEBUG(printf("rdToken: \"%s\" (%d)\n", prToken(token), token));
311 DEBUG(valueDump("rdToken:", state->tokenValue, stdout));
316 static Value doLogical(ParseState state);
319 * @param state expression parser state
321 static Value doPrimary(ParseState state)
325 DEBUG(printf("doPrimary()\n"));
327 switch (state->nextToken) {
331 v = doLogical(state);
332 if (state->nextToken != TOK_CLOSE_P) {
333 rpmlog(RPMLOG_ERR, _("unmatched (\n"));
342 v = state->tokenValue;
347 case TOK_IDENTIFIER: {
348 const char *name = state->tokenValue->data.s;
350 v = valueMakeString( rpmExpand(name, NULL) );
360 v = doPrimary(state);
364 if (! valueIsInteger(v)) {
365 rpmlog(RPMLOG_ERR, _("- only on numbers\n"));
369 v = valueMakeInteger(- v->data.i);
376 v = doPrimary(state);
380 if (! valueIsInteger(v)) {
381 rpmlog(RPMLOG_ERR, _("! only on numbers\n"));
385 v = valueMakeInteger(! v->data.i);
392 DEBUG(valueDump("doPrimary:", v, stdout));
397 * @param state expression parser state
399 static Value doMultiplyDivide(ParseState state)
403 DEBUG(printf("doMultiplyDivide()\n"));
405 v1 = doPrimary(state);
409 while (state->nextToken == TOK_MULTIPLY
410 || state->nextToken == TOK_DIVIDE) {
411 int op = state->nextToken;
416 if (v2) valueFree(v2);
418 v2 = doPrimary(state);
422 if (! valueSameType(v1, v2)) {
423 rpmlog(RPMLOG_ERR, _("types must match\n"));
427 if (valueIsInteger(v1)) {
428 int i1 = v1->data.i, i2 = v2->data.i;
431 if (op == TOK_MULTIPLY)
432 v1 = valueMakeInteger(i1 * i2);
434 v1 = valueMakeInteger(i1 / i2);
436 rpmlog(RPMLOG_ERR, _("* / not suported for strings\n"));
441 if (v2) valueFree(v2);
446 * @param state expression parser state
448 static Value doAddSubtract(ParseState state)
452 DEBUG(printf("doAddSubtract()\n"));
454 v1 = doMultiplyDivide(state);
458 while (state->nextToken == TOK_ADD || state->nextToken == TOK_MINUS) {
459 int op = state->nextToken;
464 if (v2) valueFree(v2);
466 v2 = doMultiplyDivide(state);
470 if (! valueSameType(v1, v2)) {
471 rpmlog(RPMLOG_ERR, _("types must match\n"));
475 if (valueIsInteger(v1)) {
476 int i1 = v1->data.i, i2 = v2->data.i;
480 v1 = valueMakeInteger(i1 + i2);
482 v1 = valueMakeInteger(i1 - i2);
486 if (op == TOK_MINUS) {
487 rpmlog(RPMLOG_ERR, _("- not suported for strings\n"));
491 copy = xmalloc(strlen(v1->data.s) + strlen(v2->data.s) + 1);
492 (void) stpcpy( stpcpy(copy, v1->data.s), v2->data.s);
495 v1 = valueMakeString(copy);
499 if (v2) valueFree(v2);
504 * @param state expression parser state
506 static Value doRelational(ParseState state)
510 DEBUG(printf("doRelational()\n"));
512 v1 = doAddSubtract(state);
516 while (state->nextToken >= TOK_EQ && state->nextToken <= TOK_GE) {
517 int op = state->nextToken;
522 if (v2) valueFree(v2);
524 v2 = doAddSubtract(state);
528 if (! valueSameType(v1, v2)) {
529 rpmlog(RPMLOG_ERR, _("types must match\n"));
533 if (valueIsInteger(v1)) {
534 int i1 = v1->data.i, i2 = v2->data.i, r = 0;
558 v1 = valueMakeInteger(r);
560 const char * s1 = v1->data.s;
561 const char * s2 = v2->data.s;
565 r = (strcmp(s1,s2) == 0);
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);
586 v1 = valueMakeInteger(r);
590 if (v2) valueFree(v2);
595 * @param state expression parser state
597 static Value doLogical(ParseState state)
601 DEBUG(printf("doLogical()\n"));
603 v1 = doRelational(state);
607 while (state->nextToken == TOK_LOGICAL_AND
608 || state->nextToken == TOK_LOGICAL_OR) {
609 int op = state->nextToken;
614 if (v2) valueFree(v2);
616 v2 = doRelational(state);
620 if (! valueSameType(v1, v2)) {
621 rpmlog(RPMLOG_ERR, _("types must match\n"));
625 if (valueIsInteger(v1)) {
626 int i1 = v1->data.i, i2 = v2->data.i;
629 if (op == TOK_LOGICAL_AND)
630 v1 = valueMakeInteger(i1 && i2);
632 v1 = valueMakeInteger(i1 || i2);
634 rpmlog(RPMLOG_ERR, _("&& and || not suported for strings\n"));
639 if (v2) valueFree(v2);
643 int parseExpressionBoolean(rpmSpec spec, const char *expr)
645 struct _parseState state;
649 DEBUG(printf("parseExprBoolean(?, '%s')\n", expr));
651 /* Initialize the expression parser state. */
652 state.p = state.str = xstrdup(expr);
655 state.tokenValue = NULL;
656 (void) rdToken(&state);
658 /* Parse the expression. */
659 v = doLogical(&state);
661 state.str = _free(state.str);
665 /* If the next token is not TOK_EOF, we have a syntax error. */
666 if (state.nextToken != TOK_EOF) {
667 rpmlog(RPMLOG_ERR, _("syntax error in expression\n"));
668 state.str = _free(state.str);
672 DEBUG(valueDump("parseExprBoolean:", v, stdout));
675 case VALUE_TYPE_INTEGER:
676 result = v->data.i != 0;
678 case VALUE_TYPE_STRING:
679 result = v->data.s[0] != '\0';
685 state.str = _free(state.str);
690 char * parseExpressionString(rpmSpec spec, const char *expr)
692 struct _parseState state;
696 DEBUG(printf("parseExprString(?, '%s')\n", expr));
698 /* Initialize the expression parser state. */
699 state.p = state.str = xstrdup(expr);
702 state.tokenValue = NULL;
703 (void) rdToken(&state);
705 /* Parse the expression. */
706 v = doLogical(&state);
708 state.str = _free(state.str);
712 /* If the next token is not TOK_EOF, we have a syntax error. */
713 if (state.nextToken != TOK_EOF) {
714 rpmlog(RPMLOG_ERR, _("syntax error in expression\n"));
715 state.str = _free(state.str);
719 DEBUG(valueDump("parseExprString:", v, stdout));
722 case VALUE_TYPE_INTEGER: {
723 rasprintf(&result, "%d", v->data.i);
725 case VALUE_TYPE_STRING:
726 result = xstrdup(v->data.s);
732 state.str = _free(state.str);