ExprEval Library


Contents

Introduction

ExprEval Help document. This document is probably full of bugs and mispellings. I may get around to proofreading it later.

ExprEval is a C/C++ based expression evaluation library. It is entirely C based, but it comes with a C++ wrapper class that simplifies it's use. The source code is provided for the library so that it can be recompiled for the specific system or compiler.

ExprEval makes adding mathematical expression support to an application easy. It takes an expression string and parses it, and then it can evaluate it over and over. This library also has support for functions, constants, and variables. All of these items are stored in seperate lists so they can be shared among expressions or they can be private to a single expression or any mix and match. It is up to the developer how to link them together. You can also create your own custom functions. However, there is no C++ wrapper for custom functions, you must use the C routines for the function itself. You can still use the C++ routines to add the functions to the lists and work with the expression, just the function code must use the C routines.

License

This library is licensed under the ExprEval License.

Expression Syntax

Expressions have pretty much the same syntax as they would have on paper, with the following exceptions:

More than one expression may be contained within an expression string. As shown above, each expression must end with a semicolon, even if only one expression is in the string. The value of an expression string is the value of the last expression in the string.
Examlples:

Some functions may take reference parameters. These parameters are references to other variables. You can mix reference parameters with normal parameters. The order of the normal parameters must remain the same and the order of the reference parameters must remain the same.
Examples:

Expressions may also be nested with parenthesis.
Examples:

Expressions may also have whitespace characters and comments. Whitespace characters such as newlines, linefeeds, carriage returns, spaces, and tabs are ignored. Comments begin with the less than-sign '<' and end with the greater than-sign '>'. Comments may be nested as well.
Example:

If a variable is used in an expression, but that variable does not exist, it is considered zero. If it does exist then its value is used instead.

Using ExprEval in an Application

Using ExprEval in an application can be a little difficult. You generally follow these steps:

You can manipulate the lists in any order after their creation. However, functions are translated during the parse, so after parsing an expression, manipulating the function list will make no change to an expression. Variables and constants can be manipulated after a parse to change the result of an expression. However, you must add any constants to be used by an expression to the constant list BEFORE parsing the expression, otherwise it will be seen as a variable. Applications can change both variables and constants, however the expression can only change variables. Expressions may NOT assign to a constant and expressions may NOT use constants as a reference parameter.

Function, variable, and constant list example:

Expression object example:

Expression parse example:

Expression evaluation example:

Free the expression object and lists example:

Fast Variable Access

A new feature in ExprEval is fast variable access. This is simply a technique of quickly accessing variables by directly accessing their memory locations instead of using the value list functions. If you choose to enable fast variable access within ExprEval, you must NOT clear a variable list until after all expressions using it are completely finished evaluating. Then you must reparse the expressions before using them again. The reason is simple. When fast variable access is not used, the value list functions are used and they make sure to access the correct memory or add a variable if needed. However, when fast variable access is used, the variable memory location is directly accessed, and the value list functions are not used. If you clear a variable list and then evaluate an expression, it will access invalid memory.

You can also use fast variable access in you application to dramatically speed up loops. This is accomplished as follows:

Using the Internal Functions and Constants

To use the internal functions, they must first be initialized into a function list with exprFuncListInit. To use the internal constants, they must first be initialized into a value list with exprValListInit. For a list of the internal functions and constants, see the application help template file: ExprTmpl.html You may use this file in your own applications so you don't have to write a detail on the functions in ExprEval. All you have to do is add you own functions and constants to the file if there are any.

Creating Custom Functions

Custom functions must use the C routines, not the C++ wrapper classes. There are several macros that make creating custom functions easy. They require that the variables passed to these functions be the same though. This is how a custom function should normally look:

o is a pointer to the expression object that called the function, n is a pointer to an array of nodes that are the parameters of this function, count is the number of items in the array (the number of parameters), refitems is an array of pointers to referenced variables, refcount is the number of referenced variables, and val is a pointer to a variable to recieve the result of the function. The function should return an error value indicating the error status of the function.

If the variables to the function are as above, the macros will make working with them easier. Solving a function typically goes as follows:

Example without the macros:

Same example with the macros:

In order to use a custom function, it must be added to a function list before the expression is parsed by using exprFuncListAdd

The following is a list of the macros, their uses, and their definitions. They require the custom function arguments to have the same names as above (struct _exprObj *o, struct _exprNode *n, int count, EXPRTYPE *val).

