Change 'undo' and 'clear' buttons to use symbolic icons
[gcalctool.git] / src / mp-equation.c
blobdc1eff7b3e3fb366523c60175fa55c7ad3892607
1 /*
2 * Copyright (C) 2004-2008 Sami Pietila
3 * Copyright (C) 2008-2011 Robert Ancell.
4 *
5 * This program is free software: you can redistribute it and/or modify it under
6 * the terms of the GNU General Public License as published by the Free Software
7 * Foundation, either version 2 of the License, or (at your option) any later
8 * version. See http://www.gnu.org/copyleft/gpl.html the full text of the
9 * license.
12 #include <ctype.h>
14 #include "mp-equation-private.h"
15 #include "mp-equation-parser.h"
16 #include "mp-equation-lexer.h"
18 extern int _mp_equation_parse(yyscan_t yyscanner);
21 static int
22 variable_is_defined(MPEquationParserState *state, const char *name)
24 /* FIXME: Make more generic */
25 if (strcmp(name, "e") == 0 || strcmp(name, "i") == 0 || strcmp(name, "π") == 0)
26 return 1;
27 if (state->options->variable_is_defined)
28 return state->options->variable_is_defined(name, state->options->callback_data);
29 return 0;
33 static int
34 get_variable(MPEquationParserState *state, const char *name, MPNumber *z)
36 int result = 1;
38 if (strcmp(name, "e") == 0)
39 mp_get_eulers(z);
40 else if (strcmp(name, "i") == 0)
41 mp_get_i(z);
42 else if (strcmp(name, "π") == 0)
43 mp_get_pi(z);
44 else if (state->options->get_variable)
45 result = state->options->get_variable(name, z, state->options->callback_data);
46 else
47 result = 0;
49 return result;
52 static void
53 set_variable(MPEquationParserState *state, const char *name, const MPNumber *x)
55 // Reserved words, e, π, mod, and, or, xor, not, abs, log, ln, sqrt, int, frac, sin, cos, ...
56 if (strcmp(name, "e") == 0 || strcmp(name, "i") == 0 || strcmp(name, "π") == 0)
57 return; // FALSE
59 if (state->options->set_variable)
60 state->options->set_variable(name, x, state->options->callback_data);
63 // FIXME: Accept "2sin" not "2 sin", i.e. let the tokenizer collect the multiple
64 // Parser then distinguishes between "sin"="s*i*n" or "sin5" = "sin 5" = "sin(5)"
65 // i.e. numbers+letters = variable or function depending on following arg
66 // letters+numbers = numbers+letters+numbers = function
69 int
70 sub_atoi(const char *data)
72 int i, value = 0;
73 const char *digits[] = {"₀", "₁", "₂", "₃", "₄", "₅", "₆", "₇", "₈", "₉", NULL};
75 do {
76 for(i = 0; digits[i] != NULL && strncmp(data, digits[i], strlen(digits[i])) != 0; i++);
77 if(digits[i] == NULL)
78 return -1;
79 data += strlen(digits[i]);
80 value = value * 10 + i;
81 } while(*data != '\0');
83 return value;
86 int
87 super_atoi(const char *data)
89 int i, sign = 1, value = 0;
90 const char *digits[11] = {"⁰", "¹", "²", "³", "⁴", "⁵", "⁶", "⁷", "⁸", "⁹", NULL};
92 if(strncmp(data, "⁻", strlen("⁻")) == 0) {
93 sign = -1;
94 data += strlen("⁻");
97 do {
98 for(i = 0; digits[i] != NULL && strncmp(data, digits[i], strlen(digits[i])) != 0; i++);
99 if(digits[i] == NULL)
100 return 0;
101 value = value * 10 + i;
102 data += strlen(digits[i]);
103 } while(*data != '\0');
105 return sign * value;
109 static int
110 function_is_defined(MPEquationParserState *state, const char *name)
112 char *c, *lower_name;
114 lower_name = strdup(name);
115 for (c = lower_name; *c; c++)
116 *c = tolower(*c);
118 /* FIXME: Make more generic */
119 if (strcmp(lower_name, "log") == 0 ||
120 (strncmp(lower_name, "log", 3) == 0 && sub_atoi(lower_name + 3) >= 0) ||
121 strcmp(lower_name, "ln") == 0 ||
122 strcmp(lower_name, "sqrt") == 0 ||
123 strcmp(lower_name, "abs") == 0 ||
124 strcmp(lower_name, "sgn") == 0 ||
125 strcmp(lower_name, "arg") == 0 ||
126 strcmp(lower_name, "conj") == 0 ||
127 strcmp(lower_name, "int") == 0 ||
128 strcmp(lower_name, "frac") == 0 ||
129 strcmp(lower_name, "floor") == 0 ||
130 strcmp(lower_name, "ceil") == 0 ||
131 strcmp(lower_name, "round") == 0 ||
132 strcmp(lower_name, "re") == 0 ||
133 strcmp(lower_name, "im") == 0 ||
134 strcmp(lower_name, "sin") == 0 || strcmp(lower_name, "cos") == 0 || strcmp(lower_name, "tan") == 0 ||
135 strcmp(lower_name, "sin⁻¹") == 0 || strcmp(lower_name, "cos⁻¹") == 0 || strcmp(lower_name, "tan⁻¹") == 0 ||
136 strcmp(lower_name, "sinh") == 0 || strcmp(lower_name, "cosh") == 0 || strcmp(lower_name, "tanh") == 0 ||
137 strcmp(lower_name, "sinh⁻¹") == 0 || strcmp(lower_name, "cosh⁻¹") == 0 || strcmp(lower_name, "tanh⁻¹") == 0 ||
138 strcmp(lower_name, "asinh") == 0 || strcmp(lower_name, "acosh") == 0 || strcmp(lower_name, "atanh") == 0 ||
139 strcmp(lower_name, "ones") == 0 ||
140 strcmp(lower_name, "twos") == 0) {
141 g_free (lower_name);
142 return 1;
144 g_free (lower_name);
146 if (state->options->function_is_defined)
147 return state->options->function_is_defined(name, state->options->callback_data);
148 return 0;
152 static int
153 get_function(MPEquationParserState *state, const char *name, const MPNumber *x, MPNumber *z)
155 char *c, *lower_name;
156 int result = 1;
158 lower_name = strdup(name);
159 for (c = lower_name; *c; c++)
160 *c = tolower(*c);
162 // FIXME: Re Im ?
164 if (strcmp(lower_name, "log") == 0)
165 mp_logarithm(10, x, z); // FIXME: Default to ln
166 else if (strncmp(lower_name, "log", 3) == 0) {
167 int base;
169 base = sub_atoi(lower_name + 3);
170 if (base < 0)
171 result = 0;
172 else
173 mp_logarithm(base, x, z);
175 else if (strcmp(lower_name, "ln") == 0)
176 mp_ln(x, z);
177 else if (strcmp(lower_name, "sqrt") == 0) // √x
178 mp_sqrt(x, z);
179 else if (strcmp(lower_name, "abs") == 0) // |x|
180 mp_abs(x, z);
181 else if (strcmp(lower_name, "sgn") == 0)
182 mp_sgn(x, z);
183 else if (strcmp(lower_name, "arg") == 0)
184 mp_arg(x, state->options->angle_units, z);
185 else if (strcmp(lower_name, "conj") == 0)
186 mp_conjugate(x, z);
187 else if (strcmp(lower_name, "int") == 0)
188 mp_integer_component(x, z);
189 else if (strcmp(lower_name, "frac") == 0)
190 mp_fractional_component(x, z);
191 else if (strcmp(lower_name, "floor") == 0)
192 mp_floor(x, z);
193 else if (strcmp(lower_name, "ceil") == 0)
194 mp_ceiling(x, z);
195 else if (strcmp(lower_name, "round") == 0)
196 mp_round(x, z);
197 else if (strcmp(lower_name, "re") == 0)
198 mp_real_component(x, z);
199 else if (strcmp(lower_name, "im") == 0)
200 mp_imaginary_component(x, z);
201 else if (strcmp(lower_name, "sin") == 0)
202 mp_sin(x, state->options->angle_units, z);
203 else if (strcmp(lower_name, "cos") == 0)
204 mp_cos(x, state->options->angle_units, z);
205 else if (strcmp(lower_name, "tan") == 0)
206 mp_tan(x, state->options->angle_units, z);
207 else if (strcmp(lower_name, "sin⁻¹") == 0 || strcmp(lower_name, "asin") == 0)
208 mp_asin(x, state->options->angle_units, z);
209 else if (strcmp(lower_name, "cos⁻¹") == 0 || strcmp(lower_name, "acos") == 0)
210 mp_acos(x, state->options->angle_units, z);
211 else if (strcmp(lower_name, "tan⁻¹") == 0 || strcmp(lower_name, "atan") == 0)
212 mp_atan(x, state->options->angle_units, z);
213 else if (strcmp(lower_name, "sinh") == 0)
214 mp_sinh(x, z);
215 else if (strcmp(lower_name, "cosh") == 0)
216 mp_cosh(x, z);
217 else if (strcmp(lower_name, "tanh") == 0)
218 mp_tanh(x, z);
219 else if (strcmp(lower_name, "sinh⁻¹") == 0 || strcmp(lower_name, "asinh") == 0)
220 mp_asinh(x, z);
221 else if (strcmp(lower_name, "cosh⁻¹") == 0 || strcmp(lower_name, "acosh") == 0)
222 mp_acosh(x, z);
223 else if (strcmp(lower_name, "tanh⁻¹") == 0 || strcmp(lower_name, "atanh") == 0)
224 mp_atanh(x, z);
225 else if (strcmp(lower_name, "ones") == 0)
226 mp_ones_complement(x, state->options->wordlen, z);
227 else if (strcmp(lower_name, "twos") == 0)
228 mp_twos_complement(x, state->options->wordlen, z);
229 else if (state->options->get_function)
230 result = state->options->get_function(name, x, z, state->options->callback_data);
231 else
232 result = 0;
234 free(lower_name);
236 return result;
240 static int
241 convert(MPEquationParserState *state, const MPNumber *x, const char *x_units, const char *z_units, MPNumber *z)
243 if (state->options->convert)
244 return state->options->convert(x, x_units, z_units, z, state->options->callback_data);
245 else
246 return 0;
250 MPErrorCode
251 mp_equation_parse(const char *expression, MPEquationOptions *options, MPNumber *result, char **error_token)
253 int ret;
254 MPEquationParserState state;
255 yyscan_t yyscanner;
256 YY_BUFFER_STATE buffer;
258 if (!(expression && result) || strlen(expression) == 0)
259 return PARSER_ERR_INVALID;
261 memset(&state, 0, sizeof(MPEquationParserState));
262 state.options = options;
263 state.variable_is_defined = variable_is_defined;
264 state.get_variable = get_variable;
265 state.set_variable = set_variable;
266 state.function_is_defined = function_is_defined;
267 state.get_function = get_function;
268 state.convert = convert;
269 state.error = 0;
271 mp_clear_error();
273 _mp_equation_lex_init_extra(&state, &yyscanner);
274 buffer = _mp_equation__scan_string(expression, yyscanner);
276 ret = _mp_equation_parse(yyscanner);
277 if (state.error_token != NULL && error_token != NULL) {
278 *error_token = state.error_token;
281 _mp_equation__delete_buffer(buffer, yyscanner);
282 _mp_equation_lex_destroy(yyscanner);
284 /* Error during parsing */
285 if (state.error)
286 return state.error;
288 if (mp_get_error())
289 return PARSER_ERR_MP;
291 /* Failed to parse */
292 if (ret)
293 return PARSER_ERR_INVALID;
295 mp_set_from_mp(&state.ret, result);
297 return PARSER_ERR_NONE;
301 const char *
302 mp_error_code_to_string(MPErrorCode error_code)
304 switch(error_code)
306 case PARSER_ERR_NONE:
307 return "PARSER_ERR_NONE";
308 case PARSER_ERR_INVALID:
309 return "PARSER_ERR_INVALID";
310 case PARSER_ERR_OVERFLOW:
311 return "PARSER_ERR_OVERFLOW";
312 case PARSER_ERR_UNKNOWN_VARIABLE:
313 return "PARSER_ERR_UNKNOWN_VARIABLE";
314 case PARSER_ERR_UNKNOWN_FUNCTION:
315 return "PARSER_ERR_UNKNOWN_FUNCTION";
316 case PARSER_ERR_UNKNOWN_CONVERSION:
317 return "PARSER_ERR_UNKNOWN_CONVERSION";
318 case PARSER_ERR_MP:
319 return "PARSER_ERR_MP";
320 default:
321 return "Unknown parser error";
326 int _mp_equation_error(void *yylloc, MPEquationParserState *state, char *text)
328 return 0;