condition 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
5075f6ea
LM
10/* global variables */
11
12double answer = 0;
13
6ba1dd0f
LM
14#define STORAGE_SIZE 10
15double storage[STORAGE_SIZE] = {0};
16
bc97a989
LM
17/* compare codes */
18
19int codecmp (char *ref, char *str)
20{
21 int sig;
22
23 while (*ref != '\0') {
0b9cc9b0
LM
24 if (*ref == '\t') {
25 sig = (*str == '.') ? -1 : ((*str >= '0') && (*str <= '9'));
26 } else {
27 sig = *str - *ref;
28 }
bc97a989
LM
29 if (sig != 0) {
30 return (sig > 0) ? 1 : -1;
31 }
0b9cc9b0
LM
32 str++;
33 ref++;
bc97a989
LM
34 }
35
36 return 0;
37}
38
39/* allocate new element */
40
11cda8d7 41element_t *newelement (func_t function, int nbops, int prio)
bc97a989
LM
42{
43 element_t *new = (element_t *) calloc (1, sizeof (element_t));
44 if (new == NULL) {
04d68907 45 VERBOSE (ERROR, fprintf (stderr, "can't allocate memory\n"));
bc97a989
LM
46 return NULL;
47 }
48 new->func = function;
49 new->nbops = nbops;
11cda8d7 50 new->prio = prio;
bc97a989
LM
51
52 return new;
53}
54
031d7bba
LM
55/* desallocate element */
56
57void delelement (element_t *root)
58{
59 int i;
60 if ((root != NULL) && (root != ERROR_OP)) {
61 for (i = 0; i < root->nbops; i++) {
62 if ((root->ops[i] != NULL) && (root->ops[i] != ERROR_OP)) {
63 delelement (root->ops[i]);
64 }
65 }
66 free (root);
67 }
68}
69
bc97a989
LM
70/* functions */
71
ca3e2a2f 72#define NB_OPERATORS 14
bc97a989 73keyword_t operators[NB_OPERATORS] = {
11cda8d7
LM
74 { "+\t", Add, 2, 1, 1},
75 { "-\t", Sub, 2, 1, 1},
76 { "*", Mul, 2, 1, 2},
77 { "/", Div, 2, 1, 2},
c47a9298 78 { "%", Mod, 2, 1, 3},
7fe742c9 79 { "^", Pow, 2, 1, 4},
ca3e2a2f
LM
80 { "==", Equal, 2, 2, -1},
81 { "!=", Diff, 2, 2, -1},
82 { ">=", Ge, 2, 2, -1},
83 { "<=", Le, 2, 2, -1},
84 { ">", Gt, 2, 1, -1},
85 { "<", Lt, 2, 1, -1},
86 { "&", And, 2, 1, -2},
87 { "|", Or, 2, 1, -2}
bc97a989
LM
88};
89
94b4e517 90#define NB_FUNCTIONS 14
bc97a989 91keyword_t functions[NB_FUNCTIONS] = {
c47a9298
LM
92 { "sqrt", Sqr, 1, 4, 5},
93 { "pow", Pow, 2, 3, 5},
94 { "cos", Cos, 1, 3, 5},
95 { "sin", Sin, 1, 3, 5},
471da7c9 96 { "atan", Atan, 1, 4, 5},
c47a9298 97 { "exp", Exp, 1, 3, 5},
89cf0955 98 { "log", Log, 1, 3, 5},
ca3e2a2f 99 { "sto", Store, 2, 3, 9},
6ba1dd0f 100 { "rcl", Recall, 1, 3, 5},
ca3e2a2f
LM
101 { "disp", Disp, 0, 4, 9},
102 { "quit", Quit, 0, 4, 9},
103 { "help", Help, 0, 4, 9},
94b4e517
LM
104 { "!", Not, 1, 1, 6},
105 { "cond", Cond, 3, 4, 5}
471da7c9
LM
106};
107
5075f6ea 108#define NB_CONSTANTS 3
471da7c9 109keyword_t constants[NB_CONSTANTS] = {
5075f6ea
LM
110 { "ans", Ans, 0, 3, 5},
111 { "e", E, 0, 1, 5},
112 { "pi", Pi, 0, 2, 5}
bc97a989
LM
113};
114
d2ff8478
LM
115/* subparser function */
116
117element_t *subparser (element_t **proot, char **pstr, func_t func, int nbops, int prio)
118{
119 element_t *new = newelement (func, nbops, prio);
120 if (new == NULL) {
121 return ERROR_OP;
122 }
123 new->ops[0] = *proot;
124 new->ops[1] = parser (*pstr, pstr, new->prio);
686c4bc2 125 if ((new->ops[1] == NULL) || ((new->ops[1] != ERROR_OP) && (new->ops[1]->prio == 9))) {
67b13e3d
LM
126 delelement (new->ops[1]);
127 new->ops[1] = ERROR_OP;
128 }
d2ff8478 129 if (new->ops[1] == ERROR_OP) {
031d7bba
LM
130 delelement (new);
131 *proot = NULL;
d2ff8478
LM
132 return ERROR_OP;
133 }
3b4b0bbe 134 *proot = newelement (Val, 1, 5);
031d7bba
LM
135 if (*proot == NULL) {
136 delelement (new);
d2ff8478
LM
137 return ERROR_OP;
138 }
139 (*proot)->ops[0] = new;
140
141 return *proot;
142}
143
bc97a989
LM
144/* parser function */
145
ef37d966 146element_t *parser (char *str, char **next, int prio)
0b489a77 147{
bc97a989 148 element_t *root = NULL;
85b4a72c 149 int i;
bc97a989 150
04d68907 151 VERBOSE (DEBUG, fprintf (stdout, "Starting parsing\n"));
49223129 152
bc97a989
LM
153 /* main loop */
154 while (*str != '\0') {
155 int found = 0;
156 element_t *new = NULL;
04d68907 157 VERBOSE (INFO, fprintf (stdout, "Processing: %s\n", str));
bc97a989
LM
158
159 /* skip spaces and tabs */
160
161 if ((*str == ' ') || (*str == '\t')) {
162 str++;
163 continue;
164 }
165
85b4a72c 166 /* check for open bracket */
0b489a77 167
0b489a77 168 if (*str == '(') {
04d68907 169 VERBOSE (DEBUG, fprintf (stdout, "start processing bracket\n"));
0b489a77 170 if (root) {
85b4a72c
LM
171 do {
172 found = 0;
ef37d966 173 new = parser (str + 1, &str, 0);
686c4bc2 174 if ((new == NULL) || ((new != ERROR_OP) && (new->prio == 9))) {
67b13e3d
LM
175 delelement (new);
176 new = ERROR_OP;
177 }
686c4bc2 178 if ((new == NULL) || (new == ERROR_OP)) {
031d7bba 179 delelement (root);
85b4a72c
LM
180 return ERROR_OP;
181 }
182 for (i = 0; i < root->nbops; i++) {
183 if (root->ops[i] == NULL) {
0b489a77
LM
184 root->ops[i] = new;
185 found = 1;
186 break;
187 }
188 }
85b4a72c 189 if (!found) {
031d7bba
LM
190 delelement (new);
191 delelement (root);
85b4a72c
LM
192 return ERROR_OP;
193 }
194 } while (*str == ',');
195 } else {
3b4b0bbe 196 root = newelement (Val, 1, 5);
85b4a72c 197 if (root == NULL) {
0b489a77
LM
198 return ERROR_OP;
199 }
ef37d966 200 new = parser (str + 1, &str, 0);
686c4bc2 201 if ((new == NULL) || ((new != ERROR_OP) && (new->prio == 9))) {
67b13e3d
LM
202 delelement (new);
203 new = ERROR_OP;
204 }
686c4bc2 205 if ((new == NULL) || (new == ERROR_OP) || (*str == ',')) {
031d7bba
LM
206 delelement (new);
207 delelement (root);
efdfb543
LM
208 return ERROR_OP;
209 }
85b4a72c 210 root->ops[0] = new;
0b489a77 211 }
3c0db5bc
LM
212 if (*str != ')') {
213 delelement (root);
214 return ERROR_OP;
215 }
85b4a72c 216 str++;
04d68907 217 VERBOSE (DEBUG, fprintf (stdout, "stop processing bracket\n"));
efdfb543 218 continue;
49223129 219 }
85b4a72c
LM
220
221 /* check for closing bracket or koma */
222
223 if ((*str == ')') || (*str == ',')) {
49223129 224 if (next != NULL) {
85b4a72c 225 *next = str;
49223129
LM
226 }
227 return root;
0b489a77
LM
228 }
229
bc97a989
LM
230 /* look for operators */
231
232 for (i = 0; i < NB_OPERATORS; i++) {
233 keyword_t *operator = operators + i;
234 if (codecmp (operator->keyword, str) == 0) {
04d68907 235 VERBOSE (DEBUG, fprintf (stdout, "start processing operator\n"));
11cda8d7 236 if (root) {
ef37d966 237 if ((prio) && (prio > operator->prio)) {
04d68907 238 VERBOSE (DEBUG, fprintf (stdout, "stop because operator priority\n"));
ef37d966
LM
239 *next = str;
240 return root;
241 }
242 str += operator->offset;
04d68907 243 VERBOSE (INFO, fprintf (stdout, "Oper: %d\n", operator->func));
d2ff8478 244 if (subparser (&root, &str, operator->func, operator->nbops, operator->prio) == ERROR_OP) {
031d7bba 245 delelement (root);
ef37d966 246 return ERROR_OP;
11cda8d7 247 }
3b4b0bbe
LM
248 } else if (*str == '-') {
249 new = newelement (Sig, 1, 9);
250 if (new == NULL) {
251 return ERROR_OP;
252 }
253 root = new;
49223129 254 } else {
0b489a77 255 return ERROR_OP;
bc97a989 256 }
bc97a989 257 found = 1;
04d68907 258 VERBOSE (DEBUG, fprintf (stdout, "stop processing operator\n"));
bc97a989
LM
259 break;
260 }
261 }
262 if (found) {
bc97a989
LM
263 continue;
264 }
49223129 265
bc97a989
LM
266 /* look for functions */
267
268 for (i = 0; i < NB_FUNCTIONS; i++) {
269 keyword_t *function = functions + i;
270 if (codecmp (function->keyword, str) == 0) {
04d68907 271 VERBOSE (DEBUG, fprintf (stdout, "start processing function\n"));
bc97a989 272 if (root == NULL) {
04d68907 273 VERBOSE (INFO, fprintf (stdout, "Func: %d\n", function->func));
11cda8d7 274 new = newelement (function->func, function->nbops, function->prio);
bc97a989 275 if (new == NULL) {
0b489a77 276 return ERROR_OP;
bc97a989
LM
277 }
278 root = new;
031d7bba
LM
279 } else {
280 delelement (root);
0b489a77 281 return ERROR_OP;
bc97a989
LM
282 }
283 str += function->offset;
284 found = 1;
04d68907 285 VERBOSE (DEBUG, fprintf (stdout, "stop processing function\n"));
bc97a989
LM
286 break;
287 }
288 }
289 if (found) {
bc97a989
LM
290 continue;
291 }
292
471da7c9
LM
293 /* look for constant */
294
295 for (i = 0; i < NB_CONSTANTS; i++) {
296 keyword_t *constant = constants + i;
297 if (codecmp (constant->keyword, str) == 0) {
298 VERBOSE (DEBUG, fprintf (stdout, "start processing constant\n"));
299 if (root == NULL) {
5075f6ea 300 VERBOSE (INFO, fprintf (stdout, "Const: %d\n", constant->func));
471da7c9
LM
301 new = newelement (constant->func, constant->nbops, constant->prio);
302 if (new == NULL) {
303 return ERROR_OP;
304 }
305 root = new;
306 } else {
307 delelement (root);
308 return ERROR_OP;
309 }
310 str += constant->offset;
311 found = 1;
312 VERBOSE (DEBUG, fprintf (stdout, "stop processing constant\n"));
313 break;
314 }
315 }
316 if (found) {
317 continue;
318 }
319
bc97a989
LM
320 /* look for number */
321
0b9cc9b0
LM
322 if (((*str >= '0') && (*str <= '9')) ||
323 (*str == '.') || (*str == '+') || (*str == '-')) {
04d68907 324 VERBOSE (DEBUG, fprintf (stdout, "start processing value\n"));
bc97a989 325 char *pt;
fd88e359 326 double value = strtod (str, &pt);
04d68907 327 VERBOSE (INFO, fprintf (stdout, "Value: %f\n", value));
bc97a989 328 if (str != pt) {
ce6627f2 329 if ((root == NULL) || (root->prio == 6)) {
3b4b0bbe 330 new = newelement (Val, 1, 5);
c47a9298
LM
331 if (new == NULL) {
332 return ERROR_OP;
333 }
334 new->value = value;
ce6627f2
LM
335 if (root == NULL) {
336 root = new;
337 } else {
338 for (i = 0; i < root->nbops; i++) {
339 if (root->ops[i] == NULL) {
340 root->ops[i] = new;
341 found = 1;
342 break;
343 }
344 }
345 if (!found) {
346 delelement (new);
347 delelement (root);
348 return ERROR_OP;
349 }
350 }
c47a9298 351 str = pt;
32741902
LM
352 } else if ((*str == '+') || (*str == '-')) {
353 if ((prio) && (prio > 1)) {
354 VERBOSE (DEBUG, fprintf (stdout, "stop because operator priority\n"));
355 *next = str;
356 return root;
357 }
358 if (subparser (&root, &str, Add, 2, 1) == ERROR_OP) {
031d7bba 359 delelement (root);
0b489a77 360 return ERROR_OP;
bc97a989 361 }
0b9cc9b0 362 } else {
031d7bba 363 delelement (root);
0b9cc9b0 364 return ERROR_OP;
bc97a989 365 }
bc97a989
LM
366 found = 1;
367 }
04d68907 368 VERBOSE (DEBUG, fprintf (stdout, "stop processing value\n"));
bc97a989
LM
369 }
370
371 /* error */
372
373 if (!found) {
031d7bba 374 delelement (root);
0b489a77 375 return ERROR_OP;
bc97a989
LM
376 }
377
378 }
379
49223129
LM
380 if (next != NULL) {
381 *next = str;
382 }
031d7bba 383
bc97a989
LM
384 return root;
385}
386
387/* print element tree */
388
389void print_element (element_t *root, int level)
390{
391 char *func = NULL;
392 int i;
393
49223129 394 if ((root == NULL) || (root == ERROR_OP)) {
bc97a989 395 return;
49223129
LM
396 }
397
bc97a989 398 for (i = 0; i < level; i++) {
04d68907 399 fprintf (stdout, " ");
bc97a989
LM
400 }
401
402 switch (root->func) {
403 case Val: func = "Value"; break;
89cf0955 404 case Sig: func = "Sign"; break;
bc97a989
LM
405 case Add: func = "Addition"; break;
406 case Sub: func = "Subtraction"; break;
407 case Mul: func = "Multiplication"; break;
408 case Div: func = "Division"; break;
c47a9298 409 case Mod: func = "Modulo"; break;
bc97a989
LM
410 case Pow: func = "Power"; break;
411 case Sqr: func = "Square Root"; break;
412 case Cos: func = "Cosine"; break;
413 case Sin: func = "Sine"; break;
471da7c9 414 case Atan: func = "Arc Tangent"; break;
bc97a989
LM
415 case Log: func = "Logarithm"; break;
416 case Exp: func = "Exponantial"; break;
6ba1dd0f
LM
417 case Store: func = "Store"; break;
418 case Recall: func = "Recall"; break;
419 case Disp: func = "Display"; break;
471da7c9
LM
420 case Quit: func = "Quit"; break;
421 case Help: func = "Help"; break;
5075f6ea 422 case Ans: func = "Ans"; break;
471da7c9
LM
423 case Pi: func = "Pi"; break;
424 case E: func = "E"; break;
7fe742c9
LM
425 case Equal: func = "Equal"; break;
426 case Diff: func = "Different"; break;
427 case Ge: func = "Greater or equal"; break;
428 case Le: func = "Lesser or equal"; break;
429 case Gt: func = "Greater"; break;
430 case Lt: func = "Lesser"; break;
ca3e2a2f
LM
431 case And: func = "And"; break;
432 case Or: func = "Or"; break;
433 case Not: func = "Not"; break;
94b4e517 434 case Cond: func = "Condition"; break;
bc97a989
LM
435 }
436
04d68907 437 fprintf (stdout, "Function: %s\n", func);
bc97a989 438
85b4a72c 439 if ((root->func == Val) && (root->ops[0] == NULL)) {
bc97a989 440 for (i = 0; i < level; i++) {
04d68907 441 fprintf (stdout, " ");
bc97a989 442 }
04d68907 443 fprintf (stdout, "value: %f\n", root->value);
bc97a989
LM
444 } else {
445 for (i = 0; i < root->nbops; i++) {
446 print_element (root->ops[i], level + 1);
447 }
448 }
449}
450
6ba1dd0f
LM
451/* storage functions */
452
3639df66 453double store (int index, double value)
6ba1dd0f
LM
454{
455 if ((index > 0) && (index <= STORAGE_SIZE)) {
3639df66 456 storage[index - 1] = value;
6ba1dd0f
LM
457 } else {
458 VERBOSE (WARNING, fprintf (stdout, "invalid index (%d) [1, %d]\n", index, STORAGE_SIZE));
459 }
3639df66 460 return value;
6ba1dd0f
LM
461}
462
463double recall (int index)
464{
465 if ((index > 0) && (index <= STORAGE_SIZE)) {
466 return storage[index - 1];
467 } else {
468 VERBOSE (WARNING, fprintf (stdout, "invalid index (%d) [1, %d]\n", index, STORAGE_SIZE));
469 }
470 return 0;
471}
472
473void display (void)
474{
475 int i;
476 fprintf (stdout, "storage:");
477 for (i = 0; i < STORAGE_SIZE; i++) {
478 fprintf (stdout, " %g", storage[i]);
479 }
480 fprintf (stdout, "\n");
481}
482
89cf0955
LM
483/* quit function */
484
485void quit (void)
486{
04d68907 487 fprintf (stdout, "bye\n");
89cf0955
LM
488 exit (0);
489}
490
491/* help message */
492
493void help (void)
494{
04d68907 495 fprintf (stdout, "calc is a simple calculator\n\n");
94b4e517
LM
496 fprintf (stdout, "supported operators:");
497 fprintf (stdout, " + - * / %% ^\n");
498 fprintf (stdout, "camparison operators:");
499 fprintf (stdout, " == != >= <= > <\n");
500 fprintf (stdout, "logical operators:");
501 fprintf (stdout, " & | !\n");
502 fprintf (stdout, "supported functions:");
503 fprintf (stdout, " pow sqrt cos sin atan log exp\n");
504 fprintf (stdout, "storage functions:");
505 fprintf (stdout, " sto rcl\n");
506 fprintf (stdout, "conditional functions:");
507 fprintf (stdout, " cond\n");
508 fprintf (stdout, "miscellaneous functions:");
509 fprintf (stdout, " quit help\n");
510 fprintf (stdout, "supported constants:");
471da7c9 511 fprintf (stdout, " e pi\n");
89cf0955 512}
3b4b0bbe 513
f2927108
LM
514/* evaluate element tree */
515
89cf0955
LM
516#define MASK_SUB 0x1
517#define MASK_DIV 0x2
518
3b4b0bbe 519double evaluate_element (element_t *root, char mask)
f2927108
LM
520{
521 double op0 = 0, op1 = 0;
3b4b0bbe 522 char nextmask = mask;
f2927108
LM
523
524 if ((root == NULL) || (root == ERROR_OP)) {
04d68907 525 VERBOSE (WARNING, fprintf (stdout, "error while evaluating\n"));
f2927108
LM
526 return 0;
527 }
528
3b4b0bbe
LM
529 /* mask to manage sub operator sub and div */
530 switch (root->func) {
531 case Add:
532 nextmask &= ~MASK_SUB;
533 nextmask &= ~MASK_DIV;
534 break;
535 case Sub:
536 nextmask |= MASK_SUB;
537 nextmask &= ~MASK_DIV;
538 break;
539 case Mul:
540 nextmask &= ~MASK_DIV;
541 break;
542 case Div:
543 nextmask |= MASK_DIV;
544 break;
545 default:
546 nextmask = mask;
547 }
548
f2927108
LM
549 switch (root->func) {
550 case Val:
3b4b0bbe
LM
551 case Sig:
552 op0 = (root->ops[0]) ? evaluate_element (root->ops[0], nextmask) : root->value;
f2927108
LM
553 break;
554 case Add:
555 case Sub:
556 case Mul:
557 case Div:
c47a9298 558 case Mod:
f2927108 559 case Pow:
3639df66 560 case Store:
7fe742c9
LM
561 case Equal:
562 case Diff:
563 case Ge:
564 case Le:
565 case Gt:
566 case Lt:
ca3e2a2f
LM
567 case And:
568 case Or:
f2927108 569 if (root->ops[1]) {
3b4b0bbe 570 op1 = evaluate_element (root->ops[1], nextmask);
3639df66 571 } else if (root->func != Store) {
04d68907 572 VERBOSE (WARNING, fprintf (stdout, "error while evaluating (op[1])\n"));
f2927108
LM
573 return 0;
574 }
575 /* fallthrough */
576 case Sqr:
577 case Cos:
578 case Sin:
471da7c9 579 case Atan:
f2927108
LM
580 case Log:
581 case Exp:
6ba1dd0f 582 case Recall:
ca3e2a2f 583 case Not:
94b4e517 584 case Cond:
f2927108 585 if (root->ops[0]) {
3b4b0bbe 586 op0 = evaluate_element (root->ops[0], 0);
f2927108 587 } else {
04d68907 588 VERBOSE (WARNING, fprintf (stdout, "error while evaluating (op[0])\n"));
f2927108
LM
589 return 0;
590 }
89cf0955 591 break;
6ba1dd0f 592 case Disp:
471da7c9
LM
593 case Quit:
594 case Help:
5075f6ea 595 case Ans:
471da7c9
LM
596 case Pi:
597 case E:
89cf0955 598 break;
f2927108
LM
599 }
600
601 switch (root->func) {
3b4b0bbe
LM
602 case Val: return op0;
603 case Sig: return -op0;
604 case Add: return ((mask & MASK_SUB) == 0) ? op0 + op1 : op0 - op1;
605 case Sub: return ((mask & MASK_SUB) == 0) ? op0 - op1 : op0 + op1;
606 case Mul: return ((mask & MASK_DIV) == 0) ? op0 * op1 : op0 / op1;
607 case Div: return ((mask & MASK_DIV) == 0) ? op0 / op1 : op0 * op1;
c47a9298 608 case Mod: return fmod (op0, op1);
f2927108
LM
609 case Pow: return pow (op0, op1);
610 case Sqr: return sqrt (op0);
611 case Cos: return cos (op0);
612 case Sin: return sin (op0);
471da7c9 613 case Atan: return atan (op0);
f2927108
LM
614 case Log: return log (op0);
615 case Exp: return exp (op0);
3639df66 616 case Store: return store ((int)op0, (op1) ? op1 : answer);
6ba1dd0f
LM
617 case Recall: return recall ((int)op0);
618 case Disp: display (); break;
471da7c9
LM
619 case Quit: quit (); break;
620 case Help: help (); break;
5075f6ea 621 case Ans: return answer;
471da7c9
LM
622 case Pi: return M_PI;
623 case E: return M_E;
7fe742c9
LM
624 case Equal: return op0 == op1;
625 case Diff: return op0 != op1;
626 case Ge: return op0 >= op1;
627 case Le: return op0 <= op1;
628 case Gt: return op0 > op1;
629 case Lt: return op0 < op1;
ca3e2a2f
LM
630 case And: return (op0 != 0) && (op1 != 0);
631 case Or: return (op0 != 0) || (op1 != 0);
632 case Not: return (op0 == 0);
94b4e517
LM
633 case Cond:
634 if ((op0) && (root->ops[1])) {
635 return evaluate_element (root->ops[1], 0);
636 } else if ((!op0) && (root->ops[2])) {
637 return evaluate_element (root->ops[2], 0);
638 } else {
639 return 0;
640 }
f2927108
LM
641 }
642
643 return 0;
644}
645
bc97a989 646/* vim: set ts=4 sw=4 et: */