/* $Id$
 * $URL$
 *
 * expression evaluation
 *
 * Copyright (C) 1999, 2000 Michael Reinelt <michael@reinelt.co.at>
 * Copyright (C) 2004 The LCD4Linux Team <lcd4linux-devel@users.sourceforge.net>
 *
 * This file is part of LCD4Linux.
 *
 * LCD4Linux is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2, or (at your option)
 * any later version.
 *
 * LCD4Linux is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 */


/* 
 * exported functions:
 *
 * int SetVariable (char *name, RESULT *value)
 *   adds a generic variable to the evaluator
 *
 * int SetVariableNumeric (char *name, double value)
 *   adds a numerical variable to the evaluator
 *
 * int SetVariableString (char *name, char *value)
 *   adds a numerical variable to the evaluator
 *
 * int AddFunction (char *name, int argc, void (*func)())
 *   adds a function to the evaluator
 *
 * void DeleteVariables    (void);
 *   frees all allocated variables
 *
 * void DeleteFunctions    (void);
 *   frees all allocated functions
 *
 * void DelResult (RESULT *result)
 *   sets a result to none
 *   frees a probably allocated memory
 *
 * RESULT* SetResult (RESULT **result, int type, void *value)
 *   initializes a result
 *
 * double R2N (RESULT *result)
 *   converts a result into a number
 *
 * char* R2S (RESULT *result)
 *   converts a result into a string
 *
 *
 * int Compile (char* expression, void **tree)
 *   compiles a expression into a tree
 * 
 * int Eval (void *tree, RESULT *result)
 *   evaluates an expression
 *
 * void DelTree (void *tree)
 *   frees a compiled tree
 */


#include "config.h"

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <math.h>
#include <setjmp.h>

#include "debug.h"
#include "evaluator.h"

#ifdef WITH_DMALLOC
#include <dmalloc.h>
#endif

/* string buffer chunk size */
#define CHUNK_SIZE 16

typedef enum {
    T_UNDEF,
    T_NAME,
    T_NUMBER,
    T_STRING,
    T_OPERATOR,
    T_VARIABLE,
    T_FUNCTION
} TOKEN;

typedef enum {
    O_UNDEF,			/* undefined */
    O_LST,			/* expression lists */
    O_SET,			/* variable assignements */
    O_CND,			/* conditional a?b:c */
    O_COL,			/* colon in a?b:c */
    O_OR,			/* logical OR */
    O_AND,			/* logical AND */
    O_NEQ,			/* numeric equal */
    O_NNE,			/* numeric not equal */
    O_NLT,			/* numeric less than */
    O_NLE,			/* numeric less or equal */
    O_NGT,			/* numeric greater than */
    O_NGE,			/* numeric greater or equal */
    O_SEQ,			/* string equal */
    O_SNE,			/* string not equal */
    O_SLT,			/* string less than */
    O_SLE,			/* string less or equal */
    O_SGT,			/* string greater than */
    O_SGE,			/* string greater or equal */
    O_ADD,			/* addition */
    O_SUB,			/* subtraction */
    O_SGN,			/* sign '-' */
    O_CAT,			/* string concatenation */
    O_MUL,			/* multiplication */
    O_DIV,			/* division */
    O_MOD,			/* modulo */
    O_POW,			/* power */
    O_NOT,			/* logical NOT */
    O_BRO,			/* open brace */
    O_COM,			/* comma (argument seperator) */
    O_BRC			/* closing brace */
} OPERATOR;

typedef struct {
    char *pattern;
    int len;
    OPERATOR op;
} PATTERN;

typedef struct {
    char *name;
    RESULT *value;
} VARIABLE;

typedef struct {
    char *name;
    int argc;
    void (*func) ();
} FUNCTION;

typedef struct _NODE {
    TOKEN Token;
    OPERATOR Operator;
    RESULT *Result;
    VARIABLE *Variable;
    FUNCTION *Function;
    int Children;
    struct _NODE **Child;
} NODE;



/* non-alphanumeric operators */
/* IMPORTANT! list must be sorted by length! */
static PATTERN Pattern1[] = {
    {";", 1, O_LST},		/* expression lists */
    {"=", 1, O_SET},		/* variable assignements */
    {"?", 1, O_CND},		/* conditional a?b:c */
    {":", 1, O_COL},		/* colon a?b:c */
    {"|", 1, O_OR},		/* logical OR */
    {"&", 1, O_AND},		/* logical AND */
    {"<", 1, O_NLT},		/* numeric less than */
    {">", 1, O_NGT},		/* numeric greater than */
    {"+", 1, O_ADD},		/* addition */
    {"-", 1, O_SUB},		/* subtraction or sign */
    {".", 1, O_CAT},		/* string concatenation */
    {"*", 1, O_MUL},		/* multiplication */
    {"/", 1, O_DIV},		/* division */
    {"%", 1, O_MOD},		/* modulo */
    {"^", 1, O_POW},		/* power */
    {"!", 1, O_NOT},		/* logical NOT */
    {"(", 1, O_BRO},		/* open brace */
    {",", 1, O_COM},		/* comma (argument seperator) */
    {")", 1, O_BRC},		/* closing brace */
    {"==", 2, O_NEQ},		/* numeric equal */
    {"!=", 2, O_NNE},		/* numeric not equal */
    {"<=", 2, O_NLE},		/* numeric less or equal */
    {">=", 2, O_NGE}		/* numeric greater or equal */
};

