Files
epics-base/modules/libcom/src/calc/postfix.h

374 lines
14 KiB
C

/*************************************************************************\
* Copyright (c) 2010 UChicago Argonne LLC, as Operator of Argonne
* National Laboratory.
* Copyright (c) 2002 The Regents of the University of California, as
* Operator of Los Alamos National Laboratory.
* SPDX-License-Identifier: EPICS
* EPICS BASE is distributed subject to a Software License Agreement found
* in file LICENSE that is included with this distribution.
\*************************************************************************/
/**
* \file postfix.h
* \author Bob Dalesio, Andrew Johnson
*
* \brief 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.
*/
#ifndef INCpostfixh
#define INCpostfixh
#include "libComAPI.h"
/** \brief Number of input arguments to a calc expression (A-U) */
#define CALCPERFORM_NARGS 21
/** \brief Size of the internal partial result stack */
#define CALCPERFORM_STACK 80
/**
* \name Postfix and Infix Buffer Sizes
* @{
*/
/**
* \brief 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 \c 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
\code
.1?.1:
\endcode
* which is 6 characters long and results in 21 bytes of postfix:
\code
.1 => LITERAL_DOUBLE + 8 byte value
? => COND_IF
.1 => LITERAL_DOUBLE + 8 byte value
: => COND_ELSE
...
=> COND_END
\endcode
* 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).
*/
#define INFIX_TO_POSTFIX_SIZE(n) ((n)*21/6)
/**
* \brief Size of a "standard" infix string.
*
* This is not a hard limit, just the default size for the database
*/
#define MAX_INFIX_SIZE 160
/**
* \brief Size of a "standard" postfix buffer.
*
* This is not a hard limit, just the default size for the database
*/
#define MAX_POSTFIX_SIZE INFIX_TO_POSTFIX_SIZE(MAX_INFIX_SIZE)
/** @} */
/** \name Calc Engine Error Codes
* \note Changes in these errors must also be made in calcErrorStr().
* @{
*/
/** \brief No error */
#define CALC_ERR_NONE 0
/** \brief Too many results returned */
#define CALC_ERR_TOOMANY 1
/** \brief Bad numeric literal */
#define CALC_ERR_BAD_LITERAL 2
/** \brief Bad assignment target */
#define CALC_ERR_BAD_ASSIGNMENT 3
/** \brief Comma without parentheses */
#define CALC_ERR_BAD_SEPERATOR 4
/** \brief Close parenthesis without open */
#define CALC_ERR_PAREN_NOT_OPEN 5
/** \brief Open parenthesis at end of expression */
#define CALC_ERR_PAREN_OPEN 6
/** \brief Unbalanced conditional ?: operators */
#define CALC_ERR_CONDITIONAL 7
/** \brief Incomplete expression, operand missing */
#define CALC_ERR_INCOMPLETE 8
/** \brief Runtime stack would underflow */
#define CALC_ERR_UNDERFLOW 9
/** \brief Runtime stack would overflow */
#define CALC_ERR_OVERFLOW 10
/** \brief Syntax error */
#define CALC_ERR_SYNTAX 11
/** \brief NULL or empty input argument */
#define CALC_ERR_NULL_ARG 12
/** \brief Internal error, bad element type */
#define CALC_ERR_INTERNAL 13
/** @} */
#ifdef __cplusplus
extern "C" {
#endif
/** \brief Compile an infix expression into postfix byte-code
*
* Converts an expression from an infix string to postfix byte-code
*
* \param pinfix Pointer to the infix string
* \param ppostfix Pointer to the postfix buffer
* \param perror Place to return an error code
* \return Non-zero value in event of error
*
* It is the caller's responsibility to ensure that \c 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.
*
* -# 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.
*
* -# ***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
*
* -# ***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.
*
* -# ***Variables***
* Variables are used to provide inputs to an expression, and are named
* using the single letters A through U 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 INPU 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).
*
* -# ***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)
*
* -# ***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)
*
* -# ***Algebraic Functions***
* 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
*
* -# ***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)
*
* -# ***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)
*
* -# ***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
*
* -# ***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
*
* -# ***Bitwise Operators***
* 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
*
* -# ***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
*
* -# ***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
*
* -# ***Parentheses***
* 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).
*/
LIBCOM_API long
postfix(const char *pinfix, char *ppostfix, short *perror);
/** \brief Run the calculation engine
*
* Evaluates the postfix expression against a set ot input values.
*
* \param parg Pointer to an array of double values for the arguments A-U
* that can appear in the expression. Note that the argument values may be
* modified if the expression uses the assignment operator.
* \param presult Where to put the calculated result, which may be a NaN or Infinity.
* \param ppostfix The postfix expression created by postfix().
* \return Status value 0 for OK, or non-zero if an error is discovered
* during the evaluation process.
*/
LIBCOM_API long
calcPerform(double *parg, double *presult, const char *ppostfix);
/** \brief 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 \c pinputs and \c 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 \c *pinputs will be set
* if the expression depends on the argument A, and so on through bit 20 for
* the argument U. 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 \c *pstores will be set if the expression assigns
* a value to the argument A, bit 1 for argument B etc.
* \param ppostfix A postfix expression created by postfix().
* \param pinputs Bitmap pointer.
* \param pstores Bitmap pointer.
* \return The return value will be non-zero if the ppostfix expression was
* illegal, otherwise 0.
*/
LIBCOM_API long
calcArgUsage(const char *ppostfix, unsigned long *pinputs, unsigned long *pstores);
/** \brief 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 \c CALC_ERR_
* \param error Error code
* \return A string representation of the error code
*/
LIBCOM_API const char *
calcErrorStr(short error);
/** \brief Disassemble a postfix expression
*
* Convert the byte-code stream to text and print to stdout.
* \param pinst postfix instructions
*/
LIBCOM_API void
calcExprDump(const char *pinst);
#ifdef __cplusplus
}
#endif
#endif /* INCpostfixh */