Warning, /education/labplot/src/backend/gsl/parser.y is written in an unsupported language. File is not indexed.
0001 /*************************************************************************** 0002 File : parser.y 0003 Project : LabPlot 0004 Description : Parser for mathematical expressions 0005 -------------------------------------------------------------------- 0006 Copyright : (C) 2014 Alexander Semke (alexander.semke@web.de) 0007 Copyright : (C) 2014-2020 Stefan Gerlach (stefan.gerlach@uni.kn) 0008 0009 ***************************************************************************/ 0010 0011 /*************************************************************************** 0012 * * 0013 * This program is free software; you can redistribute it and/or modify * 0014 * it under the terms of the GNU General Public License as published by * 0015 * the Free Software Foundation; either version 2 of the License, or * 0016 * (at your option) any later version. * 0017 * * 0018 * This program is distributed in the hope that it will be useful, * 0019 * but WITHOUT ANY WARRANTY; without even the implied warranty of * 0020 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * 0021 * GNU General Public License for more details. * 0022 * * 0023 * You should have received a copy of the GNU General Public License * 0024 * along with this program; if not, write to the Free Software * 0025 * Foundation, Inc., 51 Franklin Street, Fifth Floor, * 0026 * Boston, MA 02110-1301 USA * 0027 * * 0028 ***************************************************************************/ 0029 0030 %{ 0031 #include <string.h> 0032 #include <ctype.h> 0033 #include <stdlib.h> 0034 #include <locale.h> 0035 #ifdef HAVE_XLOCALE 0036 #include <xlocale.h> 0037 #endif 0038 #include "parser.h" 0039 #include "constants.h" 0040 #include "functions.h" 0041 #if defined(_WIN32) 0042 #define locale_t _locale_t 0043 #define strtod_l _strtod_l 0044 #define freelocale _free_locale 0045 #endif 0046 0047 #ifdef PDEBUG 0048 #include <stdio.h> 0049 #define pdebug(...) fprintf(stderr, __VA_ARGS__) 0050 #else 0051 #define pdebug(...) {} 0052 #endif 0053 0054 #define YYERROR_VERBOSE 1 0055 0056 /* params passed to yylex (and yyerror) */ 0057 typedef struct param { 0058 size_t pos; /* current position in string */ 0059 char* string; /* the string to parse */ 0060 const char* locale; /* name of locale to convert numbers */ 0061 } param; 0062 0063 int yyerror(param *p, const char *err); 0064 int yylex(param *p); 0065 0066 double res; 0067 %} 0068 0069 %lex-param {param *p} 0070 %parse-param {param *p} 0071 0072 %union { 0073 double dval; /* For returning numbers */ 0074 symbol *tptr; /* For returning symbol-table pointers */ 0075 } 0076 0077 %token <dval> NUM /* Simple double precision number */ 0078 %token <tptr> VAR FNCT /* VARiable and FuNCTion */ 0079 %type <dval> expr 0080 0081 %right '=' 0082 %left '-' '+' 0083 %left '*' '/' '%' 0084 %left NEG /* Negation--unary minus */ 0085 %right '^' '!' 0086 0087 %% 0088 input: /* empty */ 0089 | input line 0090 ; 0091 0092 line: '\n' 0093 | expr '\n' { res=$1; } 0094 | error '\n' { yyerrok; } 0095 ; 0096 0097 expr: NUM { $$ = $1; } 0098 | VAR { $$ = $1->value.var; } 0099 | VAR '=' expr { $$ = $3; $1->value.var = $3; } 0100 | FNCT '(' ')' { $$ = (*($1->value.fnctptr))(); } 0101 | FNCT '(' expr ')' { $$ = (*((func_t1)($1->value.fnctptr)))($3); } 0102 | FNCT '(' expr ',' expr ')' { $$ = (*((func_t2)($1->value.fnctptr)))($3,$5); } 0103 | FNCT '(' expr ',' expr ','expr ')' { $$ = (*((func_t3)($1->value.fnctptr)))($3,$5,$7); } 0104 | FNCT '(' expr ',' expr ',' expr ','expr ')' { $$ = (*((func_t4)($1->value.fnctptr)))($3,$5,$7,$9); } 0105 | FNCT '(' expr ';' expr ')' { $$ = (*((func_t2)($1->value.fnctptr)))($3,$5); } 0106 | FNCT '(' expr ';' expr ';'expr ')' { $$ = (*((func_t3)($1->value.fnctptr)))($3,$5,$7); } 0107 | FNCT '(' expr ';' expr ';' expr ';'expr ')' { $$ = (*((func_t4)($1->value.fnctptr)))($3,$5,$7,$9); } 0108 | expr '+' expr { $$ = $1 + $3; } 0109 | expr '-' expr { $$ = $1 - $3; } 0110 | expr '*' expr { $$ = $1 * $3; } 0111 | expr '/' expr { $$ = $1 / $3; } 0112 | expr '%' expr { $$ = (int)($1) % (int)($3); } 0113 | '-' expr %prec NEG{ $$ = -$2; } 0114 | expr '^' expr { $$ = pow($1, $3); } 0115 | expr '*' '*' expr { $$ = pow($1, $4); } 0116 | '(' expr ')' { $$ = $2; } 0117 | '|' expr '|' { $$ = fabs($2); } 0118 | expr '!' { $$ = gsl_sf_fact((unsigned int)$1); } 0119 /* logical operators (!,&&,||) are not supported */ 0120 ; 0121 0122 %% 0123 0124 /* global symbol table (as linked list) */ 0125 symbol *symbol_table = 0; 0126 0127 int parse_errors(void) { 0128 return yynerrs; 0129 } 0130 0131 int yyerror(param *p, const char *s) { 0132 /* remove trailing newline */ 0133 p->string[strcspn(p->string, "\n")] = 0; 0134 printf("PARSER ERROR: %s @ position %d of string '%s'\n", s, (int)(p->pos), p->string); 0135 0136 return 0; 0137 } 0138 0139 /* save symbol in symbol table (at start of linked list) */ 0140 symbol* put_symbol(const char *symbol_name, int symbol_type) { 0141 /* pdebug("PARSER: put_symbol(): symbol_name = '%s'\n", symbol_name); */ 0142 0143 symbol *ptr = (symbol *)malloc(sizeof(symbol)); 0144 ptr->name = (char *)malloc(strlen(symbol_name) + 1); 0145 strcpy(ptr->name, symbol_name); 0146 ptr->type = symbol_type; 0147 ptr->value.var = 0; /* set value to 0 even if fctn */ 0148 ptr->next = (symbol *)symbol_table; 0149 symbol_table = ptr; 0150 0151 /* pdebug("PARSER: put_symbol() DONE\n"); */ 0152 return ptr; 0153 } 0154 0155 /* remove symbol of name symbol_name from symbol table 0156 removes only variables of value 0 0157 returns 0 on success */ 0158 int remove_symbol(const char *symbol_name) { 0159 symbol* ptr = symbol_table; 0160 0161 /* check if head contains symbol */ 0162 if (ptr && (strcmp(ptr->name, symbol_name) == 0)) { 0163 if (ptr->type == VAR && ptr->value.var == 0) { 0164 pdebug("PARSER: REMOVING symbol '%s'\n", symbol_name); 0165 symbol_table = ptr->next; 0166 free(ptr->name); 0167 free(ptr); 0168 } 0169 return 0; 0170 } 0171 0172 /* search for symbol to be deleted */ 0173 symbol* prev; 0174 while (ptr && (strcmp(ptr->name, symbol_name) != 0)) { 0175 prev = ptr; 0176 ptr = ptr->next; 0177 } 0178 0179 /* symbol not found or is not a variable or is not 0 */ 0180 if (!ptr || ptr->type != VAR || ptr->value.var != 0) 0181 return 1; 0182 0183 /* remove symbol */ 0184 pdebug("PARSER: REMOVING symbol '%s'\n", symbol_name); 0185 prev->next = ptr->next; 0186 free(ptr->name); 0187 free(ptr); 0188 0189 return 0; 0190 } 0191 0192 /* get symbol from symbol table 0193 returns 0 if symbol not found */ 0194 symbol* get_symbol(const char *symbol_name) { 0195 pdebug("PARSER: get_symbol(): symbol_name = '%s'\n", symbol_name); 0196 0197 symbol *ptr; 0198 for (ptr = symbol_table; ptr != 0; ptr = (symbol *)ptr->next) { 0199 /* pdebug("%s ", ptr->name); */ 0200 if (strcmp(ptr->name, symbol_name) == 0) { 0201 pdebug("PARSER: SYMBOL FOUND\n"); 0202 return ptr; 0203 } 0204 } 0205 0206 pdebug("PARSER: SYMBOL NOT FOUND\n"); 0207 return 0; 0208 } 0209 0210 /* initialize symbol table with all known functions and constants */ 0211 void init_table(void) { 0212 pdebug("PARSER: init_table()\n"); 0213 0214 symbol *ptr = 0; 0215 int i; 0216 /* add functions */ 0217 for (i = 0; _functions[i].name != 0; i++) { 0218 ptr = put_symbol(_functions[i].name, FNCT); 0219 ptr->value.fnctptr = _functions[i].fnct; 0220 } 0221 /* add constants */ 0222 for (i = 0; _constants[i].name != 0; i++) { 0223 ptr = put_symbol(_constants[i].name, VAR); 0224 ptr->value.var = _constants[i].value; 0225 } 0226 0227 pdebug("PARSER: init_table() DONE. sym_table = %p\n", ptr); 0228 } 0229 0230 void delete_table(void) { 0231 pdebug("PARSER: delete_table()\n"); 0232 while(symbol_table) { 0233 symbol *tmp = symbol_table; 0234 symbol_table = symbol_table->next; 0235 free(tmp->name); 0236 free(tmp); 0237 } 0238 } 0239 0240 /* add new symbol with value or just set value if symbol is a variable */ 0241 symbol* assign_symbol(const char* symbol_name, double value) { 0242 pdebug("PARSER: assign_symbol() : symbol_name = '%s', value = %g\n", symbol_name, value); 0243 0244 /* be sure that the symbol table has been initialized */ 0245 if (!symbol_table) 0246 init_table(); 0247 0248 symbol* ptr = get_symbol(symbol_name); 0249 if (!ptr) { 0250 pdebug("PARSER: calling putsymbol(): symbol_name = '%s'\n", symbol_name); 0251 ptr = put_symbol(symbol_name, VAR); 0252 } else { 0253 pdebug("PARSER: Symbol already assigned\n"); 0254 } 0255 0256 /* do not assign value if symbol already exits as function */ 0257 if (ptr->type == VAR) 0258 ptr->value.var = value; 0259 0260 return ptr; 0261 }; 0262 0263 static int getcharstr(param *p) { 0264 pdebug(" getcharstr() pos = %d\n", (int)(p->pos)); 0265 0266 if (p->string[p->pos] == '\0') 0267 return EOF; 0268 /* pdebug("PARSER: char is %c\n", p->string[p->pos]); */ 0269 return p->string[(p->pos)++]; 0270 } 0271 0272 static void ungetcstr(size_t *pos) { 0273 /* pdebug("PARSER: ungetcstr()\n"); */ 0274 if (*pos > 0) 0275 (*pos)--; 0276 } 0277 0278 double parse(const char* string, const char* locale) { 0279 pdebug("\nPARSER: parse('%s') len = %d\n********************************\n", string, (int)strlen(string)); 0280 0281 /* be sure that the symbol table has been initialized */ 0282 if (!symbol_table) 0283 init_table(); 0284 0285 param p; 0286 p.pos = 0; 0287 p.locale = locale; 0288 0289 /* leave space to terminate string by "\n\0" */ 0290 const size_t slen = strlen(string) + 2; 0291 p.string = (char *) malloc(slen * sizeof(char)); 0292 if (p.string == NULL) { 0293 printf("PARSER ERROR: Out of memory for parsing string\n"); 0294 return 0.; 0295 } 0296 0297 strcpy(p.string, string); 0298 p.string[strlen(string)] = '\n'; // end for parsing 0299 p.string[strlen(string)+1] = '\0'; // end of string 0300 /* pdebug("PARSER: Call yyparse() for \"%s\" (len = %d)\n", p.string, (int)strlen(p.string)); */ 0301 0302 /* parameter for yylex */ 0303 res = NAN; /* default value */ 0304 yynerrs = 0; /* reset error count */ 0305 yyparse(&p); 0306 0307 pdebug("PARSER: parse() DONE (result = %g, errors = %d)\n*******************************\n", res, parse_errors()); 0308 free(p.string); 0309 p.string = 0; 0310 0311 return res; 0312 } 0313 0314 double parse_with_vars(const char *str, const parser_var *vars, int nvars, const char* locale) { 0315 pdebug("\nPARSER: parse_with_var(\"%s\") len = %d\n", str, (int)strlen(str)); 0316 0317 int i; 0318 for(i = 0; i < nvars; i++) { /*assign vars */ 0319 pdebug("PARSER: Assign '%s' the value %g\n", vars[i].name, vars[i].value); 0320 assign_symbol(vars[i].name, vars[i].value); 0321 } 0322 0323 return parse(str, locale); 0324 } 0325 0326 int yylex(param *p) { 0327 pdebug("PARSER: YYLEX()"); 0328 0329 /* get char and skip white space */ 0330 int c; 0331 while ((c = getcharstr(p)) == ' ' || c == '\t'); 0332 0333 /* finish if reached EOF */ 0334 if (c == EOF) { 0335 pdebug("PARSER: FINISHED\n"); 0336 return 0; 0337 } 0338 /* check for non-ASCII chars */ 0339 if (!isascii(c)) { 0340 pdebug(" non-ASCII character found. Giving up\n"); 0341 yynerrs++; 0342 return 0; 0343 } 0344 if (c == '\n') { 0345 pdebug("PARSER: Reached EOL\n"); 0346 return c; 0347 } 0348 0349 pdebug("PARSER: PROCESSING character '%c'\n", c); 0350 0351 /* process numbers */ 0352 if (isdigit(c)) { 0353 pdebug("PARSER: Found NUMBER (starts with digit)\n"); 0354 ungetcstr(&(p->pos)); 0355 char *s = &(p->string[p->pos]); 0356 0357 /* convert to double */ 0358 char *remain; 0359 #if defined(_WIN32) 0360 locale_t locale = _create_locale(LC_NUMERIC, p->locale); 0361 if (locale == NULL) { 0362 pdebug("PARSER ERROR in newlocale(%s): %s. Trying system locale.\n", p->locale, strerror(errno)); 0363 locale = _create_locale(LC_NUMERIC, ""); 0364 } 0365 #else 0366 locale_t locale = newlocale(LC_NUMERIC_MASK, p->locale, (locale_t)0); 0367 if (locale == (locale_t)0) { 0368 pdebug("PARSER ERROR in newlocale(%s): %s. Trying system locale.\n", p->locale, strerror(errno)); 0369 locale = newlocale(LC_NUMERIC_MASK, "", (locale_t)0); 0370 pdebug("PARSER: Reading: '%s' with system locale\n", s); 0371 } else { 0372 pdebug("PARSER: Reading: '%s' with locale %s\n", s, p->locale); 0373 } 0374 #endif 0375 double result; 0376 if (locale != NULL) { 0377 result = strtod_l(s, &remain, locale); 0378 freelocale(locale); 0379 } else // use C locale 0380 result = strtod(s, &remain); 0381 0382 pdebug("PARSER: Remain: '%s'\n", remain); 0383 0384 /* check conversion */ 0385 if(strlen(s) == strlen(remain)) 0386 return 0; 0387 0388 pdebug("PARSER: Result = %g\n", result); 0389 yylval.dval = result; 0390 0391 p->pos += strlen(s) - strlen(remain); 0392 0393 return NUM; 0394 } 0395 0396 /* process symbol */ 0397 if (isalpha (c) || c == '.') { 0398 pdebug("PARSER: Found SYMBOL (starts with alpha)\n"); 0399 static char *symbol_name = 0; 0400 static int length = 0; 0401 int i = 0; 0402 0403 /* Initially make the buffer long enough for a 10-character symbol name */ 0404 if (length == 0) { 0405 length = 10; 0406 symbol_name = (char *) malloc(length + 1); 0407 } 0408 0409 do { 0410 pdebug("PARSER: Reading symbol .. "); 0411 /* If buffer is full, make it bigger */ 0412 if (i == length) { 0413 length *= 2; 0414 symbol_name = (char *) realloc(symbol_name, length + 1); 0415 } 0416 symbol_name[i++] = c; 0417 c = getcharstr(p); 0418 pdebug("PARSER: got '%c'\n", c); 0419 } 0420 while (c != EOF && (isalnum(c) || c == '_' || c == '.')); 0421 pdebug("PARSER: Reading SYMBOL DONE\n"); 0422 0423 if (c != EOF) 0424 ungetcstr(&(p->pos)); 0425 symbol_name[i] = '\0'; 0426 0427 symbol *s = get_symbol(symbol_name); 0428 if(s == 0) { /* symbol unknown */ 0429 pdebug("PARSER ERROR: Symbol '%s' UNKNOWN\n", symbol_name); 0430 yynerrs++; 0431 return 0; 0432 /* old behavior: add symbol */ 0433 /* s = put_symbol(symbol_name, VAR); */ 0434 } 0435 0436 yylval.tptr = s; 0437 return s->type; 0438 } 0439 0440 /* else: single operator */ 0441 pdebug("PARSER: Found single operator '%c'\n", c); 0442 return c; 0443 }