This is Unofficial EPICS BASE Doxygen Site
postfix.h File Reference

The API for the EPICS Calculation Engine. More...

#include "libComAPI.h"
+ Include dependency graph for postfix.h:
+ This graph shows which files directly or indirectly include this file:

Go to the source code of this file.

Macros

#define CALCPERFORM_NARGS   12
 Number of input arguments to a calc expression (A-L) More...
 
#define CALCPERFORM_STACK   80
 Size of the internal partial result stack. More...
 
Postfix and Infix Buffer Sizes
#define INFIX_TO_POSTFIX_SIZE(n)   ((n)*21/6)
 Calculate required size of postfix buffer from infix. More...
 
#define MAX_INFIX_SIZE   100
 Size of a "standard" infix string. More...
 
#define MAX_POSTFIX_SIZE   INFIX_TO_POSTFIX_SIZE(MAX_INFIX_SIZE)
 Size of a "standard" postfix buffer. More...
 
Calc Engine Error Codes
Note
Changes in these errors must also be made in calcErrorStr().
#define CALC_ERR_NONE   0
 No error. More...
 
#define CALC_ERR_TOOMANY   1
 Too many results returned. More...
 
#define CALC_ERR_BAD_LITERAL   2
 Bad numeric literal. More...
 
#define CALC_ERR_BAD_ASSIGNMENT   3
 Bad assignment target. More...
 
#define CALC_ERR_BAD_SEPERATOR   4
 Comma without parentheses. More...
 
#define CALC_ERR_PAREN_NOT_OPEN   5
 Close parenthesis without open. More...
 
#define CALC_ERR_PAREN_OPEN   6
 Open parenthesis at end of expression. More...
 
#define CALC_ERR_CONDITIONAL   7
 Unbalanced conditional ?: operators. More...
 
#define CALC_ERR_INCOMPLETE   8
 Incomplete expression, operand missing. More...
 
#define CALC_ERR_UNDERFLOW   9
 Runtime stack would underflow. More...
 
#define CALC_ERR_OVERFLOW   10
 Runtime stack would overflow. More...
 
#define CALC_ERR_SYNTAX   11
 Syntax error. More...
 
#define CALC_ERR_NULL_ARG   12
 NULL or empty input argument. More...
 
#define CALC_ERR_INTERNAL   13
 Internal error, bad element type. More...
 

Functions

LIBCOM_API long postfix (const char *pinfix, char *ppostfix, short *perror)
 Compile an infix expression into postfix byte-code. More...
 
LIBCOM_API long calcPerform (double *parg, double *presult, const char *ppostfix)
 Run the calculation engine. More...
 
LIBCOM_API long calcArgUsage (const char *ppostfix, unsigned long *pinputs, unsigned long *pstores)
 Find the inputs and outputs of an expression. More...
 
LIBCOM_API const char * calcErrorStr (short error)
 Convert an error code to a string. More...
 
LIBCOM_API void calcExprDump (const char *pinst)
 Disassemble a postfix expression. More...
 

Detailed Description

The API for the EPICS Calculation Engine.

Author
Bob Dalesio, Andrew Johnson

Defines macros and the routines provided by the calculation engine for subsystems that need to evaluate mathematical expressions.

Definition in file postfix.h.

Macro Definition Documentation

#define CALC_ERR_BAD_ASSIGNMENT   3

Bad assignment target.

Definition at line 87 of file postfix.h.

#define CALC_ERR_BAD_LITERAL   2

Bad numeric literal.

Definition at line 85 of file postfix.h.

#define CALC_ERR_BAD_SEPERATOR   4

Comma without parentheses.

Definition at line 89 of file postfix.h.

#define CALC_ERR_CONDITIONAL   7

Unbalanced conditional ?: operators.

Definition at line 95 of file postfix.h.

#define CALC_ERR_INCOMPLETE   8

Incomplete expression, operand missing.

Definition at line 97 of file postfix.h.

#define CALC_ERR_INTERNAL   13

Internal error, bad element type.

Definition at line 107 of file postfix.h.

#define CALC_ERR_NONE   0

No error.

Definition at line 81 of file postfix.h.

#define CALC_ERR_NULL_ARG   12

NULL or empty input argument.

Definition at line 105 of file postfix.h.

#define CALC_ERR_OVERFLOW   10

Runtime stack would overflow.

Definition at line 101 of file postfix.h.

#define CALC_ERR_PAREN_NOT_OPEN   5

Close parenthesis without open.

Definition at line 91 of file postfix.h.

#define CALC_ERR_PAREN_OPEN   6

Open parenthesis at end of expression.

Definition at line 93 of file postfix.h.

#define CALC_ERR_SYNTAX   11

Syntax error.

Definition at line 103 of file postfix.h.

#define CALC_ERR_TOOMANY   1

Too many results returned.

Definition at line 83 of file postfix.h.

#define CALC_ERR_UNDERFLOW   9

Runtime stack would underflow.

Definition at line 99 of file postfix.h.

#define CALCPERFORM_NARGS   12

Number of input arguments to a calc expression (A-L)

Definition at line 25 of file postfix.h.

#define CALCPERFORM_STACK   80

Size of the internal partial result stack.

Definition at line 27 of file postfix.h.

#define INFIX_TO_POSTFIX_SIZE (   n)    ((n)*21/6)

Calculate required size of postfix buffer from infix.

This macro calculates the maximum size of postfix buffer needed for an infix expression buffer of a given size. The argument n must count the trailing nil byte in the input expression string. The actual size needed is never larger than this value, although it is actually a few bytes smaller for some sizes.

