Change 'undo' and 'clear' buttons to use symbolic icons
[gcalctool.git] / src / mp-equation-parser.y
blobcb9e49e1d0a5958f6cdff78744e5466f1eee9064
1 %{
2 /*
3 * Copyright (C) 2004-2008 Sami Pietila
4 * Copyright (C) 2008-2011 Robert Ancell
6 * This program is free software: you can redistribute it and/or modify it under
7 * the terms of the GNU General Public License as published by the Free Software
8 * Foundation, either version 2 of the License, or (at your option) any later
9 * version. See http://www.gnu.org/copyleft/gpl.html the full text of the
10 * license.
13 #include <stdio.h>
14 #include <stdlib.h>
15 #include <math.h>
16 #include <errno.h>
17 #include <assert.h>
19 #include "mp-equation-private.h"
20 #include "mp-equation-parser.h"
21 #include "mp-equation-lexer.h"
23 // fixme support x log x
24 // treat exp NAME exp as a function always and pass both arguments, i.e.
25 // can do mod using both and all others use $1 * NAME($3)
27 static void set_error(yyscan_t yyscanner, int error, const char *token)
29 _mp_equation_get_extra(yyscanner)->error = error;
30 if (token)
31 _mp_equation_get_extra(yyscanner)->error_token = strdup(token);
34 static void set_result(yyscan_t yyscanner, const MPNumber *x)
36 mp_set_from_mp(x, &(_mp_equation_get_extra(yyscanner))->ret);
39 static char *
40 utf8_next_char (const char *c)
42 c++;
43 while ((*c & 0xC0) == 0x80)
44 c++;
45 return (char *)c;
48 static int get_variable(yyscan_t yyscanner, const char *name, int power, MPNumber *z)
50 int result = 0;
52 /* If defined, then get the variable */
53 if (_mp_equation_get_extra(yyscanner)->get_variable(_mp_equation_get_extra(yyscanner), name, z)) {
54 mp_xpowy_integer(z, power, z);
55 return 1;
58 /* If has more than one character then assume a multiplication of variables */
59 if (utf8_next_char(name)[0] != '\0') {
60 const char *c, *next;
61 char *buffer = malloc(sizeof(char) * strlen(name));
62 MPNumber value;
64 result = 1;
65 mp_set_from_integer(1, &value);
66 for (c = name; *c != '\0'; c = next) {
67 MPNumber t;
69 next = utf8_next_char(c);
70 snprintf(buffer, next - c + 1, "%s", c);
72 if (!_mp_equation_get_extra(yyscanner)->get_variable(_mp_equation_get_extra(yyscanner), buffer, &t)) {
73 result = 0;
74 break;
77 /* If last term do power */
78 if (*next == '\0')
79 mp_xpowy_integer(&t, power, &t);
81 mp_multiply(&value, &t, &value);
84 free(buffer);
85 if (result)
86 mp_set_from_mp(&value, z);
89 if (!result)
90 set_error(yyscanner, PARSER_ERR_UNKNOWN_VARIABLE, name);
92 return result;
95 static void set_variable(yyscan_t yyscanner, const char *name, MPNumber *x)
97 _mp_equation_get_extra(yyscanner)->set_variable(_mp_equation_get_extra(yyscanner), name, x);
100 static int get_function(yyscan_t yyscanner, const char *name, const MPNumber *x, MPNumber *z)
102 if (!_mp_equation_get_extra(yyscanner)->get_function(_mp_equation_get_extra(yyscanner), name, x, z)) {
103 set_error(yyscanner, PARSER_ERR_UNKNOWN_FUNCTION, name);
104 return 0;
106 return 1;
109 static int get_inverse_function(yyscan_t yyscanner, const char *name, const MPNumber *x, MPNumber *z)
111 char *inv_name;
112 int result;
114 inv_name = malloc(sizeof(char) * (strlen(name) + strlen("⁻¹") + 1));
115 strcpy(inv_name, name);
116 strcat(inv_name, "⁻¹");
117 result = get_function(yyscanner, inv_name, x, z);
118 free(inv_name);
120 return result;
123 static void do_not(yyscan_t yyscanner, const MPNumber *x, MPNumber *z)
125 if (!mp_is_overflow(x, _mp_equation_get_extra(yyscanner)->options->wordlen)) {
126 set_error(yyscanner, PARSER_ERR_OVERFLOW, NULL);
128 mp_not(x, _mp_equation_get_extra(yyscanner)->options->wordlen, z);
131 static char *make_unit(const char *name, int power)
133 char *name2;
135 // FIXME: Hacky
136 if (power == 2) {
137 name2 = malloc(sizeof(char) * (strlen(name) + strlen("²") + 1));
138 sprintf(name2, "%s²", name);
140 else if (power == 3) {
141 name2 = malloc(sizeof(char) * (strlen(name) + strlen("³") + 1));
142 sprintf(name2, "%s³", name);
144 else {
145 name2 = malloc(sizeof(char) * (strlen(name) + strlen("?") + 1));
146 sprintf(name2, "%s?", name);
149 return name2;
152 static void do_conversion(yyscan_t yyscanner, const MPNumber *x, const char *x_units, const char *z_units, MPNumber *z)
154 if (!_mp_equation_get_extra(yyscanner)->convert(_mp_equation_get_extra(yyscanner), x, x_units, z_units, z))
155 set_error(yyscanner, PARSER_ERR_UNKNOWN_CONVERSION, NULL);
160 %pure-parser
161 %name-prefix="_mp_equation_"
162 %locations
163 %parse-param {yyscan_t yyscanner}
164 %lex-param {yyscan_t yyscanner}
166 %union {
167 MPNumber int_t;
168 int integer;
169 char *name;
172 %left <int_t> tNUMBER
173 %left tLFLOOR tRFLOOR tLCEILING tRCEILING
174 %left UNARY_PLUS
175 %left tADD tSUBTRACT
176 %left tAND tOR tXOR tXNOR
177 %left tMULTIPLY MULTIPLICATION
178 %left tDIVIDE tMOD
179 %left tNOT
180 %left tROOT tROOT3 tROOT4
181 %left <name> tVARIABLE tFUNCTION
182 %right <integer> tSUBNUM tSUPNUM tNSUPNUM
183 %left BOOLEAN_OPERATOR
184 %left PERCENTAGE
185 %left UNARY_MINUS
186 %right '^' '!' '|'
187 %left tIN
189 %type <int_t> exp variable term
190 %type <name> unit
191 %start statement
195 statement:
196 exp { set_result(yyscanner, &$1); }
197 | exp '=' { set_result(yyscanner, &$1); }
198 | tVARIABLE '=' exp {set_variable(yyscanner, $1, &$3); set_result(yyscanner, &$3); }
199 | tNUMBER unit tIN unit { MPNumber t; do_conversion(yyscanner, &$1, $2, $4, &t); set_result(yyscanner, &t); free($2); free($4); }
200 | unit tIN unit { MPNumber x, t; mp_set_from_integer(1, &x); do_conversion(yyscanner, &x, $1, $3, &t); set_result(yyscanner, &t); free($1); free($3); }
203 unit:
204 tVARIABLE {$$ = $1;}
205 | tVARIABLE tSUPNUM {$$ = make_unit($1, $2); free($1);}
207 /* |x| gets confused and thinks = |x|(...||) */
209 exp:
210 '(' exp ')' {mp_set_from_mp(&$2, &$$);}
211 | exp tDIVIDE exp '(' exp ')' {mp_divide(&$1, &$3, &$$); mp_multiply(&$5, &$$, &$$);}
212 | exp tMOD exp '(' exp ')' {mp_modulus_divide(&$1, &$3, &$$); mp_multiply(&$5, &$$, &$$);}
213 | exp '(' exp ')' {mp_multiply(&$1, &$3, &$$);}
214 | tLFLOOR exp tRFLOOR {mp_floor(&$2, &$$);}
215 | tLCEILING exp tRCEILING {mp_ceiling(&$2, &$$);}
216 | '[' exp ']' {mp_round(&$2, &$$);}
217 | '{' exp '}' {mp_fractional_part(&$2, &$$);}
218 | '|' exp '|' {mp_abs(&$2, &$$);}
219 | exp '^' exp {mp_xpowy(&$1, &$3, &$$);}
220 | exp tSUPNUM {mp_xpowy_integer(&$1, $2, &$$);}
221 | exp tNSUPNUM {mp_xpowy_integer(&$1, $2, &$$);}
222 | exp '!' {mp_factorial(&$1, &$$);}
223 | variable {mp_set_from_mp(&$1, &$$);}
224 | tNUMBER variable %prec MULTIPLICATION {mp_multiply(&$1, &$2, &$$);}
225 | tSUBTRACT exp %prec UNARY_MINUS {mp_invert_sign(&$2, &$$);}
226 | tADD tNUMBER %prec UNARY_PLUS {mp_set_from_mp(&$2, &$$);}
227 | exp tDIVIDE exp {mp_divide(&$1, &$3, &$$);}
228 | exp tMOD exp {mp_modulus_divide(&$1, &$3, &$$);}
229 | exp tMULTIPLY exp {mp_multiply(&$1, &$3, &$$);}
230 | exp tADD exp '%' %prec PERCENTAGE {mp_add_integer(&$3, 100, &$3); mp_divide_integer(&$3, 100, &$3); mp_multiply(&$1, &$3, &$$);}
231 | exp tSUBTRACT exp '%' %prec PERCENTAGE {mp_add_integer(&$3, -100, &$3); mp_divide_integer(&$3, -100, &$3); mp_multiply(&$1, &$3, &$$);}
232 | exp tADD exp {mp_add(&$1, &$3, &$$);}
233 | exp tSUBTRACT exp {mp_subtract(&$1, &$3, &$$);}
234 | exp '%' {mp_divide_integer(&$1, 100, &$$);}
235 | tNOT exp {do_not(yyscanner, &$2, &$$);}
236 | exp tAND exp %prec BOOLEAN_OPERATOR {mp_and(&$1, &$3, &$$);}
237 | exp tOR exp %prec BOOLEAN_OPERATOR {mp_or(&$1, &$3, &$$);}
238 | exp tXOR exp %prec BOOLEAN_OPERATOR {mp_xor(&$1, &$3, &$$);}
239 | tNUMBER {mp_set_from_mp(&$1, &$$);}
243 variable:
244 term {mp_set_from_mp(&$1, &$$);}
245 | tFUNCTION exp {if (!get_function(yyscanner, $1, &$2, &$$)) YYABORT; free($1);}
246 | tFUNCTION tSUPNUM exp {if (!get_function(yyscanner, $1, &$3, &$$)) YYABORT; mp_xpowy_integer(&$$, $2, &$$); free($1);}
247 | tFUNCTION tNSUPNUM exp {if (!get_inverse_function(yyscanner, $1, &$3, &$$)) YYABORT; mp_xpowy_integer(&$$, -$2, &$$); free($1);}
248 | tVARIABLE tSUPNUM exp {set_error(yyscanner, PARSER_ERR_UNKNOWN_FUNCTION, $1); free($1); YYABORT;}
249 | tSUBNUM tROOT exp {mp_root(&$3, $1, &$$);}
250 | tROOT exp {mp_sqrt(&$2, &$$);}
251 | tROOT3 exp {mp_root(&$2, 3, &$$);}
252 | tROOT4 exp {mp_root(&$2, 4, &$$);}
255 term:
256 tVARIABLE {if (!get_variable(yyscanner, $1, 1, &$$)) YYABORT; free($1);}
257 | tVARIABLE tSUPNUM {if (!get_variable(yyscanner, $1, $2, &$$)) YYABORT; free($1);}
258 | term term {mp_multiply(&$1, &$2, &$$);}