#include <windows.h>
#include "mathcrt.h"
#include "MyMath.h"
#include "Math.h"

extern "C" int _fltused;
int _fltused;
ExpressionItem *stack;

int UserVarsCount, UserFuncsCount;
UserVar UserVars[MAX_USER_VARS];
UserFunc UserFuncs[MAX_USER_FUNCS];

void PrintTree(ExpressionItem *root, char *str);
void ParseString(char *&sp, ExpressionItem* &itemplace);
void CleanupItems(ExpressionItem* &itemplace);
void PlaceVariable(char *&vb, ParseInfo *pi);

void PlaceNewItem(char *&vb, ParseInfo *pi, int precedence)
{
    ExpressionItem *newroot;
    PlaceVariable(vb, pi);
    if (pi->item == NULL) return;

    while ((pi->OpsStack) && ((((int) pi->OpsStack->param2) < precedence)
        || ((((int)pi->OpsStack->param2) == precedence) 
            && (precedence != OPERATOR_SET_PRECEDENCE))))
    {
        // second operand for our operator        
        newroot = pi->OpsStack;
        *((ExpressionItem **)&(newroot->param2)) = pi->item;
        pi->OpsStack = newroot->next;
        newroot->next = NULL;
        pi->item = newroot;
    }
    // finally we have got new root 
    newroot = pi->item;

    if (pi->SetupNewRoot)
    {
        (*pi->root)->next = newroot;
        pi->root = &((*pi->root)->next);
        pi->SetupNewRoot = 0;
    }
    if (pi->place == *pi->root) pi->place = *pi->root = newroot;
    else *pi->root = newroot;    
    // no item at our pockets
    pi->item = NULL;
}

#define NSIS_VARS_COUNT 27
#define NSIS_VARS_STACK 25
#define NSIS_VARS_NSTACK 26

typedef char smallstr[2];
const smallstr NSISVariablesNames[NSIS_VARS_COUNT] = {{'r','0'}, {'r','1'}, {'r','2'}, {'r','3'}, {'r','4'}, {'r','5'}, {'r','6'}, {'r','7'}, {'r','8'}, {'r','9'}, 
{'R','0'}, {'R','1'}, {'R','2'}, {'R','3'}, {'R','4'}, {'R','5'}, {'R','6'}, {'R','7'}, {'R','8'}, {'R','9'}, 
{'C','L'}, {'I','D'}, {'O','D'}, {'E','D'}, {'L','G'}, {'S',0}, {'N','S'}};

ExpressionItem *FindVariable(char *varname)
{
    ExpressionItem *item = AllocItem();
    // check NSIS variables
    for (int i = 0; i < NSIS_VARS_COUNT; i++)
    {
        if (lstrcmpn(varname, NSISVariablesNames[i],2) == 0)
        {
            if (i == NSIS_VARS_STACK) item->type = IT_VARIABLE | ITV_STACK;
            else if (i == NSIS_VARS_NSTACK) item->type = IT_VARIABLE | ITV_NSTACK;
            else
                item->type = (IT_VARIABLE | ITV_NSIS) + i;
            return item;
        }
    }
    // no.. that's user variable
    for (int i = 0; i < UserVarsCount; i++)
    {
        if (lstrcmp(varname, UserVars[i].name) == 0)
        {
            // ok. we found user var expression needed
            break;
        }
    }
    if (i == UserVarsCount)
    {
        // new variable
        UserVarsCount++;
        lstrcpy(UserVars[i].name, varname);
        UserVars[i].item = NULL;
    }
    item->type = (IT_VARIABLE | ITV_USER) + i;
    return item;
}

void PlaceVariable(char *&vb, ParseInfo *pi)
{
    if (vb <= pi->valbuf) return;
    *vb = 0;
    pi->item = FindVariable(pi->valbuf);
    vb = pi->valbuf;
}

#define MATHFUNCNUM 29
const MathFunction MathFunctions[MATHFUNCNUM] = {
    {{'s','i','n'}, ITF_MATH1 >> 8, _fsin},
    {{'s','n','h'}, ITF_MATH1 >> 8, _fsinh},
    {{'a','s','n'}, ITF_MATH1 >> 8, _fasin},
    {{'c','o','s'}, ITF_MATH1 >> 8, _fcos},
    {{'c','s','h'}, ITF_MATH1 >> 8, _fcosh},
    {{'a','c','s'}, ITF_MATH1 >> 8, _facos},
    {{'t','a','n'}, ITF_MATH1 >> 8, _ftan},
    {{'t','n','h'}, ITF_MATH1 >> 8, _ftanh},
    {{'a','t','n'}, ITF_MATH1 >> 8, _fatan},
    {{'a','b','s'}, ITF_MATH1 >> 8, _fabs},
    {{'l','n',0}, ITF_MATH1 >> 8, _flog},
    {{'l','o','g'}, ITF_MATH1 >> 8, _flog10},
    {{'e','x','p'}, ITF_MATH1 >> 8, _fexp},
    {{'s','q','t'}, ITF_MATH1 >> 8, _fsqrt},
    {{'c','e','l'}, ITF_MATH1 >> 8, _fceil},
    {{'f','l','r'}, ITF_MATH1 >> 8, _floor},

    {{'a','t','2'}, ITF_MATH2 >> 8, (Math1FuncPtr)_fatan2},
    {{'p','o','w'}, ITF_MATH2 >> 8, (Math1FuncPtr)_fpow},
    {{'f','m','d'}, ITF_MATH2 >> 8, (Math1FuncPtr)_fmod},

    // type conversions
    {{'i',0,0}, ITF_TYPE >> 8, (Math1FuncPtr)ITC_INT},
    {{'s',0,0}, ITF_TYPE >> 8, (Math1FuncPtr)ITC_STRING},
    {{'f',0,0}, ITF_TYPE >> 8, (Math1FuncPtr)ITC_FLOAT},
    {{'a',0,0}, ITF_TYPE >> 8, (Math1FuncPtr)ITC_ARRAY},
#define ITFT_CARRAY_ID    23
    {{'c','a',0}, ITF_TYPE >> 8, (Math1FuncPtr)ITC_ARRAY},
    {{'f','f',0}, ITF_TYPE >> 8, (Math1FuncPtr)FTT_FLOATF},
    {{'l',0,0}, ITF_TYPE >> 8, (Math1FuncPtr)FTT_LEN},
    {{'c',0,0}, ITF_TYPE >> 8, (Math1FuncPtr)FTT_CHAR},

    {{'f','e','x'}, ITF_MATH2 >> 8, (Math1FuncPtr)_frexp},
    {{'m','d','f'}, ITF_MATH2 >> 8, (Math1FuncPtr)_fmodf},
};