The maximum expansion from infix to postfix is for the sub-expression

1 .1?.1:

which is 6 characters long and results in 21 bytes of postfix:

1 .1 => LITERAL_DOUBLE + 8 byte value
2 ? => COND_IF
3 .1 => LITERAL_DOUBLE + 8 byte value
4 : => COND_ELSE
5 ...
6  => COND_END

For other short expressions the factor 21/6 always gives a big enough postfix buffer (proven by hand, look at '1+' and '.1+' as well).

Definition at line 58 of file postfix.h.

#define MAX_INFIX_SIZE   100

Size of a "standard" infix string.

This is not a hard limit, just the default size for the database

Definition at line 65 of file postfix.h.

#define MAX_POSTFIX_SIZE   INFIX_TO_POSTFIX_SIZE(MAX_INFIX_SIZE)

Size of a "standard" postfix buffer.

This is not a hard limit, just the default size for the database

Definition at line 71 of file postfix.h.

Function Documentation

LIBCOM_API long calcArgUsage ( const char *  ppostfix,
unsigned long *  pinputs,
unsigned long *  pstores 
)

Find the inputs and outputs of an expression.

Software using the calc subsystem may need to know what expression arguments are used and/or modified by a particular expression. It can discover this from the postfix string by calling calcArgUsage(), which takes two pointers pinputs and pstores to a pair of unsigned long bitmaps which return that information to the caller. Passing a NULL value for either of these pointers is legal if only the other is needed.

The least signficant bit (bit 0) of the bitmap at *pinputs will be set if the expression depends on the argument A, and so on through bit 11 for the argument L. An argument that is not used until after a value has been assigned to it will not be set in the pinputs bitmap, thus the bits can be used to determine whether a value needs to be supplied for their associated argument or not for the purposes of evaluating the expression.

Bit 0 of the bitmap at *pstores will be set if the expression assigns a value to the argument A, bit 1 for argument B etc.

Parameters
ppostfixA postfix expression created by postfix().
pinputsBitmap pointer.
pstoresBitmap pointer.
Returns
The return value will be non-zero if the ppostfix expression was illegal, otherwise 0.

Definition at line 405 of file calcPerform.c.

406 {
407  unsigned long inputs = 0;
408  unsigned long stores = 0;
409  char op;
410  while ((op = *pinst++) != END_EXPRESSION) {
411  switch (op) {
412 
413  case LITERAL_DOUBLE:
414  pinst += sizeof(double);
415  break;
416  case LITERAL_INT:
417  pinst += sizeof(epicsInt32);
418  break;
419  case MIN:
420  case MAX:
421  case FINITE:
422  case ISNAN:
423  pinst++;
424  break;
425 
426  case FETCH_A:
427  case FETCH_B:
428  case FETCH_C:
429  case FETCH_D:
430  case FETCH_E:
431  case FETCH_F:
432  case FETCH_G:
433  case FETCH_H:
434  case FETCH_I:
435  case FETCH_J:
436  case FETCH_K:
437  case FETCH_L:
438  /* Don't claim to use an arg we already stored to */
439  inputs |= (1 << (op - FETCH_A)) & ~stores;
440  break;
441 
442  case STORE_A:
443  case STORE_B:
444  case STORE_C:
445  case STORE_D:
446  case STORE_E:
447  case STORE_F:
448  case STORE_G:
449  case STORE_H:
450  case STORE_I:
451  case STORE_J:
452  case STORE_K:
453  case STORE_L:
454  stores |= (1 << (op - STORE_A));
455  break;
456 
457  default:
458  break;
459  }
460  }
461  if (pinputs) *pinputs = inputs;
462  if (pstores) *pstores = stores;
463  return 0;
464 }
Definition: postfixPvt.h:56
Definition: postfixPvt.h:57
ChannelPut::shared_pointer op
Definition: pvAccess.cpp:132
int epicsInt32
Definition: epicsTypes.h:42
LIBCOM_API const char* calcErrorStr ( short  error)

Convert an error code to a string.

Gives out a printable version of an individual error code. The error codes are macros defined here with names starting CALC_ERR_

Parameters
errorError code
Returns
A string representation of the error code

Definition at line 493 of file postfix.c.

494 {
495  static const char *errStrs[] = {
496  "No error",
497  "Too many results returned",
498  "Badly formed numeric literal",
499  "Bad assignment target",
500  "Comma without enclosing parentheses",
501  "Close parenthesis found without open",
502  "Parenthesis still open at end of expression",
503  "Unbalanced conditional ?: operators",
504  "Incomplete expression, operand missing",
505  "Not enough operands provided",
506  "Runtime stack overflow",
507  "Syntax error, unknown operator/operand",
508  "NULL or empty input argument to postfix()",
509  "Internal error, unknown element type",
510  };
511 
512  if (error < CALC_ERR_NONE || error > CALC_ERR_INTERNAL)
513  return NULL;
514  return errStrs[error];
515 }
#define NULL
Definition: catime.c:38
#define CALC_ERR_INTERNAL
Internal error, bad element type.
Definition: postfix.h:107
LIBCOM_API void calcExprDump ( const char *  pinst)

Disassemble a postfix expression.

Convert the byte-code stream to text and print to stdout.

Parameters
pinstpostfix instructions

Definition at line 523 of file postfix.c.