/* alphanumeric operators */
/* IMPORTANT! list must be sorted by length! */
static PATTERN Pattern2[] = {
    {"eq", 2, O_SEQ},		/* string equal */
    {"ne", 2, O_SNE},		/* string not equal */
    {"lt", 2, O_SLT},		/* string less than */
    {"le", 2, O_SLE},		/* string less or equal */
    {"gt", 2, O_SGT},		/* string greater than */
    {"ge", 2, O_SGE}		/* string greater or equal */
};


static char *Expression = NULL;
static char *ExprPtr = NULL;
static char *Word = NULL;
static TOKEN Token = T_UNDEF;
static OPERATOR Operator = O_UNDEF;

static VARIABLE Variable[255];
static unsigned int nVariable = 0;

static FUNCTION *Function = NULL;
static unsigned int nFunction = 0;


/* strndup() may be not available on several platforms */
#ifndef HAVE_STRNDUP
char *strndup(const char *source, size_t len)
{
    char *tmp = NULL;

    if (source == NULL)
	return NULL;

    if (len >= strlen(source))
	return strdup(source);

    tmp = malloc(len + 1);
    if (tmp == 0)
	return NULL;

    strncpy(tmp, source, len);
    tmp[len] = '\0';

    return (tmp);
}
#endif


void DelResult(RESULT * result)
{
    result->type = 0;
    result->size = 0;
    result->number = 0.0;
    if (result->string) {
	free(result->string);
	result->string = NULL;
    }
}


static void FreeResult(RESULT * result)
{
    if (result != NULL) {
	DelResult(result);
	free(result);
    }
}


static RESULT *NewResult(void)
{
    RESULT *result = malloc(sizeof(RESULT));
    if (result == NULL) {
	error("Evaluator: cannot allocate result: out of memory!");
	return NULL;
    }
    result->type = 0;
    result->size = 0;
    result->number = 0.0;
    result->string = NULL;

    return result;
}


RESULT *SetResult(RESULT ** result, const int type, const void *value)
{
    if (*result == NULL) {
	if ((*result = NewResult()) == NULL)
	    return NULL;
    } else if (type == R_NUMBER) {
	DelResult(*result);
    }

    if (type == R_NUMBER) {
	(*result)->type = R_NUMBER;
	(*result)->size = 0;
	(*result)->number = *(double *) value;
	(*result)->string = NULL;
    }

    else if (type == R_STRING) {
	int len = strlen((char *) value);
	(*result)->type = R_STRING;
	(*result)->number = 0.0;
	if ((*result)->string == NULL || len >= (*result)->size) {
	    /* buffer is either empty or too small */
	    if ((*result)->string)
		free((*result)->string);
	    /* allocate memory in multiples of CHUNK_SIZE */
	    (*result)->size = CHUNK_SIZE * ((len + 1) / CHUNK_SIZE + 1);
	    (*result)->string = malloc((*result)->size);
	}
	strcpy((*result)->string, value);
    } else {
	error("Evaluator: internal error: invalid result type %d", type);
	return NULL;
    }

    return *result;
}


RESULT *CopyResult(RESULT ** result, RESULT * value)
{
    if (*result == NULL) {
	if ((*result = NewResult()) == NULL)
	    return NULL;
    }

    (*result)->type = value->type;
    (*result)->number = value->number;

    if (value->string == NULL) {
	(*result)->size = 0;
	if ((*result)->string)
	    free((*result)->string);
	(*result)->string = NULL;
    } else {
	/* is buffer large enough? */
	if ((*result)->string == NULL || value->size > (*result)->size) {
	    if ((*result)->string)
		free((*result)->string);
	    (*result)->size = value->size;
	    (*result)->string = malloc((*result)->size);
	}
	strcpy((*result)->string, value->string);
    }
    return *result;
}


double R2N(RESULT * result)
{
    if (result == NULL) {
	error("Evaluator: internal error: NULL result");
	return 0.0;
    }

    if (result->type == 0) {
	return 0.0;
    }

    if (result->type & R_NUMBER) {
	return result->number;
    }

    if (result->type & R_STRING) {
	result->type |= R_NUMBER;
	result->number = atof(result->string);
	return result->number;
    }

    error("Evaluator: internal error: invalid result type %d", result->type);
    return 0.0;
}