void PlaceFunction(char *&vb, char *&sp, ParseInfo *pi, int redefine)
{
    ExpressionItem *item = pi->item = AllocItem();
    *vb = 0;
    
    // check BUILTIN functions
    for (int i = 0; i < MATHFUNCNUM; i++)
    {
        if (lstrcmpn(pi->valbuf, MathFunctions[i].name, 3) == 0)
        {
            item->type = IT_FUNCTION | (MathFunctions[i].type << 8) | i;
            // get first argument
            sp++;
            ParseString(sp, *((ExpressionItem **)(&item->param1)));
            if (*sp == ',')
            {
                // get second argument
                sp++;
                ParseString(sp, *((ExpressionItem **)(&item->param2)));
            }
            sp++; vb = pi->valbuf;            
            return;
        }
    }

    // heh, may be it user function
    for (int i = 0; i < UserFuncsCount; i++)
    {
        if (lstrcmp(pi->valbuf, UserFuncs[i].name) == 0)
        {
            // Function found? Redefine option specified?
            if (redefine) break;

            item->type = IT_FUNCTION | ITF_USER | i;
            // get arguments list
            ExpressionItem **newplace = ((ExpressionItem **)(&pi->item->param1));
            while (*sp != ')')
            {
                *newplace = AllocItem();
                (*newplace)->type = IT_EXPRESSION;
                sp++;
                ParseString(sp, *((ExpressionItem **)(&(*newplace)->param1)));
                newplace = &((*newplace)->next);
            }
            sp++; vb = pi->valbuf;            
            return;
        }
    }

    // oops, we need no item for function defenition
    CleanupItems(item); pi->item = NULL;    

    // it's user function define
    int flags = 0;
    char buffer[128], *buf = buffer;

    // workaround for redefine flag - if the function already present,
    // it will be cleared and redefined
    UserFunc *f = &UserFuncs[i];
    if (i == UserFuncsCount) UserFuncsCount++;
    else CleanupItems(f->root);

    lstrcpy(f->name, pi->valbuf);
    f->varflags = 0;
    f->varsnum = 0;    
    do
    {
        sp++;
        switch (*sp)
        {
        case ' ':
            break;
        case ',':
        case ')':            
            if (buf > buffer)
            {
                *buf = 0;
                // it should be user variable
                ExpressionItem *it = FindVariable(buffer);
                f->vars[f->varsnum++] = (it->type) & ITEMOPTIONS;
                CleanupItems(it);
                buf = buffer;
                flags <<= 1;
            }            
            break;
        case '&':
            flags |= 1;
            break;
        default:
            *(buf++) = *sp;
            break;
        }
    }
    while (*sp != ')');

    // prepare flag for fast analisys
    for (int i = 0; i < f->varsnum; i++)
    {
        f->varflags <<= 1;
        flags >>= 1;
        f->varflags |= flags&1;
    }

    // find nearest round bracket - function body
    while (*sp != '(') sp++;
    sp++;    

    // now we are ready to parse function body
    ParseString(sp, f->root);
    sp++; // closing bracket
    vb = pi->valbuf;

#ifdef _DEBUG
  // dump function (in debug mode)
  char place[1024];
  wsprintf(place, "function %s(", f->name);
  flags = f->varflags;
  for (int i = 0; i < f->varsnum; i++)
  {
    if (flags&1) lstrcat(place, "&");
    lstrcat(place, UserVars[f->vars[i]].name);
    if (i < f->varsnum-1) lstrcat(place, ", ");
    flags >>= 1;
  }
  lstrcat(place, ")");                
  PrintTree(f->root, place);
#endif
}

// operator options
#define PO_UNARYPRE    0x1 // this operator can be uniary pre (--a) for ex
#define PO_UNARYPOST   0x2 // this op can be uniary post (a++) (couldn't be binary)
#define PO_PRENONCONST  0x4 // pre argument (a = b) -> a is non const
#define PO_POSTNONCONST 0x8 // post argument (b--) is non const
#define PO_LASTOP      0x10 // op should be the last item at expression (=, -=, etc)
#define PO_SET         0x20 // op will set new value to one of args
#define PO_USESPRE     0x40 // operator will use pre operand
#define PO_USESPOST    0x80 // operator will use post operan

void PlaceOp(char *&vb, int type, int precedence, ParseInfo *pi)
{
    PlaceVariable(vb, pi);
    if ((type & PO_UNARYPRE) && (!pi->item))
    {
        // uniary pre op
        ExpressionItem *item = AllocItem();
        item->type = type;
        item->param2 = (EIPARAM) precedence;
        item->next = pi->OpsStack;        
        pi->OpsStack = item;
    }
    else 
    {
        // get previous tree as items and operators of lower precedence
        PlaceNewItem(vb, pi, precedence);
        // post operators
        ExpressionItem *item = AllocItem();
        item->type = type;
        item->param1 = (EIPARAM) (*pi->root);

        if (pi->place == *pi->root) pi->place = *pi->root = NULL;
        else *pi->root = NULL;    

        if (type & PO_UNARYPOST)
        {
            // uniary post op
            pi->item = item;
        } else
        {
            // binary operator
            item->param2 = (EIPARAM) precedence;
            item->next = pi->OpsStack;
            pi->OpsStack = item;
        }
    }
}

#define OPSNUM  35
const OpStruct Operators[OPSNUM] =
{
// three byte ops
{{'>','>','='}, 14, ITO_SHR | PO_PRENONCONST | PO_LASTOP | PO_SET | PO_USESPRE | PO_USESPOST},
{{'<','<','='}, 14, ITO_SHL | PO_PRENONCONST | PO_LASTOP | PO_SET | PO_USESPRE | PO_USESPOST},

// two byte ops
// !!! don't forget to change Set Operator Precedence !!!
{"-=", 14, ITO_MINUS | PO_PRENONCONST | PO_LASTOP | PO_SET | PO_USESPRE | PO_USESPOST},
{"+=", 14, ITO_PLUS | PO_PRENONCONST | PO_LASTOP | PO_SET | PO_USESPRE | PO_USESPOST},
{"/=", 14, ITO_DIV | PO_PRENONCONST | PO_LASTOP | PO_SET | PO_USESPRE | PO_USESPOST},
{"*=", 14, ITO_MUL | PO_PRENONCONST | PO_LASTOP | PO_SET | PO_USESPRE | PO_USESPOST},
{"|=", 14, ITO_OR | PO_PRENONCONST | PO_LASTOP | PO_SET | PO_USESPRE | PO_USESPOST},
{"&=", 14, ITO_AND | PO_PRENONCONST | PO_LASTOP | PO_SET | PO_USESPRE | PO_USESPOST},
{"^=", 14, ITO_XOR | PO_PRENONCONST | PO_LASTOP | PO_SET | PO_USESPRE | PO_USESPOST},
{"%=", 14, ITO_MOD | PO_PRENONCONST | PO_LASTOP | PO_SET | PO_USESPRE | PO_USESPOST},
{"--", 2, ITO_DEC | PO_POSTNONCONST | PO_PRENONCONST | PO_UNARYPRE | PO_UNARYPOST | PO_SET | PO_USESPRE | PO_USESPOST},
{"++", 2, ITO_INC | PO_POSTNONCONST | PO_PRENONCONST | PO_UNARYPRE | PO_UNARYPOST | PO_SET | PO_USESPRE | PO_USESPOST},
{">>", 6, ITO_SHR | PO_USESPRE | PO_USESPOST},
{"<<", 6, ITO_SHL | PO_USESPRE | PO_USESPOST},

// logical
{"&&", 12, ITO_LAND | PO_USESPRE | PO_USESPOST},
{"||", 13, ITO_LOR | PO_USESPRE | PO_USESPOST},

// comparisons
{"<=", 7, ITO_LE | PO_USESPRE | PO_USESPOST},
{"=<", 7, ITO_LE | PO_USESPRE | PO_USESPOST},
{">=", 7, ITO_GE | PO_USESPRE | PO_USESPOST},
{"=>", 7, ITO_GE | PO_USESPRE | PO_USESPOST},
{"!=", 8, ITO_NE | PO_USESPRE | PO_USESPOST},
{"==", 8, ITO_EQ | PO_USESPRE | PO_USESPOST},

// single byte ops
// !!! don't forget to change Set Operator Precedence !!!
{"=", 14, ITO_SET | PO_PRENONCONST | PO_LASTOP | PO_SET | PO_USESPOST}, 
{"+", 5, ITO_PLUS | PO_USESPRE | PO_USESPOST},
{"-", 5, ITO_MINUS | PO_USESPRE | PO_USESPOST | PO_UNARYPRE},
{"*", 4, ITO_MUL | PO_USESPRE | PO_USESPOST | PO_UNARYPRE},
{"/", 4, ITO_DIV | PO_USESPRE | PO_USESPOST},
{"%", 4, ITO_MOD | PO_USESPRE | PO_USESPOST},
{"<", 7, ITO_LS | PO_USESPRE | PO_USESPOST},
{">", 7, ITO_GR | PO_USESPRE | PO_USESPOST},
{"&", 9, ITO_AND | PO_USESPRE | PO_USESPOST | PO_UNARYPRE},
{"|", 11, ITO_OR | PO_USESPRE | PO_USESPOST},
{"^", 10, ITO_XOR | PO_USESPRE | PO_USESPOST},
{"~", 3, ITO_NOT | PO_USESPOST | PO_UNARYPRE},
{"!", 3, ITO_LNOT |PO_USESPOST | PO_UNARYPRE}
};