524 {
525  static const char *opcodes[] = {
526  "End Expression",
527  /* Operands */
528  "LITERAL_DOUBLE", "LITERAL_INT", "VAL",
529  "FETCH_A", "FETCH_B", "FETCH_C", "FETCH_D", "FETCH_E", "FETCH_F",
530  "FETCH_G", "FETCH_H", "FETCH_I", "FETCH_J", "FETCH_K", "FETCH_L",
531  /* Assignment */
532  "STORE_A", "STORE_B", "STORE_C", "STORE_D", "STORE_E", "STORE_F",
533  "STORE_G", "STORE_H", "STORE_I", "STORE_J", "STORE_K", "STORE_L",
534  /* Trigonometry Constants */
535  "CONST_PI",
536  "CONST_D2R",
537  "CONST_R2D",
538  /* Arithmetic */
539  "UNARY_NEG",
540  "ADD",
541  "SUB",
542  "MULT",
543  "DIV",
544  "MODULO",
545  "POWER",
546  /* Algebraic */
547  "ABS_VAL",
548  "EXP",
549  "LOG_10",
550  "LOG_E",
551  "MAX",
552  "MIN",
553  "SQU_RT",
554  /* Trigonometric */
555  "ACOS",
556  "ASIN",
557  "ATAN",
558  "ATAN2",
559  "COS",
560  "COSH",
561  "SIN",
562  "SINH",
563  "TAN",
564  "TANH",
565  /* Numeric */
566  "CEIL",
567  "FLOOR",
568  "FINITE",
569  "ISINF",
570  "ISNAN",
571  "NINT",
572  "RANDOM",
573  /* Boolean */
574  "REL_OR",
575  "REL_AND",
576  "REL_NOT",
577  /* Bitwise */
578  "BIT_OR",
579  "BIT_AND",
580  "BIT_EXCL_OR",
581  "BIT_NOT",
582  "RIGHT_SHIFT_ARITH",
583  "LEFT_SHIFT_ARITH",
584  "RIGHT_SHIFT_LOGIC",
585  /* Relationals */
586  "NOT_EQ",
587  "LESS_THAN",
588  "LESS_OR_EQ",
589  "EQUAL",
590  "GR_OR_EQ",
591  "GR_THAN",
592  /* Conditional */
593  "COND_IF",
594  "COND_ELSE",
595  "COND_END",
596  /* Misc */
597  "NOT_GENERATED"
598  };
599  char op;
600  double lit_d;
601  epicsInt32 lit_i;
602 
603  while ((op = *pinst) != END_EXPRESSION) {
604  switch (op) {
605  case LITERAL_DOUBLE:
606  memcpy(&lit_d, ++pinst, sizeof(double));
607  printf("\tDouble %g\n", lit_d);
608  pinst += sizeof(double);
609  break;
610  case LITERAL_INT:
611  memcpy(&lit_i, ++pinst, sizeof(epicsInt32));
612  printf("\tInteger %d (0x%x)\n", lit_i, lit_i);
613  pinst += sizeof(epicsInt32);
614  break;
615  case MIN:
616  case MAX:
617  case FINITE:
618  case ISNAN:
619  printf("\t%s, %d arg(s)\n", opcodes[(int) op], *++pinst);
620  pinst++;
621  break;
622  default:
623  printf("\t%s\n", opcodes[(int) op]);
624  pinst++;
625  }
626  }
627 }
Definition: postfixPvt.h:56
#define printf
Definition: epicsStdio.h:41
Definition: postfixPvt.h:57
ChannelPut::shared_pointer op
Definition: pvAccess.cpp:132
int epicsInt32
Definition: epicsTypes.h:42
LIBCOM_API long calcPerform ( double *  parg,
double *  presult,
const char *  ppostfix 
)

Run the calculation engine.

Evaluates the postfix expression against a set ot input values.

Parameters
pargPointer to an array of double values for the arguments A-L that can appear in the expression. Note that the argument values may be modified if the expression uses the assignment operator.
presultWhere to put the calculated result, which may be a NaN or Infinity.
ppostfixThe postfix expression created by postfix().
Returns
Status value 0 for OK, or non-zero if an error is discovered during the evaluation process.

Definition at line 45 of file calcPerform.c.

