/*
    File: expreval.c
    Auth: Brian Allen Vanderburg II
    Date: Wednesday, April 30, 2003
    Desc: Evaluation routines for the ExprEval library

    This file is part of ExprEval.
*/

/* Includes */
#include "exprincl.h"

#include "exprpriv.h"

/* Defines for error checking */
#include <errno.h>

#if(EXPR_ERROR_LEVEL >= EXPR_ERROR_LEVEL_CHECK)
#define EXPR_RESET_ERR() errno = 0
#define EXPR_CHECK_ERR() if(errno) return EXPR_ERROR_OUTOFRANGE
#else
#define EXPR_RESET_ERR()
#define EXPR_CHECK_ERR()
#endif


/* This routine will evaluate an expression */
int exprEval(exprObj *obj, EXPRTYPE *val)
    {
    EXPRTYPE dummy;

    if(val ==  NULL)
        val = &dummy;

    /* Make sure it was parsed successfully */
    if(!obj->parsedbad && obj->parsedgood && obj->headnode)
        {
        /* Do NOT reset the break count.  Let is accumulate
           between calls until breaker function is called */
        return exprEvalNode(obj, obj->headnode, 0, val);
        }
    else
        return EXPR_ERROR_BADEXPR;
    }

/* Evaluate a node */
int exprEvalNode(exprObj *obj, exprNode *nodes, int curnode, EXPRTYPE *val)
    {
    int err;
    int pos;
    EXPRTYPE d1, d2;

    if(obj == NULL || nodes == NULL)
        return EXPR_ERROR_NULLPOINTER;

    /* Update n to point to correct node */
    nodes += curnode;

    /* Check breaker count */
    if(obj->breakcur-- <= 0)
        {
        /* Reset count before returning */
        obj->breakcur = obj->breakcount;
                
        if(exprGetBreakResult(obj))
            {
            return EXPR_ERROR_BREAK;
            }
        }

    switch(nodes->type)
        {
        case EXPR_NODETYPE_MULTI:
            {
            /* Multi for multiple expressions in one string */
            for(pos = 0; pos < nodes->data.oper.nodecount; pos++)
                {
                err = exprEvalNode(obj, nodes->data.oper.nodes, pos, val);
                if(err)
                    return err;
                }
            break;
            }

        case EXPR_NODETYPE_ADD:
            {
            /* Addition */
            err = exprEvalNode(obj, nodes->data.oper.nodes, 0, &d1);

            if(!err)
                err = exprEvalNode(obj, nodes->data.oper.nodes, 1, &d2);

            if(!err)
                *val = d1 + d2;
            else
                return err;

            break;
            }

        case EXPR_NODETYPE_SUBTRACT:
            {
            /* Subtraction */
            err = exprEvalNode(obj, nodes->data.oper.nodes, 0, &d1);
            
            if(!err)
                err = exprEvalNode(obj, nodes->data.oper.nodes, 1, &d2);

            if(!err)
                *val = d1 - d2;
            else
                return err;

            break;
            }

        case EXPR_NODETYPE_MULTIPLY:
            {
            /* Multiplication */
            err = exprEvalNode(obj, nodes->data.oper.nodes, 0, &d1);

            if(!err)
                err = exprEvalNode(obj, nodes->data.oper.nodes, 1, &d2);

            if(!err)
                *val = d1 * d2;
            else
                return err;

            break;
            }

        case EXPR_NODETYPE_DIVIDE:
            {
            /* Division */
            err = exprEvalNode(obj, nodes->data.oper.nodes, 0, &d1);

            if(!err)
                err = exprEvalNode(obj, nodes->data.oper.nodes, 1, &d2);

            if(!err)
                {
                if(d2 != 0.0)
                    *val = d1 / d2;
                else
                    {
#if(EXPR_ERROR_LEVEL >= EXPR_ERROR_LEVEL_CHECK)
                    return EXPR_ERROR_DIVBYZERO;
#else
                    *val = 0.0;
                    return EXPR_ERROR_NOERROR;
#endif
                    }
                }
            else
                return err;

            break;
            }

        case EXPR_NODETYPE_EXPONENT:
            {
            /* Exponent */
            err = exprEvalNode(obj, nodes->data.oper.nodes, 0, &d1);

            if(!err)
                err = exprEvalNode(obj, nodes->data.oper.nodes, 1, &d2);

            if(!err)
                {
                EXPR_RESET_ERR();
                *val = pow(d1, d2);
                EXPR_CHECK_ERR();
                }
            else
                return err;

            break;
            }

        case EXPR_NODETYPE_NEGATE:
            {
            /* Negative value */
            err = exprEvalNode(obj, nodes->data.oper.nodes, 0, &d1);

            if(!err)
                *val = -d1;
            else
                return err;
           
            break;
            }


        case EXPR_NODETYPE_VALUE:
            {
            /* Directly access the value */
            *val = nodes->data.value.value;
            break;
            }

        case EXPR_NODETYPE_VARIABLE:
            {
            /* Directly access the variable or constant */
            *val = *(nodes->data.variable.vaddr);
            break;
            }

        case EXPR_NODETYPE_ASSIGN:
            {
            /* Evaluate assignment subnode */
            err = exprEvalNode(obj, nodes->data.assign.node, 0, val);

            if(!err)
                {
                /* Directly assign the variable */
                *(nodes->data.assign.vaddr) = *val;
                }
            else
                return err;
            
            break;
            }

        case EXPR_NODETYPE_FUNCTION:
            {
            /* Evaluate the function */
            if(nodes->data.function.fptr == NULL)
                {
                /* No function pointer means we are not using
                   function solvers.  See if the function has a
                   type to solve directly. */
                switch(nodes->data.function.type)
                    {
                    /* This is to keep the file from being too crowded.
                       See exprilfs.h for the definitions. */
#include "exprilfs.h"
                   

                    default:
                        {
                        return EXPR_ERROR_UNKNOWN;
                        }
                    }
                }
            else
                {
                /* Call the correct function */
                return (*(nodes->data.function.fptr))(obj,
                    nodes->data.function.nodes, nodes->data.function.nodecount,
                    nodes->data.function.refs, nodes->data.function.refcount, val);
                }

            break;
            }

        default:
            {
            /* Unknown node type */
            return EXPR_ERROR_UNKNOWN;
            }
        }

    return EXPR_ERROR_NOERROR;
    }