void CheckForOperator(char *&sp, char *&vb, ParseInfo *pi)
{
    for (int op = 0; op < OPSNUM; op++)
    {        
        int c = lstrlen(Operators[op].name);
        if (c > 3) c = 3; // real operator length
        if (lstrcmpn(sp, Operators[op].name, c))
        {
            // wrong - different op
            continue;
        }
        // that is our op
        sp += c;
        PlaceOp(vb, ((int) Operators[op].type) | IT_OPERATOR, Operators[op].precedence, pi);
        break;
    }
}

void ParseString(char *&sp, ExpressionItem* &itemplace)
{
    ParseInfo pi = {0, NULL, NULL, itemplace, &itemplace};

    int redefine = 0;
    char *vb = pi.valbuf;
    // cycle until current expression end
    while ((*sp != 0) && (*sp != ')') && (*sp != '}') &&
        (*sp != ']') && (*sp != ','))
    {
        int processed = 1;
        switch (*sp)
        {
        case ' ':
        case '\t':
            sp++;
            break;
        case ';':
            // expression delimeter
            PlaceNewItem(vb, &pi, 255);
            if (*pi.root) pi.SetupNewRoot = 1;
            sp++;
            break;
        case '0': case '1': case '2': case '3': case '4': 
        case '5': case '6': case '7': case '8': case '9':
            // variable & function names could contain numbers as non first chars
            if (vb > pi.valbuf) 
            {
                processed = FALSE;
                break;
            }
        case '\'': case '\"': case '`':
            // constant meet
            pi.item = AllocItem();
            StringToItem(sp, pi.item, STI_STRING | STI_FLOAT | STI_INT);
            break;

        case '(': // start of function or expression
            if (vb > pi.valbuf)
            {
                // thats function
                PlaceFunction(vb, sp, &pi, redefine);                
            } else
            {
                // expression
                sp++;
                ParseString(sp, pi.item);
                if (*sp == ')') sp++;
            }
            redefine = 0;
            break;

        case '#':   // start of one of logical expresions
            sp++;            
            if ((*sp != '[') && (*sp != '{'))
            {
                // function redefine flag
                redefine = 1;
                break;
            }
            {
                pi.item = AllocItem();
                // IF or WHILE
                pi.item->type = ((*sp == '[')?(IT_LOGIC | ITL_IF):(IT_LOGIC | ITL_WHILE));
                // first expr - logic statement
                sp++;
            
                ParseString(sp, *((ExpressionItem **)(&pi.item->param1)));
                // ok, second expr - then, third - else statement.. others???
                ExpressionItem **newplace = ((ExpressionItem **)(&pi.item->param2));
                while (*sp == ',')
                {
                    *newplace = AllocItem();
                    (*newplace)->type = IT_EXPRESSION;
                    sp++;
                    ParseString(sp, *((ExpressionItem **)(&(*newplace)->param1)));
                    newplace = &((*newplace)->next);
                }
            }
            sp++;
            break;

        case '[':
            {
            // thats array access 
            PlaceOp(vb, IT_ARRAY | ITA_ACCESS | PO_UNARYPOST, 1, &pi);
            sp++;
            // item index
            ParseString(sp, *(ExpressionItem **)&(pi.item->param2));
            if (*sp == ',')
            {
                // two indexes - string access
                ExpressionItem *it = AllocItem();
                it->type = IT_EXPRESSION;
                it->param1 = (EIPARAM)  *(ExpressionItem **)&(pi.item->param2);
                *(ExpressionItem **)&(pi.item->param2) = it;
                it = it->next = AllocItem();
                it->type = IT_EXPRESSION;
                sp++;
                ParseString(sp, *((ExpressionItem **)(&it->param1)));
            }
            sp++;
            }
            break;
 
        case '{':   // start of array define
            {            
                // array define - constists of array copy operator and array define itself

                // type conversion item (to create a copy of array)
                pi.item = AllocItem();
                pi.item->type = IT_FUNCTION | ITF_TYPE | ITFT_CARRAY_ID | ITFA_COPY;

                // during first create our array descriptor and array pointers 
                ExpressionItem *ai = AllocArray(DEFAULT_ARRAY_SIZE);
                pi.item->param1 = (EIPARAM)  ai;
                ArrayDesc *ad = *((ArrayDesc**)&(ai->param1));

                // parse array initializers
                while (*sp != '}')
                {
                    sp++;
                    ParseString(sp, ad->array[ad->count++]); 
                }

                sp++;
            }
            break;
        case '-': case '+': case '<': case '=': case '>':
        case '/': case '*': case '~': case '^': case '!':
        case '&': case '|': case '%': 
            CheckForOperator(sp, vb, &pi);
            break;

        // non expression? ok, then it should be common char, like function or var name
        default:
            processed = FALSE;
            break;
        }
        if (!processed) *(vb++) = *(sp++);
    }
    PlaceNewItem(vb, &pi, 255);
}

void CleanupArray(ArrayDesc *ad)
{
    if (!(--(ad->references)))
    {
        // last array reference, we could kill it
        // cleanup array items
        for (int i = 0; i < ad->count; i++)
        {
            ExpressionItem *aritem = ad->array[i];
            if (aritem)
                CleanupItems(aritem);
        }
        // cleanup ptrs and descriptor
        dbgGlobalFree(ad->array);
        dbgGlobalFree(ad);
    }
 }