46 {
47  double stack[CALCPERFORM_STACK+1]; /* zero'th entry not used */
48  double *ptop; /* stack pointer */
49  double top; /* value from top of stack */
50  epicsInt32 itop; /* integer from top of stack */
51  int op;
52  int nargs;
53 
54  /* initialize */
55  ptop = stack;
56 
57  /* RPN evaluation loop */
58  while ((op = *pinst++) != END_EXPRESSION){
59  switch (op){
60 
61  case LITERAL_DOUBLE:
62  memcpy(++ptop, pinst, sizeof(double));
63  pinst += sizeof(double);
64  break;
65 
66  case LITERAL_INT:
67  memcpy(&itop, pinst, sizeof(epicsInt32));
68  *++ptop = itop;
69  pinst += sizeof(epicsInt32);
70  break;
71 
72  case FETCH_VAL:
73  *++ptop = *presult;
74  break;
75 
76  case FETCH_A:
77  case FETCH_B:
78  case FETCH_C:
79  case FETCH_D:
80  case FETCH_E:
81  case FETCH_F:
82  case FETCH_G:
83  case FETCH_H:
84  case FETCH_I:
85  case FETCH_J:
86  case FETCH_K:
87  case FETCH_L:
88  *++ptop = parg[op - FETCH_A];
89  break;
90 
91  case STORE_A:
92  case STORE_B:
93  case STORE_C:
94  case STORE_D:
95  case STORE_E:
96  case STORE_F:
97  case STORE_G:
98  case STORE_H:
99  case STORE_I:
100  case STORE_J:
101  case STORE_K:
102  case STORE_L:
103  parg[op - STORE_A] = *ptop--;
104  break;
105 
106  case CONST_PI:
107  *++ptop = PI;
108  break;
109 
110  case CONST_D2R:
111  *++ptop = PI/180.;
112  break;
113 
114  case CONST_R2D:
115  *++ptop = 180./PI;
116  break;
117 
118  case UNARY_NEG:
119  *ptop = - *ptop;
120  break;
121 
122  case ADD:
123  top = *ptop--;
124  *ptop += top;
125  break;
126 
127  case SUB:
128  top = *ptop--;
129  *ptop -= top;
130  break;
131 
132  case MULT:
133  top = *ptop--;
134  *ptop *= top;
135  break;
136 
137  case DIV:
138  top = *ptop--;
139  *ptop /= top;
140  break;
141 
142  case MODULO:
143  itop = (epicsInt32) *ptop--;
144  if (itop)
145  *ptop = (epicsInt32) *ptop % itop;
146  else
147  *ptop = epicsNAN;
148  break;
149 
150  case POWER:
151  top = *ptop--;
152  *ptop = pow(*ptop, top);
153  break;
154 
155  case ABS_VAL:
156  *ptop = fabs(*ptop);
157  break;
158 
159  case EXP:
160  *ptop = exp(*ptop);
161  break;
162 
163  case LOG_10:
164  *ptop = log10(*ptop);
165  break;
166 
167  case LOG_E:
168  *ptop = log(*ptop);
169  break;
170 
171  case MAX:
172  nargs = *pinst++;
173  while (--nargs) {
174  top = *ptop--;
175  if (*ptop < top || isnan(top))
176  *ptop = top;
177  }
178  break;
179 
180  case MIN:
181  nargs = *pinst++;
182  while (--nargs) {
183  top = *ptop--;
184  if (*ptop > top || isnan(top))
185  *ptop = top;
186  }
187  break;
188 
189  case SQU_RT:
190  *ptop = sqrt(*ptop);
191  break;
192 
193  case ACOS:
194  *ptop = acos(*ptop);
195  break;
196 
197  case ASIN:
198  *ptop = asin(*ptop);
199  break;
200 
201  case ATAN:
202  *ptop = atan(*ptop);
203  break;
204 
205  case ATAN2:
206  top = *ptop--;
207  *ptop = atan2(top, *ptop); /* Ouch!: Args backwards! */
208  break;
209 
210  case COS:
211  *ptop = cos(*ptop);
212  break;
213 
214  case SIN:
215  *ptop = sin(*ptop);
216  break;
217 
218  case TAN:
219  *ptop = tan(*ptop);
220  break;
221 
222  case COSH:
223  *ptop = cosh(*ptop);
224  break;
225 
226  case SINH:
227  *ptop = sinh(*ptop);
228  break;
229 
230  case TANH:
231  *ptop = tanh(*ptop);
232  break;
233 
234  case CEIL:
235  *ptop = ceil(*ptop);
236  break;
237 
238  case FLOOR:
239  *ptop = floor(*ptop);
240  break;
241 
242  case FINITE:
243  nargs = *pinst++;
244  top = finite(*ptop);
245  while (--nargs) {
246  --ptop;
247  top = top && finite(*ptop);
248  }
249  *ptop = top;
250  break;
251 
252  case ISINF:
253  *ptop = isinf(*ptop);
254  break;
255 
256  case ISNAN:
257  nargs = *pinst++;
258  top = isnan(*ptop);
259  while (--nargs) {
260  --ptop;
261  top = top || isnan(*ptop);
262  }
263  *ptop = top;
264  break;
265 
266  case NINT:
267  top = *ptop;
268  *ptop = (epicsInt32) (top >= 0 ? top + 0.5 : top - 0.5);
269  break;
270 
271  case RANDOM:
272  *++ptop = calcRandom();
273  break;
274 
275  case REL_OR:
276  top = *ptop--;
277  *ptop = *ptop || top;
278  break;
279 
280  case REL_AND:
281  top = *ptop--;
282  *ptop = *ptop && top;
283  break;
284 
285  case REL_NOT:
286  *ptop = ! *ptop;
287  break;
288 
289  /* Be VERY careful converting double to int in case bit 31 is set!
290  * Out-of-range errors give very different results on different sytems.
291  * Convert negative doubles to signed and positive doubles to unsigned
292  * first to avoid overflows if bit 32 is set.
293  * The result is always signed, values with bit 31 set are negative
294  * to avoid problems when writing the value to signed integer fields
295  * like longout.VAL or ao.RVAL. However unsigned fields may give
296  * problems on some architectures. (Fewer than giving problems with
297  * signed integer. Maybe the conversion functions should handle
298  * overflows better.)
299  */
300  #define d2i(x) ((x)<0?(epicsInt32)(x):(epicsInt32)(epicsUInt32)(x))
301  #define d2ui(x) ((x)<0?(epicsUInt32)(epicsInt32)(x):(epicsUInt32)(x))
302 
303  case BIT_OR:
304  top = *ptop--;
305  *ptop = (double)(d2i(*ptop) | d2i(top));
306  break;
307 
308  case BIT_AND:
309  top = *ptop--;
310  *ptop = (double)(d2i(*ptop) & d2i(top));
311  break;
312 
313  case BIT_EXCL_OR:
314  top = *ptop--;
315  *ptop = (double)(d2i(*ptop) ^ d2i(top));
316  break;
317 
318  case BIT_NOT:
319  *ptop = (double)~d2i(*ptop);
320  break;
321 
322  /* In C the shift operators decide on an arithmetic or logical shift
323  * based on whether the integer is signed or unsigned.
324  * With signed integers, a right-shift is arithmetic and will
325  * extend the sign bit into the left-hand end of the value. When used
326  * with unsigned values a logical shift is performed. The
327  * double-casting through signed/unsigned here is important, see above.
328  */
329 
330  case RIGHT_SHIFT_ARITH:
331  top = *ptop--;
332  *ptop = (double)(d2i(*ptop) >> (d2i(top) & 31));
333  break;
334 
335  case LEFT_SHIFT_ARITH:
336  top = *ptop--;
337  *ptop = (double)(d2i(*ptop) << (d2i(top) & 31));
338  break;
339 
340  case RIGHT_SHIFT_LOGIC:
341  top = *ptop--;
342  *ptop = (double)(d2ui(*ptop) >> (d2ui(top) & 31u));
343  break;
344 
345  case NOT_EQ:
346  top = *ptop--;
347  *ptop = *ptop != top;
348  break;
349 
350  case LESS_THAN:
351  top = *ptop--;
352  *ptop = *ptop < top;
353  break;
354 
355  case LESS_OR_EQ:
356  top = *ptop--;
357  *ptop = *ptop <= top;
358  break;
359 
360  case EQUAL:
361  top = *ptop--;
362  *ptop = *ptop == top;
363  break;
364 
365  case GR_OR_EQ:
366  top = *ptop--;
367  *ptop = *ptop >= top;
368  break;
369 
370  case GR_THAN:
371  top = *ptop--;
372  *ptop = *ptop > top;
373  break;
374 
375  case COND_IF:
376  if (*ptop-- == 0.0 &&
377  cond_search(&pinst, COND_ELSE)) return -1;
378  break;
379 
380  case COND_ELSE:
381  if (cond_search(&pinst, COND_END)) return -1;
382  break;
383 
384  case COND_END:
385  break;
386 
387  default:
388  errlogPrintf("calcPerform: Bad Opcode %d at %p\n", op, pinst-1);
389  return -1;
390  }
391  }
392 
393  /* The stack should now have one item on it, the expression value */
394  if (ptop != stack + 1)
395  return -1;
396  *presult = *ptop;
397  return 0;
398 }
Definition: postfixPvt.h:56
#define isinf(x)
Definition: epicsMath.h:16
Definition: postfixPvt.h:45
Definition: postfixPvt.h:57
#define PI
Definition: calcPerform.c:32
#define isnan(x)
Definition: epicsMath.h:21
#define d2ui(x)
Definition: postfixPvt.h:46
Definition: postfixPvt.h:64
int errlogPrintf(const char *pFormat,...)
Definition: errlog.c:105
float epicsNAN
Definition: epicsMath.cpp:35
Definition: postfixPvt.h:68
Definition: postfixPvt.h:48
Definition: postfixPvt.h:53
#define CALCPERFORM_STACK
Size of the internal partial result stack.
Definition: postfix.h:27
Definition: postfixPvt.h:66
ChannelPut::shared_pointer op
Definition: pvAccess.cpp:132
#define d2i(x)
#define finite(x)
Definition: epicsMath.h:16
int epicsInt32
Definition: epicsTypes.h:42
LIBCOM_API long postfix ( const char *  pinfix,
char *  ppostfix,
short *  perror 
)

