• Main Page
  • Related Pages
  • Modules
  • Data Structures
  • Files
  • File List
  • Globals

build/expression.c

Go to the documentation of this file.
00001 
00014 #include "system.h"
00015 
00016 #include <rpmio.h>
00017 #include <rpmbuild.h>
00018 #include <rpmlib.h>
00019 
00020 #include "debug.h"
00021 
00022 /* #define DEBUG_PARSER 1 */
00023 
00024 #ifdef DEBUG_PARSER
00025 #include <stdio.h>
00026 #define DEBUG(x) do { x ; } while (0)
00027 #else
00028 #define DEBUG(x)
00029 #endif
00030 
00034 typedef struct _value {
00035   enum { VALUE_TYPE_INTEGER, VALUE_TYPE_STRING } type;
00036   union {
00037     const char *s;
00038     int i;
00039   } data;
00040 } *Value;
00041 
00044 static Value valueMakeInteger(int i)
00045         /*@*/
00046 {
00047   Value v;
00048 
00049   v = (Value) xmalloc(sizeof(*v));
00050   v->type = VALUE_TYPE_INTEGER;
00051   v->data.i = i;
00052   return v;
00053 }
00054 
00057 static Value valueMakeString(/*@only@*/ const char *s)
00058         /*@*/
00059 {
00060   Value v;
00061 
00062   v = (Value) xmalloc(sizeof(*v));
00063   v->type = VALUE_TYPE_STRING;
00064   v->data.s = s;
00065   return v;
00066 }
00067 
00070 static void valueFree( /*@only@*/ Value v)
00071         /*@modifies v @*/
00072 {
00073   if (v) {
00074     if (v->type == VALUE_TYPE_STRING)
00075         v->data.s = _free(v->data.s);
00076     v = _free(v);
00077   }
00078 }
00079 
00080 #ifdef DEBUG_PARSER
00081 static void valueDump(const char *msg, Value v, FILE *fp)
00082         /*@*/
00083 {
00084   if (msg)
00085     fprintf(fp, "%s ", msg);
00086   if (v) {
00087     if (v->type == VALUE_TYPE_INTEGER)
00088       fprintf(fp, "INTEGER %d\n", v->data.i);
00089     else
00090       fprintf(fp, "STRING '%s'\n", v->data.s);
00091   } else
00092     fprintf(fp, "NULL\n");
00093 }
00094 #endif
00095 
00096 #define valueIsInteger(v) ((v)->type == VALUE_TYPE_INTEGER)
00097 #define valueIsString(v) ((v)->type == VALUE_TYPE_STRING)
00098 #define valueSameType(v1,v2) ((v1)->type == (v2)->type)
00099 
00100 
00104 typedef struct _parseState {
00105 /*@owned@*/
00106     char *str;          
00107 /*@dependent@*/
00108     char *p;            
00109     int nextToken;      
00110 /*@relnull@*/
00111     Value tokenValue;   
00112     Spec spec;          
00113 } *ParseState;
00114 
00115 
00120 #define TOK_EOF          1
00121 #define TOK_INTEGER      2
00122 #define TOK_STRING       3
00123 #define TOK_IDENTIFIER   4
00124 #define TOK_ADD          5
00125 #define TOK_MINUS        6
00126 #define TOK_MULTIPLY     7
00127 #define TOK_DIVIDE       8
00128 #define TOK_OPEN_P       9
00129 #define TOK_CLOSE_P     10
00130 #define TOK_EQ          11
00131 #define TOK_NEQ         12
00132 #define TOK_LT          13
00133 #define TOK_LE          14
00134 #define TOK_GT          15
00135 #define TOK_GE          16
00136 #define TOK_NOT         17
00137 #define TOK_LOGICAL_AND 18
00138 #define TOK_LOGICAL_OR  19
00139 
00141 #define EXPRBUFSIZ      BUFSIZ
00142 
00143 #if defined(DEBUG_PARSER)
00144 typedef struct exprTokTableEntry {
00145     const char *name;
00146     int val;
00147 } ETTE_t;
00148 
00149 ETTE_t exprTokTable[] = {
00150     { "EOF",    TOK_EOF },
00151     { "I",      TOK_INTEGER },
00152     { "S",      TOK_STRING },
00153     { "ID",     TOK_IDENTIFIER },
00154     { "+",      TOK_ADD },
00155     { "-",      TOK_MINUS },
00156     { "*",      TOK_MULTIPLY },
00157     { "/",      TOK_DIVIDE },
00158     { "( ",     TOK_OPEN_P },
00159     { " )",     TOK_CLOSE_P },
00160     { "==",     TOK_EQ },
00161     { "!=",     TOK_NEQ },
00162     { "<",      TOK_LT },
00163     { "<=",     TOK_LE },
00164     { ">",      TOK_GT },
00165     { ">=",     TOK_GE },
00166     { "!",      TOK_NOT },
00167     { "&&",     TOK_LOGICAL_AND },
00168     { "||",     TOK_LOGICAL_OR },
00169     { NULL, 0 }
00170 };
00171 
00172 static const char *prToken(int val)
00173         /*@*/
00174 {
00175     ETTE_t *et;
00176     
00177     for (et = exprTokTable; et->name != NULL; et++) {
00178         if (val == et->val)
00179             return et->name;
00180     }
00181     return "???";
00182 }
00183 #endif  /* DEBUG_PARSER */
00184 
00188 static int rdToken(ParseState state)
00189         /*@globals rpmGlobalMacroContext, h_errno @*/
00190         /*@modifies state->nextToken, state->p, state->tokenValue,
00191                 rpmGlobalMacroContext @*/
00192 {
00193   int token;
00194   Value v = NULL;
00195   char *p = state->p;
00196 
00197   /* Skip whitespace before the next token. */
00198   while (*p && xisspace(*p)) p++;
00199 
00200   switch (*p) {
00201   case '\0':
00202     token = TOK_EOF;
00203     p--;
00204     break;
00205   case '+':
00206     token = TOK_ADD;
00207     break;
00208   case '-':
00209     token = TOK_MINUS;
00210     break;
00211   case '*':
00212     token = TOK_MULTIPLY;
00213     break;
00214   case '/':
00215     token = TOK_DIVIDE;
00216     break;
00217   case '(':
00218     token = TOK_OPEN_P;
00219     break;
00220   case ')':
00221     token = TOK_CLOSE_P;
00222     break;
00223   case '=':
00224     if (p[1] == '=') {
00225       token = TOK_EQ;
00226       p++;
00227     } else {
00228       rpmlog(RPMLOG_ERR, _("syntax error while parsing ==\n"));
00229       return -1;
00230     }
00231     break;
00232   case '!':
00233     if (p[1] == '=') {
00234       token = TOK_NEQ;
00235       p++;
00236     } else
00237       token = TOK_NOT;
00238     break;
00239   case '<':
00240     if (p[1] == '=') {
00241       token = TOK_LE;
00242       p++;
00243     } else
00244       token = TOK_LT;
00245     break;
00246   case '>':
00247     if (p[1] == '=') {
00248       token = TOK_GE;
00249       p++;
00250     } else
00251       token = TOK_GT;
00252     break;
00253   case '&':
00254     if (p[1] == '&') {
00255       token = TOK_LOGICAL_AND;
00256       p++;
00257     } else {
00258       rpmlog(RPMLOG_ERR, _("syntax error while parsing &&\n"));
00259       return -1;
00260     }
00261     break;
00262   case '|':
00263     if (p[1] == '|') {
00264       token = TOK_LOGICAL_OR;
00265       p++;
00266     } else {
00267       rpmlog(RPMLOG_ERR, _("syntax error while parsing ||\n"));
00268       return -1;
00269     }
00270     break;
00271 
00272   default:
00273     if (xisdigit(*p)) {
00274       char temp[EXPRBUFSIZ], *t = temp;
00275 
00276       temp[0] = '\0';
00277       while (*p && xisdigit(*p))
00278         *t++ = *p++;
00279       *t++ = '\0';
00280       p--;
00281 
00282       token = TOK_INTEGER;
00283       v = valueMakeInteger(atoi(temp));
00284 
00285     } else if (xisalpha(*p)) {
00286       char temp[EXPRBUFSIZ], *t = temp;
00287 
00288       temp[0] = '\0';
00289       while (*p && (xisalnum(*p) || *p == '_'))
00290         *t++ = *p++;
00291       *t++ = '\0';
00292       p--;
00293 
00294       token = TOK_IDENTIFIER;
00295       v = valueMakeString( xstrdup(temp) );
00296 
00297     } else if (*p == '\"') {
00298       char temp[EXPRBUFSIZ], *t = temp;
00299 
00300       temp[0] = '\0';
00301       p++;
00302       while (*p && *p != '\"')
00303         *t++ = *p++;
00304       *t++ = '\0';
00305 
00306       token = TOK_STRING;
00307       v = valueMakeString( rpmExpand(temp, NULL) );
00308 
00309     } else {
00310       rpmlog(RPMLOG_ERR, _("parse error in expression\n"));
00311       return -1;
00312     }
00313   }
00314 
00315   state->p = p + 1;
00316   state->nextToken = token;
00317   state->tokenValue = v;
00318 
00319   DEBUG(printf("rdToken: \"%s\" (%d)\n", prToken(token), token));
00320   DEBUG(valueDump("rdToken:", state->tokenValue, stdout));
00321 
00322   return 0;
00323 }
00324 
00325 /*@null@*/
00326 static Value doLogical(ParseState state)
00327         /*@globals rpmGlobalMacroContext, h_errno @*/
00328         /*@modifies state->nextToken, state->p, state->tokenValue,
00329                 rpmGlobalMacroContext @*/;
00330 
00334 /*@null@*/
00335 static Value doPrimary(ParseState state)
00336         /*@globals rpmGlobalMacroContext, h_errno @*/
00337         /*@modifies state->nextToken, state->p, state->tokenValue,
00338                 rpmGlobalMacroContext @*/
00339 {
00340   Value v;
00341 
00342   DEBUG(printf("doPrimary()\n"));
00343 
00344   switch (state->nextToken) {
00345   case TOK_OPEN_P:
00346     if (rdToken(state))
00347       return NULL;
00348     v = doLogical(state);
00349     if (state->nextToken != TOK_CLOSE_P) {
00350       rpmlog(RPMLOG_ERR, _("unmatched (\n"));
00351       return NULL;
00352     }
00353     if (rdToken(state))
00354       return NULL;
00355     break;
00356 
00357   case TOK_INTEGER:
00358   case TOK_STRING:
00359     v = state->tokenValue;
00360     if (rdToken(state))
00361       return NULL;
00362     break;
00363 
00364   case TOK_IDENTIFIER: {
00365     const char *name = state->tokenValue->data.s;
00366 
00367     v = valueMakeString( rpmExpand(name, NULL) );
00368     if (rdToken(state))
00369       return NULL;
00370     break;
00371   }
00372 
00373   case TOK_MINUS:
00374     if (rdToken(state))
00375       return NULL;
00376 
00377     v = doPrimary(state);
00378     if (v == NULL)
00379       return NULL;
00380 
00381     if (! valueIsInteger(v)) {
00382       rpmlog(RPMLOG_ERR, _("- only on numbers\n"));
00383       return NULL;
00384     }
00385 
00386     v = valueMakeInteger(- v->data.i);
00387     break;
00388 
00389   case TOK_NOT:
00390     if (rdToken(state))
00391       return NULL;
00392 
00393     v = doPrimary(state);
00394     if (v == NULL)
00395       return NULL;
00396 
00397     if (! valueIsInteger(v)) {
00398       rpmlog(RPMLOG_ERR, _("! only on numbers\n"));
00399       return NULL;
00400     }
00401 
00402     v = valueMakeInteger(! v->data.i);
00403     break;
00404   default:
00405     return NULL;
00406     /*@notreached@*/ break;
00407   }
00408 
00409   DEBUG(valueDump("doPrimary:", v, stdout));
00410   return v;
00411 }
00412 
00416 /*@null@*/
00417 static Value doMultiplyDivide(ParseState state)
00418         /*@globals rpmGlobalMacroContext, h_errno @*/
00419         /*@modifies state->nextToken, state->p, state->tokenValue,
00420                 rpmGlobalMacroContext @*/
00421 {
00422   Value v1, v2 = NULL;
00423 
00424   DEBUG(printf("doMultiplyDivide()\n"));
00425 
00426   v1 = doPrimary(state);
00427   if (v1 == NULL)
00428     return NULL;
00429 
00430   while (state->nextToken == TOK_MULTIPLY
00431          || state->nextToken == TOK_DIVIDE) {
00432     int op = state->nextToken;
00433 
00434     if (rdToken(state))
00435       return NULL;
00436 
00437     if (v2) valueFree(v2);
00438 
00439     v2 = doPrimary(state);
00440     if (v2 == NULL)
00441       return NULL;
00442 
00443     if (! valueSameType(v1, v2)) {
00444       rpmlog(RPMLOG_ERR, _("types must match\n"));
00445       return NULL;
00446     }
00447 
00448     if (valueIsInteger(v1)) {
00449       int i1 = v1->data.i, i2 = v2->data.i;
00450 
00451       valueFree(v1);
00452       if (op == TOK_MULTIPLY)
00453         v1 = valueMakeInteger(i1 * i2);
00454       else
00455         v1 = valueMakeInteger(i1 / i2);
00456     } else {
00457       rpmlog(RPMLOG_ERR, _("* / not suported for strings\n"));
00458       return NULL;
00459     }
00460   }
00461 
00462   if (v2) valueFree(v2);
00463   return v1;
00464 }
00465 
00469 /*@null@*/
00470 static Value doAddSubtract(ParseState state)
00471         /*@globals rpmGlobalMacroContext, h_errno @*/
00472         /*@modifies state->nextToken, state->p, state->tokenValue,
00473                 rpmGlobalMacroContext @*/
00474 {
00475   Value v1, v2 = NULL;
00476 
00477   DEBUG(printf("doAddSubtract()\n"));
00478 
00479   v1 = doMultiplyDivide(state);
00480   if (v1 == NULL)
00481     return NULL;
00482 
00483   while (state->nextToken == TOK_ADD || state->nextToken == TOK_MINUS) {
00484     int op = state->nextToken;
00485 
00486     if (rdToken(state))
00487       return NULL;
00488 
00489     if (v2) valueFree(v2);
00490 
00491     v2 = doMultiplyDivide(state);
00492     if (v2 == NULL)
00493       return NULL;
00494 
00495     if (! valueSameType(v1, v2)) {
00496       rpmlog(RPMLOG_ERR, _("types must match\n"));
00497       return NULL;
00498     }
00499 
00500     if (valueIsInteger(v1)) {
00501       int i1 = v1->data.i, i2 = v2->data.i;
00502 
00503       valueFree(v1);
00504       if (op == TOK_ADD)
00505         v1 = valueMakeInteger(i1 + i2);
00506       else
00507         v1 = valueMakeInteger(i1 - i2);
00508     } else {
00509       char *copy;
00510 
00511       if (op == TOK_MINUS) {
00512         rpmlog(RPMLOG_ERR, _("- not suported for strings\n"));
00513         return NULL;
00514       }
00515 
00516       copy = xmalloc(strlen(v1->data.s) + strlen(v2->data.s) + 1);
00517       (void) stpcpy( stpcpy(copy, v1->data.s), v2->data.s);
00518 
00519       valueFree(v1);
00520       v1 = valueMakeString(copy);
00521     }
00522   }
00523 
00524   if (v2) valueFree(v2);
00525   return v1;
00526 }
00527 
00531 /*@null@*/
00532 static Value doRelational(ParseState state)
00533         /*@globals rpmGlobalMacroContext, h_errno @*/
00534         /*@modifies state->nextToken, state->p, state->tokenValue,
00535                 rpmGlobalMacroContext @*/
00536 {
00537   Value v1, v2 = NULL;
00538 
00539   DEBUG(printf("doRelational()\n"));
00540 
00541   v1 = doAddSubtract(state);
00542   if (v1 == NULL)
00543     return NULL;
00544 
00545   while (state->nextToken >= TOK_EQ && state->nextToken <= TOK_GE) {
00546     int op = state->nextToken;
00547 
00548     if (rdToken(state))
00549       return NULL;
00550 
00551     if (v2) valueFree(v2);
00552 
00553     v2 = doAddSubtract(state);
00554     if (v2 == NULL)
00555       return NULL;
00556 
00557     if (! valueSameType(v1, v2)) {
00558       rpmlog(RPMLOG_ERR, _("types must match\n"));
00559       return NULL;
00560     }
00561 
00562     if (valueIsInteger(v1)) {
00563       int i1 = v1->data.i, i2 = v2->data.i, r = 0;
00564       switch (op) {
00565       case TOK_EQ:
00566         r = (i1 == i2);
00567         /*@switchbreak@*/ break;
00568       case TOK_NEQ:
00569         r = (i1 != i2);
00570         /*@switchbreak@*/ break;
00571       case TOK_LT:
00572         r = (i1 < i2);
00573         /*@switchbreak@*/ break;
00574       case TOK_LE:
00575         r = (i1 <= i2);
00576         /*@switchbreak@*/ break;
00577       case TOK_GT:
00578         r = (i1 > i2);
00579         /*@switchbreak@*/ break;
00580       case TOK_GE:
00581         r = (i1 >= i2);
00582         /*@switchbreak@*/ break;
00583       default:
00584         /*@switchbreak@*/ break;
00585       }
00586       valueFree(v1);
00587       v1 = valueMakeInteger(r);
00588     } else {
00589       const char * s1 = v1->data.s;
00590       const char * s2 = v2->data.s;
00591       int r = 0;
00592       switch (op) {
00593       case TOK_EQ:
00594         r = (strcmp(s1,s2) == 0);
00595         /*@switchbreak@*/ break;
00596       case TOK_NEQ:
00597         r = (strcmp(s1,s2) != 0);
00598         /*@switchbreak@*/ break;
00599       case TOK_LT:
00600         r = (strcmp(s1,s2) < 0);
00601         /*@switchbreak@*/ break;
00602       case TOK_LE:
00603         r = (strcmp(s1,s2) <= 0);
00604         /*@switchbreak@*/ break;
00605       case TOK_GT:
00606         r = (strcmp(s1,s2) > 0);
00607         /*@switchbreak@*/ break;
00608       case TOK_GE:
00609         r = (strcmp(s1,s2) >= 0);
00610         /*@switchbreak@*/ break;
00611       default:
00612         /*@switchbreak@*/ break;
00613       }
00614       valueFree(v1);
00615       v1 = valueMakeInteger(r);
00616     }
00617   }
00618 
00619   if (v2) valueFree(v2);
00620   return v1;
00621 }
00622 
00626 static Value doLogical(ParseState state)
00627         /*@globals rpmGlobalMacroContext, h_errno @*/
00628         /*@modifies state->nextToken, state->p, state->tokenValue,
00629                 rpmGlobalMacroContext @*/
00630 {
00631   Value v1, v2 = NULL;
00632 
00633   DEBUG(printf("doLogical()\n"));
00634 
00635   v1 = doRelational(state);
00636   if (v1 == NULL)
00637     return NULL;
00638 
00639   while (state->nextToken == TOK_LOGICAL_AND
00640          || state->nextToken == TOK_LOGICAL_OR) {
00641     int op = state->nextToken;
00642 
00643     if (rdToken(state))
00644       return NULL;
00645 
00646     if (v2) valueFree(v2);
00647 
00648     v2 = doRelational(state);
00649     if (v2 == NULL)
00650       return NULL;
00651 
00652     if (! valueSameType(v1, v2)) {
00653       rpmlog(RPMLOG_ERR, _("types must match\n"));
00654       return NULL;
00655     }
00656 
00657     if (valueIsInteger(v1)) {
00658       int i1 = v1->data.i, i2 = v2->data.i;
00659 
00660       valueFree(v1);
00661       if (op == TOK_LOGICAL_AND)
00662         v1 = valueMakeInteger(i1 && i2);
00663       else
00664         v1 = valueMakeInteger(i1 || i2);
00665     } else {
00666       rpmlog(RPMLOG_ERR, _("&& and || not suported for strings\n"));
00667       return NULL;
00668     }
00669   }
00670 
00671   if (v2) valueFree(v2);
00672   return v1;
00673 }
00674 
00675 int parseExpressionBoolean(Spec spec, const char *expr)
00676 {
00677   struct _parseState state;
00678   int result = -1;
00679   Value v;
00680 
00681   DEBUG(printf("parseExprBoolean(?, '%s')\n", expr));
00682 
00683   /* Initialize the expression parser state. */
00684   state.p = state.str = xstrdup(expr);
00685   state.spec = spec;
00686   state.nextToken = 0;
00687   state.tokenValue = NULL;
00688   (void) rdToken(&state);
00689 
00690   /* Parse the expression. */
00691   v = doLogical(&state);
00692   if (!v) {
00693     state.str = _free(state.str);
00694     return -1;
00695   }
00696 
00697   /* If the next token is not TOK_EOF, we have a syntax error. */
00698   if (state.nextToken != TOK_EOF) {
00699     rpmlog(RPMLOG_ERR, _("syntax error in expression\n"));
00700     state.str = _free(state.str);
00701     return -1;
00702   }
00703 
00704   DEBUG(valueDump("parseExprBoolean:", v, stdout));
00705 
00706   switch (v->type) {
00707   case VALUE_TYPE_INTEGER:
00708     result = v->data.i != 0;
00709     break;
00710   case VALUE_TYPE_STRING:
00711     result = v->data.s[0] != '\0';
00712     break;
00713   default:
00714     break;
00715   }
00716 
00717   state.str = _free(state.str);
00718   valueFree(v);
00719   return result;
00720 }
00721 
00722 char * parseExpressionString(Spec spec, const char *expr)
00723 {
00724   struct _parseState state;
00725   char *result = NULL;
00726   Value v;
00727 
00728   DEBUG(printf("parseExprString(?, '%s')\n", expr));
00729 
00730   /* Initialize the expression parser state. */
00731   state.p = state.str = xstrdup(expr);
00732   state.spec = spec;
00733   state.nextToken = 0;
00734   state.tokenValue = NULL;
00735   (void) rdToken(&state);
00736 
00737   /* Parse the expression. */
00738   v = doLogical(&state);
00739   if (!v) {
00740     state.str = _free(state.str);
00741     return NULL;
00742   }
00743 
00744   /* If the next token is not TOK_EOF, we have a syntax error. */
00745   if (state.nextToken != TOK_EOF) {
00746     rpmlog(RPMLOG_ERR, _("syntax error in expression\n"));
00747     state.str = _free(state.str);
00748     return NULL;
00749   }
00750 
00751   DEBUG(valueDump("parseExprString:", v, stdout));
00752 
00753   switch (v->type) {
00754   case VALUE_TYPE_INTEGER: {
00755     char buf[128];
00756     sprintf(buf, "%d", v->data.i);
00757     result = xstrdup(buf);
00758   } break;
00759   case VALUE_TYPE_STRING:
00760     result = xstrdup(v->data.s);
00761     break;
00762   default:
00763     break;
00764   }
00765 
00766   state.str = _free(state.str);
00767   valueFree(v);
00768   return result;
00769 }

Generated on Mon Nov 29 2010 05:18:41 for rpm by  doxygen 1.7.2