/* Copyright (C) 2006 Sergei Golubchik This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License version 2 as published by the Free Software Foundation This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ %{ #include #include #include "expr.h" int yylex (void); void yyerror (char const *); char *yy_err, *yy_str; Expr *yy_expr; int yy_nsum, yy_ndiff; static double e_num(Expr *me, Econtext *ctx) { return me->val.num; } static double e_var(Expr *me, Econtext *ctx) { char c, *s, *e; double v; if (ctx->pmatch[me->val.var].rm_so < 0) return 0; s=ctx->str+ctx->pmatch[me->val.var].rm_so; e=ctx->str+ctx->pmatch[me->val.var].rm_eo; c=*e; *e=0; v=strtod(s, 0); *e=c; return v; } static double e_if(Expr *me, Econtext *ctx) { return me->val.arg[0]->eval(me->val.arg[0], ctx) ? me->val.arg[1]->eval(me->val.arg[1], ctx) : me->val.arg[2]->eval(me->val.arg[2], ctx) ; } static double e_lt(Expr *me, Econtext *ctx) { return me->val.arg[0]->eval(me->val.arg[0], ctx) < me->val.arg[1]->eval(me->val.arg[1], ctx); } static double e_gt(Expr *me, Econtext *ctx) { return me->val.arg[0]->eval(me->val.arg[0], ctx) > me->val.arg[1]->eval(me->val.arg[1], ctx); } static double e_add(Expr *me, Econtext *ctx) { return me->val.arg[0]->eval(me->val.arg[0], ctx) + me->val.arg[1]->eval(me->val.arg[1], ctx); } static double e_sub(Expr *me, Econtext *ctx) { return me->val.arg[0]->eval(me->val.arg[0], ctx) - me->val.arg[1]->eval(me->val.arg[1], ctx); } static double e_mul(Expr *me, Econtext *ctx) { return me->val.arg[0]->eval(me->val.arg[0], ctx) * me->val.arg[1]->eval(me->val.arg[1], ctx); } static double e_div(Expr *me, Econtext *ctx) { return me->val.arg[0]->eval(me->val.arg[0], ctx) / me->val.arg[1]->eval(me->val.arg[1], ctx); } static double e_sum(Expr *me, Econtext *ctx) { int slot=(long)me->val.arg[1]; return ctx->sum_acc[slot]+=me->val.arg[0]->eval(me->val.arg[0], ctx); } static double e_diff(Expr *me, Econtext *ctx) { double e, v; int slot=(long)me->val.arg[1]; ctx->diff_new[slot]=e=me->val.arg[0]->eval(me->val.arg[0], ctx); v=e-ctx->diff_old[slot]; return v; } static Expr *new_num(double val) { Expr *e=malloc(sizeof(Expr)); e->eval=e_num; e->val.num=val; return e; } static Expr *new_var(int var) { Expr *e=malloc(sizeof(Expr)); e->eval=e_var; e->val.var=var; return e; } static Expr *new_op(Expr *e1, Expr *e2, double (*eval)(Expr *, Econtext *)) { Expr *e=calloc(1, sizeof(Expr)); e->eval=eval; e->val.arg[0]=e1; e->val.arg[1]=e2; return e; } %} %error-verbose %union { int var; double val; Expr *expr; } /* Bison declarations. */ %token NUM %token VAR %token SUM DIFF %type expr %left '?' ':' %left '<' '>' %left '-' '+' %left '*' '/' %left NEG %% /* The grammar follows. */ input: expr { yy_expr=$1; } expr: NUM { $$=new_num($1); } | VAR { if ($1 >= MAX_VAR) { yyerror("too large register number"); YYERROR; } $$=new_var($1); } | SUM '(' expr ')' { $$=new_op($3, (Expr *)(long)yy_nsum++, e_sum);} | DIFF '(' expr ')' { $$=new_op($3, (Expr *)(long)yy_ndiff++, e_diff);} | expr '?' expr ':' expr { $$=new_op($1, $3, e_if); $$->val.arg[2]=$5; } | expr '<' expr { $$=new_op($1, $3, e_lt); } | expr '>' expr { $$=new_op($1, $3, e_gt); } | expr '+' expr { $$=new_op($1, $3, e_add); } | expr '-' expr { $$=new_op($1, $3, e_sub); } | expr '*' expr { $$=new_op($1, $3, e_mul); } | expr '/' expr { $$=new_op($1, $3, e_div); } | '-' expr %prec NEG { $$=new_op(new_num(0) ,$2, e_sub); } | '+' expr %prec NEG { $$=$2; } | '(' expr ')' { $$=$2; } ; %% #include #include #define prefix(S,CONST) !strncasecmp((S),CONST,sizeof(CONST)-1) int yylex(void) { while (isspace(*yy_str)) yy_str++; if (isdigit(*yy_str)) { yylval.val=strtod(yy_str, &yy_str); return NUM; } if (*yy_str == '\\') { if (!isdigit(*++yy_str)) return '\\'; yylval.var=strtol(yy_str, &yy_str, 10); return VAR; } if (prefix(yy_str, "sum")) { yy_str +=3; return SUM; } if (prefix(yy_str, "diff")) { yy_str +=4; return DIFF; } return *yy_str++; } /* Called by yyparse on error. */ void yyerror (char const *s) { strcpy(yy_err, s); }