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