void CleanupItems(ExpressionItem* &itemplace)
{
    if (itemplace == NULL) return;
    ExpressionItem *item = itemplace, *itemnext;
    do
    {
        if (((item->type & (ITEMTYPE|ITEMSUBTYPE)) == (IT_VARIABLE|ITV_ARRITEM))
            ||
            ((item->type & (ITEMTYPE|ITEMSUBTYPE)) == (IT_CONST|ITC_ARRAY)))
        {
            CleanupArray((ArrayDesc *)item->param1);
        }
        else
        if ((item->type & ITEMTYPE) == IT_CONST)
        {
            if ((item->type & ITEMSUBTYPE) == ITC_STRING)
                dbgGlobalFree((HGLOBAL) item->param1);
        } else 
        {
            CleanupItems(*((ExpressionItem**) &item->param1));
            CleanupItems(*((ExpressionItem**) &item->param2));
        }
        // free the item itself
        itemnext = item->next;
        dbgGlobalFree((HGLOBAL) item);
        item = itemnext;
    } while (item != NULL);    
    itemplace = NULL;
}

#ifdef _DEBUG
HANDLE myfile;

char *opsnames[] = {"", "-", "+", "<<", ">>", "*", "/", "=", "&&", "||", "++", "--",
"=<", ">=", "!=", "==", "<", ">", "&", "%", "|", "^", "~", "!"};

void PrintNode(int index, int spaces, ExpressionItem* itemplace)
{
    if (itemplace == NULL) return;
    
    ExpressionItem *item = itemplace;
    do
    {
        DWORD wrote;
        char buffer[1024], *place = buffer;
        for (int k = 0; k < spaces; k++)
            *(place++) = 32;
        *place = 0;

        switch (item->type & ITEMTYPE)
        {
        case  IT_EXPRESSION:
            wsprintf(place, "Expression Place-Holder   ");
            break;
        case  IT_CONST:
            switch (item->type & ITEMSUBTYPE)
            {
            case ITC_STRING:                
                wsprintf(place, "String: \"%s\"", (char *) item->param1);
                break;
            case ITC_INT:
                wsprintf(place, "Int: ");
                itoa64(*((__int64*)&(item->param1)), place+5);
                break;
            case ITC_FLOAT:                
                wsprintf(place, "Float: ");
                FloatFormat(place+7, *((double*)&(item->param1)), 6);
                break;
            case ITC_ARRAY:
                ArrayDesc *ad = (ArrayDesc*) item->param1;
                wsprintf(place, "Array, ptr %08X, size %d, count %d, references %d", 
                    ad->array, ad->size, ad->count, ad->references);                
                break;
            }
            strcat(place, "    ");
            break;
        case IT_OPERATOR:
            wsprintf(place, "Op: %s%s    ", opsnames[(item->type & ITEMSUBTYPE) >> 8], (item->type & PO_SET)?("(=)"):(""));
            break;
        case IT_VARIABLE:
            switch (item->type & ITEMSUBTYPE)
            {
            case ITV_NSIS:
                {
                char buffer[128];
                buffer[0] = NSISVariablesNames[item->type & ITEMOPTIONS][0];
                buffer[1] = NSISVariablesNames[item->type & ITEMOPTIONS][1];
                buffer[2] = 0;
                wsprintf(place, "Var: %s (%d)   ", 
                    buffer,
                    item->type & ITEMOPTIONS);
                }
                break;
            case ITV_USER:
                wsprintf(place, "Var: %s (%d)   ", UserVars[item->type & ITEMOPTIONS].name, item->type & ITEMOPTIONS);
                break;
            case ITV_STACK:
                wsprintf(place, "Plugin Stack   ");
                break;
            case ITV_NSTACK:
                wsprintf(place, "NSIS Stack   ");
                break;
            }
            break;
        case IT_LOGIC:
            if ((item->type & ITEMSUBTYPE) == ITL_IF)
                wsprintf(place, "IF ");
            else
                wsprintf(place, "WHILE ");
            break;
        case IT_FUNCTION:
            if (((item->type & ITEMSUBTYPE) == ITF_MATH1) ||
                ((item->type & ITEMSUBTYPE) == ITF_MATH2) ||
                ((item->type & ITEMSUBTYPE) == ITF_TYPE))
            {
                char buffer[128];
                buffer[0] = (MathFunctions[item->type &ITEMOPTIONS].name)[0];
                buffer[1] = (MathFunctions[item->type &ITEMOPTIONS].name)[1];
                buffer[2] = (MathFunctions[item->type &ITEMOPTIONS].name)[2];
                buffer[3] = 0;
                wsprintf(place, "Built-In Function %s() [%d] ", 
                    buffer,
                    item->type &ITEMOPTIONS);
            }
            else
            {
                UserFunc *f = &(UserFuncs[item->type & ITEMOPTIONS]);
                wsprintf(place, "User Function: %s(", f->name);
                int flags = f->varflags;
                for (int i = 0; i < f->varsnum; i++)
                {
                    if (flags&1) lstrcat(place, "&");
                    lstrcat(place, UserVars[f->vars[i]].name);
                    if (i < f->varsnum-1) lstrcat(place, ", ");
                    flags >>= 1;
                }
                lstrcat(place, ")   ");
            }
            break;
        case IT_ARRAY:
            wsprintf(place, "Array access []   ");
            break;
        } 
        place += lstrlen(place);
        wsprintf(place, "Addr: %08X Type: %08X   Next: %08X  Param1: %08X  Param2: %08X", item, item->type, item->next, item->param1, item->param2);
        lstrcat(place, "\n");
        WriteFile(myfile, buffer, lstrlen(buffer), &wrote, NULL);
        if (((item->type & ITEMTYPE) != IT_CONST) && ((item->type & (ITEMTYPE|ITEMSUBTYPE)) != (IT_VARIABLE|ITV_ARRITEM)))
        {
            place = buffer;
            for (int k = 0; k < spaces+2; k++)
                *(place++) = 32;
            int show = 0;
            if (((item->param1 != NULL) && ((*((ExpressionItem**) &item->param1))->next != NULL)) ||
                ((item->param2 != NULL) && ((*((ExpressionItem**) &item->param2))->next != NULL)))
                show = 1;
            if (show)
            {
                wsprintf(place, "Sub1:\n"); 
                WriteFile(myfile, buffer, lstrlen(buffer), &wrote, NULL);
            }
            PrintNode(1, spaces + 4, *((ExpressionItem**) &item->param1));
            if (show)
            {
                wsprintf(place, "Sub2:\n"); 
                WriteFile(myfile, buffer, lstrlen(buffer), &wrote, NULL);
            }
            PrintNode(2, spaces + 4, *((ExpressionItem**) &item->param2));
        } else if ((item->type & (ITEMSUBTYPE|ITEMTYPE)) == (ITC_ARRAY|IT_CONST))
        {
            ArrayDesc *ad = (ArrayDesc *) item->param1;
            for (int i = 0; i < ad->count; i++)
            {
                ExpressionItem *aritem = ad->array[i];
                if (aritem)
                    PrintNode(2, spaces + 4, aritem);
            }
        }
        item = item->next;
    } while (item != NULL);    
}

