Instrument Neutral Distributed Interface INDI 2.2.5
compiler.c
Go to the documentation of this file.
1/* module to compile and execute a c-style arithmetic expression made of INDI
2 * operands. operand names must contain 2 dots and be surrounded by quotes.
3 * The expression is compiled and the names of each operand are stored. The
4 * values of an operand can be set later by name. Evaluation uses the last
5 * known operand value.
6 *
7 * one reason this is so nice and tight is that all opcodes are the same size
8 * (an int) and the tokens the parser returns are directly usable as opcodes.
9 * constants and variables are compiled as an opcode with an offset into the
10 * auxiliary consts and vars arrays.
11 *
12 * this is not reentrant, but new expressions can be compiled as desired.
13 */
14
15#include <ctype.h>
16#include <math.h>
17#include <stdio.h>
18#include <stdlib.h>
19#include <string.h>
20
21static int next_token(void);
22static int chk_funcs(void);
23static void skip_double(void);
24static int compile(int prec);
25static int execute(double *result);
26static int parse_fieldname(char name[], int len);
27
28/* parser tokens and opcodes, as necessary */
29enum
30{
31 HALT, /* HALT = 0 serves as good initial default */
32
33 /* binary operators (precedences in table, below) */
46
47 /* unary ops, precedence all UNI_PREC */
50
51 /* semantically operands, ie, constants, variables and all functions */
62 PITOK, /* built-in constant, pi */
71};
72
73/* purely tokens - never get compiled as such */
74#define LPAREN 255
75#define RPAREN 254
76#define COMMA 253
77#define ERR (-1)
78
79/* precedence of each of the binary operators.
80 * in case of a tie, compiler associates left-to-right.
81 * N.B. each entry's index must correspond to its #define!
82 */
83static int precedence[] = { 0, 5, 5, 6, 6, 2, 1, 4, 4, 3, 3, 4, 4 };
84#define UNI_PREC 7 /* unary ops have highest precedence */
85
86/* execute-time operand stack */
87#define MAX_STACK 32
88static double stack[MAX_STACK], *sp;
89
90/* space for compiled opcodes - the "program".
91 * opcodes go in lower 8 bits.
92 * when an opcode has an operand (as CONST and VAR) it is really an array
93 * index in the remaining upper bits.
94 */
95#define MAX_PROG 32
96static int program[MAX_PROG], *pc;
97#define OP_SHIFT 8
98#define OP_MASK 0xff
99
100/* auxiliary operand info.
101 * the operands (all but lower 8 bits) of CONST and VAR are really indeces
102 * into these arrays. thus, no point in making them any longer than you have
103 * bits more than 8 in your machine's int to index into it, ie, make
104 * MAX_OPX < 1 << ((sizeof(int)-1)*8)
105 */
106#define MAX_OPX 64 /* max number of operands */
107#define MAXFLDLEN 64 /* longest allowed operand name */
108typedef struct
109{
110 int set; /* 1 when v has been set */
111 char name[MAXFLDLEN]; /* name of operand */
112 double v; /* last known value of this operand */
113} Var;
114static Var vars[MAX_OPX]; /* operands */
115static int nvars; /* number of vars[] in actual use */
116static double consts[MAX_OPX]; /* constants */
117static int nconsts; /* number of consts[] in actual use */
118
119/* these are global just for easy/rapid access */
120static int parens_nest; /* to check that parens end up nested */
121static char *err_msg; /* caller provides storage; we point at it with this */
122static char *cexpr, *lcexpr; /* pointers that move along caller's expression */
123
124/* compile the given c-style expression.
125 * return 0 if ok, else return -1 and a reason message in errbuf.
126 */
127int compileExpr(char *exp, char *errbuf)
128{
129 /* init the globals.
130 * also delete any flogs used in the previous program.
131 */
132 cexpr = exp;
133 err_msg = errbuf;
134 pc = program;
135 nvars = nconsts = 0;
136 parens_nest = 0;
137
138 pc = program;
139 if (compile(0) == ERR)
140 {
141 (void)sprintf(err_msg + strlen(err_msg), " near `%.10s'", lcexpr);
142 return (-1);
143 }
144 if (pc == program)
145 {
146 (void)sprintf(err_msg, "Null program");
147 return (-1);
148 }
149 *pc++ = HALT;
150 return (0);
151}
152
153/* execute the expression previously compiled with compileExpr().
154 * return 0 with *vp set to the answer if ok, else return -1 with a reason
155 * why not message in errbuf.
156 */
157int evalExpr(double *vp, char *errbuf)
158{
159 err_msg = errbuf;
160 sp = stack + MAX_STACK; /* grows towards lower addresses */
161 pc = program;
162 return (execute(vp));
163}
164
165/* set the value for an operand with the given name to the given value.
166 * return 0 if found else -1.
167 */
168int setOperand(char *name, double valu)
169{
170 int i;
171
172 for (i = 0; i < nvars; i++)
173 {
174 if (strcmp(name, vars[i].name) == 0)
175 {
176 vars[i].v = valu;
177 vars[i].set = 1;
178 return (0);
179 }
180 }
181
182 return (-1);
183}
184
185/* return 0 if all operands are set, else -1 */
187{
188 int i;
189
190 for (i = 0; i < nvars; i++)
191 if (!vars[i].set)
192 return (-1);
193 return (0);
194}
195
196/* return a malloced array of each operand name.
197 * N.B. caller must free array, and not modify names.
198 */
199int getAllOperands(char ***ops)
200{
201 int i;
202
203 *ops = (char **)malloc(nvars * sizeof(char *));
204
205 for (i = 0; i < nvars; i++)
206 (*ops)[i] = vars[i].name;
207
208 return (nvars);
209}
210
211/* return a malloced array of each initialized operand name.
212 * N.B. caller must free array, and not modify the names.
213 */
214int getSetOperands(char ***ops)
215{
216 int i, n;
217
218 *ops = (char **)malloc(nvars * sizeof(char *));
219
220 for (n = i = 0; i < nvars; i++)
221 if (vars[i].set)
222 (*ops)[n++] = vars[i].name;
223
224 return (n);
225}
226
227/* return a malloced array of each uninitialized operand name.
228 * N.B. caller must free array, and not modify the names.
229 */
230int getUnsetOperands(char ***ops)
231{
232 int i, n;
233
234 *ops = (char **)malloc(nvars * sizeof(char *));
235
236 for (n = i = 0; i < nvars; i++)
237 if (!vars[i].set)
238 (*ops)[n++] = vars[i].name;
239
240 return (n);
241}
242
243/* called when each different field is written.
244 * this is just called by srch_log() to hide the fact from users of srch*
245 * that srch is really using our vars array to store values.
246 * since this gets called for all fields, it's not an error to not find name.
247 * don't stop when see the first one because a term might appear more than once.
248 */
249void compiler_log(char* name, double value)
250{
251 for (Var *vp = vars; vp < &vars[nvars]; vp++)
252 if (strcmp(vp->name, name) == 0)
253 vp->v = value;
254}
255
256/* get and return the opcode corresponding to the next token.
257 * leave with lcexpr pointing at the new token, cexpr just after it.
258 * also watch for mismatches parens and proper operator/operand alternation.
259 */
260static int next_token()
261{
262 static const char toomv[] = "More than %d variables";
263 static const char toomc[] = "More than %d constants";
264 static const char badop[] = "Illegal operator";
265 int tok = ERR; /* just something illegal */
266 char c;
267
268 while (isspace(c = *cexpr))
269 cexpr++;
270 lcexpr = cexpr++;
271
272 /* mainly check for a binary operator */
273 switch (c)
274 {
275 case ',':
276 tok = COMMA;
277 break;
278 case '\0':
279 --cexpr;
280 tok = HALT;
281 break; /* keep returning HALT */
282 case '+':
283 tok = ADD;
284 break; /* compiler knows when it's really unary */
285 case '-':
286 tok = SUB;
287 break; /* compiler knows when it's really negate */
288 case '*':
289 tok = MULT;
290 break;
291 case '/':
292 tok = DIV;
293 break;
294 case '(':
295 parens_nest++;
296 tok = LPAREN;
297 break;
298 case ')':
299 if (--parens_nest < 0)
300 {
301 (void)sprintf(err_msg, "Too many right parens");
302 return (ERR);
303 }
304 else
305 tok = RPAREN;
306 break;
307 case '|':
308 if (*cexpr == '|')
309 {
310 cexpr++;
311 tok = OR;
312 }
313 else
314 {
315 (void)strcpy(err_msg, badop);
316 return (ERR);
317 }
318 break;
319 case '&':
320 if (*cexpr == '&')
321 {
322 cexpr++;
323 tok = AND;
324 }
325 else
326 {
327 (void)strcpy(err_msg, badop);
328 return (ERR);
329 }
330 break;
331 case '=':
332 if (*cexpr == '=')
333 {
334 cexpr++;
335 tok = EQ;
336 }
337 else
338 {
339 (void)strcpy(err_msg, badop);
340 return (ERR);
341 }
342 break;
343 case '!':
344 if (*cexpr == '=')
345 {
346 cexpr++;
347 tok = NE;
348 }
349 else
350 {
351 tok = NOT;
352 }
353 break;
354 case '<':
355 if (*cexpr == '=')
356 {
357 cexpr++;
358 tok = LE;
359 }
360 else
361 tok = LT;
362 break;
363 case '>':
364 if (*cexpr == '=')
365 {
366 cexpr++;
367 tok = GE;
368 }
369 else
370 tok = GT;
371 break;
372 }
373
374 if (tok != ERR)
375 return (tok);
376
377 /* not op so check for a constant, variable or function */
378 if (isdigit(c) || c == '.')
379 {
380 /* looks like a constant.
381 * leading +- already handled
382 */
383 if (nconsts > MAX_OPX)
384 {
385 (void)sprintf(err_msg, toomc, MAX_OPX);
386 return (ERR);
387 }
388 consts[nconsts] = atof(lcexpr);
389 tok = CONST | (nconsts++ << OP_SHIFT);
390 skip_double();
391 }
392 else if (isalpha(c))
393 {
394 /* check list of functions */
395 tok = chk_funcs();
396 if (tok == ERR)
397 {
398 (void)sprintf(err_msg, "Bad function");
399 return (ERR);
400 }
401 }
402 else if (c == '"')
403 {
404 /* a variable */
405 if (nvars > MAX_OPX)
406 {
407 (void)sprintf(err_msg, toomv, MAX_OPX);
408 return (ERR);
409 }
410 if (parse_fieldname(vars[nvars].name, MAXFLDLEN) < 0)
411 {
412 (void)sprintf(err_msg, "Bad field");
413 return (ERR);
414 }
415 else
416 tok = VAR | (nvars++ << OP_SHIFT);
417 }
418
419 if (tok != ERR)
420 return (tok);
421
422 /* what the heck is it? */
423 (void)sprintf(err_msg, "Syntax error");
424 return (ERR);
425}
426
427/* return function token, else ERR.
428 * if find one, update cexpr too.
429 */
430static int chk_funcs()
431{
432 static struct
433 {
434 char *st_name;
435 int st_tok;
436 } symtab[] = {
437 /* be sure to put short names AFTER longer ones.
438 * otherwise, order does not matter.
439 */
440 { "abs", ABS }, { "floor", FLOOR }, { "acos", ACOS }, { "asin", ASIN }, { "atan2", ATAN2 },
441 { "atan", ATAN }, { "cos", COS }, { "degrad", DEGRAD }, { "exp", EXP }, { "log10", LOG10 },
442 { "log", LOG }, { "pi", PITOK }, { "pow", POW }, { "raddeg", RADDEG }, { "sin", SIN },
443 { "sqrt", SQRT }, { "tan", TAN },
444 };
445 for (int i = 0; i < (int)(sizeof(symtab) / sizeof(symtab[0])); i++)
446 {
447 int l = strlen(symtab[i].st_name);
448
449 if (strncmp(lcexpr, symtab[i].st_name, l) == 0)
450 {
451 cexpr += l - 1;
452 return (symtab[i].st_tok);
453 }
454 }
455 return (ERR);
456}
457
458/* move cexpr on past a double.
459 * allow sci notation.
460 * no need to worry about a leading '-' or '+' but allow them after an 'e'.
461 * TODO: this handles all the desired cases, but also admits a bit too much
462 * such as things like 1eee2...3. geeze; to skip a double right you almost
463 * have to go ahead and crack it!
464 */
465static void skip_double()
466{
467 int sawe = 0; /* so we can allow '-' or '+' right after an 'e' */
468
469 for (;;)
470 {
471 char c = *cexpr;
472 if (isdigit(c) || c == '.' || (sawe && (c == '-' || c == '+')))
473 {
474 sawe = 0;
475 cexpr++;
476 }
477 else if (c == 'e')
478 {
479 sawe = 1;
480 cexpr++;
481 }
482 else
483 break;
484 }
485}
486
487/* call this whenever you want to dig out the next (sub)expression.
488 * keep compiling instructions as long as the operators are higher precedence
489 * than prec (or until see HALT, COMMA or RPAREN) then return that
490 * "look-ahead" token.
491 * if error, fill in a message in err_msg[] and return ERR.
492 */
493static int compile(int prec)
494{
495 int expect_binop = 0; /* set after we have seen any operand.
496 * used by SUB so it can tell if it really
497 * should be taken to be a NEG instead.
498 */
499 int tok = next_token();
500 int *oldpc;
501
502 for (;;)
503 {
504 int p;
505 if (tok == ERR)
506 return (ERR);
507 if (pc - program >= MAX_PROG)
508 {
509 (void)sprintf(err_msg, "Program is too long");
510 return (ERR);
511 }
512
513 /* check for special things like functions, constants and parens */
514 switch (tok & OP_MASK)
515 {
516 case COMMA:
517 return (tok);
518 case HALT:
519 return (tok);
520 case ADD:
521 if (expect_binop)
522 break; /* procede with binary addition */
523 /* just skip a unary positive(?) */
524 tok = next_token();
525 if (tok == HALT)
526 {
527 (void)sprintf(err_msg, "Term expected after unary +");
528 return (ERR);
529 }
530 continue;
531 case SUB:
532 if (expect_binop)
533 break; /* procede with binary subtract */
534 oldpc = pc;
535 tok = compile(UNI_PREC);
536 if (oldpc == pc)
537 {
538 (void)sprintf(err_msg, "Term expected after unary -");
539 return (ERR);
540 }
541 *pc++ = NEG;
542 expect_binop = 1;
543 continue;
544 case NOT:
545 oldpc = pc;
546 tok = compile(UNI_PREC);
547 if (oldpc == pc)
548 {
549 (void)sprintf(err_msg, "Term expected after unary !");
550 return (ERR);
551 }
552 *pc++ = NOT;
553 expect_binop = 1;
554 continue;
555 /* one-arg functions */
556 case ABS:
557 case FLOOR:
558 case SIN:
559 case COS:
560 case TAN:
561 case ASIN:
562 case ACOS:
563 case ATAN:
564 case DEGRAD:
565 case RADDEG:
566 case LOG:
567 case LOG10:
568 case EXP:
569 case SQRT:
570 /* eat up the function's parenthesized argument */
571 if (next_token() != LPAREN)
572 {
573 (void)sprintf(err_msg, "expecting '(' after function");
574 return (ERR);
575 }
576 oldpc = pc;
577 if (compile(0) != RPAREN || oldpc == pc)
578 {
579 (void)sprintf(err_msg, "1-arg function arglist error");
580 return (ERR);
581 }
582 *pc++ = tok;
583 tok = next_token();
584 expect_binop = 1;
585 continue;
586 /* two-arg functions */
587 case POW:
588 case ATAN2:
589 /* eat up the function's parenthesized arguments */
590 if (next_token() != LPAREN)
591 {
592 (void)sprintf(err_msg, "Saw a built-in function: expecting (");
593 return (ERR);
594 }
595 oldpc = pc;
596 if (compile(0) != COMMA || oldpc == pc)
597 {
598 (void)sprintf(err_msg, "1st of 2-arg function arglist error");
599 return (ERR);
600 }
601 oldpc = pc;
602 if (compile(0) != RPAREN || oldpc == pc)
603 {
604 (void)sprintf(err_msg, "2nd of 2-arg function arglist error");
605 return (ERR);
606 }
607 *pc++ = tok;
608 tok = next_token();
609 expect_binop = 1;
610 continue;
611 /* constants and variables are just like 0-arg functions w/o ()'s */
612 case CONST:
613 case PITOK:
614 case VAR:
615 *pc++ = tok;
616 tok = next_token();
617 expect_binop = 1;
618 continue;
619 case LPAREN:
620 oldpc = pc;
621 if (compile(0) != RPAREN)
622 {
623 (void)sprintf(err_msg, "Unmatched left paren");
624 return (ERR);
625 }
626 if (oldpc == pc)
627 {
628 (void)sprintf(err_msg, "Null expression");
629 return (ERR);
630 }
631 tok = next_token();
632 expect_binop = 1;
633 continue;
634 case RPAREN:
635 return (RPAREN);
636 }
637
638 /* everything else is a binary operator */
639 if (tok < ADD || tok > LE)
640 {
641 printf("Bug! Bogus token: %d\n", tok);
642 abort();
643 }
644 p = precedence[tok];
645 if (p > prec)
646 {
647 int newtok;
648 oldpc = pc;
649 newtok = compile(p);
650 if (newtok == ERR)
651 return (ERR);
652 if (oldpc == pc)
653 {
654 (void)strcpy(err_msg, "Term or factor expected");
655 return (ERR);
656 }
657 *pc++ = tok;
658 expect_binop = 1;
659 tok = newtok;
660 }
661 else
662 return (tok);
663 }
664}
665
666/* "run" the program[] compiled with compile().
667 * if ok, return 0 and the final result,
668 * else return -1 with a reason why not message in err_msg.
669 */
670static int execute(double *result)
671{
672 int instr;
673
674 do
675 {
676 instr = *pc++;
677 switch (instr & OP_MASK)
678 {
679 /* put these in numeric order so hopefully even the dumbest
680 * compiler will choose to use a jump table, not a cascade of ifs.
681 */
682 case HALT:
683 break; /* outer loop will stop us */
684 case ADD:
685 sp[1] = sp[1] + sp[0];
686 sp++;
687 break;
688 case SUB:
689 sp[1] = sp[1] - sp[0];
690 sp++;
691 break;
692 case MULT:
693 sp[1] = sp[1] * sp[0];
694 sp++;
695 break;
696 case DIV:
697 sp[1] = sp[1] / sp[0];
698 sp++;
699 break;
700 case AND:
701 sp[1] = sp[1] && sp[0] ? 1 : 0;
702 sp++;
703 break;
704 case OR:
705 sp[1] = sp[1] || sp[0] ? 1 : 0;
706 sp++;
707 break;
708 case GT:
709 sp[1] = sp[1] > sp[0] ? 1 : 0;
710 sp++;
711 break;
712 case GE:
713 sp[1] = sp[1] >= sp[0] ? 1 : 0;
714 sp++;
715 break;
716 case EQ:
717 sp[1] = sp[1] == sp[0] ? 1 : 0;
718 sp++;
719 break;
720 case NE:
721 sp[1] = sp[1] != sp[0] ? 1 : 0;
722 sp++;
723 break;
724 case LT:
725 sp[1] = sp[1] < sp[0] ? 1 : 0;
726 sp++;
727 break;
728 case LE:
729 sp[1] = sp[1] <= sp[0] ? 1 : 0;
730 sp++;
731 break;
732 case NEG:
733 *sp = -*sp;
734 break;
735 case NOT:
736 *sp = (double)(*sp == 0);
737 break;
738 case CONST:
739 *--sp = consts[instr >> OP_SHIFT];
740 break;
741 case VAR:
742 *--sp = vars[instr >> OP_SHIFT].v;
743 break;
744 case PITOK:
745 *--sp = 4.0 * atan(1.0);
746 break;
747 case ABS:
748 *sp = fabs(*sp);
749 break;
750 case FLOOR:
751 *sp = floor(*sp);
752 break;
753 case SIN:
754 *sp = sin(*sp);
755 break;
756 case COS:
757 *sp = cos(*sp);
758 break;
759 case TAN:
760 *sp = tan(*sp);
761 break;
762 case ASIN:
763 *sp = asin(*sp);
764 break;
765 case ACOS:
766 *sp = acos(*sp);
767 break;
768 case ATAN:
769 *sp = atan(*sp);
770 break;
771 case DEGRAD:
772 *sp *= atan(1.0) / 45.0;
773 break;
774 case RADDEG:
775 *sp *= 45.0 / atan(1.0);
776 break;
777 case LOG:
778 *sp = log(*sp);
779 break;
780 case LOG10:
781 *sp = log10(*sp);
782 break;
783 case EXP:
784 *sp = exp(*sp);
785 break;
786 case SQRT:
787 *sp = sqrt(*sp);
788 break;
789 case POW:
790 sp[1] = pow(sp[1], sp[0]);
791 sp++;
792 break;
793 case ATAN2:
794 sp[1] = atan2(sp[1], sp[0]);
795 sp++;
796 break;
797 default:
798 (void)sprintf(err_msg, "Bug! bad opcode: 0x%x", instr);
799 return (-1);
800 }
801 if (sp < stack)
802 {
803 (void)sprintf(err_msg, "Runtime stack overflow");
804 return (-1);
805 }
806 else if (sp - stack > MAX_STACK)
807 {
808 (void)sprintf(err_msg, "Bug! runtime stack underflow");
809 return (-1);
810 }
811 } while (instr != HALT);
812
813 /* result should now be on top of stack */
814 if (sp != &stack[MAX_STACK - 1])
815 {
816 (void)sprintf(err_msg, "Bug! stack has %ld items", (long int)(MAX_STACK - (sp - stack)));
817 return (-1);
818 }
819 *result = *sp;
820 return (0);
821}
822
823/* starting with lcexpr pointing at a string expected to be a field name,
824 * ie, at a '"', fill into up to the next '"' into name[], including trailing 0.
825 * if there IS no '"' within len-1 chars, return -1, else 0.
826 * the only sanity check is the string contains two dots.
827 * when return, leave lcexpr alone but move cexpr to just after the second '"'.
828 */
829static int parse_fieldname(char name[], int len)
830{
831 char c = '\0';
832 int ndots = 0;
833
834 cexpr = lcexpr + 1;
835 while (--len > 0 && (c = *cexpr++) != '"' && c)
836 {
837 *name++ = c;
838 ndots += c == '.';
839 }
840 if (len == 0 || c != '"' || ndots != 2)
841 return (-1);
842 *name = '\0';
843 return (0);
844}
void log(const std::string &log)
Definition Utils.cpp:161
#define OP_SHIFT
Definition compiler.c:97
int getAllOperands(char ***ops)
Definition compiler.c:199
#define MAX_OPX
Definition compiler.c:106
int evalExpr(double *vp, char *errbuf)
Definition compiler.c:157
int allOperandsSet()
Definition compiler.c:186
#define RPAREN
Definition compiler.c:75
#define MAXFLDLEN
Definition compiler.c:107
#define ERR
Definition compiler.c:77
int compileExpr(char *exp, char *errbuf)
Definition compiler.c:127
#define OP_MASK
Definition compiler.c:98
#define COMMA
Definition compiler.c:76
@ SQRT
Definition compiler.c:68
@ NOT
Definition compiler.c:49
@ PITOK
Definition compiler.c:62
@ ATAN
Definition compiler.c:61
@ ASIN
Definition compiler.c:59
@ SUB
Definition compiler.c:35
@ GT
Definition compiler.c:40
@ MULT
Definition compiler.c:36
@ EXP
Definition compiler.c:67
@ POW
Definition compiler.c:69
@ CONST
Definition compiler.c:52
@ ACOS
Definition compiler.c:60
@ LT
Definition compiler.c:44
@ COS
Definition compiler.c:57
@ NE
Definition compiler.c:43
@ SIN
Definition compiler.c:56
@ GE
Definition compiler.c:41
@ ABS
Definition compiler.c:54
@ LE
Definition compiler.c:45
@ DIV
Definition compiler.c:37
@ AND
Definition compiler.c:38
@ OR
Definition compiler.c:39
@ DEGRAD
Definition compiler.c:63
@ EQ
Definition compiler.c:42
@ HALT
Definition compiler.c:31
@ FLOOR
Definition compiler.c:55
@ TAN
Definition compiler.c:58
@ LOG
Definition compiler.c:65
@ ADD
Definition compiler.c:34
@ LOG10
Definition compiler.c:66
@ ATAN2
Definition compiler.c:70
@ RADDEG
Definition compiler.c:64
@ VAR
Definition compiler.c:53
@ NEG
Definition compiler.c:48
#define LPAREN
Definition compiler.c:74
#define MAX_PROG
Definition compiler.c:95
int getSetOperands(char ***ops)
Definition compiler.c:214
int getUnsetOperands(char ***ops)
Definition compiler.c:230
#define UNI_PREC
Definition compiler.c:84
void compiler_log(char *name, double value)
Definition compiler.c:249
#define MAX_STACK
Definition compiler.c:87
int setOperand(char *name, double valu)
Definition compiler.c:168
char name[MAXFLDLEN]
Definition compiler.c:111
double v
Definition compiler.c:112
int set
Definition compiler.c:110