char *R2S(RESULT * result)
{
    if (result == NULL) {
	error("Evaluator: internal error: NULL result");
	return NULL;
    }

    if (result->type == 0) {
	return NULL;
    }

    if (result->type & R_STRING) {
	return result->string;
    }

    if (result->type & R_NUMBER) {
	result->type |= R_STRING;
	if (result->string)
	    free(result->string);
	result->size = CHUNK_SIZE;
	result->string = malloc(result->size);
	snprintf(result->string, result->size, "%g", result->number);
	return result->string;
    }

    error("Evaluator: internal error: invalid result type %d", result->type);
    return NULL;

}


static VARIABLE *FindVariable(const char *name)
{
    unsigned int i;

    for (i = 0; i < nVariable; i++) {
	if (strcmp(name, Variable[i].name) == 0) {
	    return &Variable[i];
	}
    }
    return NULL;
}


int SetVariable(const char *name, RESULT * value)
{
    VARIABLE *V;

    V = FindVariable(name);
    if (V != NULL) {
	CopyResult(&V->value, value);
	return 1;
    }

    if (nVariable >= sizeof(Variable) / sizeof(Variable[0])) {
	error("Evaluator: cannot set variable <%s>: out of slots", name);
	return -1;
    }

    nVariable++;
    Variable[nVariable - 1].name = strdup(name);
    Variable[nVariable - 1].value = NULL;
    CopyResult(&Variable[nVariable - 1].value, value);

    return 0;
}


int SetVariableNumeric(const char *name, const double value)
{
    RESULT result = { 0, 0, 0, NULL };
    RESULT *rp = &result;

    SetResult(&rp, R_NUMBER, &value);

    return SetVariable(name, rp);
}


int SetVariableString(const char *name, const char *value)
{
    RESULT result = { 0, 0, 0, NULL };
    RESULT *rp = &result;

    SetResult(&rp, R_STRING, value);

    return SetVariable(name, rp);
}


void DeleteVariables(void)
{
    unsigned int i;

    for (i = 0; i < nVariable; i++) {
	free(Variable[i].name);
	FreeResult(Variable[i].value);
    }
    nVariable = 0;
}


/* bsearch compare function for functions */
static int LookupFunction(const void *a, const void *b)
{
    char *n = (char *) a;
    FUNCTION *f = (FUNCTION *) b;

    return strcmp(n, f->name);
}


/* qsort compare function for functions */
static int SortFunction(const void *a, const void *b)
{
    FUNCTION *fa = (FUNCTION *) a;
    FUNCTION *fb = (FUNCTION *) b;

    return strcmp(fa->name, fb->name);
}


static FUNCTION *FindFunction(const char *name)
{
    return bsearch(name, Function, nFunction, sizeof(FUNCTION), LookupFunction);
}


int AddFunction(const char *name, const int argc, void (*func) ())
{
    nFunction++;
    Function = realloc(Function, nFunction * sizeof(FUNCTION));
    Function[nFunction - 1].name = strdup(name);
    Function[nFunction - 1].argc = argc;
    Function[nFunction - 1].func = func;

    qsort(Function, nFunction, sizeof(FUNCTION), SortFunction);

    return 0;
}


void DeleteFunctions(void)
{
    unsigned int i;

    for (i = 0; i < nFunction; i++) {
	free(Function[i].name);
    }
    free(Function);
    Function = NULL;
    nFunction = 0;
}


#define is_space(c)  ((c) == ' ' || (c) == '\t')
#define is_digit(c)  ((c) >= '0' && (c) <= '9')
#define is_alpha(c) (((c) >= 'A' && (c) <= 'Z') || ((c) >= 'a' && (c) <= 'z') || ((c) == '_'))
#define is_alnum(c) (is_alpha(c) || is_digit(c))

