"Fossies" - the Fresh Open Source Software Archive

Member "labplot-2.8.2/src/backend/gsl/parser.y" (24 Feb 2021, 13163 Bytes) of package /linux/privat/labplot-2.8.2.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) Bison source code syntax highlighting (style: standard) with prefixed line numbers. Alternatively you can here view or download the uninterpreted source code file. See also the latest Fossies "Diffs" side-by-side code changes report for "parser.y": 2.8.1_vs_2.8.2.

    1 /***************************************************************************
    2     File                 : parser.y
    3     Project              : LabPlot
    4     Description          : Parser for mathematical expressions
    5     --------------------------------------------------------------------
    6     Copyright            : (C) 2014 Alexander Semke (alexander.semke@web.de)
    7     Copyright            : (C) 2014-2020 Stefan Gerlach (stefan.gerlach@uni.kn)
    8 
    9  ***************************************************************************/
   10 
   11 /***************************************************************************
   12  *                                                                         *
   13  *  This program is free software; you can redistribute it and/or modify   *
   14  *  it under the terms of the GNU General Public License as published by   *
   15  *  the Free Software Foundation; either version 2 of the License, or      *
   16  *  (at your option) any later version.                                    *
   17  *                                                                         *
   18  *  This program is distributed in the hope that it will be useful,        *
   19  *  but WITHOUT ANY WARRANTY; without even the implied warranty of         *
   20  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the          *
   21  *  GNU General Public License for more details.                           *
   22  *                                                                         *
   23  *   You should have received a copy of the GNU General Public License     *
   24  *   along with this program; if not, write to the Free Software           *
   25  *   Foundation, Inc., 51 Franklin Street, Fifth Floor,                    *
   26  *   Boston, MA  02110-1301  USA                                           *
   27  *                                                                         *
   28  ***************************************************************************/
   29 
   30 %{
   31 #include <string.h>
   32 #include <ctype.h>
   33 #include <stdlib.h>
   34 #include <locale.h>
   35 #ifdef HAVE_XLOCALE
   36 #include <xlocale.h>
   37 #endif
   38 #include "parser.h"
   39 #include "constants.h"
   40 #include "functions.h"
   41 #if defined(_WIN32)
   42 #define locale_t _locale_t
   43 #define strtod_l _strtod_l
   44 #define freelocale _free_locale
   45 #endif
   46 
   47 #ifdef PDEBUG
   48 #include <stdio.h>
   49 #define pdebug(...) fprintf(stderr, __VA_ARGS__)
   50 #else
   51 #define pdebug(...) {}
   52 #endif
   53 
   54 #define YYERROR_VERBOSE 1
   55 
   56 /* params passed to yylex (and yyerror) */
   57 typedef struct param {
   58     size_t pos;     /* current position in string */
   59     char* string;       /* the string to parse */
   60     const char* locale; /* name of locale to convert numbers */
   61 } param;
   62 
   63 int yyerror(param *p, const char *err);
   64 int yylex(param *p);
   65 
   66 double res;
   67 %}
   68 
   69 %lex-param {param *p}
   70 %parse-param {param *p}
   71 
   72 %union {
   73 double dval;    /* For returning numbers */
   74 symbol *tptr;   /* For returning symbol-table pointers */
   75 }
   76 
   77 %token <dval>  NUM  /* Simple double precision number */
   78 %token <tptr> VAR FNCT  /* VARiable and FuNCTion */
   79 %type  <dval>  expr
   80 
   81 %right '='
   82 %left '-' '+'
   83 %left '*' '/' '%'
   84 %left NEG     /* Negation--unary minus */
   85 %right '^' '!'
   86 
   87 %%
   88 input:   /* empty */
   89     | input line
   90 ;
   91 
   92 line:   '\n'
   93     | expr '\n'   { res=$1; }
   94     | error '\n' { yyerrok; }
   95 ;
   96 
   97 expr:      NUM       { $$ = $1;                            }
   98 | VAR                { $$ = $1->value.var;                 }
   99 | VAR '=' expr       { $$ = $3; $1->value.var = $3;        }
  100 | FNCT '(' ')'       { $$ = (*($1->value.fnctptr))();      }
  101 | FNCT '(' expr ')'  { $$ = (*((func_t1)($1->value.fnctptr)))($3); }
  102 | FNCT '(' expr ',' expr ')'  { $$ = (*((func_t2)($1->value.fnctptr)))($3,$5); }
  103 | FNCT '(' expr ',' expr ','expr ')'  { $$ = (*((func_t3)($1->value.fnctptr)))($3,$5,$7); }
  104 | FNCT '(' expr ',' expr ',' expr ','expr ')'  { $$ = (*((func_t4)($1->value.fnctptr)))($3,$5,$7,$9); }
  105 | FNCT '(' expr ';' expr ')'  { $$ = (*((func_t2)($1->value.fnctptr)))($3,$5); }
  106 | FNCT '(' expr ';' expr ';'expr ')'  { $$ = (*((func_t3)($1->value.fnctptr)))($3,$5,$7); }
  107 | FNCT '(' expr ';' expr ';' expr ';'expr ')'  { $$ = (*((func_t4)($1->value.fnctptr)))($3,$5,$7,$9); }
  108 | expr '+' expr      { $$ = $1 + $3;                       }
  109 | expr '-' expr      { $$ = $1 - $3;                       }
  110 | expr '*' expr      { $$ = $1 * $3;                       }
  111 | expr '/' expr      { $$ = $1 / $3;                       }
  112 | expr '%' expr      { $$ = (int)($1) % (int)($3);         }
  113 | '-' expr  %prec NEG{ $$ = -$2;                           }
  114 | expr '^' expr      { $$ = pow($1, $3);                   }
  115 | expr '*' '*' expr  { $$ = pow($1, $4);                   }
  116 | '(' expr ')'       { $$ = $2;                            }
  117 | '|' expr '|'       { $$ = fabs($2);                      }
  118 | expr '!'           { $$ = gsl_sf_fact((unsigned int)$1); }
  119 /* logical operators (!,&&,||) are not supported */
  120 ;
  121 
  122 %%
  123 
  124 /* global symbol table (as linked list) */
  125 symbol *symbol_table = 0;
  126 
  127 int parse_errors(void) {
  128     return yynerrs;
  129 }
  130 
  131 int yyerror(param *p, const char *s) {
  132     /* remove trailing newline */
  133     p->string[strcspn(p->string, "\n")] = 0;
  134     printf("PARSER ERROR: %s @ position %d of string '%s'\n", s, (int)(p->pos), p->string);
  135 
  136     return 0;
  137 }
  138 
  139 /* save symbol in symbol table (at start of linked list) */
  140 symbol* put_symbol(const char *symbol_name, int symbol_type) {
  141 /*  pdebug("PARSER: put_symbol(): symbol_name = '%s'\n", symbol_name); */
  142 
  143     symbol *ptr = (symbol *)malloc(sizeof(symbol));
  144     ptr->name = (char *)malloc(strlen(symbol_name) + 1);
  145     strcpy(ptr->name, symbol_name);
  146     ptr->type = symbol_type;
  147     ptr->value.var = 0; /* set value to 0 even if fctn */
  148     ptr->next = (symbol *)symbol_table;
  149     symbol_table = ptr;
  150     
  151 /*  pdebug("PARSER: put_symbol() DONE\n"); */
  152     return ptr;
  153 }
  154 
  155 /* remove symbol of name symbol_name from symbol table
  156    removes only variables of value 0
  157    returns 0 on success */
  158 int remove_symbol(const char *symbol_name) {
  159     symbol* ptr = symbol_table;
  160 
  161     /* check if head contains symbol */
  162     if (ptr && (strcmp(ptr->name, symbol_name) == 0)) {
  163         if (ptr->type == VAR && ptr->value.var == 0) {
  164             pdebug("PARSER: REMOVING symbol '%s'\n", symbol_name);
  165             symbol_table = ptr->next;
  166             free(ptr->name);
  167             free(ptr);
  168         }
  169         return 0;
  170     }
  171 
  172     /* search for symbol to be deleted */
  173     symbol* prev;
  174     while (ptr && (strcmp(ptr->name, symbol_name) != 0)) {
  175         prev = ptr;
  176         ptr = ptr->next;
  177     }
  178 
  179     /* symbol not found or is not a variable or is not 0 */
  180     if (!ptr || ptr->type != VAR || ptr->value.var != 0)
  181         return 1;
  182 
  183     /* remove symbol */
  184     pdebug("PARSER: REMOVING symbol '%s'\n", symbol_name);
  185     prev->next = ptr->next;
  186     free(ptr->name);
  187     free(ptr);
  188 
  189     return 0;
  190 }
  191 
  192 /* get symbol from symbol table
  193    returns 0 if symbol not found */
  194 symbol* get_symbol(const char *symbol_name) {
  195     pdebug("PARSER: get_symbol(): symbol_name = '%s'\n", symbol_name);
  196     
  197     symbol *ptr;
  198     for (ptr = symbol_table; ptr != 0; ptr = (symbol *)ptr->next) {
  199         /* pdebug("%s ", ptr->name); */
  200         if (strcmp(ptr->name, symbol_name) == 0) {
  201             pdebug("PARSER:     SYMBOL FOUND\n");
  202             return ptr;
  203         }
  204     }
  205 
  206     pdebug("PARSER:     SYMBOL NOT FOUND\n");
  207     return 0;
  208 }
  209 
  210 /* initialize symbol table with all known functions and constants */
  211 void init_table(void) {
  212     pdebug("PARSER: init_table()\n");
  213 
  214     symbol *ptr = 0;
  215     int i;
  216     /* add functions */
  217     for (i = 0; _functions[i].name != 0; i++) {
  218         ptr = put_symbol(_functions[i].name, FNCT);
  219         ptr->value.fnctptr = _functions[i].fnct;
  220     }
  221     /* add constants */
  222     for (i = 0; _constants[i].name != 0; i++) {
  223         ptr = put_symbol(_constants[i].name, VAR);
  224         ptr->value.var = _constants[i].value;
  225     }
  226 
  227     pdebug("PARSER: init_table() DONE. sym_table = %p\n", ptr);
  228 }
  229 
  230 void delete_table(void) {
  231     pdebug("PARSER: delete_table()\n");
  232     while(symbol_table) {
  233         symbol *tmp = symbol_table;
  234         symbol_table = symbol_table->next;
  235         free(tmp->name);
  236         free(tmp);
  237     }
  238 }
  239 
  240 /* add new symbol with value or just set value if symbol is a variable */
  241 symbol* assign_symbol(const char* symbol_name, double value) {
  242     pdebug("PARSER: assign_symbol() : symbol_name = '%s', value = %g\n", symbol_name, value);
  243 
  244     /* be sure that the symbol table has been initialized */
  245     if (!symbol_table)
  246         init_table();
  247 
  248     symbol* ptr = get_symbol(symbol_name);
  249     if (!ptr) {
  250         pdebug("PARSER: calling putsymbol(): symbol_name = '%s'\n", symbol_name);
  251         ptr = put_symbol(symbol_name, VAR);
  252     } else {
  253         pdebug("PARSER: Symbol already assigned\n");
  254     }
  255 
  256     /* do not assign value if symbol already exits as function */
  257     if (ptr->type == VAR)
  258         ptr->value.var = value;
  259 
  260     return ptr;
  261 };
  262 
  263 static char getcharstr(param *p) {
  264     pdebug(" getcharstr() pos = %d\n", (int)(p->pos));
  265 
  266     if (p->string[p->pos] == '\0')
  267         return EOF;
  268     /* pdebug("PARSER:  char is %c\n", p->string[p->pos]); */
  269     return p->string[(p->pos)++];
  270 }
  271 
  272 static void ungetcstr(size_t *pos) {
  273     /* pdebug("PARSER: ungetcstr()\n"); */
  274     if (*pos > 0)
  275         (*pos)--;
  276 }
  277 
  278 double parse(const char* string, const char* locale) {
  279     pdebug("\nPARSER: parse('%s') len = %d\n********************************\n", string, (int)strlen(string));
  280 
  281     /* be sure that the symbol table has been initialized */
  282     if (!symbol_table)
  283         init_table();
  284 
  285     param p;
  286     p.pos = 0;
  287     p.locale = locale;
  288 
  289     /* leave space to terminate string by "\n\0" */
  290     const size_t slen = strlen(string) + 2;
  291     p.string = (char *) malloc(slen * sizeof(char));
  292     if (p.string == NULL) {
  293         printf("PARSER ERROR: Out of memory for parsing string\n");
  294         return 0.;
  295     }
  296 
  297     strcpy(p.string, string);
  298     p.string[strlen(string)] = '\n';    // end for parsing
  299     p.string[strlen(string)+1] = '\0';  // end of string
  300     /* pdebug("PARSER: Call yyparse() for \"%s\" (len = %d)\n", p.string, (int)strlen(p.string)); */
  301 
  302     /* parameter for yylex */
  303     res = NAN;  /* default value */
  304     yynerrs = 0;    /* reset error count */
  305     yyparse(&p);
  306 
  307     pdebug("PARSER: parse() DONE (result = %g, errors = %d)\n*******************************\n", res, parse_errors());
  308     free(p.string);
  309     p.string = 0;
  310 
  311     return res;
  312 }
  313 
  314 double parse_with_vars(const char *str, const parser_var *vars, int nvars, const char* locale) {
  315     pdebug("\nPARSER: parse_with_var(\"%s\") len = %d\n", str, (int)strlen(str));
  316 
  317     int i;
  318     for(i = 0; i < nvars; i++) {    /*assign vars */
  319         pdebug("PARSER: Assign '%s' the value %g\n", vars[i].name, vars[i].value);
  320         assign_symbol(vars[i].name, vars[i].value);
  321     }
  322 
  323     return parse(str, locale);
  324 }
  325 
  326 int yylex(param *p) {
  327     pdebug("PARSER: YYLEX()");
  328 
  329     /* get char and skip white space */
  330     char c;
  331     while ((c = getcharstr(p)) == ' ' || c == '\t');
  332 
  333     /* finish if reached EOF */
  334     if (c == EOF) {
  335         pdebug("PARSER: FINISHED\n");
  336         return 0;
  337     }
  338     /* check for non-ASCII chars */
  339     if (!isascii(c)) {
  340         pdebug(" non-ASCII character found. Giving up\n");
  341         yynerrs++;
  342         return 0;
  343     }
  344     if (c == '\n') {
  345         pdebug("PARSER: Reached EOL\n");
  346         return c;
  347     }
  348 
  349     pdebug("PARSER: PROCESSING character '%c'\n", c);
  350 
  351     /* process numbers */
  352     if (isdigit(c)) {
  353         pdebug("PARSER: Found NUMBER (starts with digit)\n");
  354                 ungetcstr(&(p->pos));
  355                 char *s = &(p->string[p->pos]);
  356 
  357         /* convert to double */
  358         char *remain;
  359 #if defined(_WIN32)
  360         locale_t locale = _create_locale(LC_NUMERIC, p->locale);
  361         if (locale == NULL) {
  362             pdebug("PARSER ERROR in newlocale(%s): %s. Trying system locale.\n", p->locale, strerror(errno));
  363             locale = _create_locale(LC_NUMERIC, "");
  364         }
  365 #else
  366         locale_t locale = newlocale(LC_NUMERIC_MASK, p->locale, (locale_t)0);
  367         if (locale == (locale_t)0) {
  368             pdebug("PARSER ERROR in newlocale(%s): %s. Trying system locale.\n", p->locale, strerror(errno));
  369             locale = newlocale(LC_NUMERIC_MASK, "", (locale_t)0);
  370             pdebug("PARSER:     Reading: '%s' with system locale\n", s);
  371         } else {
  372             pdebug("PARSER:     Reading: '%s' with locale %s\n", s, p->locale);
  373         }
  374 #endif
  375         double result;
  376         if (locale != NULL) {
  377             result = strtod_l(s, &remain, locale);
  378             freelocale(locale);
  379         } else // use C locale
  380             result = strtod(s, &remain);
  381 
  382         pdebug("PARSER:     Remain: '%s'\n", remain);
  383 
  384         /* check conversion */
  385         if(strlen(s) == strlen(remain))
  386             return 0;
  387 
  388         pdebug("PARSER:     Result = %g\n", result);
  389         yylval.dval = result;
  390 
  391                 p->pos += strlen(s) - strlen(remain);
  392 
  393         return NUM;
  394     }
  395 
  396     /* process symbol */
  397     if (isalpha (c) || c == '.') {
  398         pdebug("PARSER: Found SYMBOL (starts with alpha)\n");
  399         static char *symbol_name = 0;
  400         static int length = 0;
  401         int i = 0;
  402 
  403         /* Initially make the buffer long enough for a 10-character symbol name */
  404         if (length == 0) {
  405             length = 10;
  406             symbol_name = (char *) malloc(length + 1);
  407         }
  408 
  409         do {
  410             pdebug("PARSER: Reading symbol .. ");
  411             /* If buffer is full, make it bigger */
  412             if (i == length) {
  413                 length *= 2;
  414                 symbol_name = (char *) realloc(symbol_name, length + 1);
  415             }
  416             symbol_name[i++] = c;
  417             c = getcharstr(p);
  418             pdebug("PARSER:     got '%c'\n", c);
  419         }
  420         while (c != EOF && (isalnum(c) || c == '_' || c == '.'));
  421         pdebug("PARSER: Reading SYMBOL DONE\n");
  422 
  423         if (c != EOF)
  424             ungetcstr(&(p->pos));
  425         symbol_name[i] = '\0';
  426 
  427         symbol *s = get_symbol(symbol_name);
  428         if(s == 0) {    /* symbol unknown */
  429             pdebug("PARSER ERROR: Symbol '%s' UNKNOWN\n", symbol_name);
  430             yynerrs++;
  431             return 0;
  432             /* old behavior: add symbol */
  433             /* s = put_symbol(symbol_name, VAR); */
  434         }
  435 
  436         yylval.tptr = s;
  437         return s->type;
  438     }
  439 
  440     /* else: single operator */
  441     pdebug("PARSER: Found single operator '%c'\n", c);
  442     return c;
  443 }