Compile an infix expression into postfix byte-code.

Converts an expression from an infix string to postfix byte-code

Parameters
pinfixPointer to the infix string
ppostfixPointer to the postfix buffer
perrorPlace to return an error code
Returns
Non-zero value in event of error

It is the callers's responsibility to ensure that ppostfix points to sufficient storage to hold the postfix expression. The macro INFIX_TO_POSTFIX_SIZE(n) can be used to calculate an appropriate postfix buffer size from the length of the infix buffer.

Note
"n" must count the terminating nil byte too.
  1. The infix expressions that can be used are very similar to the C expression syntax, but with some additions and subtle differences in operator meaning and precedence. The string may contain a series of expressions separated by a semi-colon character ';' any one of which may actually provide the calculation result; however all of the other expressions included must assign their result to a variable. All alphabetic elements described below are case independent, so upper and lower case letters may be used and mixed in the variable and function names as desired. Spaces may be used anywhere within an expression except between the characters that make up a single expression element.
  2. ***Numeric Literals*** The simplest expression element is a numeric literal, any (positive) number expressed using the standard floating point syntax that can be stored as a double precision value. This now includes the values Infinity and NaN (not a number). Note that negative numbers will be encoded as a positive literal to which the unary negate operator is applied.
    • Examples:
      • 1
      • 2.718281828459
      • Inf
  3. ***Constants*** There are three trigonometric constants available to any expression which return a value:
    • pi returns the value of the mathematical constant pi.
    • D2R evaluates to pi/180 which, when used as a multiplier, converts an angle from degrees to radians.
    • R2D evaluates to 180/pi which as a multiplier converts an angle from radians to degrees.
  4. ***Variables*** Variables are used to provide inputs to an expression, and are named using the single letters A through L inclusive or the keyword VAL which refers to the previous result of this calculation. The software that makes use of the expression evaluation code should document how the individual variables are given values; for the calc record type the input links INPA through INPL can be used to obtain these from other record fields, and VAL refers to the the VAL field (which can be overwritten from outside the record via Channel Access or a database link).
  5. ***Variable Assignment Operator*** Recently added is the ability to assign the result of a sub-expression to any of the single letter variables, which can then be used in another sub-expression. The variable assignment operator is the character pair := and must immediately follow the name of the variable to receive the expression value. Since the infix string must return exactly one value, every expression string must have exactly one sub-expression that is not an assignment, which can appear anywhere in the string. Sub-expressions within the string are separated by a semi-colon character.
    • Examples:
      • B; B:=A
      • i:=i+1; a*sin(i*D2R)
  6. ***Arithmetic Operators*** The usual binary arithmetic operators are provided: + - * and / with their usual relative precedence and left-to-right associativity, and - may also be used as a unary negate operator where it has a higher precedence and associates from right to left. There is no unary plus operator, so numeric literals cannot begin with a + sign.

    • Examples:
      • a*b + c
      • a/-4 - b

    Three other binary operators are also provided: % is the integer modulo operator, while the synonymous operators ** and ^ raise their left operand to the power of the right operand. % has the same precedence and associativity as * and /, while the power operators associate left-to-right and have a precedence in between * and unary minus.

    • Examples:
      • e:=a%10;
      • d:=a/10%10;
      • c:=a/100%10;
      • b:=a/1000%10;
      • b*4096+c*256+d*16+e
      • sqrt(a**2 + b**2)
  7. ***Algebraic Functions*** Various algebraic functions are available which take parameters inside parentheses. The parameter seperator is a comma.
    • Absolute value: abs(a)
    • Exponential ea: exp(a)
    • Logarithm, base 10: log(a)
    • Natural logarithm (base e): ln(a) or loge(a)
    • n parameter maximum value: max(a, b, ...)
    • n parameter minimum value: min(a, b, ...)
    • Square root: sqr(a) or sqrt(a)
  8. ***Trigonometric Functions*** Standard circular trigonometric functions, with angles expressed in radians:
    • Sine: sin(a)
    • Cosine: cos(a)
    • Tangent: tan(a)
    • Arcsine: asin(a)
    • Arccosine: acos(a)
    • Arctangent: atan(a)
    • 2 parameter arctangent: atan2(a, b)
      Note
      Note that these arguments are the reverse of the ANSI C function, so while C would return arctan(a/b) the calc expression engine returns arctan(b/a)
  9. ***Hyperbolic Trigonometry*** The basic hyperbolic functions are provided, but no inverse functions (which are not provided by the ANSI C math library either).
    • Hyperbolic sine: sinh(a)
    • Hyperbolic cosine: cosh(a)
    • Hyperbolic tangent: tanh(a)
  10. ***Numeric Functions*** The numeric functions perform operations related to the floating point numeric representation and truncation or rounding.
    • Round up to next integer: ceil(a)
    • Round down to next integer: floor(a)
    • Round to nearest integer: nint(a)
    • Test for infinite result: isinf(a)
    • Test for any non-numeric values: isnan(a, ...)
    • Test for all finite, numeric values: finite(a, ...)
    • Random number between 0 and 1: rndm
  11. ***Boolean Operators*** These operators regard their arguments as true or false, where 0.0 is false and any other value is true.
    • Boolean and: a && b
    • Boolean or: a || b
    • Boolean not: !a
  12. ***Bitwise Operators*** The bitwise operators convert their arguments to an integer (by truncation), perform the appropriate bitwise operation and convert back to a floating point value. Unlike in C though, ^ is not a bitwise exclusive-or operator.
    • Bitwise and: a & b or a and b
    • Bitwise or: a | b or a or b
    • Bitwise exclusive or: a xor b
    • Bitwise not (ones complement): ~a or not a
    • Bitwise left shift: a << b
    • Bitwise right shift: a >> b
  13. ***Relational Operators*** Standard numeric comparisons between two values:
    • Less than: a < b
    • Less than or equal to: a <= b
    • Equal to: a = b or a == b
    • Greater than or equal to: a >= b
    • Greater than: a > b
    • Not equal to: a != b or a # b
  14. ***Conditional Operator*** Expressions can use the C conditional operator, which has a lower precedence than all of the other operators except for the assignment operator.
    • condition ? true result : false result
      • Example:
        • a < 360 ? a+1 : 0
  15. ***Parentheses*** Sub-expressions can be placed within parentheses to override operator precence rules. Parentheses can be nested to any depth, but the intermediate value stack used by the expression evaluation engine is limited to 80 results (which require an expression at least 321 characters long to reach).

