diff options
-rw-r--r-- | ChangeLog | 3 | ||||
-rw-r--r-- | grammar/grammar.y | 20 | ||||
-rw-r--r-- | grammar/lexer.l | 13 | ||||
-rw-r--r-- | grammar/rainerscript.c | 156 | ||||
-rw-r--r-- | grammar/rainerscript.h | 9 |
5 files changed, 174 insertions, 27 deletions
@@ -1,4 +1,7 @@ --------------------------------------------------------------------------- +Version 7.1.7 [devel] 2012-09-?? +- implemented RainerScript array-based string comparison operations +--------------------------------------------------------------------------- Version 7.1.6 [devel] 2012-09-28 - implemented RainerScript input() statement, including support for it in major input plugins diff --git a/grammar/grammar.y b/grammar/grammar.y index fcb9119b..89fd2289 100644 --- a/grammar/grammar.y +++ b/grammar/grammar.y @@ -51,12 +51,12 @@ extern int yyerror(char*); struct nvlst *nvlst; struct objlst *objlst; struct cnfexpr *expr; + struct cnfarray *arr; struct cnffunc *func; struct cnffparamlst *fparams; } %token <estr> NAME -%token <estr> VALUE %token <estr> FUNC %token <objType> BEGINOBJ %token ENDOBJ @@ -94,20 +94,14 @@ extern int yyerror(char*); %token CMP_STARTSWITH %token CMP_STARTSWITHI +%type <estr> value %type <nvlst> nv nvlst %type <obj> obj property constant %type <objlst> propconst -/*%type <actlst> actlst -%type <actlst> act -%type <actlst> block -*/ %type <expr> expr %type <stmt> stmt s_act actlst block script -/* -%type <rule> rule -%type <rule> scriptfilt -*/ %type <fparams> fparams +%type <arr> array arrayelt %left AND OR %left CMP_EQ CMP_NE CMP_LE CMP_GE CMP_LT CMP_GT CMP_CONTAINS CMP_CONTAINSI CMP_STARTSWITH CMP_STARTSWITHI @@ -147,7 +141,9 @@ property: BEGIN_PROPERTY nvlst ENDOBJ { $$ = cnfobjNew(CNFOBJ_PROPERTY, $2); } constant: BEGIN_CONSTANT nvlst ENDOBJ { $$ = cnfobjNew(CNFOBJ_CONSTANT, $2); } nvlst: { $$ = NULL; } | nvlst nv { $2->next = $1; $$ = $2; } -nv: NAME '=' VALUE { $$ = nvlstNew($1, $3); } +nv: NAME '=' value { $$ = nvlstNew($1, $3); } +value: STRING { $$ = $1; } + | array { dbgprintf("DDDD: value array\n"); } script: stmt { $$ = $1; } | script stmt { $$ = scriptAddStmt($1, $2); } stmt: actlst { $$ = $1; } @@ -196,8 +192,12 @@ expr: expr AND expr { $$ = cnfexprNew(AND, $1, $3); } | NUMBER { $$ = (struct cnfexpr*) cnfnumvalNew($1); } | STRING { $$ = (struct cnfexpr*) cnfstringvalNew($1); } | VAR { $$ = (struct cnfexpr*) cnfvarNew($1); } + | array { $$ = (struct cnfexpr*) $1; } fparams: expr { $$ = cnffparamlstNew($1, NULL); } | expr ',' fparams { $$ = cnffparamlstNew($1, $3); } +array: '[' arrayelt ']' { $$ = $2; } +arrayelt: STRING { $$ = cnfarrayNew($1); } + | arrayelt ',' STRING { $$ = cnfarrayAdd($1, $3); } %% /* diff --git a/grammar/lexer.l b/grammar/lexer.l index 0fc333e4..bc56d4e5 100644 --- a/grammar/lexer.l +++ b/grammar/lexer.l @@ -108,6 +108,8 @@ int fileno(FILE *stream); <EXPR>"+" | <EXPR>"&" | <EXPR>"-" | +<EXPR>"[" | +<EXPR>"]" | <EXPR>"(" | <EXPR>")" { return yytext[0]; } <EXPR>"==" { return CMP_EQ; } @@ -188,10 +190,17 @@ int fileno(FILE *stream); <INOBJ>")" { BEGIN INITIAL; return ENDOBJ; } <INOBJ>[a-z][a-z0-9_\.]* { yylval.estr = es_newStrFromCStr(yytext, yyleng); return NAME; } +<INOBJ>"," | +<INOBJ>"[" | +<INOBJ>"]" | <INOBJ>"=" { return(yytext[0]); } <INOBJ>\"([^"\\]|\\['"?\\abfnrtv]|\\[0-7]{1,3})*\" { - yylval.estr = es_newStrFromBuf(yytext+1, yyleng-2); - return VALUE; } + yytext[yyleng-1] = '\0'; + unescapeStr((uchar*)yytext+1, yyleng-2); + yylval.estr = es_newStrFromBuf(yytext+1, strlen(yytext)-1); + return STRING; } + /*yylval.estr = es_newStrFromBuf(yytext+1, yyleng-2); + return VALUE; }*/ "/*" { preCommentState = YY_START; BEGIN COMMENT; } <EXPR>"/*" { preCommentState = YY_START; BEGIN COMMENT; } <COMMENT>"*/" { BEGIN preCommentState; } diff --git a/grammar/rainerscript.c b/grammar/rainerscript.c index 4e484804..d77721b8 100644 --- a/grammar/rainerscript.c +++ b/grammar/rainerscript.c @@ -22,7 +22,6 @@ * A copy of the GPL can be found in the file "COPYING" in this distribution. * A copy of the LGPL can be found in the file "COPYING.LESSER" in this distribution. */ - #include "config.h" #include <stdio.h> #include <stdlib.h> @@ -1100,6 +1099,41 @@ evalVar(struct cnfvar *var, void *usrptr, struct var *ret) } +/* perform a string comparision operation against a while array. Semantic is + * that one one comparison is true, the whole construct is true. + * TODO: we can obviously optimize this process. One idea is to + * compile a regex, which should work faster than serial comparison. + */ +static int +evalStrArrayCmp(es_str_t *estr_l, struct cnfarray* ar, int cmpop) +{ + int i; + int r = 0; + for(i = 0 ; (r == 0) && (i < ar->nmemb) ; ++i) { + switch(cmpop) { + case CMP_EQ: + r = es_strcmp(estr_l, ar->arr[i]) == 0; + break; + case CMP_NE: + r = es_strcmp(estr_l, ar->arr[i]) != 0; + break; + case CMP_STARTSWITH: + r = es_strncmp(estr_l, ar->arr[i], es_strlen(ar->arr[i])) == 0; + break; + case CMP_STARTSWITHI: + r = es_strncasecmp(estr_l, ar->arr[i], es_strlen(ar->arr[i])) == 0; + break; + case CMP_CONTAINS: + r = es_strContains(estr_l, ar->arr[i]) != -1; + break; + case CMP_CONTAINSI: + r = es_strCaseContains(estr_l, ar->arr[i]) != -1; + break; + } + } + return r; +} + #define FREE_BOTH_RET \ if(r.datatype == 'S') es_deleteStr(r.d.estr); \ if(l.datatype == 'S') es_deleteStr(l.d.estr) @@ -1111,13 +1145,14 @@ evalVar(struct cnfvar *var, void *usrptr, struct var *ret) ret->d.n = var2Number(&l, &convok_l) x var2Number(&r, &convok_r); \ FREE_BOTH_RET +/* NOTE: array as right-hand argument MUST be handled by user */ #define PREP_TWO_STRINGS \ cnfexprEval(expr->l, &l, usrptr); \ estr_l = var2String(&l, &bMustFree2); \ if(expr->r->nodetype == 'S') { \ estr_r = ((struct cnfstringval*)expr->r)->estr;\ bMustFree = 0; \ - } else { \ + } else if(expr->r->nodetype != S_ARRAY) { \ cnfexprEval(expr->r, &r, usrptr); \ estr_r = var2String(&r, &bMustFree); \ } @@ -1161,6 +1196,8 @@ cnfexprEval(struct cnfexpr *expr, struct var *ret, void* usrptr) if(l.datatype == 'S') { if(expr->r->nodetype == 'S') { ret->d.n = !es_strcmp(l.d.estr, ((struct cnfstringval*)expr->r)->estr); /*CMP*/ + } else if(expr->r->nodetype == S_ARRAY) { + ret->d.n = evalStrArrayCmp(l.d.estr, (struct cnfarray*) expr->r, CMP_EQ); } else { cnfexprEval(expr->r, &r, usrptr); if(r.datatype == 'S') { @@ -1200,16 +1237,22 @@ cnfexprEval(struct cnfexpr *expr, struct var *ret, void* usrptr) cnfexprEval(expr->r, &r, usrptr); ret->datatype = 'N'; if(l.datatype == 'S') { - if(r.datatype == 'S') { - ret->d.n = es_strcmp(l.d.estr, r.d.estr); /*CMP*/ + if(expr->r->nodetype == 'S') { + ret->d.n = es_strcmp(l.d.estr, ((struct cnfstringval*)expr->r)->estr); /*CMP*/ + } else if(expr->r->nodetype == S_ARRAY) { + ret->d.n = evalStrArrayCmp(l.d.estr, (struct cnfarray*) expr->r, CMP_NE); } else { - n_l = var2Number(&l, &convok_l); - if(convok_l) { - ret->d.n = (n_l != r.d.n); /*CMP*/ + if(r.datatype == 'S') { + ret->d.n = es_strcmp(l.d.estr, r.d.estr); /*CMP*/ } else { - estr_r = var2String(&r, &bMustFree); - ret->d.n = es_strcmp(l.d.estr, estr_r); /*CMP*/ - if(bMustFree) es_deleteStr(estr_r); + n_l = var2Number(&l, &convok_l); + if(convok_l) { + ret->d.n = (n_l != r.d.n); /*CMP*/ + } else { + estr_r = var2String(&r, &bMustFree); + ret->d.n = es_strcmp(l.d.estr, estr_r); /*CMP*/ + if(bMustFree) es_deleteStr(estr_r); + } } } } else { @@ -1363,25 +1406,41 @@ cnfexprEval(struct cnfexpr *expr, struct var *ret, void* usrptr) case CMP_STARTSWITH: PREP_TWO_STRINGS; ret->datatype = 'N'; - ret->d.n = es_strncmp(estr_l, estr_r, estr_r->lenStr) == 0; + if(expr->r->nodetype == S_ARRAY) { + ret->d.n = evalStrArrayCmp(estr_l, (struct cnfarray*) expr->r, CMP_STARTSWITH); + } else { + ret->d.n = es_strncmp(estr_l, estr_r, estr_r->lenStr) == 0; + } FREE_TWO_STRINGS; break; case CMP_STARTSWITHI: PREP_TWO_STRINGS; ret->datatype = 'N'; - ret->d.n = es_strncasecmp(estr_l, estr_r, estr_r->lenStr) == 0; + if(expr->r->nodetype == S_ARRAY) { + ret->d.n = evalStrArrayCmp(estr_l, (struct cnfarray*) expr->r, CMP_STARTSWITHI); + } else { + ret->d.n = es_strncasecmp(estr_l, estr_r, estr_r->lenStr) == 0; + } FREE_TWO_STRINGS; break; case CMP_CONTAINS: PREP_TWO_STRINGS; ret->datatype = 'N'; - ret->d.n = es_strContains(estr_l, estr_r) != -1; + if(expr->r->nodetype == S_ARRAY) { + ret->d.n = evalStrArrayCmp(estr_l, (struct cnfarray*) expr->r, CMP_CONTAINS); + } else { + ret->d.n = es_strContains(estr_l, estr_r) != -1; + } FREE_TWO_STRINGS; break; case CMP_CONTAINSI: PREP_TWO_STRINGS; ret->datatype = 'N'; - ret->d.n = es_strCaseContains(estr_l, estr_r) != -1; + if(expr->r->nodetype == S_ARRAY) { + ret->d.n = evalStrArrayCmp(estr_l, (struct cnfarray*) expr->r, CMP_CONTAINSI); + } else { + ret->d.n = es_strCaseContains(estr_l, estr_r) != -1; + } FREE_TWO_STRINGS; break; case OR: @@ -1428,12 +1487,23 @@ cnfexprEval(struct cnfexpr *expr, struct var *ret, void* usrptr) ret->datatype = 'S'; ret->d.estr = es_strdup(((struct cnfstringval*)expr)->estr); break; + case S_ARRAY: + /* if an array is used with "normal" operations, it just evaluates + * to its first element. + */ + ret->datatype = 'S'; + ret->d.estr = es_strdup(((struct cnfarray*)expr)->arr[0]); + break; case 'V': evalVar((struct cnfvar*)expr, usrptr, ret); break; case '&': /* TODO: think about optimization, should be possible ;) */ PREP_TWO_STRINGS; + if(expr->r->nodetype == S_ARRAY) { + estr_r = ((struct cnfarray*)expr->r)->arr[0]; + bMustFree = 0; + } ret->datatype = 'S'; ret->d.estr = es_strdup(estr_l); es_addStr(&ret->d.estr, estr_r); @@ -1475,6 +1545,17 @@ cnfexprEval(struct cnfexpr *expr, struct var *ret, void* usrptr) //--------------------------------------------------------- static inline void +cnfarrayDestruct(struct cnfarray *ar) +{ + unsigned short i; + + for(i = 0 ; i < ar->nmemb ; ++i) { + es_deleteStr(ar->arr[i]); + } + free(ar->arr); +} + +static inline void cnffuncDestruct(struct cnffunc *func) { unsigned short i; @@ -1538,6 +1619,9 @@ cnfexprDestruct(struct cnfexpr *expr) case 'F': cnffuncDestruct((struct cnffunc*)expr); break; + case S_ARRAY: + cnfarrayDestruct((struct cnfarray*)expr); + break; default:break; } free(expr); @@ -1588,7 +1672,7 @@ cnfexprPrint(struct cnfexpr *expr, int indent) struct cnffunc *func; int i; - //dbgprintf("expr %p, indent %d, type '%c'\n", expr, indent, expr->nodetype); + dbgprintf("expr %p, indent %d, type '%c'\n", expr, indent, expr->nodetype); switch(expr->nodetype) { case CMP_EQ: cnfexprPrint(expr->l, indent+1); @@ -1672,6 +1756,15 @@ cnfexprPrint(struct cnfexpr *expr, int indent) cstrPrint("string '", ((struct cnfstringval*)expr)->estr); dbgprintf("'\n"); break; + case S_ARRAY: +dbgprintf("DDDD: %d members\n", ((struct cnfarray*)expr)->nmemb); + doIndent(indent); dbgprintf("ARRAY:\n"); + for(i = 0 ; i < ((struct cnfarray*)expr)->nmemb ; ++i) { + doIndent(indent+1); + cstrPrint("string '", ((struct cnfarray*)expr)->arr[i]); + dbgprintf("'\n"); + } + break; case 'N': doIndent(indent); dbgprintf("%lld\n", ((struct cnfnumval*)expr)->val); @@ -1813,6 +1906,39 @@ cnfstringvalNew(es_str_t *estr) return strval; } +/* creates array AND adds first element to it */ +struct cnfarray* +cnfarrayNew(es_str_t *val) +{ + struct cnfarray *ar; + if((ar = malloc(sizeof(struct cnfarray))) != NULL) { + ar->nodetype = S_ARRAY; + ar->nmemb = 1; + if((ar->arr = malloc(sizeof(es_str_t*))) == NULL) { + free(ar); + ar = NULL; + goto done; + } + ar->arr[0] = val; + } +done: return ar; +} + +/* creates array AND adds first element to it */ +struct cnfarray* +cnfarrayAdd(struct cnfarray *ar, es_str_t *val) +{ + es_str_t **newptr; + if((newptr = realloc(ar->arr, (ar->nmemb+1)*sizeof(es_str_t*))) == NULL) { + DBGPRINTF("cnfarrayAdd: realloc failed, item ignored, ar->arr=%p\n", ar->arr); + goto done; + } else { + ar->arr = newptr; + ar->arr[ar->nmemb] = val; + ar->nmemb++; + } +done: return ar; +} struct cnfvar* cnfvarNew(char *name) diff --git a/grammar/rainerscript.h b/grammar/rainerscript.h index 6bd1660a..974f2160 100644 --- a/grammar/rainerscript.h +++ b/grammar/rainerscript.h @@ -116,6 +116,7 @@ struct nvlst { * V - var * ... plus the S_* #define's below: */ +#define S_ARRAY 3000 #define S_STOP 4000 #define S_PRIFILT 4001 #define S_PROPFILT 4002 @@ -200,6 +201,12 @@ struct cnfvar { char *name; }; +struct cnfarray { + unsigned nodetype; + int nmemb; + es_str_t **arr; +}; + struct cnffparamlst { unsigned nodetype; /* P */ struct cnffparamlst *next; @@ -315,6 +322,8 @@ struct cnfstmt * cnfstmtNewSet(char *var, struct cnfexpr *expr); struct cnfstmt * cnfstmtNewUnset(char *var); void cnfstmtDestruct(struct cnfstmt *root); void cnfstmtOptimize(struct cnfstmt *root); +struct cnfarray* cnfarrayNew(es_str_t *val); +struct cnfarray* cnfarrayAdd(struct cnfarray *ar, es_str_t *val); char* getFIOPName(unsigned iFIOP); rsRetVal initRainerscript(void); void unescapeStr(uchar *s, int len); |