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.
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(struct _value));
48 v->type = VALUE_TYPE_INTEGER;
55 static Value valueMakeString(/*@only@*/ const char *s)
59 v = (Value) xmalloc(sizeof(struct _value));
60 v->type = VALUE_TYPE_STRING;
67 static void valueFree( /*@only@*/ 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 /*@owned@*/ char *str; /*!< expression string */
101 /*@dependent@*/ char *p; /*!< current position in expression string */
102 int nextToken; /*!< current lookahead token */
103 Value tokenValue; /*!< valid when TOK_INTEGER or TOK_STRING */
104 Spec spec; /*!< spec file that we are parsing inside of */
109 * \name Parser tokens
113 #define TOK_INTEGER 2
115 #define TOK_IDENTIFIER 4
118 #define TOK_MULTIPLY 7
121 #define TOK_CLOSE_P 10
129 #define TOK_LOGICAL_AND 18
130 #define TOK_LOGICAL_OR 19
133 #define EXPRBUFSIZ BUFSIZ
135 #if defined(DEBUG_PARSER)
136 typedef struct exprTokTableEntry {
141 ETTE_t exprTokTable[] = {
143 { "I", TOK_INTEGER },
145 { "ID", TOK_IDENTIFIER },
148 { "*", TOK_MULTIPLY },
150 { "( ", TOK_OPEN_P },
151 { " )", TOK_CLOSE_P },
159 { "&&", TOK_LOGICAL_AND },
160 { "||", TOK_LOGICAL_OR },
164 static const char *prToken(int val)
168 for (et = exprTokTable; et->name != NULL; et++) {
174 #endif /* DEBUG_PARSER */
177 * @param state expression parser state
179 static int rdToken(ParseState state)
185 /* Skip whitespace before the next token. */
186 while (*p && xisspace(*p)) p++;
200 token = TOK_MULTIPLY;
216 rpmError(RPMERR_BADSPEC, _("syntax error while parsing ==\n"));
243 token = TOK_LOGICAL_AND;
246 rpmError(RPMERR_BADSPEC, _("syntax error while parsing &&\n"));
252 token = TOK_LOGICAL_OR;
255 rpmError(RPMERR_BADSPEC, _("syntax error while parsing ||\n"));
262 char temp[EXPRBUFSIZ], *t = temp;
264 while (*p && xisdigit(*p))
270 v = valueMakeInteger(atoi(temp));
272 } else if (xisalpha(*p)) {
273 char temp[EXPRBUFSIZ], *t = temp;
275 while (*p && (xisalnum(*p) || *p == '_'))
280 token = TOK_IDENTIFIER;
281 v = valueMakeString( xstrdup(temp) );
283 } else if (*p == '\"') {
284 char temp[EXPRBUFSIZ], *t = temp;
287 while (*p && *p != '\"')
292 v = valueMakeString( rpmExpand(temp, NULL) );
295 rpmError(RPMERR_BADSPEC, _("parse error in expression\n"));
301 state->nextToken = token;
302 state->tokenValue = v;
304 DEBUG(printf("rdToken: \"%s\" (%d)\n", prToken(token), token));
305 DEBUG(valueDump("rdToken:", state->tokenValue, stdout));
310 static Value doLogical(ParseState state);
313 * @param state expression parser state
315 static Value doPrimary(ParseState state)
319 DEBUG(printf("doPrimary()\n"));
321 switch (state->nextToken) {
325 v = doLogical(state);
326 if (state->nextToken != TOK_CLOSE_P) {
327 rpmError(RPMERR_BADSPEC, _("unmatched (\n"));
334 v = state->tokenValue;
339 case TOK_IDENTIFIER: {
340 const char *name = state->tokenValue->data.s;
342 v = valueMakeString( rpmExpand(name, NULL) );
352 v = doPrimary(state);
356 if (! valueIsInteger(v)) {
357 rpmError(RPMERR_BADSPEC, _("- only on numbers\n"));
361 v = valueMakeInteger(- v->data.i);
368 v = doPrimary(state);
372 if (! valueIsInteger(v)) {
373 rpmError(RPMERR_BADSPEC, _("! only on numbers\n"));
377 v = valueMakeInteger(! v->data.i);
381 /*@notreached@*/ break;
384 DEBUG(valueDump("doPrimary:", v, stdout));
389 * @param state expression parser state
391 static Value doMultiplyDivide(ParseState state)
395 DEBUG(printf("doMultiplyDivide()\n"));
397 v1 = doPrimary(state);
401 while (state->nextToken == TOK_MULTIPLY
402 || state->nextToken == TOK_DIVIDE) {
403 int op = state->nextToken;
408 if (v2) valueFree(v2);
410 v2 = doPrimary(state);
414 if (! valueSameType(v1, v2)) {
415 rpmError(RPMERR_BADSPEC, _("types must match\n"));
419 if (valueIsInteger(v1)) {
420 int i1 = v1->data.i, i2 = v2->data.i;
423 if (op == TOK_MULTIPLY)
424 v1 = valueMakeInteger(i1 * i2);
426 v1 = valueMakeInteger(i1 / i2);
428 rpmError(RPMERR_BADSPEC, _("* / not suported for strings\n"));
433 if (v2) valueFree(v2);
438 * @param state expression parser state
440 static Value doAddSubtract(ParseState state)
444 DEBUG(printf("doAddSubtract()\n"));
446 v1 = doMultiplyDivide(state);
450 while (state->nextToken == TOK_ADD || state->nextToken == TOK_MINUS) {
451 int op = state->nextToken;
456 if (v2) valueFree(v2);
458 v2 = doMultiplyDivide(state);
462 if (! valueSameType(v1, v2)) {
463 rpmError(RPMERR_BADSPEC, _("types must match\n"));
467 if (valueIsInteger(v1)) {
468 int i1 = v1->data.i, i2 = v2->data.i;
472 v1 = valueMakeInteger(i1 + i2);
474 v1 = valueMakeInteger(i1 - i2);
478 if (op == TOK_MINUS) {
479 rpmError(RPMERR_BADSPEC, _("- not suported for strings\n"));
483 copy = xmalloc(strlen(v1->data.s) + strlen(v2->data.s) + 1);
484 (void) stpcpy( stpcpy(copy, v1->data.s), v2->data.s);
487 v1 = valueMakeString(copy);
491 if (v2) valueFree(v2);
496 * @param state expression parser state
498 static Value doRelational(ParseState state)
502 DEBUG(printf("doRelational()\n"));
504 v1 = doAddSubtract(state);
508 while (state->nextToken >= TOK_EQ && state->nextToken <= TOK_GE) {
509 int op = state->nextToken;
514 if (v2) valueFree(v2);
516 v2 = doAddSubtract(state);
520 if (! valueSameType(v1, v2)) {
521 rpmError(RPMERR_BADSPEC, _("types must match\n"));
525 if (valueIsInteger(v1)) {
526 int i1 = v1->data.i, i2 = v2->data.i, r = 0;
550 v1 = valueMakeInteger(r);
552 const char * s1 = v1->data.s;
553 const char * s2 = v2->data.s;
557 r = (strcmp(s1,s2) == 0);
560 r = (strcmp(s1,s2) != 0);
563 r = (strcmp(s1,s2) < 0);
566 r = (strcmp(s1,s2) <= 0);
569 r = (strcmp(s1,s2) > 0);
572 r = (strcmp(s1,s2) >= 0);
578 v1 = valueMakeInteger(r);
582 if (v2) valueFree(v2);
587 * @param state expression parser state
589 static Value doLogical(ParseState state)
593 DEBUG(printf("doLogical()\n"));
595 v1 = doRelational(state);
599 while (state->nextToken == TOK_LOGICAL_AND
600 || state->nextToken == TOK_LOGICAL_OR) {
601 int op = state->nextToken;
606 if (v2) valueFree(v2);
608 v2 = doRelational(state);
612 if (! valueSameType(v1, v2)) {
613 rpmError(RPMERR_BADSPEC, _("types must match\n"));
617 if (valueIsInteger(v1)) {
618 int i1 = v1->data.i, i2 = v2->data.i;
621 if (op == TOK_LOGICAL_AND)
622 v1 = valueMakeInteger(i1 && i2);
624 v1 = valueMakeInteger(i1 || i2);
626 rpmError(RPMERR_BADSPEC, _("&& and || not suported for strings\n"));
631 if (v2) valueFree(v2);
635 int parseExpressionBoolean(Spec spec, const char *expr)
637 struct _parseState state;
641 DEBUG(printf("parseExprBoolean(?, '%s')\n", expr));
643 /* Initialize the expression parser state. */
644 state.p = state.str = xstrdup(expr);
647 state.tokenValue = NULL;
648 (void) rdToken(&state);
650 /* Parse the expression. */
651 v = doLogical(&state);
653 state.str = _free(state.str);
657 /* If the next token is not TOK_EOF, we have a syntax error. */
658 if (state.nextToken != TOK_EOF) {
659 rpmError(RPMERR_BADSPEC, _("syntax error in expression\n"));
660 state.str = _free(state.str);
664 DEBUG(valueDump("parseExprBoolean:", v, stdout));
667 case VALUE_TYPE_INTEGER:
668 result = v->data.i != 0;
670 case VALUE_TYPE_STRING:
671 result = v->data.s[0] != '\0';
677 state.str = _free(state.str);
682 char * parseExpressionString(Spec spec, const char *expr)
684 struct _parseState state;
688 DEBUG(printf("parseExprString(?, '%s')\n", expr));
690 /* Initialize the expression parser state. */
691 state.p = state.str = xstrdup(expr);
694 state.tokenValue = NULL;
695 (void) rdToken(&state);
697 /* Parse the expression. */
698 v = doLogical(&state);
700 state.str = _free(state.str);
704 /* If the next token is not TOK_EOF, we have a syntax error. */
705 if (state.nextToken != TOK_EOF) {
706 rpmError(RPMERR_BADSPEC, _("syntax error in expression\n"));
707 state.str = _free(state.str);
711 DEBUG(valueDump("parseExprString:", v, stdout));
714 case VALUE_TYPE_INTEGER: {
716 sprintf(buf, "%d", v->data.i);
717 result = xstrdup(buf);
719 case VALUE_TYPE_STRING:
720 result = xstrdup(v->data.s);
726 state.str = _free(state.str);