Definition at line 209 of file postfix.c.

210 {
211  ELEMENT stack[80];
212  ELEMENT *pstacktop = stack;
213  const ELEMENT *pel;
214  int operand_needed = TRUE;
215  int runtime_depth = 0;
216  int cond_count = 0;
217  char * const pdest = pout;
218  char *pnext;
219 
220  if (psrc == NULL || *psrc == '\0' ||
221  pout == NULL || perror == NULL) {
222  if (perror) *perror = CALC_ERR_NULL_ARG;
223  if (pout) *pout = END_EXPRESSION;
224  return -1;
225  }
226 
227  /* place the expression elements into postfix */
228  *pout = END_EXPRESSION;
229  *perror = CALC_ERR_NONE;
230 
231  while (get_element(operand_needed, &psrc, &pel)) {
232  switch (pel->type) {
233 
234  case OPERAND:
235  *pout++ = pel->code;
236  runtime_depth += pel->runtime_effect;
237  operand_needed = FALSE;
238  break;
239 
240  case LITERAL_OPERAND:
241  runtime_depth += pel->runtime_effect;
242 
243  psrc -= strlen(pel->name);
244  if (pel->code == LITERAL_DOUBLE) {
245  double lit_d;
246  epicsInt32 lit_i;
247 
248  if (epicsParseDouble(psrc, &lit_d, &pnext)) {
249  *perror = CALC_ERR_BAD_LITERAL;
250  goto bad;
251  }
252  psrc = pnext;
253  lit_i = (epicsInt32) lit_d;
254  if (lit_d != (double) lit_i) {
255  *pout++ = pel->code;
256  memcpy(pout, &lit_d, sizeof(double));
257  pout += sizeof(double);
258  } else {
259  *pout++ = LITERAL_INT;
260  memcpy(pout, &lit_i, sizeof(epicsInt32));
261  pout += sizeof(epicsInt32);
262  }
263  }
264  else {
265  epicsUInt32 lit_ui;
266 
267  assert(pel->code == LITERAL_INT);
268  if (epicsParseUInt32(psrc, &lit_ui, 0, &pnext)) {
269  *perror = CALC_ERR_BAD_LITERAL;
270  goto bad;
271  }
272  psrc = pnext;
273  *pout++ = LITERAL_INT;
274  memcpy(pout, &lit_ui, sizeof(epicsUInt32));
275  pout += sizeof(epicsUInt32);
276  }
277 
278  operand_needed = FALSE;
279  break;
280 
281  case STORE_OPERATOR:
282  if (pout == pdest || pstacktop > stack ||
283  *--pout < FETCH_A || *pout > FETCH_L) {
284  *perror = CALC_ERR_BAD_ASSIGNMENT;
285  goto bad;
286  }
287  /* Convert fetch into a store on the stack */
288  *++pstacktop = *pel;
289  pstacktop->code = STORE_A + *pout - FETCH_A;
290  runtime_depth -= 1;
291  operand_needed = TRUE;
292  break;
293 
294  case UNARY_OPERATOR:
295  case VARARG_OPERATOR:
296  /* Move operators of >= priority to the output */
297  while ((pstacktop > stack) &&
298  (pstacktop->in_stack_pri >= pel->in_coming_pri)) {
299  *pout++ = pstacktop->code;
300  if (pstacktop->type == VARARG_OPERATOR) {
301  *pout++ = 1 - pstacktop->runtime_effect;
302  }
303  runtime_depth += pstacktop->runtime_effect;
304  pstacktop--;
305  }
306 
307  /* Push new operator onto stack */
308  pstacktop++;
309  *pstacktop = *pel;
310  break;
311 
312  case BINARY_OPERATOR:
313  /* Move operators of >= priority to the output */
314  while ((pstacktop > stack) &&
315  (pstacktop->in_stack_pri >= pel->in_coming_pri)) {
316  *pout++ = pstacktop->code;
317  if (pstacktop->type == VARARG_OPERATOR) {
318  *pout++ = 1 - pstacktop->runtime_effect;
319  }
320  runtime_depth += pstacktop->runtime_effect;
321  pstacktop--;
322  }
323 
324  /* Push new operator onto stack */
325  pstacktop++;
326  *pstacktop = *pel;
327 
328  operand_needed = TRUE;
329  break;
330 
331  case SEPERATOR:
332  /* Move operators to the output until open paren */
333  while (pstacktop->name[0] != '(') {
334  if (pstacktop <= stack+1) {
335  *perror = CALC_ERR_BAD_SEPERATOR;
336  goto bad;
337  }
338  *pout++ = pstacktop->code;
339  if (pstacktop->type == VARARG_OPERATOR) {
340  *pout++ = 1 - pstacktop->runtime_effect;
341  }
342  runtime_depth += pstacktop->runtime_effect;
343  pstacktop--;
344  }
345  operand_needed = TRUE;
346  pstacktop->runtime_effect -= 1;
347  break;
348 
349  case CLOSE_PAREN:
350  /* Move operators to the output until matching paren */
351  while (pstacktop->name[0] != '(') {
352  if (pstacktop <= stack+1) {
353  *perror = CALC_ERR_PAREN_NOT_OPEN;
354  goto bad;
355  }
356  *pout++ = pstacktop->code;
357  if (pstacktop->type == VARARG_OPERATOR) {
358  *pout++ = 1 - pstacktop->runtime_effect;
359  }
360  runtime_depth += pstacktop->runtime_effect;
361  pstacktop--;
362  }
363  pstacktop--; /* remove ( from stack */
364  /* if there is a vararg operator before the opening paren,
365  it inherits the (opening) paren's stack effect */
366  if ((pstacktop > stack) &&
367  pstacktop->type == VARARG_OPERATOR) {
368  pstacktop->runtime_effect = (pstacktop+1)->runtime_effect;
369  /* check for no arguments */
370  if (pstacktop->runtime_effect > 0) {
371  *perror = CALC_ERR_INCOMPLETE;
372  goto bad;
373  }
374  }
375  break;
376 
377  case CONDITIONAL:
378  /* Move operators of > priority to the output */
379  while ((pstacktop > stack) &&
380  (pstacktop->in_stack_pri > pel->in_coming_pri)) {
381  *pout++ = pstacktop->code;
382  if (pstacktop->type == VARARG_OPERATOR) {
383  *pout++ = 1 - pstacktop->runtime_effect;
384  }
385  runtime_depth += pstacktop->runtime_effect;
386  pstacktop--;
387  }
388 
389  /* Add new element to the output */
390  *pout++ = pel->code;
391  runtime_depth += pel->runtime_effect;
392 
393  /* For : operator, also push COND_END code to stack */
394  if (pel->name[0] == ':') {
395  if (--cond_count < 0) {
396  *perror = CALC_ERR_CONDITIONAL;
397  goto bad;
398  }
399  pstacktop++;
400  *pstacktop = *pel;
401  pstacktop->code = COND_END;
402  pstacktop->runtime_effect = 0;
403  } else {
404  cond_count++;
405  }
406 
407  operand_needed = TRUE;
408  break;
409 
410  case EXPR_TERMINATOR:
411  /* Move everything from stack to the output */
412  while (pstacktop > stack) {
413  if (pstacktop->name[0] == '(') {
414  *perror = CALC_ERR_PAREN_OPEN;
415  goto bad;
416  }
417  *pout++ = pstacktop->code;
418  if (pstacktop->type == VARARG_OPERATOR) {
419  *pout++ = 1 - pstacktop->runtime_effect;
420  }
421  runtime_depth += pstacktop->runtime_effect;
422  pstacktop--;
423  }
424 
425  if (cond_count != 0) {
426  *perror = CALC_ERR_CONDITIONAL;
427  goto bad;
428  }
429  if (runtime_depth > 1) {
430  *perror = CALC_ERR_TOOMANY;
431  goto bad;
432  }
433 
434  operand_needed = TRUE;
435  break;
436 
437  default:
438  *perror = CALC_ERR_INTERNAL;
439  goto bad;
440  }
441 
442  if (runtime_depth < 0) {
443  *perror = CALC_ERR_UNDERFLOW;
444  goto bad;
445  }
446  if (runtime_depth >= CALCPERFORM_STACK) {
447  *perror = CALC_ERR_OVERFLOW;
448  goto bad;
449  }
450  }
451 
452  if (*psrc != '\0') {
453  *perror = CALC_ERR_SYNTAX;
454  goto bad;
455  }
456 
457  /* Move everything from stack to the output */
458  while (pstacktop > stack) {
459  if (pstacktop->name[0] == '(') {
460  *perror = CALC_ERR_PAREN_OPEN;
461  goto bad;
462  }
463  *pout++ = pstacktop->code;
464  if (pstacktop->type == VARARG_OPERATOR) {
465  *pout++ = 1 - pstacktop->runtime_effect;
466  }
467  runtime_depth += pstacktop->runtime_effect;
468  pstacktop--;
469  }
470  *pout = END_EXPRESSION;
471 
472  if (cond_count != 0) {
473  *perror = CALC_ERR_CONDITIONAL;
474  goto bad;
475  }
476  if (operand_needed || runtime_depth != 1) {
477  *perror = CALC_ERR_INCOMPLETE;
478  goto bad;
479  }
480  return 0;
481 
482 bad:
483  *pdest = END_EXPRESSION;
484  return -1;
485 }
#define CALC_ERR_NULL_ARG
NULL or empty input argument.
Definition: postfix.h:105
#define assert(exp)
Declare that a condition should be true.
Definition: epicsAssert.h:70
#define FALSE
Definition: dbDefs.h:32
#define CALC_ERR_BAD_LITERAL
Bad numeric literal.
Definition: postfix.h:85
#define NULL
Definition: catime.c:38
rpn_opcode code
Definition: postfix.c:57
unsigned int epicsUInt32
Definition: epicsTypes.h:43
#define CALC_ERR_OVERFLOW
Runtime stack would overflow.
Definition: postfix.h:101
#define CALC_ERR_TOOMANY
Too many results returned.
Definition: postfix.h:83
#define CALC_ERR_INTERNAL
Internal error, bad element type.
Definition: postfix.h:107
#define CALC_ERR_UNDERFLOW
Runtime stack would underflow.
Definition: postfix.h:99
#define CALC_ERR_INCOMPLETE
Incomplete expression, operand missing.
Definition: postfix.h:97
#define CALC_ERR_PAREN_NOT_OPEN
Close parenthesis without open.
Definition: postfix.h:91
#define CALC_ERR_BAD_SEPERATOR
Comma without parentheses.
Definition: postfix.h:89
#define CALC_ERR_BAD_ASSIGNMENT
Bad assignment target.
Definition: postfix.h:87
LIBCOM_API int epicsParseDouble(const char *str, double *to, char **units)
Definition: epicsStdlib.c:149
element_type type
Definition: postfix.c:56
signed char runtime_effect
Definition: postfix.c:55
#define TRUE
Definition: dbDefs.h:27
#define CALCPERFORM_STACK
Size of the internal partial result stack.
Definition: postfix.h:27
LIBCOM_API int epicsParseUInt32(const char *str, epicsUInt32 *to, int base, char **units)
Definition: epicsStdlib.c:263
#define CALC_ERR_CONDITIONAL
Unbalanced conditional ?: operators.
Definition: postfix.h:95
char in_coming_pri
Definition: postfix.c:54
#define CALC_ERR_SYNTAX
Syntax error.
Definition: postfix.h:103
#define CALC_ERR_NONE
No error.
Definition: postfix.h:81
int epicsInt32
Definition: epicsTypes.h:42
#define CALC_ERR_PAREN_OPEN
Open parenthesis at end of expression.
Definition: postfix.h:93