Home | History | Annotate | Download | only in exp
      1 %{
      2 
      3 /*
      4  * (C) Copyright 2014, Stephen M. Cameron.
      5  *
      6  *  This program is free software; you can redistribute it and/or modify
      7  *  it under the terms of the GNU General Public License version 2 as
      8  *  published by the Free Software Foundation.
      9  *
     10  *  This program is distributed in the hope that it will be useful,
     11  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
     12  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     13  *  GNU General Public License for more details.
     14  *
     15  *  You should have received a copy of the GNU General Public License
     16  *  along with this program; if not, write to the Free Software
     17  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
     18  *
     19  */
     20 
     21 #include <stdio.h>
     22 #include <string.h>
     23 #include <math.h>
     24 
     25 struct parser_value_type {
     26 	double dval;
     27 	long long ival;
     28 	int has_dval;
     29 	int has_error;
     30 };
     31 
     32 typedef union valtype {
     33 	struct parser_value_type v;
     34 } PARSER_VALUE_TYPE;
     35 
     36 #define YYSTYPE PARSER_VALUE_TYPE
     37 
     38 int yyerror(__attribute__((unused)) long long *result,
     39 		__attribute__((unused)) double *dresult,
     40 		__attribute__((unused)) int *has_error,
     41 		__attribute__((unused)) int *units_specified,
     42 		__attribute__((unused)) const char *msg);
     43 
     44 extern int yylex(void);
     45 extern void yyrestart(FILE *file);
     46 extern int lexer_value_is_time;
     47 
     48 %}
     49 
     50 %union valtype {
     51 	struct parser_value_type {
     52 		double dval;
     53 		long long ival;
     54 		int has_dval;
     55 		int has_error;
     56 	} v;
     57 };
     58 
     59 %token <v> NUMBER
     60 %token <v> BYE
     61 %token <v> SUFFIX
     62 %left '-' '+'
     63 %right SUFFIX
     64 %left '*' '/'
     65 %right '^'
     66 %left '%'
     67 %nonassoc UMINUS
     68 %parse-param { long long *result }
     69 %parse-param { double *dresult }
     70 %parse-param { int *has_error }
     71 %parse-param { int *units_specified }
     72 
     73 %type <v> expression
     74 %%
     75 
     76 top_level:	expression {
     77 				*result = $1.ival;
     78 				*dresult = $1.dval;
     79 				*has_error = $1.has_error;
     80 			}
     81 		| expression error {
     82 				*result = $1.ival;
     83 				*dresult = $1.dval;
     84 				*has_error = 1;
     85 			}
     86 expression:	expression '+' expression {
     87 			if (!$1.has_dval && !$3.has_dval)
     88 				$$.ival = $1.ival + $3.ival;
     89 			else
     90 				$$.ival = (long long) ($1.dval + $3.dval);
     91 			$$.dval = $1.dval + $3.dval;
     92 			$$.has_error = $1.has_error || $3.has_error;
     93 		}
     94 	|	expression '-' expression {
     95 			if (!$1.has_dval && !$3.has_dval)
     96 				$$.ival = $1.ival - $3.ival;
     97 			else
     98 				$$.ival = (long long) ($1.dval - $3.dval);
     99 			$$.dval = $1.dval - $3.dval;
    100 			$$.has_error = $1.has_error || $3.has_error;
    101 		}
    102 	|	expression '*' expression {
    103 			if (!$1.has_dval && !$3.has_dval)
    104 				$$.ival = $1.ival * $3.ival;
    105 			else
    106 				$$.ival = (long long) ($1.dval * $3.dval);
    107 			$$.dval = $1.dval * $3.dval;
    108 			$$.has_error = $1.has_error || $3.has_error;
    109 		}
    110 	|	expression '/' expression {
    111 			if ($3.ival == 0)
    112 				yyerror(0, 0, 0, 0, "divide by zero");
    113 			else
    114 				$$.ival = $1.ival / $3.ival;
    115 			if ($3.dval < 1e-20 && $3.dval > -1e-20)
    116 				yyerror(0, 0, 0, 0, "divide by zero");
    117 			else
    118 				$$.dval = $1.dval / $3.dval;
    119 			if ($3.has_dval || $1.has_dval)
    120 				$$.ival = (long long) $$.dval;
    121 			$$.has_error = $1.has_error || $3.has_error;
    122 		}
    123 	|	'-' expression %prec UMINUS {
    124 			$$.ival = -$2.ival;
    125 			$$.dval = -$2.dval;
    126 			$$.has_error = $2.has_error;
    127 		}
    128 	|	'(' expression ')' { $$ = $2; }
    129 	|	expression SUFFIX {
    130 			if (!$1.has_dval && !$2.has_dval)
    131 				$$.ival = $1.ival * $2.ival;
    132 			else
    133 				$$.ival = (long long) $1.dval * $2.dval;
    134 			if ($1.has_dval || $2.has_dval)
    135 				$$.dval = $1.dval * $2.dval;
    136 			else
    137 				$$.dval = $1.ival * $2.ival;
    138 			$$.has_error = $1.has_error || $2.has_error;
    139 			*units_specified = 1;
    140 		}
    141 	|	expression '%' expression {
    142 			if ($1.has_dval || $3.has_dval)
    143 				yyerror(0, 0, 0, 0, "modulo on floats");
    144 			if ($3.ival == 0)
    145 				yyerror(0, 0, 0, 0, "divide by zero");
    146 			else {
    147 				$$.ival = $1.ival % $3.ival;
    148 				$$.dval = $$.ival;
    149 			}
    150 			$$.has_error = $1.has_error || $3.has_error;
    151 		}
    152 	|	expression '^' expression {
    153 			$$.has_error = $1.has_error || $3.has_error;
    154 			if (!$1.has_dval && !$3.has_dval) {
    155 				int i;
    156 
    157 				if ($3.ival == 0) {
    158 					$$.ival = 1;
    159 				} else if ($3.ival > 0) {
    160 					long long tmp = $1.ival;
    161 					$$.ival = 1.0;
    162 					for (i = 0; i < $3.ival; i++)
    163 						$$.ival *= tmp;
    164 				}  else {
    165 					/* integers, 2^-3, ok, we now have doubles */
    166 					double tmp;
    167 					if ($1.ival == 0 && $3.ival == 0) {
    168 						tmp = 1.0;
    169 						$$.has_error = 1;
    170 					} else {
    171 						double x = (double) $1.ival;
    172 						double y = (double) $3.ival;
    173 						tmp = pow(x, y);
    174 					}
    175 					$$.ival = (long long) tmp;
    176 				}
    177 				$$.dval = pow($1.dval, $3.dval);
    178 			} else {
    179 				$$.dval = pow($1.dval, $3.dval);
    180 				$$.ival = (long long) $$.dval;
    181 			}
    182 		}
    183 	|	NUMBER { $$ = $1; };
    184 %%
    185 #include <stdio.h>
    186 
    187 /* Urgh.  yacc and lex are kind of horrible.  This is not thread safe, obviously. */
    188 static int lexer_read_offset = 0;
    189 static char lexer_input_buffer[1000];
    190 
    191 int lexer_input(char* buffer, unsigned int *bytes_read, int bytes_requested)
    192 {
    193 	int bytes_left = strlen(lexer_input_buffer) - lexer_read_offset;
    194 
    195 	if (bytes_requested > bytes_left )
    196 		bytes_requested = bytes_left;
    197 	memcpy(buffer, &lexer_input_buffer[lexer_read_offset], bytes_requested);
    198 	*bytes_read = bytes_requested;
    199 	lexer_read_offset += bytes_requested;
    200 	return 0;
    201 }
    202 
    203 static void setup_to_parse_string(const char *string)
    204 {
    205 	unsigned int len;
    206 
    207 	len = strlen(string);
    208 	if (len > sizeof(lexer_input_buffer) - 3)
    209 		len = sizeof(lexer_input_buffer) - 3;
    210 
    211 	strncpy(lexer_input_buffer, string, len);
    212 	lexer_input_buffer[len] = '\0';
    213 	lexer_input_buffer[len + 1] = '\0';  /* lex/yacc want string double null terminated! */
    214 	lexer_read_offset = 0;
    215 }
    216 
    217 int evaluate_arithmetic_expression(const char *buffer, long long *ival, double *dval,
    218 					double implied_units, int is_time)
    219 {
    220 	int rc, units_specified = 0, has_error = 0;
    221 
    222 	lexer_value_is_time = is_time;
    223 	setup_to_parse_string(buffer);
    224 	rc = yyparse(ival, dval, &has_error, &units_specified);
    225 	yyrestart(NULL);
    226 	if (rc || has_error) {
    227 		*ival = 0;
    228 		*dval = 0;
    229 		has_error = 1;
    230 	}
    231 	if (!units_specified) {
    232 		*ival = (int) ((double) *ival * implied_units);
    233 		*dval = *dval * implied_units;
    234 	}
    235 	return has_error;
    236 }
    237 
    238 int yyerror(__attribute__((unused)) long long *result,
    239 		__attribute__((unused)) double *dresult,
    240 		__attribute__((unused)) int *has_error,
    241 		__attribute__((unused)) int *units_specified,
    242 		__attribute__((unused)) const char *msg)
    243 {
    244 	/* We do not need to do anything here. */
    245 	return 0;
    246 }
    247 
    248