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