Macro Usage Definition
EXPR_EVALNODE(num, res) Evaluate a node. num is the zero based node number to evaluate. res is the variable to store the result in.
err = exprEvalNode(o, &(n[num]), &res);
if(err != EXPR_ERROR_NOERROR)
    return err;
                    
EXPR_REQUIRECOUNT(c) Require exactly c arguments.
if(count != c)
    return EXPR_ERROR_BADNUMBERARGUMENTS;
                    
EXPR_REQUIRECOUNTMIN(c) Require at least c arguments.
if(count < c)
    return EXPR_ERROR_BADNUMBERARGUMENTS;
                    
EXPR_REQUIRECOUNTMAX(c) Require at most c arguments.
if(count > c)
    return EXPR_ERROR_BADNUMBERARGUMENTS;
                    
EXPR_REQUIRECOUNTRANGE(c1, c2) Require at least c1 arguments and at most c2 arguments.
if(count < c1 || count > c2)
    return EXPR_ERROR_BADNUMBERARGUMENTS;
                    
EXPR_REQUIREREFCOUNT(c) Require exactly c ref arguments.
if(refcount != c)
    return EXPR_ERROR_BADNUMBERARGUMENTS;
                    
EXPR_REQUIREREFCOUNTMIN(c) Require at least c ref arguments.
if(refcount < c)
    return EXPR_ERROR_BADNUMBERARGUMENTS;
                    
EXPR_REQUIREREFCOUNTMAX(c) Require at most c ref arguments.
if(refcount > c)
    return EXPR_ERROR_BADNUMBERARGUMENTS;
                    
EXPR_REQUIREREFCOUNTRANGE(c1, c2) Require at least c1 ref arguments and at most c2 ref arguments.
if(refcount < c1 || refcount > c2)
    return EXPR_ERROR_BADNUMBERARGUMENTS;
                    
EXPR_CHECKMATHERR() Check for EDOM or ERANGE math error.
if(errno == ERANGE || errno == EDOM)
    {
    if(exprGetSoftErrors(o))
        {
        *val = 0.0;
        return EXPR_ERROR_NOERROR;
        }
    else
        {
        return EXPR_ERROR_OUTOFRANGE;
        }
    }
                    
EXPR_CLEARMATHERR() Clear errno.
errno = 0;
                    
EXPR_RETURNSOFTERR(s_err) If soft errors are on, set *val to 0.0 and return EXPR_ERROR_NOERROR. Else return e.
if(exprGetSoftErrors(o))
    {
    *val = 0.0;
    return EXPR_ERROR_NOERROR;
    }
else
    {
    return s_err;
    }
                    
EXPR_CHECKBREAK() If breaker function returns nonzero, stop
if(exprGetBreakResult(o))
    {
    return EXPR_ERROR_BREAK;
    }
                    
EXPR_FUNCTIONSOLVER(name) Make creating function solvers easier
int func_name##(struct _exprObj *o, struct _exprNode *n, int count, EXPRTYPE **refitems, int refcount, EXPRTYPE *val)
                    

If you use any of these macros inside any conditional or loop, make sure you put them in braces so it will work right.

Correct example:

Incorrect example:

Reference

Headers:

Defines:

Objects:

Types:

Version information functions:

Function list functions:

Value list functions:

Expression functions:

Some useful functions

Compiling the ExprEval library

Compiling the ExprEval library is pretty simple. Just compile all of the source files (*.c) and link them into a library. You need to keep "expreval.h" and "expreval.hpp" for the header files.

The library used to have an option of fast variable access or slow variable access by using EXPR_FAST_VAR_ACCES. Now, fast variable access is used whether EXPR_FAST_VAR_ACCESS is defined or not.

You may have to make some changes to the library. I've tried to make doing so as simple as possible. If you need to change the include files or some macros or whatnot, edit the file "exprincl.h" This file includes any other files needed. For example, on Borland compilers you will need to change the include of "memory.h" to "mem.h". If some functions are not provided for your system, Edit the file "exprincl.c". You will need to add the functions as they are used by the library, and make them call the correct function for your system and return the value as the library expects. You should not have to change to much. I have tried to stick as close to ANSI/ISO C as I can.

Example:

/* This example assumes that your compiler does not have
   the malloc or free functions. */
