#ifndef DMEVAL_H
#define DMEVAL_H

#include "dmlib.h"
#include <stdio.h>


typedef double DMValue;
#define DMCONVTYPE       (int)
#define DM_MAX_ARGS      8


enum
{
    OP_SUB_UNARY,
    OP_BIT_COMPLEMENT,

    OP_ADD,
    OP_SUB,
    OP_MUL,
    OP_DIV,
    OP_MOD,

    OP_BIT_LSHIFT,
    OP_BIT_RSHIFT,

    OP_BIT_AND,
    OP_BIT_OR,
    OP_BIT_XOR,

    OP_GT_EQ,
    OP_LT_EQ,
    OP_GT,
    OP_LT,

    // Special ops
    OP_FUNC,
    OP_VAR,
    OP_VALUE,
    OP_SUBEXPR,

    // Total number of operators
    OP_NOPERS,

    OP_INVALID
} DMEvalOperId;


enum
{
    OT_NONE,

    OT_LEFT,      // Left-associative
    OT_RIGHT,     // Right-associative
    OT_UNARY,
} DMEvalOperAssoc;


typedef struct
{
    char *name;     // Token
    int   assoc;    // Associativity type (DMEvalOperAssoc)
    BOOL  tokenize; // Automatically tokenize?
} DMEvalOper;


enum
{
    SYM_FUNC,
    SYM_VAR,
    SYM_CONST,
} DMEvalSymbolType;


typedef struct
{
    char *name;     // Name of the symbol
    int type;       // Type (SYM_*)
    int nargs;      // Number of arguments, if SYM_FUNC

    DMValue (*func)(DMValue arg[DM_MAX_ARGS]);

    DMValue *var;   // Pointer to variable value if SYM_VAR
    DMValue cvalue; // Const value, if SYM_CVAR
} DMEvalSymbol;


typedef struct DMEvalNode
{
    int op;               // Operator/token type
    DMValue val;          // Value, if immediate constant 
    DMEvalSymbol *symbol; // Symbol pointer, if function/variable/constvar

    struct DMEvalNode *args[DM_MAX_ARGS]; // Arguments, if function

    struct DMEvalNode *subexpr, *left, *right, *next, *prev;
} DMEvalNode;


typedef struct
{
    BOOL err;
    char *errStr;
    
    int nsymbols;
    DMEvalSymbol *symbols;

    int mode, prev, expect;
} DMEvalContext;


// Evaluation context management
DMEvalContext * dmEvalContextNew(void);
void            dmEvalContextClose(DMEvalContext *ev);
void            dmEvalContextClear(DMEvalContext *ev);


// Symbol management
DMEvalSymbol *  dmEvalContextFindSymbol(DMEvalContext *ev, const char *name);
DMEvalSymbol *  dmEvalContextAddVar(DMEvalContext *ev, const char *name, DMValue *var);
DMEvalSymbol *  dmEvalContextAddConst(DMEvalContext *ev, const char *name, DMValue value);
DMEvalSymbol *  dmEvalContextAddFunc(DMEvalContext *ev, const char *name, DMValue (*func)(DMValue *), int nargs);


// Evaluation trees, tokenization, parsing
int   dmEvalParseExpr(DMEvalContext *ev, char *expr, DMEvalNode **plist);
int   dmEvalTreeExecute(DMEvalContext *ev, DMEvalNode *tree, DMValue *presult);
void  dmEvalTreeFree(DMEvalNode *node);


void dmEvalPrintOpTree(FILE *out, DMEvalContext *ev, DMEvalNode *node);


extern const DMEvalOper dmEvalOpers[OP_NOPERS];

#endif // DMEVAL_H
