Replace lex/bison parser with hand-written parser
[gcalctool.git] / src / mp-equation.c
blob9b6a195419d89cad2cd678889577363143f6a10e
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>
13 #include <string.h>
14 #include <stdlib.h>
15 #include "parser.h"
17 static int
18 variable_is_defined(ParserState *state, const char *name)
20 /* FIXME: Make more generic */
21 if (strcmp(name, "e") == 0 || strcmp(name, "i") == 0 || strcmp(name, "π") == 0)
22 return 1;
23 if (state->options->variable_is_defined)
24 return state->options->variable_is_defined(name, state->options->callback_data);
25 return 0;
29 static int
30 get_variable(ParserState *state, const char *name, MPNumber *z)
32 int result = 1;
34 if (strcmp(name, "e") == 0)
35 mp_get_eulers(z);
36 else if (strcmp(name, "i") == 0)
37 mp_get_i(z);
38 else if (strcmp(name, "π") == 0)
39 mp_get_pi(z);
40 else if (state->options->get_variable)
41 result = state->options->get_variable(name, z, state->options->callback_data);
42 else
43 result = 0;
45 return result;
48 static void
49 set_variable(ParserState *state, const char *name, const MPNumber *x)
51 // Reserved words, e, π, mod, and, or, xor, not, abs, log, ln, sqrt, int, frac, sin, cos, ...
52 if (strcmp(name, "e") == 0 || strcmp(name, "i") == 0 || strcmp(name, "π") == 0)
53 return; // FALSE
55 if (state->options->set_variable)
56 state->options->set_variable(name, x, state->options->callback_data);
59 // FIXME: Accept "2sin" not "2 sin", i.e. let the tokenizer collect the multiple
60 // Parser then distinguishes between "sin"="s*i*n" or "sin5" = "sin 5" = "sin(5)"
61 // i.e. numbers+letters = variable or function depending on following arg
62 // letters+numbers = numbers+letters+numbers = function
65 int
66 sub_atoi(const char *data)
68 int i, value = 0;
69 const char *digits[] = {"₀", "₁", "₂", "₃", "₄", "₅", "₆", "₇", "₈", "₉", NULL};
71 do {
72 for(i = 0; digits[i] != NULL && strncmp(data, digits[i], strlen(digits[i])) != 0; i++);
73 if(digits[i] == NULL)
74 return -1;
75 data += strlen(digits[i]);
76 value = value * 10 + i;
77 } while(*data != '\0');
79 return value;
82 int
83 super_atoi(const char *data)
85 int i, sign = 1, value = 0;
86 const char *digits[11] = {"⁰", "¹", "²", "³", "⁴", "⁵", "⁶", "⁷", "⁸", "⁹", NULL};
88 if(strncmp(data, "⁻", strlen("⁻")) == 0) {
89 sign = -1;
90 data += strlen("⁻");
93 do {
94 for(i = 0; digits[i] != NULL && strncmp(data, digits[i], strlen(digits[i])) != 0; i++);
95 if(digits[i] == NULL)
96 return 0;
97 value = value * 10 + i;
98 data += strlen(digits[i]);
99 } while(*data != '\0');
101 return sign * value;
105 static int
106 function_is_defined(ParserState *state, const char *name)
108 char *c, *lower_name;
110 lower_name = strdup(name);
111 for (c = lower_name; *c; c++)
112 *c = tolower(*c);
114 /* FIXME: Make more generic */
115 if (strcmp(lower_name, "log") == 0 ||
116 (strncmp(lower_name, "log", 3) == 0 && sub_atoi(lower_name + 3) >= 0) ||
117 strcmp(lower_name, "ln") == 0 ||
118 strcmp(lower_name, "sqrt") == 0 ||
119 strcmp(lower_name, "abs") == 0 ||
120 strcmp(lower_name, "sgn") == 0 ||
121 strcmp(lower_name, "arg") == 0 ||
122 strcmp(lower_name, "conj") == 0 ||
123 strcmp(lower_name, "int") == 0 ||
124 strcmp(lower_name, "frac") == 0 ||
125 strcmp(lower_name, "floor") == 0 ||
126 strcmp(lower_name, "ceil") == 0 ||
127 strcmp(lower_name, "round") == 0 ||
128 strcmp(lower_name, "re") == 0 ||
129 strcmp(lower_name, "im") == 0 ||
130 strcmp(lower_name, "sin") == 0 || strcmp(lower_name, "cos") == 0 || strcmp(lower_name, "tan") == 0 ||
131 strcmp(lower_name, "asin") == 0 || strcmp(lower_name, "acos") == 0 || strcmp(lower_name, "atan") == 0 ||
132 strcmp(lower_name, "sin⁻¹") == 0 || strcmp(lower_name, "cos⁻¹") == 0 || strcmp(lower_name, "tan⁻¹") == 0 ||
133 strcmp(lower_name, "sinh") == 0 || strcmp(lower_name, "cosh") == 0 || strcmp(lower_name, "tanh") == 0 ||
134 strcmp(lower_name, "sinh⁻¹") == 0 || strcmp(lower_name, "cosh⁻¹") == 0 || strcmp(lower_name, "tanh⁻¹") == 0 ||
135 strcmp(lower_name, "asinh") == 0 || strcmp(lower_name, "acosh") == 0 || strcmp(lower_name, "atanh") == 0 ||
136 strcmp(lower_name, "ones") == 0 ||
137 strcmp(lower_name, "twos") == 0) {
138 g_free (lower_name);
139 return 1;
141 g_free (lower_name);
143 if (state->options->function_is_defined)
144 return state->options->function_is_defined(name, state->options->callback_data);
145 return 0;
149 static int
150 get_function(ParserState *state, const char *name, const MPNumber *x, MPNumber *z)
152 char *c, *lower_name;
153 int result = 1;
155 lower_name = strdup(name);
156 for (c = lower_name; *c; c++)
157 *c = tolower(*c);
159 // FIXME: Re Im ?
161 if (strcmp(lower_name, "log") == 0)
162 mp_logarithm(10, x, z); // FIXME: Default to ln
163 else if (strncmp(lower_name, "log", 3) == 0) {
164 int base;
166 base = sub_atoi(lower_name + 3);
167 if (base < 0)
168 result = 0;
169 else
170 mp_logarithm(base, x, z);
172 else if (strcmp(lower_name, "ln") == 0)
173 mp_ln(x, z);
174 else if (strcmp(lower_name, "sqrt") == 0) // √x
175 mp_sqrt(x, z);
176 else if (strcmp(lower_name, "abs") == 0) // |x|
177 mp_abs(x, z);
178 else if (strcmp(lower_name, "sgn") == 0)
179 mp_sgn(x, z);
180 else if (strcmp(lower_name, "arg") == 0)
181 mp_arg(x, state->options->angle_units, z);
182 else if (strcmp(lower_name, "conj") == 0)
183 mp_conjugate(x, z);
184 else if (strcmp(lower_name, "int") == 0)
185 mp_integer_component(x, z);
186 else if (strcmp(lower_name, "frac") == 0)
187 mp_fractional_component(x, z);
188 else if (strcmp(lower_name, "floor") == 0)
189 mp_floor(x, z);
190 else if (strcmp(lower_name, "ceil") == 0)
191 mp_ceiling(x, z);
192 else if (strcmp(lower_name, "round") == 0)
193 mp_round(x, z);
194 else if (strcmp(lower_name, "re") == 0)
195 mp_real_component(x, z);
196 else if (strcmp(lower_name, "im") == 0)
197 mp_imaginary_component(x, z);
198 else if (strcmp(lower_name, "sin") == 0)
199 mp_sin(x, state->options->angle_units, z);
200 else if (strcmp(lower_name, "cos") == 0)
201 mp_cos(x, state->options->angle_units, z);
202 else if (strcmp(lower_name, "tan") == 0)
203 mp_tan(x, state->options->angle_units, z);
204 else if (strcmp(lower_name, "sin⁻¹") == 0 || strcmp(lower_name, "asin") == 0)
205 mp_asin(x, state->options->angle_units, z);
206 else if (strcmp(lower_name, "cos⁻¹") == 0 || strcmp(lower_name, "acos") == 0)
207 mp_acos(x, state->options->angle_units, z);
208 else if (strcmp(lower_name, "tan⁻¹") == 0 || strcmp(lower_name, "atan") == 0)
209 mp_atan(x, state->options->angle_units, z);
210 else if (strcmp(lower_name, "sinh") == 0)
211 mp_sinh(x, z);
212 else if (strcmp(lower_name, "cosh") == 0)
213 mp_cosh(x, z);
214 else if (strcmp(lower_name, "tanh") == 0)
215 mp_tanh(x, z);
216 else if (strcmp(lower_name, "sinh⁻¹") == 0 || strcmp(lower_name, "asinh") == 0)
217 mp_asinh(x, z);
218 else if (strcmp(lower_name, "cosh⁻¹") == 0 || strcmp(lower_name, "acosh") == 0)
219 mp_acosh(x, z);
220 else if (strcmp(lower_name, "tanh⁻¹") == 0 || strcmp(lower_name, "atanh") == 0)
221 mp_atanh(x, z);
222 else if (strcmp(lower_name, "ones") == 0)
223 mp_ones_complement(x, state->options->wordlen, z);
224 else if (strcmp(lower_name, "twos") == 0)
225 mp_twos_complement(x, state->options->wordlen, z);
226 else if (state->options->get_function)
227 result = state->options->get_function(name, x, z, state->options->callback_data);
228 else
229 result = 0;
231 free(lower_name);
233 return result;
237 static int
238 convert(ParserState *state, const MPNumber *x, const char *x_units, const char *z_units, MPNumber *z)
240 if (state->options->convert)
241 return state->options->convert(x, x_units, z_units, z, state->options->callback_data);
242 else
243 return 0;
247 MPErrorCode
248 mp_equation_parse(const char *expression, MPEquationOptions *options, MPNumber *result, char **error_token)
250 int ret;
251 int err;
252 ParserState* state;
253 state = p_create_parser (expression, options);
255 if (!(expression && result) || strlen(expression) == 0)
256 return PARSER_ERR_INVALID;
258 state->variable_is_defined = variable_is_defined;
259 state->get_variable = get_variable;
260 state->set_variable = set_variable;
261 state->function_is_defined = function_is_defined;
262 state->get_function = get_function;
263 state->convert = convert;
264 state->error = 0;
265 mp_clear_error();
266 ret = p_parse (state);
267 if (state->error_token != NULL && error_token != NULL) {
268 *error_token = state->error_token;
270 /* Error during parsing */
271 if (state->error) {
272 err = state->error;
273 p_destroy_parser (state);
274 return err;
277 if (mp_get_error()) {
278 p_destroy_parser (state);
279 return PARSER_ERR_MP;
282 /* Failed to parse */
283 if (ret) {
284 p_destroy_parser (state);
285 return PARSER_ERR_INVALID;
287 mp_set_from_mp(&state->ret, result);
288 p_destroy_parser (state);
289 return PARSER_ERR_NONE;
293 const char *
294 mp_error_code_to_string(MPErrorCode error_code)
296 switch(error_code)
298 case PARSER_ERR_NONE:
299 return "PARSER_ERR_NONE";
300 case PARSER_ERR_INVALID:
301 return "PARSER_ERR_INVALID";
302 case PARSER_ERR_OVERFLOW:
303 return "PARSER_ERR_OVERFLOW";
304 case PARSER_ERR_UNKNOWN_VARIABLE:
305 return "PARSER_ERR_UNKNOWN_VARIABLE";
306 case PARSER_ERR_UNKNOWN_FUNCTION:
307 return "PARSER_ERR_UNKNOWN_FUNCTION";
308 case PARSER_ERR_UNKNOWN_CONVERSION:
309 return "PARSER_ERR_UNKNOWN_CONVERSION";
310 case PARSER_ERR_MP:
311 return "PARSER_ERR_MP";
312 default:
313 return "Unknown parser error";