This is Unofficial EPICS BASE Doxygen Site
postfix.c File Reference
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include "dbDefs.h"
#include "epicsAssert.h"
#include "epicsStdlib.h"
#include "epicsString.h"
#include "epicsTypes.h"
#include "postfix.h"
#include "postfixPvt.h"
#include "libComAPI.h"
+ Include dependency graph for postfix.c:

Go to the source code of this file.

Classes

struct  expression_element
 

Typedefs

typedef struct expression_element ELEMENT
 

Enumerations

enum  element_type {
  OPERAND, LITERAL_OPERAND, STORE_OPERATOR, UNARY_OPERATOR,
  VARARG_OPERATOR, BINARY_OPERATOR, SEPERATOR, CLOSE_PAREN,
  CONDITIONAL, EXPR_TERMINATOR
}
 

Functions

LIBCOM_API long postfix (const char *psrc, char *pout, short *perror)
 Compile an infix expression into postfix byte-code. 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...
 

Typedef Documentation

typedef struct expression_element ELEMENT

Enumeration Type Documentation

Enumerator
OPERAND 
LITERAL_OPERAND 
STORE_OPERATOR 
UNARY_OPERATOR 
VARARG_OPERATOR 
BINARY_OPERATOR 
SEPERATOR 
CLOSE_PAREN 
CONDITIONAL 
EXPR_TERMINATOR 

Definition at line 33 of file postfix.c.

Function Documentation

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 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