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 based expression evaluation library. It is entirely C based, but can be used in C++ programs as well.. 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.

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 pound sign '#' and end at the end of the line.
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. Fast variable access is always used internally in 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 used, the variable memory location is directly accessed 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 can be created for use by the library. This is how a function should look

obj is a pointer to the expression object that called the function, nodes is a pointer to an array of nodes that are the parameters of this function, nodecount is the number of items in the array (the number of parameters), refs 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.

Solving a function typically goes as follows:

Example:

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

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" for the header file.

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. You should not have to change to much. I have tried to stick as close to ANSI/ISO C as I can.

Drawbacks/Problems

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

Problems and Solutions

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.

Note that this codes has not actually been tested. See the test applications (test and imagegen) for other examples.

/* 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;
    EXPRTYPE global_value;

    /* 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);
    
    /* A way to add global variables that can be used by two different lists. */
    exprValListAddAddress(v, "global", &global_value);
    /* exprValListAddAddress(v2, "global", &global_value); */
    
    /* Also, exprValListAddAddress can be used to add variables directly.
       Instead of:
       
       EXPRTYPE *a;
       
       exprValListAdd(v, "a", 0.0);
       exprValListGetAddresss(v, "a", &a);
       
       You can do:
       
       EXPRTYPE a;
       
       exprValListAddAddresss(v, "a", &a);
       
       If you do this, you must ensure that the stack variable exists as long
       as it is used by expression, otherwise it may cause a memory access
       violation. */
    

    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);
    }