static void Parse(void)
{
    Token = T_UNDEF;
    Operator = O_UNDEF;

    if (Word) {
	free(Word);
	Word = NULL;
    }

    /* NULL expression? */
    if (ExprPtr == NULL) {
	Word = strdup("");
	return;
    }

    /* skip leading whitespace */
    while (is_space(*ExprPtr))
	ExprPtr++;

    /* names */
    if (is_alpha(*ExprPtr)) {
	int i;
	char *start = ExprPtr;
	while (is_alnum(*ExprPtr))
	    ExprPtr++;
	if (*ExprPtr == ':' && *(ExprPtr + 1) == ':' && is_alpha(*(ExprPtr + 2))) {
	    ExprPtr += 3;
	    while (is_alnum(*ExprPtr))
		ExprPtr++;
	}
	Word = strndup(start, ExprPtr - start);
	Token = T_NAME;

	/* check for alphanumeric operators */
	for (i = sizeof(Pattern2) / sizeof(Pattern2[0]) - 1; i >= 0; i--) {
	    if (strcmp(Word, Pattern2[i].pattern) == 0) {
		Token = T_OPERATOR;
		Operator = Pattern2[i].op;
		break;
	    }
	}

    }

    /* numbers */
    else if (is_digit(*ExprPtr) || (*ExprPtr == '.' && is_digit(*(ExprPtr + 1)))) {
	char *start = ExprPtr;
	while (is_digit(*ExprPtr))
	    ExprPtr++;
	if (*ExprPtr == '.') {
	    ExprPtr++;
	    while (is_digit(*ExprPtr))
		ExprPtr++;
	}
	Word = strndup(start, ExprPtr - start);
	Token = T_NUMBER;
    }

    /* strings */
    else if (*ExprPtr == '\'') {
	size_t length = 0;
	size_t size = CHUNK_SIZE;
	Word = malloc(size);
	ExprPtr++;
	while (*ExprPtr != '\0' && *ExprPtr != '\'') {
	    if (*ExprPtr == '\\') {
		switch (*(ExprPtr + 1)) {
		case '\\':
		case '\'':
		    Word[length++] = *(ExprPtr + 1);
		    ExprPtr += 2;
		    break;
		case 'a':
		    Word[length++] = '\a';
		    ExprPtr += 2;
		    break;
		case 'b':
		    Word[length++] = '\b';
		    ExprPtr += 2;
		    break;
		case 't':
		    Word[length++] = '\t';
		    ExprPtr += 2;
		    break;
		case 'n':
		    Word[length++] = '\n';
		    ExprPtr += 2;
		    break;
		case 'v':
		    Word[length++] = '\v';
		    ExprPtr += 2;
		    break;
		case 'f':
		    Word[length++] = '\f';
		    ExprPtr += 2;
		    break;
		case 'r':
		    Word[length++] = '\r';
		    ExprPtr += 2;
		    break;
		case '0':
		case '1':
		case '2':
		case '3':
		    if (*(ExprPtr + 2) >= '0' && *(ExprPtr + 2) <= '7' &&
			*(ExprPtr + 3) >= '0' && *(ExprPtr + 3) <= '7') {
			Word[length++] =
			    (*(ExprPtr + 1) - '0') * 64 + (*(ExprPtr + 2) - '0') * 8 + (*(ExprPtr + 3) - '0');
			ExprPtr += 4;
		    } else {
			error("Evaluator: illegal octal sequence '\\%c%c%c' in <%s>",
			      *(ExprPtr + 1), *(ExprPtr + 2), *(ExprPtr + 3), Expression);
			Word[length++] = *ExprPtr++;
		    }
		    break;
		default:
		    error("Evaluator: unknown escape sequence '\\%c' in <%s>", *(ExprPtr + 1), Expression);
		    Word[length++] = *ExprPtr++;
		}
	    } else {
		Word[length++] = *ExprPtr++;
	    }
	    if (length >= size) {
		size += CHUNK_SIZE;
		Word = realloc(Word, size);
	    }
	}
	Word[length] = '\0';
	Token = T_STRING;
	if (*ExprPtr == '\'') {
	    ExprPtr++;
	} else {
	    error("Evaluator: unterminated string in <%s>", Expression);
	}
    }

    /* non-alpha operators */
    else {
	int i;
	for (i = sizeof(Pattern1) / sizeof(Pattern1[0]) - 1; i >= 0; i--) {
	    int len = Pattern1[i].len;
	    if (strncmp(ExprPtr, Pattern1[i].pattern, Pattern1[i].len) == 0) {
		Word = strndup(ExprPtr, len);
		Token = T_OPERATOR;
		Operator = Pattern1[i].op;
		ExprPtr += len;
		break;
	    }
	}
    }

    /* syntax check */
    if (Token == T_UNDEF && *ExprPtr != '\0') {
	error("Evaluator: parse error in <%s>: garbage <%s>", Expression, ExprPtr);
    }

    /* skip trailing whitespace */
    while (is_space(*ExprPtr))
	ExprPtr++;

    /* empty token */
    if (Word == NULL)
	Word = strdup("");
}


static NODE *NewNode(NODE * Child)
{
    NODE *N;

    N = malloc(sizeof(NODE));
    if (N == NULL)
	return NULL;

    memset(N, 0, sizeof(NODE));
    N->Token = Token;
    N->Operator = Operator;

    if (Child != NULL) {
	N->Children = 1;
	N->Child = malloc(sizeof(NODE *));
	N->Child[0] = Child;
    }

    return N;

}


static NODE *JunkNode(void)
{
    NODE *Junk;

    Junk = NewNode(NULL);
    Junk->Token = T_STRING;
    SetResult(&Junk->Result, R_STRING, "");

    return Junk;
}


