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