Instrument Neutral Distributed Interface INDI  2.0.2
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 
21 static int next_token(void);
22 static int chk_funcs(void);
23 static void skip_double(void);
24 static int compile(int prec);
25 static int execute(double *result);
26 static int parse_fieldname(char name[], int len);
27 
28 /* parser tokens and opcodes, as necessary */
29 enum
30 {
31  HALT, /* HALT = 0 serves as good initial default */
32 
33  /* binary operators (precedences in table, below) */
34  ADD,
35  SUB,
37  DIV,
38  AND,
39  OR,
40  GT,
41  GE,
42  EQ,
43  NE,
44  LT,
45  LE,
46 
47  /* unary ops, precedence all UNI_PREC */
48  NEG,
49  NOT,
50 
51  /* symantically operands, ie, constants, variables and all functions */
53  VAR,
54  ABS,
56  SIN,
57  COS,
58  TAN,
62  PITOK, /* built-in constant, pi */
65  LOG,
67  EXP,
69  POW,
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  */
83 static 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
88 static 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
96 static 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 */
108 typedef 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;
114 static Var vars[MAX_OPX]; /* operands */
115 static int nvars; /* number of vars[] in actual use */
116 static double consts[MAX_OPX]; /* constants */
117 static int nconsts; /* number of consts[] in actual use */
118 
119 /* these are global just for easy/rapid access */
120 static int parens_nest; /* to check that parens end up nested */
121 static char *err_msg; /* caller provides storage; we point at it with this */
122 static 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  */
127 int 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  */
157 int 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  */
168 int 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  */
199 int 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  */
214 int 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  */
230 int 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  */
249 void 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  */
260 static 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 funtion token, else ERR.
428  * if find one, update cexpr too.
429  */
430 static 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  */
465 static 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  */
493 static int compile(prec) 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  */
670 static int execute(result) double *result;
671 {
672  int instr;
673 
674  do
675  {
676  instr = *pc++;
677  switch (instr & OP_MASK)
678  {
679  /* put these in numberic 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  */
829 static int parse_fieldname(name, len) char name[];
830 int len;
831 {
832  char c = '\0';
833  int ndots = 0;
834 
835  cexpr = lcexpr + 1;
836  while (--len > 0 && (c = *cexpr++) != '"' && c)
837  {
838  *name++ = c;
839  ndots += c == '.';
840  }
841  if (len == 0 || c != '"' || ndots != 2)
842  return (-1);
843  *name = '\0';
844  return (0);
845 }
#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
#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
@ 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 MAX_STACK
Definition: compiler.c:87
int setOperand(char *name, double valu)
Definition: compiler.c:168
Definition: compiler.c:109
char name[MAXFLDLEN]
Definition: compiler.c:111
double v
Definition: compiler.c:112
int set
Definition: compiler.c:110