static void LinkNode(NODE * Root, NODE * Child)
{

    if (Child == NULL)
	return;

    Root->Children++;
    Root->Child = realloc(Root->Child, Root->Children * sizeof(NODE *));
    if (Root->Child == NULL)
	return;
    Root->Child[Root->Children - 1] = Child;
}


/* forward declaration */
static NODE *Level01(void);


/* literal numbers, variables, functions */
static NODE *Level12(void)
{
    NODE *Root = NULL;

    if (Token == T_OPERATOR && Operator == O_BRO) {
	Parse();
	Root = Level01();
	if (Token != T_OPERATOR || Operator != O_BRC) {
	    error("Evaluator: unbalanced parentheses in <%s>", Expression);
	    LinkNode(Root, JunkNode());
	}
    }

    else if (Token == T_NUMBER) {
	double value = atof(Word);
	Root = NewNode(NULL);
	SetResult(&Root->Result, R_NUMBER, &value);
    }

    else if (Token == T_STRING) {
	Root = NewNode(NULL);
	SetResult(&Root->Result, R_STRING, Word);
    }

    else if (Token == T_NAME) {

	/* look-ahead for opening brace */
	if (*ExprPtr == '(') {
	    int argc = 0;
	    Root = NewNode(NULL);
	    Root->Token = T_FUNCTION;
	    Root->Result = NewResult();
	    Root->Function = FindFunction(Word);
	    if (Root->Function == NULL) {
		error("Evaluator: unknown function '%s' in <%s>", Word, Expression);
		Root->Token = T_STRING;
		SetResult(&Root->Result, R_STRING, "");
	    }

	    /* opening brace */
	    Parse();
	    do {
		Parse();	/* read argument */
		if (Token == T_OPERATOR && Operator == O_BRC) {
		    break;
		} else if (Token == T_OPERATOR && Operator == O_COM) {
		    error("Evaluator: empty argument in <%s>", Expression);
		    LinkNode(Root, JunkNode());
		} else {
		    LinkNode(Root, Level01());
		}
		argc++;
	    } while (Token == T_OPERATOR && Operator == O_COM);

	    /* check for closing brace */
	    if (Token != T_OPERATOR || Operator != O_BRC) {
		error("Evaluator: missing closing brace in <%s>", Expression);
	    }

	    /* check number of arguments */
	    if (Root->Function != NULL && Root->Function->argc >= 0 && Root->Function->argc != argc) {
		error("Evaluator: wrong number of arguments in <%s>", Expression);
		while (argc < Root->Function->argc) {
		    LinkNode(Root, JunkNode());
		    argc++;
		}
	    }

	} else {
	    Root = NewNode(NULL);
	    Root->Token = T_VARIABLE;
	    Root->Result = NewResult();
	    Root->Variable = FindVariable(Word);
	    if (Root->Variable == NULL) {
		SetVariableString(Word, "");
		Root->Variable = FindVariable(Word);
	    }
	}
    }

    else {
	error("Evaluator: syntax error in <%s>: <%s>", Expression, Word);
	Root = NewNode(NULL);
	Root->Token = T_STRING;
	SetResult(&Root->Result, R_STRING, "");
    }

    Parse();
    return Root;

}


/* unary + or - signs or logical 'not' */
static NODE *Level11(void)
{
    NODE *Root;
    TOKEN sign = T_UNDEF;

    if (Token == T_OPERATOR && (Operator == O_ADD || Operator == O_SUB || Operator == O_NOT)) {
	sign = Operator;
	if (sign == O_SUB)
	    sign = O_SGN;
	Parse();
    }

    Root = Level12();

    if (sign == O_SGN || sign == O_NOT) {
	Root = NewNode(Root);
	Root->Token = T_OPERATOR;
	Root->Operator = sign;
    }

    return Root;
}


/* x^y */
static NODE *Level10(void)
{
    NODE *Root;

    Root = Level11();

    while (Token == T_OPERATOR && Operator == O_POW) {
	Root = NewNode(Root);
	Parse();
	LinkNode(Root, Level11());
    }

    return Root;
}


/* multiplication, division, modulo */
static NODE *Level09(void)
{
    NODE *Root;

    Root = Level10();

    while (Token == T_OPERATOR && (Operator == O_MUL || Operator == O_DIV || Operator == O_MOD)) {
	Root = NewNode(Root);
	Parse();
	LinkNode(Root, Level10());
    }

    return Root;
}


/* addition, subtraction, string concatenation */
static NODE *Level08(void)
{
    NODE *Root;

    Root = Level09();

    while (Token == T_OPERATOR && (Operator == O_ADD || Operator == O_SUB || Operator == O_CAT)) {
	Root = NewNode(Root);
	Parse();
	LinkNode(Root, Level09());
    }

    return Root;
}


