add constant feature
[calc.git] / parser.c
... / ...
CommitLineData
1#include <malloc.h>
2#include <math.h>
3#include <stdio.h>
4#include <stdlib.h>
5
6#include "debug.h"
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') {
17 if (*ref == '\t') {
18 sig = (*str == '.') ? -1 : ((*str >= '0') && (*str <= '9'));
19 } else {
20 sig = *str - *ref;
21 }
22 if (sig != 0) {
23 return (sig > 0) ? 1 : -1;
24 }
25 str++;
26 ref++;
27 }
28
29 return 0;
30}
31
32/* allocate new element */
33
34element_t *newelement (func_t function, int nbops, int prio)
35{
36 element_t *new = (element_t *) calloc (1, sizeof (element_t));
37 if (new == NULL) {
38 VERBOSE (ERROR, fprintf (stderr, "can't allocate memory\n"));
39 return NULL;
40 }
41 new->func = function;
42 new->nbops = nbops;
43 new->prio = prio;
44
45 return new;
46}
47
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
63/* functions */
64
65#define NB_OPERATORS 6
66
67keyword_t operators[NB_OPERATORS] = {
68 { "+\t", Add, 2, 1, 1},
69 { "-\t", Sub, 2, 1, 1},
70 { "*", Mul, 2, 1, 2},
71 { "/", Div, 2, 1, 2},
72 { "%", Mod, 2, 1, 3},
73 { "^", Pow, 2, 1, 4}
74};
75
76#define NB_FUNCTIONS 9
77keyword_t functions[NB_FUNCTIONS] = {
78 { "sqrt", Sqr, 1, 4, 5},
79 { "pow", Pow, 2, 3, 5},
80 { "cos", Cos, 1, 3, 5},
81 { "sin", Sin, 1, 3, 5},
82 { "atan", Atan, 1, 4, 5},
83 { "exp", Exp, 1, 3, 5},
84 { "log", Log, 1, 3, 5},
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}
93};
94
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) {
106 delelement (new);
107 *proot = NULL;
108 return ERROR_OP;
109 }
110 *proot = newelement (Val, 1, 5);
111 if (*proot == NULL) {
112 delelement (new);
113 return ERROR_OP;
114 }
115 (*proot)->ops[0] = new;
116
117 return *proot;
118}
119
120/* parser function */
121
122element_t *parser (char *str, char **next, int prio)
123{
124 element_t *root = NULL;
125 int i;
126
127 VERBOSE (DEBUG, fprintf (stdout, "Starting parsing\n"));
128
129 /* main loop */
130 while (*str != '\0') {
131 int found = 0;
132 element_t *new = NULL;
133 VERBOSE (INFO, fprintf (stdout, "Processing: %s\n", str));
134
135 /* skip spaces and tabs */
136
137 if ((*str == ' ') || (*str == '\t')) {
138 str++;
139 continue;
140 }
141
142 /* check for open bracket */
143
144 if (*str == '(') {
145 VERBOSE (DEBUG, fprintf (stdout, "start processing bracket\n"));
146 if (root) {
147 do {
148 found = 0;
149 new = parser (str + 1, &str, 0);
150 if (new == ERROR_OP) {
151 delelement (root);
152 return ERROR_OP;
153 }
154 for (i = 0; i < root->nbops; i++) {
155 if (root->ops[i] == NULL) {
156 root->ops[i] = new;
157 found = 1;
158 break;
159 }
160 }
161 if (!found) {
162 delelement (new);
163 delelement (root);
164 return ERROR_OP;
165 }
166 } while (*str == ',');
167 } else {
168 root = newelement (Val, 1, 5);
169 if (root == NULL) {
170 return ERROR_OP;
171 }
172 new = parser (str + 1, &str, 0);
173 if ((new == ERROR_OP) || (*str == ',')) {
174 delelement (new);
175 delelement (root);
176 return ERROR_OP;
177 }
178 root->ops[0] = new;
179 }
180 str++;
181 VERBOSE (DEBUG, fprintf (stdout, "stop processing bracket\n"));
182 continue;
183 }
184
185 /* check for closing bracket or koma */
186
187 if ((*str == ')') || (*str == ',')) {
188 if (next != NULL) {
189 *next = str;
190 }
191 return root;
192 }
193
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) {
199 VERBOSE (DEBUG, fprintf (stdout, "start processing operator\n"));
200 if (root) {
201 if ((prio) && (prio > operator->prio)) {
202 VERBOSE (DEBUG, fprintf (stdout, "stop because operator priority\n"));
203 *next = str;
204 return root;
205 }
206 str += operator->offset;
207 VERBOSE (INFO, fprintf (stdout, "Oper: %d\n", operator->func));
208 if (subparser (&root, &str, operator->func, operator->nbops, operator->prio) == ERROR_OP) {
209 delelement (root);
210 return ERROR_OP;
211 }
212 } else if (*str == '-') {
213 new = newelement (Sig, 1, 9);
214 if (new == NULL) {
215 return ERROR_OP;
216 }
217 root = new;
218 } else {
219 return ERROR_OP;
220 }
221 found = 1;
222 VERBOSE (DEBUG, fprintf (stdout, "stop processing operator\n"));
223 break;
224 }
225 }
226 if (found) {
227 continue;
228 }
229
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) {
235 VERBOSE (DEBUG, fprintf (stdout, "start processing function\n"));
236 if (root == NULL) {
237 VERBOSE (INFO, fprintf (stdout, "Func: %d\n", function->func));
238 new = newelement (function->func, function->nbops, function->prio);
239 if (new == NULL) {
240 return ERROR_OP;
241 }
242 root = new;
243 } else {
244 delelement (root);
245 return ERROR_OP;
246 }
247 str += function->offset;
248 found = 1;
249 VERBOSE (DEBUG, fprintf (stdout, "stop processing function\n"));
250 break;
251 }
252 }
253 if (found) {
254 continue;
255 }
256
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
284 /* look for number */
285
286 if (((*str >= '0') && (*str <= '9')) ||
287 (*str == '.') || (*str == '+') || (*str == '-')) {
288 VERBOSE (DEBUG, fprintf (stdout, "start processing value\n"));
289 char *pt;
290 double value = strtod (str, &pt);
291 VERBOSE (INFO, fprintf (stdout, "Value: %f\n", value));
292 if (str != pt) {
293 if (root == NULL) {
294 new = newelement (Val, 1, 5);
295 if (new == NULL) {
296 return ERROR_OP;
297 }
298 new->value = value;
299 root = new;
300 str = pt;
301 } else if (root->func == Val) {
302 if ((*str == '+') || (*str == '-')) {
303 if ((prio) && (prio > 1)) {
304 VERBOSE (DEBUG, fprintf (stdout, "stop because operator priority\n"));
305 *next = str;
306 return root;
307 }
308 if (subparser (&root, &str, Add, 2, 1) == ERROR_OP) {
309 delelement (root);
310 return ERROR_OP;
311 }
312 } else {
313 delelement (root);
314 return ERROR_OP;
315 }
316 } else {
317 delelement (root);
318 return ERROR_OP;
319 }
320 found = 1;
321 }
322 VERBOSE (DEBUG, fprintf (stdout, "stop processing value\n"));
323 }
324
325 /* error */
326
327 if (!found) {
328 delelement (root);
329 return ERROR_OP;
330 }
331
332 }
333
334 if (next != NULL) {
335 *next = str;
336 }
337
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
348 if ((root == NULL) || (root == ERROR_OP)) {
349 return;
350 }
351
352 for (i = 0; i < level; i++) {
353 fprintf (stdout, " ");
354 }
355
356 switch (root->func) {
357 case Val: func = "Value"; break;
358 case Sig: func = "Sign"; break;
359 case Add: func = "Addition"; break;
360 case Sub: func = "Subtraction"; break;
361 case Mul: func = "Multiplication"; break;
362 case Div: func = "Division"; break;
363 case Mod: func = "Modulo"; break;
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;
368 case Atan: func = "Arc Tangent"; break;
369 case Log: func = "Logarithm"; break;
370 case Exp: func = "Exponantial"; break;
371 case Quit: func = "Quit"; break;
372 case Help: func = "Help"; break;
373 case Pi: func = "Pi"; break;
374 case E: func = "E"; break;
375 }
376
377 fprintf (stdout, "Function: %s\n", func);
378
379 if ((root->func == Val) && (root->ops[0] == NULL)) {
380 for (i = 0; i < level; i++) {
381 fprintf (stdout, " ");
382 }
383 fprintf (stdout, "value: %f\n", root->value);
384 } else {
385 for (i = 0; i < root->nbops; i++) {
386 print_element (root->ops[i], level + 1);
387 }
388 }
389}
390
391/* quit function */
392
393void quit (void)
394{
395 fprintf (stdout, "bye\n");
396 exit (0);
397}
398
399/* help message */
400
401void help (void)
402{
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");
409 fprintf (stdout, " quit help\n\n");
410 fprintf (stdout, "supported constants:\n");
411 fprintf (stdout, " e pi\n");
412}
413
414/* evaluate element tree */
415
416#define MASK_SUB 0x1
417#define MASK_DIV 0x2
418
419double evaluate_element (element_t *root, char mask)
420{
421 double op0 = 0, op1 = 0;
422 char nextmask = mask;
423
424 if ((root == NULL) || (root == ERROR_OP)) {
425 VERBOSE (WARNING, fprintf (stdout, "error while evaluating\n"));
426 return 0;
427 }
428
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
449 switch (root->func) {
450 case Val:
451 case Sig:
452 op0 = (root->ops[0]) ? evaluate_element (root->ops[0], nextmask) : root->value;
453 break;
454 case Add:
455 case Sub:
456 case Mul:
457 case Div:
458 case Mod:
459 case Pow:
460 if (root->ops[1]) {
461 op1 = evaluate_element (root->ops[1], nextmask);
462 } else {
463 VERBOSE (WARNING, fprintf (stdout, "error while evaluating (op[1])\n"));
464 return 0;
465 }
466 /* fallthrough */
467 case Sqr:
468 case Cos:
469 case Sin:
470 case Atan:
471 case Log:
472 case Exp:
473 if (root->ops[0]) {
474 op0 = evaluate_element (root->ops[0], 0);
475 } else {
476 VERBOSE (WARNING, fprintf (stdout, "error while evaluating (op[0])\n"));
477 return 0;
478 }
479 break;
480 case Quit:
481 case Help:
482 case Pi:
483 case E:
484 break;
485 }
486
487 switch (root->func) {
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;
494 case Mod: return fmod (op0, op1);
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);
499 case Atan: return atan (op0);
500 case Log: return log (op0);
501 case Exp: return exp (op0);
502 case Quit: quit (); break;
503 case Help: help (); break;
504 case Pi: return M_PI;
505 case E: return M_E;
506 }
507
508 return 0;
509}
510
511/* vim: set ts=4 sw=4 et: */