add constant feature
[calc.git] / parser.c
CommitLineData
bc97a989 1#include <malloc.h>
f2927108 2#include <math.h>
87621fe1 3#include <stdio.h>
bc97a989
LM
4#include <stdlib.h>
5
6#include "debug.h"
bc97a989
LM
7
8#include "parser.h"
9
10/* compare codes */
11
12int codecmp (char *ref, char *str)
13{
14 int sig;
15
16 while (*ref != '\0') {
0b9cc9b0
LM
17 if (*ref == '\t') {
18 sig = (*str == '.') ? -1 : ((*str >= '0') && (*str <= '9'));
19 } else {
20 sig = *str - *ref;
21 }
bc97a989
LM
22 if (sig != 0) {
23 return (sig > 0) ? 1 : -1;
24 }
0b9cc9b0
LM
25 str++;
26 ref++;
bc97a989
LM
27 }
28
29 return 0;
30}
31
32/* allocate new element */
33
11cda8d7 34element_t *newelement (func_t function, int nbops, int prio)
bc97a989
LM
35{
36 element_t *new = (element_t *) calloc (1, sizeof (element_t));
37 if (new == NULL) {
04d68907 38 VERBOSE (ERROR, fprintf (stderr, "can't allocate memory\n"));
bc97a989
LM
39 return NULL;
40 }
41 new->func = function;
42 new->nbops = nbops;
11cda8d7 43 new->prio = prio;
bc97a989
LM
44
45 return new;
46}
47
031d7bba
LM
48/* desallocate element */
49
50void delelement (element_t *root)
51{
52 int i;
53 if ((root != NULL) && (root != ERROR_OP)) {
54 for (i = 0; i < root->nbops; i++) {
55 if ((root->ops[i] != NULL) && (root->ops[i] != ERROR_OP)) {
56 delelement (root->ops[i]);
57 }
58 }
59 free (root);
60 }
61}
62
bc97a989
LM
63/* functions */
64
c47a9298 65#define NB_OPERATORS 6
bc97a989
LM
66
67keyword_t operators[NB_OPERATORS] = {
11cda8d7
LM
68 { "+\t", Add, 2, 1, 1},
69 { "-\t", Sub, 2, 1, 1},
70 { "*", Mul, 2, 1, 2},
71 { "/", Div, 2, 1, 2},
c47a9298
LM
72 { "%", Mod, 2, 1, 3},
73 { "^", Pow, 2, 1, 4}
bc97a989
LM
74};
75
89cf0955 76#define NB_FUNCTIONS 9
bc97a989 77keyword_t functions[NB_FUNCTIONS] = {
c47a9298
LM
78 { "sqrt", Sqr, 1, 4, 5},
79 { "pow", Pow, 2, 3, 5},
80 { "cos", Cos, 1, 3, 5},
81 { "sin", Sin, 1, 3, 5},
471da7c9 82 { "atan", Atan, 1, 4, 5},
c47a9298 83 { "exp", Exp, 1, 3, 5},
89cf0955 84 { "log", Log, 1, 3, 5},
471da7c9
LM
85 { "quit", Quit, 0, 4, 5},
86 { "help", Help, 0, 4, 5}
87};
88
89#define NB_CONSTANTS 2
90keyword_t constants[NB_CONSTANTS] = {
91 { "e", E, 0, 1, 5},
92 { "pi", Pi, 0, 2, 5}
bc97a989
LM
93};
94
d2ff8478
LM
95/* subparser function */
96
97element_t *subparser (element_t **proot, char **pstr, func_t func, int nbops, int prio)
98{
99 element_t *new = newelement (func, nbops, prio);
100 if (new == NULL) {
101 return ERROR_OP;
102 }
103 new->ops[0] = *proot;
104 new->ops[1] = parser (*pstr, pstr, new->prio);
105 if (new->ops[1] == ERROR_OP) {
031d7bba
LM
106 delelement (new);
107 *proot = NULL;
d2ff8478
LM
108 return ERROR_OP;
109 }
3b4b0bbe 110 *proot = newelement (Val, 1, 5);
031d7bba
LM
111 if (*proot == NULL) {
112 delelement (new);
d2ff8478
LM
113 return ERROR_OP;
114 }
115 (*proot)->ops[0] = new;
116
117 return *proot;
118}
119
bc97a989
LM
120/* parser function */
121
ef37d966 122element_t *parser (char *str, char **next, int prio)
0b489a77 123{
bc97a989 124 element_t *root = NULL;
85b4a72c 125 int i;
bc97a989 126
04d68907 127 VERBOSE (DEBUG, fprintf (stdout, "Starting parsing\n"));
49223129 128
bc97a989
LM
129 /* main loop */
130 while (*str != '\0') {
131 int found = 0;
132 element_t *new = NULL;
04d68907 133 VERBOSE (INFO, fprintf (stdout, "Processing: %s\n", str));
bc97a989
LM
134
135 /* skip spaces and tabs */
136
137 if ((*str == ' ') || (*str == '\t')) {
138 str++;
139 continue;
140 }
141
85b4a72c 142 /* check for open bracket */
0b489a77 143
0b489a77 144 if (*str == '(') {
04d68907 145 VERBOSE (DEBUG, fprintf (stdout, "start processing bracket\n"));
0b489a77 146 if (root) {
85b4a72c
LM
147 do {
148 found = 0;
ef37d966 149 new = parser (str + 1, &str, 0);
85b4a72c 150 if (new == ERROR_OP) {
031d7bba 151 delelement (root);
85b4a72c
LM
152 return ERROR_OP;
153 }
154 for (i = 0; i < root->nbops; i++) {
155 if (root->ops[i] == NULL) {
0b489a77
LM
156 root->ops[i] = new;
157 found = 1;
158 break;
159 }
160 }
85b4a72c 161 if (!found) {
031d7bba
LM
162 delelement (new);
163 delelement (root);
85b4a72c
LM
164 return ERROR_OP;
165 }
166 } while (*str == ',');
167 } else {
3b4b0bbe 168 root = newelement (Val, 1, 5);
85b4a72c 169 if (root == NULL) {
0b489a77
LM
170 return ERROR_OP;
171 }
ef37d966 172 new = parser (str + 1, &str, 0);
85b4a72c 173 if ((new == ERROR_OP) || (*str == ',')) {
031d7bba
LM
174 delelement (new);
175 delelement (root);
efdfb543
LM
176 return ERROR_OP;
177 }
85b4a72c 178 root->ops[0] = new;
0b489a77 179 }
85b4a72c 180 str++;
04d68907 181 VERBOSE (DEBUG, fprintf (stdout, "stop processing bracket\n"));
efdfb543 182 continue;
49223129 183 }
85b4a72c
LM
184
185 /* check for closing bracket or koma */
186
187 if ((*str == ')') || (*str == ',')) {
49223129 188 if (next != NULL) {
85b4a72c 189 *next = str;
49223129
LM
190 }
191 return root;
0b489a77
LM
192 }
193
bc97a989
LM
194 /* look for operators */
195
196 for (i = 0; i < NB_OPERATORS; i++) {
197 keyword_t *operator = operators + i;
198 if (codecmp (operator->keyword, str) == 0) {
04d68907 199 VERBOSE (DEBUG, fprintf (stdout, "start processing operator\n"));
11cda8d7 200 if (root) {
ef37d966 201 if ((prio) && (prio > operator->prio)) {
04d68907 202 VERBOSE (DEBUG, fprintf (stdout, "stop because operator priority\n"));
ef37d966
LM
203 *next = str;
204 return root;
205 }
206 str += operator->offset;
04d68907 207 VERBOSE (INFO, fprintf (stdout, "Oper: %d\n", operator->func));
d2ff8478 208 if (subparser (&root, &str, operator->func, operator->nbops, operator->prio) == ERROR_OP) {
031d7bba 209 delelement (root);
ef37d966 210 return ERROR_OP;
11cda8d7 211 }
3b4b0bbe
LM
212 } else if (*str == '-') {
213 new = newelement (Sig, 1, 9);
214 if (new == NULL) {
215 return ERROR_OP;
216 }
217 root = new;
49223129 218 } else {
0b489a77 219 return ERROR_OP;
bc97a989 220 }
bc97a989 221 found = 1;
04d68907 222 VERBOSE (DEBUG, fprintf (stdout, "stop processing operator\n"));
bc97a989
LM
223 break;
224 }
225 }
226 if (found) {
bc97a989
LM
227 continue;
228 }
49223129 229
bc97a989
LM
230 /* look for functions */
231
232 for (i = 0; i < NB_FUNCTIONS; i++) {
233 keyword_t *function = functions + i;
234 if (codecmp (function->keyword, str) == 0) {
04d68907 235 VERBOSE (DEBUG, fprintf (stdout, "start processing function\n"));
bc97a989 236 if (root == NULL) {
04d68907 237 VERBOSE (INFO, fprintf (stdout, "Func: %d\n", function->func));
11cda8d7 238 new = newelement (function->func, function->nbops, function->prio);
bc97a989 239 if (new == NULL) {
0b489a77 240 return ERROR_OP;
bc97a989
LM
241 }
242 root = new;
031d7bba
LM
243 } else {
244 delelement (root);
0b489a77 245 return ERROR_OP;
bc97a989
LM
246 }
247 str += function->offset;
248 found = 1;
04d68907 249 VERBOSE (DEBUG, fprintf (stdout, "stop processing function\n"));
bc97a989
LM
250 break;
251 }
252 }
253 if (found) {
bc97a989
LM
254 continue;
255 }
256
471da7c9
LM
257 /* look for constant */
258
259 for (i = 0; i < NB_CONSTANTS; i++) {
260 keyword_t *constant = constants + i;
261 if (codecmp (constant->keyword, str) == 0) {
262 VERBOSE (DEBUG, fprintf (stdout, "start processing constant\n"));
263 if (root == NULL) {
264 VERBOSE (INFO, fprintf (stdout, "Func: %d\n", constant->func));
265 new = newelement (constant->func, constant->nbops, constant->prio);
266 if (new == NULL) {
267 return ERROR_OP;
268 }
269 root = new;
270 } else {
271 delelement (root);
272 return ERROR_OP;
273 }
274 str += constant->offset;
275 found = 1;
276 VERBOSE (DEBUG, fprintf (stdout, "stop processing constant\n"));
277 break;
278 }
279 }
280 if (found) {
281 continue;
282 }
283
bc97a989
LM
284 /* look for number */
285
0b9cc9b0
LM
286 if (((*str >= '0') && (*str <= '9')) ||
287 (*str == '.') || (*str == '+') || (*str == '-')) {
04d68907 288 VERBOSE (DEBUG, fprintf (stdout, "start processing value\n"));
bc97a989 289 char *pt;
fd88e359 290 double value = strtod (str, &pt);
04d68907 291 VERBOSE (INFO, fprintf (stdout, "Value: %f\n", value));
bc97a989 292 if (str != pt) {
bc97a989 293 if (root == NULL) {
3b4b0bbe 294 new = newelement (Val, 1, 5);
c47a9298
LM
295 if (new == NULL) {
296 return ERROR_OP;
297 }
298 new->value = value;
bc97a989 299 root = new;
c47a9298 300 str = pt;
0b9cc9b0
LM
301 } else if (root->func == Val) {
302 if ((*str == '+') || (*str == '-')) {
c47a9298 303 if ((prio) && (prio > 1)) {
04d68907 304 VERBOSE (DEBUG, fprintf (stdout, "stop because operator priority\n"));
c47a9298
LM
305 *next = str;
306 return root;
307 }
d2ff8478 308 if (subparser (&root, &str, Add, 2, 1) == ERROR_OP) {
031d7bba 309 delelement (root);
0b489a77
LM
310 return ERROR_OP;
311 }
0b9cc9b0 312 } else {
031d7bba 313 delelement (root);
0b489a77 314 return ERROR_OP;
bc97a989 315 }
0b9cc9b0 316 } else {
031d7bba 317 delelement (root);
0b9cc9b0 318 return ERROR_OP;
bc97a989 319 }
bc97a989
LM
320 found = 1;
321 }
04d68907 322 VERBOSE (DEBUG, fprintf (stdout, "stop processing value\n"));
bc97a989
LM
323 }
324
325 /* error */
326
327 if (!found) {
031d7bba 328 delelement (root);
0b489a77 329 return ERROR_OP;
bc97a989
LM
330 }
331
332 }
333
49223129
LM
334 if (next != NULL) {
335 *next = str;
336 }
031d7bba 337
bc97a989
LM
338 return root;
339}
340
341/* print element tree */
342
343void print_element (element_t *root, int level)
344{
345 char *func = NULL;
346 int i;
347
49223129 348 if ((root == NULL) || (root == ERROR_OP)) {
bc97a989 349 return;
49223129
LM
350 }
351
bc97a989 352 for (i = 0; i < level; i++) {
04d68907 353 fprintf (stdout, " ");
bc97a989
LM
354 }
355
356 switch (root->func) {
357 case Val: func = "Value"; break;
89cf0955 358 case Sig: func = "Sign"; break;
bc97a989
LM
359 case Add: func = "Addition"; break;
360 case Sub: func = "Subtraction"; break;
361 case Mul: func = "Multiplication"; break;
362 case Div: func = "Division"; break;
c47a9298 363 case Mod: func = "Modulo"; break;
bc97a989
LM
364 case Pow: func = "Power"; break;
365 case Sqr: func = "Square Root"; break;
366 case Cos: func = "Cosine"; break;
367 case Sin: func = "Sine"; break;
471da7c9 368 case Atan: func = "Arc Tangent"; break;
bc97a989
LM
369 case Log: func = "Logarithm"; break;
370 case Exp: func = "Exponantial"; break;
471da7c9
LM
371 case Quit: func = "Quit"; break;
372 case Help: func = "Help"; break;
373 case Pi: func = "Pi"; break;
374 case E: func = "E"; break;
bc97a989
LM
375 }
376
04d68907 377 fprintf (stdout, "Function: %s\n", func);
bc97a989 378
85b4a72c 379 if ((root->func == Val) && (root->ops[0] == NULL)) {
bc97a989 380 for (i = 0; i < level; i++) {
04d68907 381 fprintf (stdout, " ");
bc97a989 382 }
04d68907 383 fprintf (stdout, "value: %f\n", root->value);
bc97a989
LM
384 } else {
385 for (i = 0; i < root->nbops; i++) {
386 print_element (root->ops[i], level + 1);
387 }
388 }
389}
390
89cf0955
LM
391/* quit function */
392
393void quit (void)
394{
04d68907 395 fprintf (stdout, "bye\n");
89cf0955
LM
396 exit (0);
397}
398
399/* help message */
400
401void help (void)
402{
04d68907
LM
403 fprintf (stdout, "calc is a simple calculator\n\n");
404 fprintf (stdout, "supported operators:\n");
405 fprintf (stdout, " + - * / %% ^\n\n");
406 fprintf (stdout, "supported functions:\n");
407 fprintf (stdout, " pow sqrt cos sin atan log exp\n\n");
408 fprintf (stdout, "miscellaneous functions:\n");
471da7c9
LM
409 fprintf (stdout, " quit help\n\n");
410 fprintf (stdout, "supported constants:\n");
411 fprintf (stdout, " e pi\n");
89cf0955 412}
3b4b0bbe 413
f2927108
LM
414/* evaluate element tree */
415
89cf0955
LM
416#define MASK_SUB 0x1
417#define MASK_DIV 0x2
418
3b4b0bbe 419double evaluate_element (element_t *root, char mask)
f2927108
LM
420{
421 double op0 = 0, op1 = 0;
3b4b0bbe 422 char nextmask = mask;
f2927108
LM
423
424 if ((root == NULL) || (root == ERROR_OP)) {
04d68907 425 VERBOSE (WARNING, fprintf (stdout, "error while evaluating\n"));
f2927108
LM
426 return 0;
427 }
428
3b4b0bbe
LM
429 /* mask to manage sub operator sub and div */
430 switch (root->func) {
431 case Add:
432 nextmask &= ~MASK_SUB;
433 nextmask &= ~MASK_DIV;
434 break;
435 case Sub:
436 nextmask |= MASK_SUB;
437 nextmask &= ~MASK_DIV;
438 break;
439 case Mul:
440 nextmask &= ~MASK_DIV;
441 break;
442 case Div:
443 nextmask |= MASK_DIV;
444 break;
445 default:
446 nextmask = mask;
447 }
448
f2927108
LM
449 switch (root->func) {
450 case Val:
3b4b0bbe
LM
451 case Sig:
452 op0 = (root->ops[0]) ? evaluate_element (root->ops[0], nextmask) : root->value;
f2927108
LM
453 break;
454 case Add:
455 case Sub:
456 case Mul:
457 case Div:
c47a9298 458 case Mod:
f2927108
LM
459 case Pow:
460 if (root->ops[1]) {
3b4b0bbe 461 op1 = evaluate_element (root->ops[1], nextmask);
f2927108 462 } else {
04d68907 463 VERBOSE (WARNING, fprintf (stdout, "error while evaluating (op[1])\n"));
f2927108
LM
464 return 0;
465 }
466 /* fallthrough */
467 case Sqr:
468 case Cos:
469 case Sin:
471da7c9 470 case Atan:
f2927108
LM
471 case Log:
472 case Exp:
473 if (root->ops[0]) {
3b4b0bbe 474 op0 = evaluate_element (root->ops[0], 0);
f2927108 475 } else {
04d68907 476 VERBOSE (WARNING, fprintf (stdout, "error while evaluating (op[0])\n"));
f2927108
LM
477 return 0;
478 }
89cf0955 479 break;
471da7c9
LM
480 case Quit:
481 case Help:
482 case Pi:
483 case E:
89cf0955 484 break;
f2927108
LM
485 }
486
487 switch (root->func) {
3b4b0bbe
LM
488 case Val: return op0;
489 case Sig: return -op0;
490 case Add: return ((mask & MASK_SUB) == 0) ? op0 + op1 : op0 - op1;
491 case Sub: return ((mask & MASK_SUB) == 0) ? op0 - op1 : op0 + op1;
492 case Mul: return ((mask & MASK_DIV) == 0) ? op0 * op1 : op0 / op1;
493 case Div: return ((mask & MASK_DIV) == 0) ? op0 / op1 : op0 * op1;
c47a9298 494 case Mod: return fmod (op0, op1);
f2927108
LM
495 case Pow: return pow (op0, op1);
496 case Sqr: return sqrt (op0);
497 case Cos: return cos (op0);
498 case Sin: return sin (op0);
471da7c9 499 case Atan: return atan (op0);
f2927108
LM
500 case Log: return log (op0);
501 case Exp: return exp (op0);
471da7c9
LM
502 case Quit: quit (); break;
503 case Help: help (); break;
504 case Pi: return M_PI;
505 case E: return M_E;
f2927108
LM
506 }
507
508 return 0;
509}
510
bc97a989 511/* vim: set ts=4 sw=4 et: */