/* relational operators */
static NODE *Level07(void)
{
    NODE *Root;

    Root = Level08();

    while (Token == T_OPERATOR && (Operator == O_NGT || Operator == O_NGE || Operator == O_NLT || Operator == O_NLE ||
				   Operator == O_SGT || Operator == O_SGE || Operator == O_SLT || Operator == O_SLE)) {
	Root = NewNode(Root);
	Parse();
	LinkNode(Root, Level08());
    }

    return Root;
}


/* equal, not equal */
static NODE *Level06(void)
{
    NODE *Root;

    Root = Level07();

    while (Token == T_OPERATOR && (Operator == O_NEQ || Operator == O_NNE || Operator == O_SEQ || Operator == O_SNE)) {
	Root = NewNode(Root);
	Parse();
	LinkNode(Root, Level07());
    }

    return Root;
}

/* logical 'and' */
static NODE *Level05(void)
{
    NODE *Root;

    Root = Level06();

    while (Token == T_OPERATOR && Operator == O_AND) {
	Root = NewNode(Root);
	Parse();
	LinkNode(Root, Level06());
    }

    return Root;
}


/* logical 'or' */
static NODE *Level04(void)
{
    NODE *Root;

    Root = Level05();

    while (Token == T_OPERATOR && Operator == O_OR) {
	Root = NewNode(Root);
	Parse();
	LinkNode(Root, Level05());
    }

    return Root;
}


/* conditional expression a?b:c */
static NODE *Level03(void)
{
    NODE *Root;

    Root = Level04();

    if (Token == T_OPERATOR && Operator == O_CND) {
	Root = NewNode(Root);
	Parse();
	LinkNode(Root, Level04());
	if (Token == T_OPERATOR && Operator == O_COL) {
	    Parse();
	    LinkNode(Root, Level04());
	} else {
	    error("Evaluator: syntax error in <%s>: expecting ':' got '%s'", Expression, Word);
	    LinkNode(Root, JunkNode());
	}
    }

    return Root;
}


/* variable assignments */
static NODE *Level02(void)
{
    NODE *Root;

    /* we have to do a look-ahead if it's really an assignment */
    if ((Token == T_NAME) && (*ExprPtr == '=') && (*(ExprPtr + 1) != '=')) {
	char *name = strdup(Word);
	VARIABLE *V = FindVariable(name);
	if (V == NULL) {
	    SetVariableString(name, "");
	    V = FindVariable(name);
	}
	Parse();
	Root = NewNode(NULL);
	Root->Variable = V;
	Parse();
	LinkNode(Root, Level03());
	free(name);
    } else {
	Root = Level03();
    }

    return Root;
}


/* expression lists */
static NODE *Level01(void)
{
    NODE *Root;

    Root = Level02();

    while (Token == T_OPERATOR && Operator == O_LST) {
	Root = NewNode(Root);
	Parse();
	LinkNode(Root, Level02());
    }

    return Root;
}


