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