void *malloc(int size)
    {
    /* Assume that you must request pages of memory instead */
    int pages_needed;

    /* Figure out how many pages are needed */
    pages_needed = (size + (PAGE_SIZE - 1)) / PAGE_SIZE;

    /* Assume alloc_pages allocates a number of pages and
        returns a pointer to them or NULL */
    return alloc_pages(pages_needed);
    }

void free(void *ptr)
    {
    dealloc_pages(ptr);
    }
            

Drawbacks/Problems

The following is a list of some basic drawbacks of this library:

Example Use

This is an example application of this library. It is a graphics program that calculates the color value of a pixel based on it's X,Y co-ordinate. It uses a made-up image library called graphic-lib

/* Include files */
#include <stdio.h>
#include <stdlib.h>
#include <setjmp.h>
#include "graphiclib.h"
#include "expreval.h"

char *transerr(int err)
    {
    /* Translate error code into message */
    }

void gen_image(char *name, int w, int h, char *expr);
    {
    exprFuncList *f = NULL;
    exprValList *v = NULL;
    exprValList *c = NULL;
    exprObj *o = NULL;
    int x, y, err;
    jmp_buf jumper;
    int image;
    EXPRTYPE r, g, b;

    /* Error handling */
    err = setjmp(jumper);
    if(err)
        {
        if(err != ID_IMAGENOERROR)
            printf("Error %d occurred: %s\n", err, transerr(err));

        exprFree(o);
        exprFreeFuncList(f);
        exprFreeValList(v);
        exprFreeValList(c);

        image_free(image);
        return;
        }

    /* Set up lists */

    /* Function list */
    err = exprFuncListCreate(&f);
    if(err != EXPR_ERROR_NOERROR)
        longjmp(jumper, err);

    err = exprFuncListInit(f);
    if(err != EXPR_ERROR_NOERROR)
        {
        printf("Function list init error. Functions may not be available.\n");
        }

    /* Variable list */
    err = exprValListCreate(&v);
    if(err != EXPR_ERROR_NOERROR)
        longjmp(jumper, err);

    /* Constant list */
    err = exprValListCreate(&c);
    if(err != EXPR_ERROR_NOERROR)
        {
        printf("Constants not available\n");
        }
    else
        {
        err = exprValListInit(c);
        if(err != EXPR_ERROR_NOERROR)
            printf("Constant list init error. Constants may not be available.\n");
        }

    /* Create and parse the expression */

    /* Create */
    err = exprCreate(&o, f, v, c, NULL, 0);
    if(err != EXPR_ERROR_NOERROR)
        longjmp(jumper, err);

    /* Parse expression */
    err = exprParse(o, expr);
    if(err != EXPR_ERROR_NOERROR)
        longjmp(jumper, err);


    /* Create the image */
    image = image_create(w, h);
    if(image == 0)
        {
        longjmp(jumper, ID_IMAGECREATEERROR);
        }

    /* Add width and height to variable list */
    exprValListAdd(v, "w", (EXPRTYPE)w);
    exprValListAdd(v, "h", (EXPRTYPE)h);

    for(y = 0; y < h; y++)
        {
        for(x = 0; x < w; x++)
            {
            /* Add x and y */
            exprValListAdd(v, "x", (EXPRTYPE)x);
            exprValListAdd(v, "y", (EXPRTYPE)y);

            /* Eval expression, ignoring errors */
            exprEval(o);

            /* Get colors */
            exprValListGet(v, "r", &r);
            exprValListGet(v, "g", &g);
            exprValListGet(v, "b", &b);

            /* Set pixel */
            image_setpixel(image, x, y, (int)r, (int)g, (int)b);
            }
        }

    /* Save image */
    image_save(image, name);

    /* Done */
    longjmp(jumper, ID_IMAGENOERROR);
    }

void main(void)
    {
    char name[MAXPATH]
    char tmp[10];
    char expr[4096];
    int sx, sy;

    printf("Image name to save: ");
    gets(name);

    printf("Image width: ");
    gets(tmp);
    sx = atoi(tmp);

    printf("Image height: ");
    gets(tmp);
    sy = atoi(tmp);

    printf("Color Expression (Use x, y, w, h Set r, g, b): ");
    gets(expr);

    gen_image(name, sx, sy, expr);
    }

        

Example Use with Fast Variable Access