static int EvalTree(NODE * Root)
{
    int i;
    int argc;
    int type = -1;
    double number = 0.0;
    double dummy;
    int freeme = 0;
    char *string = NULL;
    char *s1, *s2;
    RESULT *param[10];

    switch (Root->Token) {

    case T_NUMBER:
    case T_STRING:
	/* Root->Result already contains the value */
	return 0;

    case T_VARIABLE:
	CopyResult(&Root->Result, Root->Variable->value);
	return 0;

    case T_FUNCTION:
	DelResult(Root->Result);
	/* prepare parameter list */
	argc = Root->Children;
	if (argc > 10) {
	    error("evaluator: more than 10 children (operands) not supported!");
	    argc = 10;
	}
	for (i = 0; i < argc; i++) {
	    EvalTree(Root->Child[i]);
	    param[i] = Root->Child[i]->Result;
	}
	if (Root->Function->argc < 0) {
	    /* Function with variable argument list:  */
	    /* pass number of arguments as first parameter */
	    Root->Function->func(Root->Result, argc, &param);
	} else {
	    Root->Function->func(Root->Result, param[0], param[1], param[2], param[3], param[4], param[5], param[6],
				 param[7], param[8], param[9]);
	}
	return 0;

    case T_OPERATOR:
	switch (Root->Operator) {

	case O_LST:		/* expression list: result is last expression */
	    for (i = 0; i < Root->Children; i++) {
		EvalTree(Root->Child[i]);
	    }
	    i = Root->Children - 1;
	    type = Root->Child[i]->Result->type;
	    number = Root->Child[i]->Result->number;
	    string = Root->Child[i]->Result->string;
	    break;

	case O_SET:		/* variable assignment */
	    EvalTree(Root->Child[0]);
	    CopyResult(&Root->Variable->value, Root->Child[0]->Result);
	    type = Root->Child[0]->Result->type;
	    number = Root->Child[0]->Result->number;
	    string = Root->Child[0]->Result->string;
	    break;

	case O_CND:		/* conditional expression */
	    EvalTree(Root->Child[0]);
	    i = 1 + (R2N(Root->Child[0]->Result) == 0.0);
	    EvalTree(Root->Child[i]);
	    type = Root->Child[i]->Result->type;
	    number = Root->Child[i]->Result->number;
	    string = Root->Child[i]->Result->string;
	    break;

	case O_OR:		/* logical OR */
	    type = R_NUMBER;
	    EvalTree(Root->Child[0]);
	    if (R2N(Root->Child[0]->Result) == 0.0) {
		EvalTree(Root->Child[1]);
		number = (R2N(Root->Child[1]->Result) != 0.0);
	    } else {
		number = 1.0;
	    }
	    break;

	case O_AND:		/* logical AND */
	    type = R_NUMBER;
	    EvalTree(Root->Child[0]);
	    if (R2N(Root->Child[0]->Result) != 0.0) {
		EvalTree(Root->Child[1]);
		number = (R2N(Root->Child[1]->Result) != 0.0);
	    } else {
		number = 0.0;
	    }
	    break;

	case O_NEQ:		/* numeric equal */
	    type = R_NUMBER;
	    EvalTree(Root->Child[0]);
	    EvalTree(Root->Child[1]);
	    number = (R2N(Root->Child[0]->Result) == R2N(Root->Child[1]->Result));
	    break;

	case O_NNE:		/* numeric not equal */
	    type = R_NUMBER;
	    EvalTree(Root->Child[0]);
	    EvalTree(Root->Child[1]);
	    number = (R2N(Root->Child[0]->Result) != R2N(Root->Child[1]->Result));
	    break;

	case O_NLT:		/* numeric less than */
	    type = R_NUMBER;
	    EvalTree(Root->Child[0]);
	    EvalTree(Root->Child[1]);
	    number = (R2N(Root->Child[0]->Result) < R2N(Root->Child[1]->Result));
	    break;

	case O_NLE:		/* numeric less equal */
	    type = R_NUMBER;
	    EvalTree(Root->Child[0]);
	    EvalTree(Root->Child[1]);
	    number = (R2N(Root->Child[0]->Result) <= R2N(Root->Child[1]->Result));
	    break;

	case O_NGT:		/* numeric greater than */
	    type = R_NUMBER;
	    EvalTree(Root->Child[0]);
	    EvalTree(Root->Child[1]);
	    number = (R2N(Root->Child[0]->Result) > R2N(Root->Child[1]->Result));
	    break;

	case O_NGE:		/* numeric greater equal */
	    type = R_NUMBER;
	    EvalTree(Root->Child[0]);
	    EvalTree(Root->Child[1]);
	    number = (R2N(Root->Child[0]->Result) >= R2N(Root->Child[1]->Result));
	    break;

	case O_SEQ:		/* string equal */
	    type = R_NUMBER;
	    EvalTree(Root->Child[0]);
	    EvalTree(Root->Child[1]);
	    number = (strcmp(R2S(Root->Child[0]->Result), R2S(Root->Child[1]->Result)) == 0);
	    break;

	case O_SNE:		/* string not equal */
	    type = R_NUMBER;
	    EvalTree(Root->Child[0]);
	    EvalTree(Root->Child[1]);
	    number = (strcmp(R2S(Root->Child[0]->Result), R2S(Root->Child[1]->Result)) != 0);
	    break;

	case O_SLT:		/* string less than */
	    type = R_NUMBER;
	    EvalTree(Root->Child[0]);
	    EvalTree(Root->Child[1]);
	    number = (strcmp(R2S(Root->Child[0]->Result), R2S(Root->Child[1]->Result)) < 0);
	    break;

	case O_SLE:		/* string less equal */
	    type = R_NUMBER;
	    EvalTree(Root->Child[0]);
	    EvalTree(Root->Child[1]);
	    number = (strcmp(R2S(Root->Child[0]->Result), R2S(Root->Child[1]->Result)) <= 0);
	    break;

	case O_SGT:		/* string greater than */
	    type = R_NUMBER;
	    EvalTree(Root->Child[0]);
	    EvalTree(Root->Child[1]);
	    number = (strcmp(R2S(Root->Child[0]->Result), R2S(Root->Child[1]->Result)) > 0);
	    break;

	case O_SGE:		/* string greater equal */
	    type = R_NUMBER;
	    EvalTree(Root->Child[0]);
	    EvalTree(Root->Child[1]);
	    number = (strcmp(R2S(Root->Child[0]->Result), R2S(Root->Child[1]->Result)) >= 0);
	    break;

	case O_ADD:		/* addition */
	    type = R_NUMBER;
	    EvalTree(Root->Child[0]);
	    EvalTree(Root->Child[1]);
	    number = R2N(Root->Child[0]->Result) + R2N(Root->Child[1]->Result);
	    break;

	case O_SUB:		/* subtraction */
	    type = R_NUMBER;
	    EvalTree(Root->Child[0]);
	    EvalTree(Root->Child[1]);
	    number = R2N(Root->Child[0]->Result) - R2N(Root->Child[1]->Result);
	    break;

	case O_SGN:		/* sign */
	    type = R_NUMBER;
	    EvalTree(Root->Child[0]);
	    number = -R2N(Root->Child[0]->Result);
	    break;

	case O_CAT:		/* string concatenation */
	    type = R_STRING;
	    EvalTree(Root->Child[0]);
	    EvalTree(Root->Child[1]);
	    s1 = R2S(Root->Child[0]->Result);
	    s2 = R2S(Root->Child[1]->Result);
	    string = malloc(strlen(s1) + strlen(s2) + 1);
	    strcpy(string, s1);
	    strcat(string, s2);
	    freeme = 1;
	    break;

	case O_MUL:		/* multiplication */
	    type = R_NUMBER;
	    EvalTree(Root->Child[0]);
	    EvalTree(Root->Child[1]);
	    number = R2N(Root->Child[0]->Result) * R2N(Root->Child[1]->Result);
	    break;

	case O_DIV:		/* division */
	    type = R_NUMBER;
	    EvalTree(Root->Child[0]);
	    EvalTree(Root->Child[1]);
	    dummy = R2N(Root->Child[1]->Result);
	    if (dummy == 0) {
		error("Evaluator: warning: division by zero");
		number = 0.0;
	    } else {
		number = R2N(Root->Child[0]->Result) / R2N(Root->Child[1]->Result);
	    }
	    break;

	case O_MOD:		/* modulo */
	    type = R_NUMBER;
	    EvalTree(Root->Child[0]);
	    EvalTree(Root->Child[1]);
	    dummy = R2N(Root->Child[1]->Result);
	    if (dummy == 0) {
		error("Evaluator: warning: division by zero");
		number = 0.0;
	    } else {
		number = fmod(R2N(Root->Child[0]->Result), R2N(Root->Child[1]->Result));
	    }
	    break;

	case O_POW:		/* x^y */
	    type = R_NUMBER;
	    EvalTree(Root->Child[0]);
	    EvalTree(Root->Child[1]);
	    number = pow(R2N(Root->Child[0]->Result), R2N(Root->Child[1]->Result));
	    break;

	case O_NOT:		/* logical NOT */
	    type = R_NUMBER;
	    EvalTree(Root->Child[0]);
	    number = (R2N(Root->Child[0]->Result) == 0.0);
	    break;

	default:
	    error("Evaluator: internal error: unhandled operator <%d>", Root->Operator);
	    SetResult(&Root->Result, R_STRING, "");
	    return -1;
	}

	if (type == R_NUMBER) {
	    SetResult(&Root->Result, R_NUMBER, &number);
	    return 0;
	}
	if (type == R_STRING) {
	    SetResult(&Root->Result, R_STRING, string);
	    if (freeme)
		free(string);
	    return 0;
	}
	error("Evaluator: internal error: unhandled type <%d>", type);
	SetResult(&Root->Result, R_STRING, "");
	return -1;

    default:
	error("Evaluator: internal error: unhandled token <%d>", Root->Token);
	SetResult(&Root->Result, R_STRING, "");
	return -1;

    }

    return 0;
}