void PrintTree(ExpressionItem *root, char *str)
{
    myfile = CreateFile("d:\\math.debug", GENERIC_ALL, FILE_SHARE_READ, NULL, OPEN_ALWAYS, 0, 0);
    SetFilePointer(myfile, 0, NULL, FILE_END);
    char buffer[1024]; DWORD wrote;
    wsprintf(buffer, "New tree for \'%s\'\n", str);
    WriteFile(myfile, buffer, lstrlen(buffer), &wrote, NULL);

    PrintNode(0, 4, root);
    CloseHandle(myfile);
    myfile = NULL;
}
#endif

void CopyArray(ExpressionItem *&item)
{
    if (item == NULL) return;
    // especial case - array to array conversion is requested array copy
    ExpressionItem *olditem = item;
    ArrayDesc *oad = (ArrayDesc *) (olditem->param1);
    // Initialize the array of the same size
    item = AllocArray(oad->size);
    ArrayDesc *nad = (ArrayDesc *) (item->param1);
    nad->count = oad->count;
    // copy items
    for (int i = 0; i < oad->count; i++)
       nad->array[i] = CopyItem(oad->array[i], TRUE);
    // cleanup old array pointer (may be array itself)
    CleanupItems(olditem);
}

void ItemToType(ExpressionItem* &item, int type)
{
    char *buffer;
    if (item == NULL) return;
    int itemt = item->type & ITEMTYPE, oldtype = item->type & ITEMSUBTYPE;

    if (((itemt == IT_CONST) && (oldtype == type)) 
        || (itemt != IT_CONST)) return;

    switch (type)
    {
    case ITC_STRING:
        buffer = AllocString();
        ItemToString(buffer, item);
        item->param1 = (EIPARAM)  buffer;
        item->param2 = 0;
        break;
    case ITC_FLOAT:
        if (oldtype == ITC_INT) 
            *((double *)&(item->param1)) = (double) *((__int64 *)&(item->param1));
        else
        {
            buffer = (char*) item->param1;
            StringToItem(buffer, item, STI_FLOAT);
            dbgGlobalFree(buffer);
        }
        break;
    case ITC_INT:
        if (oldtype == ITC_FLOAT) 
            *((__int64 *)&(item->param1)) = (__int64) *((double *)&(item->param1));
        else
        {
            buffer = (char*) item->param1;
            StringToItem(buffer, item, STI_INT);
            dbgGlobalFree(buffer);
        }
        break;
    case ITC_ARRAY:
        if (oldtype == ITC_STRING)
        {
            char *buf = (char*) item->param1;
            int len = lstrlen(buf)+1;            
            ExpressionItem *ni = AllocArray(lstrlen(buf)+1);
            ArrayDesc *ad = (ArrayDesc*) ni->param1;
            for (int i = 0; i < len; i++)
            {
                ad->array[i] = AllocItem();
                *((__int64 *) &(ad->array[i]->param1)) = (__int64) buf[i];
            }
            ad->count = len;
            CleanupItems(item);
            item = ni;
        }
        break;
    }
    item->type = IT_CONST | type;
}

void SaveResult(ExpressionItem *var, ExpressionItem *result)
{
    if ((var->type & ITEMTYPE) != IT_VARIABLE) return;

    // result should be stored at variable to
    int varindex = var->type & ITEMOPTIONS;
    switch (var->type & ITEMSUBTYPE)
    {
    case ITV_NSIS:
        {
            // store string result direct to NSIS variable
            char *ptr = g_variables + varindex*g_stringsize;
            ItemToString(ptr, result);
        }
        break;
    case ITV_USER:
        {
            CleanupItems(UserVars[varindex].item);
            UserVars[varindex].item = CopyItem(result);
        }
        break;
    case ITV_ARRITEM:
        {
            ExpressionItem *&ei = ((ArrayDesc*)(var->param1))->array[(int)var->param2];
            CleanupItems(ei);
            ei = CopyItem(result);
        }
        break;
    case ITV_STACK:
        {
            ExpressionItem *newitem = CopyItem(result);
            newitem->next = stack;
            stack = newitem;
        }
        break;
    case ITV_NSTACK:
        {
            char *buf = AllocString();
            ItemToString(buf, result);
            pushstring(buf);
            dbgGlobalFree(buf);
        }
        break;
    }
}

void RunAndGetConst(int from, ExpressionItem* &result, int type)
{
    RunTree(*((ExpressionItem**)&(from)), result, type | RTO_NEEDCONST);
    ItemToType(result, type);
}