This is an example application of this library. It is a graphics program that calculates the color value of a pixel based on it's X,Y co-ordinate. It uses a made-up image library called graphic-lib. It uses faster variable access by using the exprValListGetAddress function.

/* Include files */
#include <stdio.h>
#include <stdlib.h>
#include <setjmp.h>
#include "graphiclib.h"
#include "expreval.h"

char *transerr(int err)
    {
    /* Translate error code into message */
    }

void gen_image(char *name, int w, int h, char *expr);
    {
    exprFuncList *f = NULL;
    exprValList *v = NULL;
    exprValList *c = NULL;
    exprObj *o = NULL;
    int x, y, err;
    jmp_buf jumper;
    int image;
    EXPRTYPE *v_x, *v_y;
    EXPRTYPE *v_r, *v_g, *v_b;

    /* Error handling */
    err = setjmp(jumper);
    if(err)
        {
        if(err != ID_IMAGENOERROR)
            printf("Error %d occurred: %s\n", err, transerr(err));

        exprFree(o);
        exprFreeFuncList(f);
        exprFreeValList(v);
        exprFreeValList(c);

        image_free(image);
        return;
        }

    /* Set up lists */

    /* Function list */
    err = exprFuncListCreate(&f);
    if(err != EXPR_ERROR_NOERROR)
        longjmp(jumper, err);

    err = exprFuncListInit(f);
    if(err != EXPR_ERROR_NOERROR)
        {
        printf("Function list init error. Functions may not be available.\n");
        }

    /* Variable list */
    err = exprValListCreate(&v);
    if(err != EXPR_ERROR_NOERROR)
        longjmp(jumper, err);

    /* Constant list */
    err = exprValListCreate(&c);
    if(err != EXPR_ERROR_NOERROR)
        {
        printf("Constants not available\n");
        }
    else
        {
        err = exprValListInit(c);
        if(err != EXPR_ERROR_NOERROR)
            printf("Constant list init error. Constants may not be available.\n");
        }

    /* Create and parse the expression */

    /* Create */
    err = exprCreate(&o, f, v, c, NULL, 0);
    if(err != EXPR_ERROR_NOERROR)
        longjmp(jumper, err);

    /* Parse expression */
    err = exprParse(o, expr);
    if(err != EXPR_ERROR_NOERROR)
        longjmp(jumper, err);


    /* Create the image */
    image = image_create(w, h);
    if(image == 0)
        {
        longjmp(jumper, ID_IMAGECREATEERROR);
        }

    /* Add width and height to variable list */
    exprValListAdd(v, "w", (EXPRTYPE)w);
    exprValListAdd(v, "h", (EXPRTYPE)h);

    /* Add x and y to the list */
    exprValListAdd(v, "x", 0.0);
    exprValListAdd(v, "y", 0.0);

    /* Add r, g, and b to the list */
    exprValListAdd(v, "r", 0.0);
    exprValListAdd(v, "g", 0.0);
    exprValListAdd(b, "b", 0.0);

    /* Get addresses.  Assume no error */
    exprValListGetAddress(v, "x", &v_x);
    exprValListGetAddress(v, "y", &v_y);

    exprValListGetAddress(v, "r", &v_r);
    exprValListGetAddress(v, "g", &v_g);
    exprValListGetAddress(v, "g", &v_b);

    for(y = 0; y < h; y++)
        {
        for(x = 0; x < w; x++)
            {
            /* Directly set the x and y variables */
            *v_x = (EXPRTYPE)x;
            *v_y = (EXPRTYPE)y;

            /* Eval expression, ignoring errors */
            exprEval(o);

            /* Set pixel, using variables directly */
            image_setpixel(image, x, y, (int)(*v_r), (int)(*v_g), (int)(*v_b));
            }
        }

    /* Save image */
    image_save(image, name);

    /* Done */
    longjmp(jumper, ID_IMAGENOERROR);
    }

void main(void)
    {
    char name[MAXPATH]
    char tmp[10];
    char expr[4096];
    int sx, sy;

    printf("Image name to save: ");
    gets(name);

    printf("Image width: ");
    gets(tmp);
    sx = atoi(tmp);

    printf("Image height: ");
    gets(tmp);
    sy = atoi(tmp);

    printf("Color Expression (Use x, y, w, h Set r, g, b): ");
    gets(expr);

    gen_image(name, sx, sy, expr);
    }