int Compile(const char *expression, void **tree)
{
    NODE *Root;

    *tree = NULL;

    Expression = (char *) expression;
    ExprPtr = Expression;

    Parse();
    if (*Word == '\0') {
	/* error ("Evaluator: empty expression <%s>", Expression); */
	free(Word);
	Word = NULL;
	return -1;
    }

    Root = Level01();

    if (*Word != '\0') {
	error("Evaluator: syntax error in <%s>: garbage <%s>", Expression, Word);
	free(Word);
	Word = NULL;
	return -1;
    }

    free(Word);
    Word = NULL;

    *(NODE **) tree = Root;

    return 0;
}


int Eval(void *tree, RESULT * result)
{
    int ret;
    NODE *Tree = (NODE *) tree;

    DelResult(result);

    if (Tree == NULL) {
	SetResult(&result, R_STRING, "");
	return 0;
    }

    ret = EvalTree(Tree);

    result->type = Tree->Result->type;
    result->size = Tree->Result->size;
    result->number = Tree->Result->number;
    if (result->size > 0) {
	result->string = malloc(result->size);
	if (Tree->Result->string != NULL) {
	    strcpy(result->string, Tree->Result->string);
	} else
	    result->string[0] = '\0';
    } else {
	result->string = NULL;
    }

    return ret;
}


void DelTree(void *tree)
{
    int i;
    NODE *Tree = (NODE *) tree;

    if (Tree == NULL)
	return;

    for (i = 0; i < Tree->Children; i++) {
	DelTree(Tree->Child[i]);
    }

    if (Tree->Result)
	FreeResult(Tree->Result);
    free(Tree);
}