📄 mfcalc.y
字号:
%{/* mfcalc.y, mfcalc.cpp, Copyright (c) 2004, 2005 R.Lackner parse string and simple math: based on the bison 'mfcalc' example This file is part of RLPlot. RLPlot is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. RLPlot 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 RLPlot; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA*/#include <math.h>#include <stdlib.h>#include <ctype.h>#include <string.h>#include <stdio.h>#include "rlplot.h"class symrec {public: int type, row, col; unsigned int h_name; char *name, *text; double (*fnctptr)(...); symrec *next; double var; symrec(unsigned int h_n, int typ, symrec *nxt); ~symrec(); double GetValue(); void GetValue(void *res); double SetValue(double v); void SetValue(void* dest, void* src); void SetName(char *nam); void InitSS();private: bool isSSval;};// syntactical informationstruct syntax_info { int last_tok; //resolve ambiguous ':' double clval; //current value for where - clause int cl1, cl2; //limits of clause formula in buffer struct syntax_info *next; };static syntax_info *syntax_level = 0L;typedef struct{ double val; int type; symrec *tptr; double *a_data; char *text; int a_count;}YYSTYPE;static symrec *putsym (unsigned int h_name, int sym_type);static symrec *getsym (unsigned int h_name, char *sym_name = 0L);static int push(YYSTYPE *res, YYSTYPE *val);static void store_res(YYSTYPE *res);static char *PushString(char *text);static char *add_strings(char *st1, char *st2);static char *string_value(YYSTYPE *exp);static double eval(YYSTYPE *sr, YYSTYPE *dst);static int range_array(YYSTYPE * res, char *range);static void exec_clause(YYSTYPE *res);static YYSTYPE *proc_clause(YYSTYPE *res);static void yyerror(char *s);static int yylex(void);static double nop() {return 0.0;};static char res_txt[1000];static anyResult line_res = {ET_UNKNOWN, 0.0, res_txt};static DataObj *curr_data;static char *last_error = 0L; //error textstatic char *last_err_desc = 0L; //short error descriptionstatic char *buffer = 0L; //the current command bufferstatic int buff_pos = 0;static bool bRecent = false; //rearrange functionsstatic int parse_level = 0; //count reentrances into parser#define MAX_PARSE 20 //maximum number of reentances %}%token <val> NUM STR ARR BLOCK PI E CLVAL PSEP IF ELSE%token <tptr> VAR FNCT AFNCT SFNCT FUNC2 TXT%type <val> exp str_exp arr%right '='%left ',' /* list separator */%left CLAUSE /* clause (where) operator */%left SER%right COLR COLC /* range sep., conditional sep. */%right '?' /* conditional assignment */%left AND OR%left EQ NE GT GE LT LE%left '-' '+'%left '*' '/'%left NEG /* negation-unary minus */%left INC DEC /* increment, decrement */%left PINC PDEC /* pre- increment, decrement */%right '^' /* exponentiation *//* Grammar follows */%%input: /* empty string */ | input line;line: '\n' | ';' | ',' | exp '\n' {store_res(&yyvsp[-1]); return 0;} | exp ';' {store_res(&yyvsp[-1]); return 0;} | exp ',' {store_res(&yyvsp[-1]); return 0;} | str_exp '\n' {store_res(&yyvsp[-1]); return 0;} | str_exp ';' {store_res(&yyvsp[-1]); return 0;} | error '\n' {yyerrok;};str_exp: STR {;} |str_exp '+' exp {yyval.text=add_strings(yyvsp[-2].text, string_value(&yyvsp[0]));} |exp '+' str_exp {yyval.text=add_strings(string_value(&yyvsp[-2]), yyvsp[0].text);} |str_exp '+' str_exp {yyval.text=add_strings(yyvsp[-2].text, yyvsp[0].text);};range: str_exp {;} |VAR COLR VAR {if(yyval.text =(char*)malloc(strlen($1->name) +strlen($3->name) + 2)) sprintf(yyval.text, "%s:%s", $1->name, $3->name);};arr: ARR |exp {if(!yyval.a_data) {yyval.a_data = (double*)malloc(sizeof(double)); yyval.a_count = 1; yyval.a_data[0] = yyval.val;}} |arr ',' arr {push(&yyval, &yyvsp[0]);} |arr CLAUSE exp {exec_clause(&yyval);} |range {range_array(&yyval, yyvsp[0].text);} |NUM SER NUM {if($1 < $3 && (yyval.a_data = (double*)malloc((int)($3-$1+2)*sizeof(double)))) for(yyval.a_count=0; $1<=$3; yyval.a_data[yyval.a_count++] = $1, $1 += 1.0 );};exp: NUM {$$ = $1; yyval.type = NUM;} |TXT {$$ = 0.0;} |CLVAL {$$ = syntax_level ? syntax_level->clval : 0.0; } |PI {$$ = _PI; yyval.type = NUM;} |E {$$ = 2.71828182845905; yyval.type = NUM;} |VAR {$1->GetValue(&yyval);} |BLOCK {eval(&yyvsp[0], &yyval);} |VAR '=' exp {$1->SetValue(&yyval, &yyvsp[0]);} |VAR '=' str_exp {$1->SetValue(&yyval, &yyvsp[0]);} |FNCT '(' exp ')' {$$ = (($1->fnctptr)($3));} |AFNCT '(' arr ')' {$$ = (($1->fnctptr)(proc_clause(&yyvsp[-1])));} |AFNCT '(' exp PSEP exp ')' {push(&yyvsp[-2], &yyvsp[0]); $$ = (($1->fnctptr)(&yyvsp[-2]));} |AFNCT '(' exp PSEP exp PSEP exp ')' {push(&yyvsp[-4], &yyvsp[-2]); push(&yyvsp[-4], &yyvsp[0]); $$ = (($1->fnctptr)(&yyvsp[-4]));} |SFNCT '(' str_exp ')' {$$ = (($1->fnctptr)(&yyvsp[-1], &yyval));} |SFNCT '(' exp ')' {yyvsp[-1].text = string_value(&yyvsp[-1]); $$ = (($1->fnctptr)(&yyvsp[-1], &yyval));} |FUNC2 '(' arr PSEP arr ')' {$$ = ((*$1->fnctptr)(proc_clause(&yyvsp[-3]), proc_clause(&yyvsp[-1]), 0L));} |FUNC2 '(' arr PSEP arr PSEP range')' {$$ = ((*$1->fnctptr)(proc_clause(&yyvsp[-5]), proc_clause(&yyvsp[-3]), yyvsp[-1].text));} |IF '(' exp ')' BLOCK {$$ = $3 != 0 ? eval(&yyvsp[0], &yyval) : 0.0;} |IF '(' exp ')' BLOCK ELSE BLOCK {$$ = $3 != 0 ? eval(&yyvsp[-2], &yyval) : eval(&yyvsp[0], &yyval);} |exp AND exp {$$ = (($1 != 0) && ($3 != 0))? 1 : 0;} |exp OR exp {$$ = (($1 != 0) || ($3 != 0))? 1 : 0;} |exp EQ exp {$$ = ($1 == $3) ? 1 : 0;} |exp NE exp {$$ = ($1 != $3) ? 1 : 0;} |exp GT exp {$$ = ($1 > $3) ? 1 : 0;} |exp GE exp {$$ = ($1 >= $3) ? 1 : 0;} |exp LT exp {$$ = ($1 < $3) ? 1 : 0;} |exp LE exp {$$ = ($1 <= $3) ? 1 : 0;} |exp '+' exp {$$ = $1 + $3;} |exp '-' exp {$$ = $1 - $3;} |exp '*' exp {$$ = $1 * $3;} |exp '/' exp {if($3 != 0.0) $$ = $1 / $3; else $$ = (getsym(HashValue((unsigned char*)"zdiv")))->GetValue(); } |'-' exp %prec NEG {$$ = -$2;} |VAR INC {$$=$1->GetValue(); $1->SetValue($$+1.0); $$ -= 1.0; yyval.type = NUM;} |VAR DEC {$$=$1->GetValue(); $1->SetValue($$-1.0); $$ += 1.0; yyval.type = NUM;} |INC VAR %prec PINC {$$=$2->GetValue(); $2->SetValue($$+1.0); yyval.type = NUM;} |DEC VAR %prec PDEC {$$=$2->GetValue(); $2->SetValue($$-1.0); yyval.type = NUM;} |exp '^' exp {$$ = pow($1, $3);} |'(' arr ')' {memcpy(&yyval, &yyvsp[-1], sizeof(YYSTYPE)); yyvsp[-1].a_data = 0L; yyvsp[-1].a_count = 0;} |exp '?' exp COLC exp {memcpy(&yyval, $1 != 0.0 ? &yyvsp[-2] : &yyvsp[0], sizeof(YYSTYPE))} |exp '?' STR COLC STR {memcpy(&yyval, $1 != 0.0 ? &yyvsp[-2] : &yyvsp[0], sizeof(YYSTYPE))} |exp '?' STR COLC exp {memcpy(&yyval, $1 != 0.0 ? &yyvsp[-2] : &yyvsp[0], sizeof(YYSTYPE))} |exp '?' exp COLC STR {memcpy(&yyval, $1 != 0.0 ? &yyvsp[-2] : &yyvsp[0], sizeof(YYSTYPE))};%%// The symrec classsymrec::symrec(unsigned int h_n, int typ, symrec *nxt) { h_name = h_n; type = typ; next = nxt; row = col = -1; name = text = 0L; var = 0.0; isSSval = false; fnctptr = (double (*)(...))nop;}symrec::~symrec(){ if(name) free(name); name = 0L; if(text) free(text); text = 0L;}doublesymrec::GetValue(){ anyResult ares; if(isSSval) { if(row < 0 && col < 0) InitSS(); //GetResult( , , ,true) inhibits reentrance into parser ! if(curr_data->GetResult(&ares, row, col, parse_level > MAX_PARSE)){ return var = ares.value; } isSSval = false; row = col = -1; } return var;}voidsymrec::GetValue(void *re){ anyResult ares; YYSTYPE *res = (YYSTYPE*)re; if(isSSval) { if(row < 0 && col < 0) InitSS(); res->a_data = 0L; res->a_count = 0; //GetResult( , , ,true) inhibits reentrance into parser ! if(curr_data->GetResult(&ares, row, col, parse_level > MAX_PARSE)){ if(text) free(text); text = 0L; if(ares.type == ET_VALUE) { res->type = VAR; res->val = ares.value; res->text = 0L; } else if(ares.type == ET_TEXT && ares.text) { res->type = STR; res->val = 0.0; text = strdup(ares.text); res->text = text; } else { res->type = NUM; res->val = var; res->text = 0L; } var = res->val; return; } isSSval = false; row = col = -1; } if(text && text[0]) { res->text = strdup(text); res->val = var; res->type = STR; } else { res->type = NUM; res->val = var; res->text = 0L; } res->a_data = 0L; res->a_count = 0L;}double symrec::SetValue(double v){ if(isSSval) { if(row < 0 && col < 0) InitSS(); if(curr_data->SetValue(row, col, v)){ if(curr_data) curr_data->Command(CMD_UPDATE, 0L, 0L); return var = v; } isSSval = false; row = col = -1; } return var = v;}void symrec::SetValue(void* d, void* s){ YYSTYPE *dest = (YYSTYPE*)d; YYSTYPE *src = (YYSTYPE*)s; if(isSSval && curr_data) { if(row < 0 && col < 0) InitSS(); if(last_err_desc) curr_data->SetText(row, col, last_err_desc); else if(src->type == STR) curr_data->SetText(row, col, src->text); else if(src->type == ARR || (src->a_data)) curr_data->SetText(row, col, "#ARRAY"); else if(src->type == VAR && src->tptr->type == TXT) curr_data->SetText(row, col, src->tptr->text); else curr_data->SetValue(row, col, src->val); curr_data->Command(CMD_UPDATE, 0L, 0L); } var = src->val; if(text) free(text); text = 0L; if(src->text && src->text[0]) text = strdup(src->text); GetValue(d); return;}voidsymrec::SetName(char *nam){ if(name || !nam || !nam[0]) return; name = strdup(nam); if((name && curr_data) && (isalpha(name[0]) || name[0] == '$') && isdigit(name[strlen(name)-1])) isSSval=true;}voidsymrec::InitSS(){ AccRange *ar; if(row<0 && col<0 &&(ar = new AccRange(name))) { ar->GetFirst(&col, &row); delete(ar); }}static void yyerror(char *s){ //called by yyparse on error if(curr_data) curr_data->Command(CMD_ERROR, last_error = s, 0L); else printf("%s\n", s);}static void yyargserr(char *s){ //call from function on argument type/number mismatch yyerror(s); last_err_desc = "#ARGS";}static void store_res(YYSTYPE *res){ if(last_err_desc) { line_res.type = ET_TEXT; line_res.value = 0.0; strcpy(res_txt, last_err_desc); } else if(res->type == STR) { line_res.type = ET_TEXT; line_res.value = 0.0; if(res->text) strcpy(res_txt, res->text); } else if((res->type == ARR || (res->a_data)) && res->a_count == 1) { line_res.type = ET_VALUE; line_res.value = res->a_data[0]; } else if(res->type == ARR && !(res->a_data) && !(res->a_count)) { line_res.type = ET_VALUE; line_res.value = res->val; } else if(res->type == ARR || (res->a_data)) { line_res.type = ET_TEXT; line_res.value = 0.0; strcpy(res_txt, "#ARRAY"); } else if(res->tptr && res->tptr->type == TXT) { line_res.type = ET_TEXT; line_res.value = 0.0; if(res->tptr->text) strcpy(res_txt, res->tptr->text); } else { line_res.type = ET_VALUE; line_res.value = res->val; }}static char *add_strings(char *st1, char *st2){ char *newstr, *ret; if(st1 && st2) { if(newstr = (char*)malloc(strlen(st1) +strlen(st2) +4)) { sprintf(newstr, "%s%s", st1, st2); ret = PushString(newstr); free(newstr); return ret; } else return 0L; } if(st1) return st1; if(st2) return st2; return 0L;}static char *string_value(YYSTYPE *exp){ char *st1, tmp[50]; if(exp->type == STR){ st1 = exp->text; } else if(exp->tptr && exp->tptr->type == TXT) { st1 = exp->tptr->text; } else { sprintf(tmp,"%g", exp->val); st1 = tmp; } return PushString(st1);}// store syntactical informationstatic void push_syntax(){ syntax_info *next; if(!(next = (syntax_info*)calloc(1, sizeof(syntax_info)))) return; if(syntax_level)memcpy(next, syntax_level, sizeof(syntax_info)); next->next=syntax_level; syntax_level = next;}static void pop_syntax(){ syntax_info *si; if(si = syntax_level) { syntax_level = si->next; free(si); }}static double eval(YYSTYPE *sr, YYSTYPE *dst) { anyResult *ar; if(!sr || !sr->text) return 0.0; parse_level++; ar = do_formula(0L, sr->text); yylval.a_data = 0L; yylval.a_count = 0; switch(ar->type) { case ET_VALUE: dst->type = NUM; dst->val = ar->value; dst->text = 0L; break; case ET_TEXT: dst->type = STR; dst->val = 0.0; dst->text = PushString(ar->text); break; default: dst->type = NUM; dst->val = 0.0; dst->text = 0L; break; } parse_level--; return dst->val;}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -