This section of the archives stores flipcode's complete Developer Toolbox collection,
featuring a variety of mini-articles and source code contributions from our readers.

Expression Compiler / Evaluator
Submitted by

This is a class that I wrote to take infix simple mathematical expressions
and compile them into Intel Architecture 32 machine code. The expressions
can later be evaluated with variable substitutions. This is an updated version
of the original code which includes support for calling user supplied functions
from within the expression.
Here's a simple example of the class's usage:

The infix to postfix code was adapted from a freely available routine by Rex Jaeshke.
I expanded this code to use floating point numbers, support unary minus and allow
functions and variables.
Possible uses might be using mathematical expressions in scripts to control material
properies, particle systems, etc.
Please report any bugs you find.
Max

/**
*
* Expression evaluating class.
*
* The class supports binary addition, subtraction, multiplication, division,
* unary minus, parenthesis, abs(), sin(), cos(), sqrt(), pi and user supplied
* C functions.
*
* The one caveat is that because of the way the compiler uses the processor's
* floating point stack, expressions are limited in their complexity (otherwise
* they will overflow the stack). In practice this doesn't seem to be a problem
* for the types of expressions you would normally want to use with this class.
*
*/

class Expression
{
public:

typedeffloat (__cdecl *UserFunction)(float);

/**
* Constructor.
*/
Expression();

/**
*
* Initializes the class with an infix expression. The expression must be
* initialized before it can be evaluated.
*
* @param expression An infix expression.
* @param variableName An array of variable names which can appear in the
* expression.
* @param numVariables The number of elements in the variable array.
* @param function An array of C function pointers which are callable from
* the expression.
* @param function An array of user supplied functions which can be called
* from inside the expression.
* @param functionName An array of user supplied function names which can
* appear in the expression.
* @param numFunctions The number of elements in the function and
* functionName arrays.
*
* @return Returns true if the expression was initialized successfully
* or false if otherwise. Possible reasons for failure are syntax errors
* or floating point stack overflow.
*
*/bool Initialize(constchar* expression, constchar* variableName[], int numVariables,
UserFunction function[], constchar* functionName[], int numFunctions);

/**
*
* Evaluates the expression with a set of variable substitutions and
* returns the result.
*
* @param variable An array of values to substitute for the variables the
* expression can contain. This array should have the same number of
* elements as the array passed into the Initialize() method.
*
*/float Evaluate(constfloat variable[]) const;

// Flag to keep track of whether or not the parser is ready for a unary
// operator to occur in the token stream. This happens after a number
// or a '(' or on the first token.
bool readyForUnaryOperator = true;

// Push a '(' on the stack. This sentinel allows us to detect when we
// flush out the stack on completion.
stack.push(Token::functionLParen);

// Push any '(' on the stack. These sentinels allows us to detect
// when have flushed out the stack when handling ')' and operators.
stack.push(Token::functionLParen);

readyForUnaryOperator = true;
++infix;
}
elseif (*infix == ')')
{

// Have a ')' so pop off the stack and put into postfix list until a
// '(' is popped. Discard the '('.
while (stack.top().type != Token::typeFunction ||
stack.top().function != Token::functionLParen)
{
postfixTokens.push_back(stack.top());
stack.pop();
}

// Have a '+' or '-'. Pop off any operators of equal or higher
// precedence (that includes all of them) and put them into
// postfix list. If a '(' is popped, put it back and stop looking.
// Push new '+' or '-'.
while (stack.top().type != Token::typeFunction ||
stack.top().function != Token::functionLParen)
{
postfixTokens.push_back(stack.top());
stack.pop();
}