postfix.h

The API for the EPICS Calculation Engine.

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

Author

Bob Dalesio, Andrew Johnson

Postfix and Infix Buffer Sizes

INFIX_TO_POSTFIX_SIZE(n)

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:
which is 6 characters long and results in 21 bytes of postfix:
.1 => LITERAL_DOUBLE + 8 byte value
?  => COND_IF
.1 => LITERAL_DOUBLE + 8 byte value
:  => COND_ELSE
...
   => 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).

MAX_INFIX_SIZE

Size of a “standard” infix string.

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

MAX_POSTFIX_SIZE

Size of a “standard” postfix buffer.

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

Calc Engine Error Codes

Note

Changes in these errors must also be made in calcErrorStr().

CALC_ERR_NONE

No error.

CALC_ERR_TOOMANY

Too many results returned.

CALC_ERR_BAD_LITERAL

Bad numeric literal.

CALC_ERR_BAD_ASSIGNMENT

Bad assignment target.

CALC_ERR_BAD_SEPERATOR

Comma without parentheses.

CALC_ERR_PAREN_NOT_OPEN

Close parenthesis without open.

CALC_ERR_PAREN_OPEN

Open parenthesis at end of expression.

CALC_ERR_CONDITIONAL

Unbalanced conditional ?: operators.

CALC_ERR_INCOMPLETE

Incomplete expression, operand missing.

CALC_ERR_UNDERFLOW

Runtime stack would underflow.

CALC_ERR_OVERFLOW

Runtime stack would overflow.

CALC_ERR_SYNTAX

Syntax error.

CALC_ERR_NULL_ARG

NULL or empty input argument.

CALC_ERR_INTERNAL

Internal error, bad element type.

Defines

CALCPERFORM_NARGS

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

CALCPERFORM_STACK

Size of the internal partial result stack.

Functions

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

It is the caller’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.

  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. 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. 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 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. 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. 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. Various algebraic functions are available which take parameters inside parentheses. The parameter separator 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)

    • Floating point modulo: fmod(num, den)

      Since

      The fmod() function was added in 7.0.8

  8. 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)

  9. 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. 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. 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. Most bitwise operators convert their arguments to 32-bit signed integer (by truncation), perform the appropriate bitwise operation, then convert back to a floating point value. The arithmetic right shift operator >> thus retains the sign bit of the left-hand argument. The logical right shift operator >>> is performed on an unsigned integer though, so injects zeros while shifting. The right-hand shift argument is masked so only the lower 5 bits are used. Unlike in C, ^ 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

    • Arithmetic left shift: a << b

    • Arithmetic right shift: a >> b

    • Logical right shift: a >>> b

  13. 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. 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. Sub-expressions can be placed within parentheses to override operator presence 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).

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)

Note

“n” must count the terminating nil byte too.

Parameters:
  • pinfix – Pointer to the infix string

  • ppostfix – Pointer to the postfix buffer

  • perror – Place to return an error code

Returns:

Non-zero value in event of error

long calcPerform(double *parg, double *presult, const char *ppostfix)

Run the calculation engine.

Evaluates the postfix expression against a set ot input values.

Parameters:
  • parg – Pointer 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.

  • presult – Where to put the calculated result, which may be a NaN or Infinity.

  • ppostfix – The postfix expression created by postfix().

Returns:

Status value 0 for OK, or non-zero if an error is discovered during the evaluation process.

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 significant 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:
  • ppostfix – A postfix expression created by postfix().

  • pinputs – Bitmap pointer.

  • pstores – Bitmap pointer.

Returns:

The return value will be non-zero if the ppostfix expression was illegal, otherwise 0.

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:

error – Error code

Returns:

A string representation of the error code

void calcExprDump(const char *pinst)

Disassemble a postfix expression.

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

Parameters:

pinst – postfix instructions