void RunTree(ExpressionItem *from, ExpressionItem* &result, int options)
{
    ExpressionItem *item = from;
    result = NULL;
    while (item != NULL)
    {
        CleanupItems(result);
        int type = item->type & ITEMTYPE, 
            subtype = item->type & ITEMSUBTYPE,
            ioptions = item->type & ITEMOPTIONS;
        switch (type)
        {
        case IT_EXPRESSION:
            RunTree(*((ExpressionItem**)&(item->param1)), result, options);
            break;
        case IT_CONST:
            result = CopyItem(item);
            break;
        case IT_VARIABLE:
            if (options & RTO_NEEDCONST)
            {
                // we need const result - :) is it nsis or common variable
                switch (subtype)
                {
                case ITV_NSIS:
                    {
                        // nsis                  
                        result = AllocItem();
                        char *variable = getuservariable(ioptions);
                        StringToItem(variable, result, options); 
                    }
                    break;
                case ITV_USER:
                    {
                        // usual variable
                        if (UserVars[ioptions].item)
                            result = CopyItem(UserVars[ioptions].item);
                        else
                            result = AllocItem();
                    }
                    break;
                case ITV_ARRITEM:
                    {
                        // array item
                        ExpressionItem *ei = ((ArrayDesc*)(item->param1))->array[(int)item->param2];
                        if (ei)
                            result = CopyItem(ei);
                        else
                            result = AllocItem();
                    }
                    break;
                case ITV_STACK:
                    {  
                        // pop from plugin stack
                        result = stack;
                        if (result == NULL) result = AllocItem();
                        stack = result->next;
                        result->next = NULL;
                    }
                    break;
                case ITV_NSTACK:
                    {
                        // NSIS stack
                        char buffer[1024], *buf = buffer;
                        result = AllocItem();
                        popstring(buffer);
                        StringToItem(buf, result, options);
                    }
                    break;
                }
            }
            else
                // if we don't need const - we will just pass variable record
                result = CopyItem(item);
            break;
        case IT_OPERATOR:
            {
                ExpressionItem *var = NULL, *item1 = NULL, *item2 = NULL;
                // prepare arguments in case of SET operator
                if (ioptions & PO_SET)
                {
                    if ((item->param1) && (ioptions & PO_PRENONCONST))
                    {
                        RunTree(*((ExpressionItem**)&(item->param1)), var, 0);
                        if (ioptions & PO_USESPRE)
                            RunTree(var, item1, RTO_NEEDCONST | STI_INT | STI_FLOAT | STI_STRING);
                    } else
                    if ((item->param2) && (ioptions & PO_POSTNONCONST))
                    {
                        RunTree(*((ExpressionItem**)&(item->param2)), var, 0);
                        if (ioptions & PO_USESPOST)
                            RunTree(var, item2, RTO_NEEDCONST | STI_INT | STI_FLOAT | STI_STRING);
                    }
                }

                // prepare arguments in case of any operator
                int needmore = 1;
                if ((!item1) && (item->param1) && (ioptions & PO_USESPRE))
                {
                    RunTree(*((ExpressionItem**)&(item->param1)), item1, RTO_NEEDCONST | STI_INT | STI_FLOAT | STI_STRING);
                    // logical expressions && and || can make decision on first arg basis
                    if ((subtype == ITO_LAND) || (subtype == ITO_LOR) )
                    {
                        ItemToType(item1, ITC_INT);
                        int res = (int) *((__int64*) &(item1->param1));
                        if (((res)&&(subtype==ITO_LOR)) || ((!res)&&(subtype==ITO_LAND)))
                            needmore = 0;
                    }
                }

                // get-reference operator
                if ((!item1) && (subtype == ITO_AND) && (!item2) && (item->param2))
                {
                    RunTree(*((ExpressionItem**)&(item->param2)), result, 0);
                    break;
                }

                if ((needmore) && (!item2) && (item->param2) && (ioptions & PO_USESPOST))
                    RunTree(*((ExpressionItem**)&(item->param2)), item2, RTO_NEEDCONST | STI_INT | STI_FLOAT | STI_STRING);

                // reference operator
                if ((!item1) && (subtype == ITO_MUL) && ((item2->type & ITEMTYPE) == (IT_VARIABLE)))
                {
                    // ok, that's the result we need                    
                    if (options & RTO_NEEDCONST)
                    {
                        RunTree(item2, result, options);
                        CleanupItems(item2);
                    } else
                        result = item2;                        
                    break;
                }                

                __int64 i1, i2, i3, i4;
                if (((!item1)||((item1->type & ITEMTYPE)==IT_CONST)) &&
                    ((!item2)||((item2->type & ITEMTYPE)==IT_CONST)))
                {

                // find the best type match for operation
                int it1 = (item1 && (ioptions & PO_USESPRE))?(item1->type & ITEMSUBTYPE):(ITC_UNKNOWN),
                    it2 = (item2 && (ioptions & PO_USESPOST))?(item2->type & ITEMSUBTYPE):(ITC_UNKNOWN),
                    type = (it1 < it2)?(it1):(it2);

                // convert operands to desired type
                ItemToType(item1, type);
                ItemToType(item2, type);
                
                switch (type)
                {
                case ITC_INT:
                    {
                        i1 = (item1)?(*((__int64*)&item1->param1)):(0);
                        i2 = (item2)?(*((__int64*)&item2->param1)):(0);
              
                    switch (subtype)
                    {
                    case ITO_MINUS: i1 -= i2; break;    // unary minus auto handled with NULL
                    case ITO_PLUS:  i1 += i2; break;
                    case ITO_SHL:   i1 <<= i2; break;
                    case ITO_SHR:   i1 >>= i2; break;
                    case ITO_MUL:   i1 *= i2; break;
                    case ITO_MOD:
                    case ITO_DIV:   
                                    if (i2 == 0) { i3 = 0; i4 = i1;  }
                                    else { i3 = i1 / i2; i4 = i1 % i2; }
                                    if (subtype == ITO_DIV) i1 = i3; else i1 = i4;
                                    break;
                    case ITO_SET:   i1 = i2; break;
                    case ITO_LE:    i1 = (i1 <= i2); break;
                    case ITO_GE:    i1 = (i1 >= i2); break;
                    case ITO_NE:    i1 = (i1 != i2); break;
                    case ITO_EQ:    i1 = (i1 == i2); break;
                    case ITO_LS:    i1 = (i1 < i2); break;
                    case ITO_GR:    i1 = (i1 > i2); break;
                    case ITO_AND:   i1 = (i1 & i2); break;
                    case ITO_OR:    i1 = (i1 | i2); break;
                    case ITO_XOR:   i1 = (i1 ^ i2); break;
                    case ITO_NOT:   i1 = ~i2; break;
                    case ITO_LNOT:  i1 = !i2; break;
                    case ITO_LAND:  i1 = i1 && i2; break;
                    case ITO_LOR:   i1 = i1 || i2; break;
                    case ITO_INC:
                            if (item1) i2 = i1++;
                            else i1 = ++i2;
                            break;
                    case ITO_DEC:
                            if (item1) i2 = i1--;
                            else i1 = --i2;
                            break;
                    }
                    result = AllocItem();
                    *((__int64*)&result->param1) = i1;
                    }
                    break;
                case ITC_FLOAT:
                    {
                        int ir = -666;
                        double i1 = (item1)?(*((double*)&item1->param1)):(0.0);
                        double i2 = (item2)?(*((double*)&item2->param1)):(0.0);
                    
                    switch (subtype)
                    {
                    case ITO_MINUS: i1 -= i2; break;    // unary minus auto handled with NULL
                    case ITO_PLUS:  i1 += i2; break;
                    case ITO_MUL:   i1 *= i2; break;
                    case ITO_DIV:   i1 /= i2; break;
                    case ITO_SET:   i1 = i2; break;
                    case ITO_LE:    ir = (i1 <= i2); break;
                    case ITO_GE:    ir = (i1 >= i2); break;
                    case ITO_NE:    ir = (i1 != i2); break;
                    case ITO_EQ:    ir = (i1 == i2); break;
                    case ITO_LS:    ir = (i1 < i2); break;
                    case ITO_GR:    ir = (i1 > i2); break;
                    }
                    result = AllocItem();
                    if (ir == -666)
                    {
                        // if ir value left intact - result is double
                        result->type = IT_CONST | ITC_FLOAT;
                        *((double*)&result->param1) = i1;            
                    } else
                        *((__int64*)&result->param1) = (__int64) ir;            
                    }
                    break;
                case ITC_STRING:
                    {
                        int ir = -666;
                        char *i1 = (item1)?((char*)item1->param1):(NULL);
                        char *i2 = (item2)?((char*)item2->param1):(NULL);
                        int sc = lstrcmp(i1, i2);
                    
                    switch (subtype)
                    {
                    case ITO_PLUS:  lstrcat(i1, i2); break;
                    case ITO_SET:   i1 = i2; break;
                    case ITO_LE:    ir = (sc <= 0); break;
                    case ITO_GE:    ir = (sc >= 0); break;
                    case ITO_NE:    ir = (sc != 0); break;
                    case ITO_EQ:    ir = (sc == 0); break;
                    case ITO_LS:    ir = (sc < 0); break;
                    case ITO_GR:    ir = (sc > 0); break;
                    }                    
                    if (ir == -666)
                    {
                        result = CopyItem((item1)?(item1):(item2));
                    } else
                    {
                        result = AllocItem();
                        *((__int64*)&result->param1) = (__int64) ir;            
                    }
                    }
                    break;
                case ITC_ARRAY:
                    result = CopyItem(item2);
                    break;
                }

                } // check for both items constant
                // the other case - usually UniaryPre operators working with non constants
                else result = CopyItem(item2);

                if (ioptions & PO_SET)
                {
                    // Save our result in output variable
                    SaveResult(var, result);
                }

                // Actual value to be returned as result is at i2 for ++ and -- ops
                if ((subtype == ITO_DEC) || (subtype == ITO_INC))
                    *((__int64*)&result->param1) = i2;

                CleanupItems(item1); CleanupItems(item2); CleanupItems(var);
            }
            break;
        case IT_LOGIC:
            {
                int ifmode = (subtype == ITL_IF);
                ExpressionItem *ifbr = *((ExpressionItem**)&(item->param1)),
                    *dobr = *((ExpressionItem**)&(item->param2)),
                    *thbr = NULL, *elbr = NULL;
                // check do branche for existance
                if (dobr && ifmode)
                {
                    // then...
                    thbr = *((ExpressionItem**)&(dobr->param1));
                    // ... and else branches
                    if (dobr->next) elbr = *((ExpressionItem**)&(dobr->next->param1));
                }
                while (true)
                {
                    RunAndGetConst((int) ifbr, result, ITC_INT);
                    if (ifmode)
                    {
                        // we need then or else branch?
                        if ((*((__int64*)&result->param1))) dobr = thbr;
                        else dobr = elbr;
                    } else
                    {
                        // while mode
                        if (!(*((__int64*)&result->param1))) break;
                    }
                    // ok, run the approtiate branch of if statement (if available)
                    if (dobr)
                    {                    
                        CleanupItems(result);
                        RunTree(dobr, result, options);
                    }
                    if (ifmode) break;
                    CleanupItems(result);
                }
            }
            break;
        case IT_FUNCTION:
            if (subtype == ITF_USER)
            {
                UserFunc *f = &(UserFuncs[ioptions]);
                int flags = f->varflags;
                ExpressionItem *ip = *((ExpressionItem**)&(item->param1));
                ExpressionItem *si = AllocItem(), *var = AllocItem();
                ExpressionItem *vals[32];
                si->type = IT_VARIABLE | ITV_STACK; 
                for (int i = 0; i < f->varsnum; i++)
                {
                    // push every variable
                    ExpressionItem *val;
                    var->type = (IT_VARIABLE | ITV_USER) + f->vars[i];
                    RunTree(var, val, RTO_NEEDCONST | ITC_STRING | ITC_INT | ITC_FLOAT | ITC_ARRAY);                    
                    SaveResult(si, val);
                    CleanupItems(val);
                    // calculate argument value and for future
                    if (ip)
                    {
                        if (flags&1)
                        {
                            // var ptr required
                            RunTree(*((ExpressionItem**)&(ip->param1)), vals[i], 0);
                        } else
                        {
                            RunTree(*((ExpressionItem**)&(ip->param1)), vals[i], RTO_NEEDCONST | ITC_STRING | ITC_INT | ITC_FLOAT | ITC_ARRAY);
                        }
                        ip = ip->next;
                    } else vals[i] = AllocItem();

                    flags >>= 1; 
                }

                // now when all values got we could save them to variables
                for (int i = 0; i < f->varsnum; i++)
                {
                        var->type = (IT_VARIABLE | ITV_USER) + f->vars[i];
                        SaveResult(var, vals[i]);
                        CleanupItems(vals[i]);
                }


                // ok, call the func
                RunTree(f->root, result, RTO_NEEDCONST | ITC_STRING | ITC_INT | ITC_FLOAT | ITC_ARRAY);

                // pop original params
                for (int i = f->varsnum-1; i >= 0; i--)
                {
                    // pop every variable
                    ExpressionItem *val;
                    var->type = (IT_VARIABLE | ITV_USER) + f->vars[i];
                    RunTree(si, val, RTO_NEEDCONST | ITC_STRING | ITC_INT | ITC_FLOAT | ITC_ARRAY);                    
                    SaveResult(var, val);
                    CleanupItems(val);
                }

                // free used items
                CleanupItems(si); CleanupItems(var); 
            } else if (subtype == ITF_TYPE)
            {
                int newtype = (int) MathFunctions[ioptions].fptr;
                if (newtype < ITC_UNKNOWN)
                {
                    // get as possibly close to ready expression                                        
                    RunAndGetConst((int)item->param1, result, newtype);
                    if (ioptions == ITFT_CARRAY_ID) 
                        CopyArray(result);
                } else if (newtype == FTT_FLOATF)
                {
                    // float format function
                    ExpressionItem *arg1, *arg2;
                    RunAndGetConst((int)item->param1, arg1, ITC_FLOAT);
                    double value = *((double*)&(arg1->param1));                
                    RunAndGetConst((int)item->param2, arg2, ITC_INT);
                    int format = (int) *((__int64*)&(arg2->param1));
                    
                    result = AllocItem();
                    result->type = IT_CONST | ITC_STRING;
                    result->param1 = (EIPARAM)  AllocString();
                    FloatFormat((char*) result->param1, value, format);
                    CleanupItems(arg1); CleanupItems(arg2);
                } else if (newtype == FTT_LEN)
                {
                    // length function
                    RunTree(*((ExpressionItem **) &(item->param1)), result, RTO_NEEDCONST | ITC_STRING | ITC_ARRAY);
                    if ((result->type & (ITEMTYPE|ITEMSUBTYPE)) == (IT_CONST|ITC_ARRAY))
                    {
                        int len = ((ArrayDesc*)(result->param1))->count;
                        CleanupItems(result);
                        result = AllocItem();
                        *((__int64*)&(result->param1)) = (__int64) len;
                        break;
                    } else
                        if ((result->type & (ITEMTYPE|ITEMSUBTYPE)) != (IT_CONST|ITC_STRING))
                            ItemToType(result, ITC_STRING);

                    if ((result->type & (ITEMTYPE|ITEMSUBTYPE)) == (IT_CONST|ITC_STRING))
                    {
                        // ok, that's string
                        int len = lstrlen((char*)result->param1);
                        dbgGlobalFree((HGLOBAL) result->param1);
                        *((__int64*)&(result->param1)) = (__int64) len;
                        result->type = IT_CONST | ITC_INT;
                    } else CleanupItems(result);
                } else
                {
                    // only one left - c() - char/int/char conversion
                    RunTree(*((ExpressionItem **) &(item->param1)), result, RTO_NEEDCONST | ITC_STRING | ITC_INT);
                    if ((result->type & (ITEMTYPE|ITEMSUBTYPE)) == (IT_CONST|ITC_STRING))
                    {
                        // ok, that's string - convert first char to int
                        int chr = (*((char*)result->param1)) & 0xFF;
                        dbgGlobalFree((HGLOBAL) result->param1);
                        *((__int64*)&(result->param1)) = (__int64) chr;
                        result->type = IT_CONST | ITC_INT;
                        break;
                    } 
                    if ((result->type & (ITEMTYPE|ITEMSUBTYPE)) == (IT_CONST|ITC_INT))
                    {
                        // ok, that's int - convert to new string (char+0)
                        int chr = (int) (*((__int64*)&(result->param1))) & 0xFF;
                        result->param1 = (EIPARAM)  AllocString();
                        *((char*)result->param1) = (char) chr;
                        *((char*)(result->param1+1)) = (char) 0;
                        result->type = IT_CONST | ITC_STRING;
                        break;
                    } else CleanupItems(result);
                }
            } else
            {
                // oops :-o function call :)
                RunAndGetConst((int)item->param1, result, ITC_FLOAT);
                double &value = *((double*)&(result->param1));
                if (subtype == ITF_MATH1)
                {
                    // Built-In math function with 1 arg
                    value = MathFunctions[ioptions].fptr(value);
                } else
                if (subtype == ITF_MATH2)
                {
                    // Built-In math function with 2 args
                    if (ioptions >= MATHFUNCNUM-2)  
                    {
                        // specific function - we need second arg as out
                        ExpressionItem *arg2, *res2 = AllocItem();
                        RunTree(*((ExpressionItem**)&(item->param2)), arg2, 0);
                        if (ioptions == MATHFUNCNUM-1)
                        {
                            // fmodf function - second arg is ptr to double
                            res2->type = IT_CONST | ITC_FLOAT;
                            double &v = *((double*)&(res2->param1));
                            value = ((Math2dFuncPtr)(MathFunctions[ioptions].fptr))(value, &v);
                        } else
                        {
                            // frexp function - second arg is ptr to int
                            int v = 0;
                            value = ((Math2iFuncPtr)(MathFunctions[ioptions].fptr))(value, &v);
                            *((__int64 *)&(res2->param1)) = (__int64) v;
                        }
                        SaveResult(arg2, res2);
                        CleanupItems(arg2); CleanupItems(res2);
                    } else
                    {
                        // normal 2-arg math function
                        ExpressionItem *arg2;
                        RunAndGetConst((int)item->param2, arg2, ITC_FLOAT);
                        double value2 = *((double*)&(arg2->param1));
                        value = ((Math2FuncPtr)(MathFunctions[ioptions].fptr))(value, value2);
                        CleanupItems(arg2);
                    }
                } 
            }
            break;
        case IT_ARRAY:
            {
                // currently only array access is used
                ExpressionItem *index, *aritem;
                RunTree(*((ExpressionItem **) &(item->param1)), aritem, RTO_NEEDCONST | ITC_STRING | ITC_ARRAY);

                if ((aritem->type & (ITEMTYPE|ITEMSUBTYPE)) == (IT_CONST | ITC_STRING))
                {
                    // argument is string
                    char *str = (char*)(aritem->param1);
                    int len = lstrlen(str);
                    // have we two indexes or one?
                    if ((*((ExpressionItem **) &(item->param2)))->type != IT_EXPRESSION)
                    {
                        // one index - user need a char
                        RunAndGetConst((int)item->param2, index, ITC_INT);
                        
                        int pos = (int) *((__int64*)&(index->param1));
                        if (pos < 0) pos += len; // -index - means from end
                        if ((pos > len) || (pos < 0))
                            *str = 0; // index is accross string boundaries
                        else
                        {
                            // new string - just a single char
                            *str = *(str+pos);
                            *(str+1) = 0;
                        }
                    } else
                    {
                        // two indexes
                        ExpressionItem *index2;
                        // if first index is skipped -> 0 (from first char)
                        if ((*((ExpressionItem **) &(item->param2)))->param1 == 0)
                            index = AllocItem();
                        else
                            RunAndGetConst((int)(*((ExpressionItem **) &(item->param2)))->param1, index, ITC_INT);
                        if ((*((ExpressionItem **) &(item->param2)))->next->param1 == 0)
                        {
                            // if second index is skipped -> -1 (till last char)
                            index2 = AllocItem();
                            *((__int64*)&(index2->param1)) = -1;
                        }
                        else
                            RunAndGetConst((int)(*((ExpressionItem **) &(item->param2)))->next->param1, index2, ITC_INT);

                        // ok, we've got two indexes
                        int pos1 = (int) *((__int64*)&(index->param1));
                        int pos2 = (int) *((__int64*)&(index2->param1));
                        if (pos1 < 0) pos1 += len; // -index - means from end
                        if (pos2 < 0) pos2 += len; // -index - means from end
                        // limit start/stop positions
                        if (pos1 < 0) pos1 = 0;
                        if (pos2 < 0) pos2 = 0;
                        if (pos1 > len) pos1 = len;
                        if (pos2 >= len) pos2 = len-1;

                        // copy string part
                        char* lpos = str + (pos2-pos1);
                        while (str <= lpos) 
                        {
                            *str = *(str + pos1);
                            str++;
                        }
                        // null-termiante
                        *str = 0;

                        CleanupItems(index2);
                    }
                    
                } else
                {
                    // argument is array
                    RunAndGetConst((int)item->param2, index, ITC_INT);

                    // convert array pointer to array item pointer
                    aritem->type = IT_VARIABLE | ITV_ARRITEM;
                    aritem->param2 = (EIPARAM) *((__int64*)&(index->param1));

                    ArrayDesc *ad = (ArrayDesc*)aritem->param1;
                    if (((int)aritem->param2) > ad->count) 
                    {
                        ad->count = ((int)aritem->param2)+1;
                        while (ad->count > ad->size)
                        {
                            // resize array
                            ExpressionItem **oldei = ad->array;                        
                            ad->array = (ExpressionItem**) dbgGlobalAlloc(GPTR, 2*ad->size*sizeof(ExpressionItem*));
                            for (int i = 0; i < ad->size; i++)
                                ad->array[i] = oldei[i];                        
                            ad->size*=2;
                            dbgGlobalFree(oldei);
                        }
                    }
                }
                                
                CleanupItems(index);

                // we need constant result?
                if (options & RTO_NEEDCONST)
                {
                    RunTree(aritem, result, options);
                    CleanupItems(aritem);
                } else result = aritem;
            }
            break;
        }
        item = item->next;
    }
}

extern "C"
void __declspec(dllexport) Script(HWND hwndParent, int string_size, 
                                      char *variables, stack_t **stacktop)
{
  Math_INIT();
  char *buffer = AllocString(), *buf = buffer;
  ExpressionItem *root = NULL; // root of current tree    

  // pop script string
  popstring(buffer);

  // parse it
  ParseString(buf, root);

#ifdef _DEBUG
  // dump
  PrintTree(root, buffer);
#endif

  ExpressionItem *result;
  RunTree(root, result, 0);
  CleanupItems(result);

  CleanupItems(root);
  dbgGlobalFree((HGLOBAL) buffer);
}

double _infinity;

void CleanAll(int init)
{
  if (init)
  {
    unsigned char _infinity_base[8] = {0, 0, 0, 0, 0, 0, 0xf0, 0x7f};
    _fltused = 0;
    _infinity = *((double*)(_infinity_base));
    _fpreset();

    stack = NULL;
    UserVarsCount = 0;
    UserFuncsCount = 0;
  } else
  {
    // cleanup stack
    CleanupItems(stack); stack = NULL;
    // cleanup user vars
    for (int i = 0; i < UserVarsCount; i++)
        CleanupItems(UserVars[i].item);
    // cleanup user funcs
    for (int i = 0; i < UserFuncsCount; i++)
        CleanupItems(UserFuncs[i].root);
    UserVarsCount = 0;
    UserFuncsCount = 0;

    dbgGlobalCheck();
  }
}

BOOL WINAPI _DllMainCRTStartup(HANDLE hInst, ULONG ul_reason_for_call, LPVOID lpReserved)
{
    CleanAll(ul_reason_for_call == DLL_PROCESS_ATTACH);
    return TRUE;
}

#ifdef _DEBUG
BOOL WINAPI DllMain(
        HANDLE  hDllHandle,
        DWORD   dwReason,
        LPVOID  lpreserved
        )
{
    CleanAll(dwReason == DLL_PROCESS_ATTACH);
    return TRUE;
}
#endif