//-< COMPILE.CPP >---------------------------------------------------*--------*
// FastDB                    Version 1.0         (c) 1999  GARRET    *     ?  *
// (Main Memory Database Management System)                          *   /\|  *
//                                                                   *  /  \  *
//                          Created:     20-Nov-98    K.A. Knizhnik  * / [] \ *
//                          Last update: 15-Jan-99    K.A. Knizhnik  * GARRET *
//-------------------------------------------------------------------*--------*
// Conditional expresion compiler
//-------------------------------------------------------------------*--------*

#define INSIDE_FASTDB

#include <ctype.h>
#include "database.h"
#include "array.h"
#include "query.h"
#include "cursor.h"
#include "compiler.h"
#include "symtab.h"

nat1 const dbExprNode::nodeTypes[] = { 
#define DBVM(cop, type, n_opernads, commutative) type,
#include "compiler.d"
0};

nat1 const dbExprNode::nodeOperands[] = { 
#define DBVM(cop, type, n_operands, commutative) n_operands,
#include "compiler.d"
0};

nat1 const dbExprNode::commutativeOperator[] = { 
#define DBVM(cop, type, n_operands, commutative) commutative,
#include "compiler.d"
0};

dbExprNode* dbExprNode::freeNodeList;
dbMutex&    dbExprNode::mutex = *new dbMutex;
bool        dbCompiler::initialized;

dbExprNodeSegment* dbExprNode::segmentList;

dbExprNode::dbExprNode(dbExprNode* node)
{
    memcpy(this, node, sizeof(*this));
    
    for (int i = nodeOperands[cop]; --i >= 0;) { 
    operand[i] = new dbExprNode(operand[i]);
    }
    if (cop == dbvmLoadStringConstant) { 
    char* s = new char[strlen(svalue.str)+1];
    strcpy(s, svalue.str);
    svalue.str = s;
    } 
}

void* dbExprNode::operator new(size_t EXTRA_DEBUG_NEW_PARAMS) 
{ 
    dbCriticalSection cs(mutex);
    dbExprNode* node = freeNodeList;
    if (node == NULL) {
    dbExprNodeSegment* seg = new dbExprNodeSegment;
    seg->next = segmentList;
    segmentList = seg;
    node = (dbExprNode*)seg->buf;
    dbExprNode* freeNode = NULL;
    for (int i = dbExprNodeSegment::allocationQuantum; --i != 0;) { 
        node->next = freeNode;
        freeNode = node++;
    } 
    freeNodeList = freeNode;
    } else { 
    freeNodeList = node->next;
    }
    return node;
}

void dbExprNode::cleanup()
{
    dbExprNode* freeNode = NULL;
    for (dbExprNodeSegment* seg = segmentList; seg != NULL; seg = seg->next) 
    { 
    dbExprNode* node = (dbExprNode*)seg->buf;
    for (int i = dbExprNodeSegment::allocationQuantum; --i >= 0;) { 
        node->next = freeNode;
        freeNode = node++;
     }
    }
    freeNodeList = freeNode;
}

dbExprNode::~dbExprNode()
{
    if (cop == dbvmLoadStringConstant) { 
    delete[] svalue.str;
    } else { 
    for (int i = nodeOperands[cop]; --i >= 0; ) { 
        delete operand[i];
    }
    }
}

int dbCompiler::scan() 
{
    char ch;
    char *p, *q;
    int  i, n, value;
    char buf[maxStrLen+1];

    if (hasToken) { 
        hasToken = false;
        return lex;
    }
   nextElement:
    if (queryElement == NULL) { 
    return tkn_eof;
    }
    if (queryElement->type != dbQueryElement::qExpression) {
    varType = queryElement->type;
    varPtr = queryElement->ptr;
    varRefTable = queryElement->ref;
    queryElement = queryElement->next;
    return tkn_var;
    } 
    p = q = (char*)queryElement->ptr + currPos;
    
    do { 
    ch = *p++;
    if (ch == '\n') { 
        offsetWithinStatement = (char*)queryElement->ptr - p;
        firstPos = 0;
    }
    } while (isspace(byte(ch)));
    currPos += p - q;

    switch (ch) { 
      case '\0':
    if ((queryElement = queryElement->next) == NULL) { 
        return tkn_eof;
    }
    currPos = 0;
    goto nextElement;
      case '+':
    return tkn_add;
      case '-':
    if (*p == '-') { 
        // ANSI comments
        q = p;
        do { 
        p += 1;
        } while (*p != '\n' && *p != '\0');
        currPos += p - q;
        goto nextElement;
    }
    return tkn_sub;
      case '*':
    return tkn_mul;
      case '/':
    return tkn_div;
      case '.':
    return tkn_dot;
      case ',':
    return tkn_comma;
      case '(':
    return tkn_lpar;
      case ')':
    return tkn_rpar;
      case '[':
    return tkn_lbr;
      case ']':
    return tkn_rbr;
      case ':':
    return tkn_col;
      case '^':
    return tkn_power;
      case '<':
    if (*p == '=') { 
        currPos += 1;
        return tkn_le;
    } else if (*p == '>') { 
        currPos += 1;
        return tkn_ne;
    }
    return tkn_lt;
      case '>':
    if (*p == '=') { 
        currPos += 1;
        return tkn_ge;
    }
    return tkn_gt;
      case '=':
    return tkn_eq;
      case '!':
    if (*p != '=') { 
        error("Invalid token '!'");
    } 
    currPos += 1;
    return tkn_ne;
      case '|':
    if (*p != '|') { 
        error("Invalid token '|'");
    } 
    currPos += 1;
    return tkn_add;
      case '\'':
    q = p;
    i = 0; 
    while (true) { 
        if (*p == '\'') { 
        if (*++p != '\'') { 
            break;
        }
        } else if (*p == '\0') { 
        error("Unterminated character constant");
        }
        if (i == maxStrLen) { 
        error("String constant too long");
        }
        buf[i++] = *p++;
    }
    buf[i++] = '\0';
    currPos += p - q;
    svalue.str = new char[i];
    strcpy(svalue.str, buf);
    svalue.len = i;
    return tkn_sconst;
      case '0': case '1': case '2': case '3': case '4': 
      case '5': case '6': case '7': case '8': case '9':
    value = ch - '0';
    for (q = p; isdigit(*(byte*)p); p++) { 
        value = value*10 + *p - '0';
    }
    if (*p == '.' || *p == 'e' || *p == 'E') { 
        if (sscanf(q-1, "%lf%n", &fvalue, &n) != 1) { 
        error("Bad floating point constant");
        }
        currPos += n - 1;
        return tkn_fconst;
    } else if (p - q >= 9) { 
        if (sscanf(q-1, INT8_FORMAT "%n", &ivalue, &n) != 1) { 
        error("Bad integer constant");
        }
        currPos += n - 1;
        return tkn_iconst;
    } else { 
        currPos += p - q; 
        ivalue = value;
        return tkn_iconst;
    }
      default:
    if (isalpha(ch) || ch == '$' || ch == '_') { 
        i = 0;
        do { 
        if (i == maxStrLen) { 
            error("Name too long");
        }
        buf[i++] = ch;
        ch = *p++;
        } while (isalnum(ch) || ch == '$' || ch == '_');
        buf[i] = '\0';
        name = buf;
        currPos += i - 1;
    } else { 
        error("Invalid symbol");
    } 
    return dbSymbolTable::add(name, tkn_ident);
   }
}

dbExprNode* dbCompiler::disjunction()
{
    dbExprNode* left = conjunction();
    if (lex == tkn_or) { 
    int pos = currPos;
    dbExprNode* right = disjunction();
    if (left->type == tpInteger && right->type == tpInteger) { 
        left = new dbExprNode(dbvmOrInt, left, right);
    } else if (left->type == tpBoolean && right->type == tpBoolean) { 
        left = new dbExprNode(dbvmOrBool, left, right);
    } else { 
        error("Bad operands for OR operator", pos);
    }
    }
    return left;
}

dbExprNode* dbCompiler::conjunction()
{
    dbExprNode* left = comparison();
    if (lex == tkn_and) { 
    int pos = currPos;
    dbExprNode* right = conjunction();
    if (left->type == tpInteger && right->type == tpInteger) { 
        left = new dbExprNode(dbvmAndInt, left, right);
    } else if (left->type == tpBoolean && right->type == tpBoolean) { 
        left = new dbExprNode(dbvmAndBool, left, right);
    } else { 
        error("Bad operands for AND operator", pos);
    }
    }
    return left;
}

inline dbExprNode* int2real(dbExprNode* expr) 
{
    if (expr->cop == dbvmLoadIntConstant) { 
    expr->fvalue = (real8)expr->ivalue;
    expr->cop = dbvmLoadRealConstant;
    expr->type = tpReal;
    } else { 
    expr = new dbExprNode(dbvmIntToReal, expr);
    }
    return expr;
}

int dbCompiler::compare(dbExprNode* expr, dbExprNode* list)
{
    int n = 1;
    if (list->operand[0] != NULL) { 
    n = compare(expr, list->operand[0]);
    expr = new dbExprNode(expr);
    }
    dbExprNode* elem = list->operand[1];
    int cop = dbvmVoid;
    int rawBinarySize = 0;
    void* rawBinaryComparator = NULL;
    if (expr->type == tpInteger) { 
    if (elem->type == tpReal) { 
        expr = int2real(expr);
        cop = dbvmEqReal;
    } else if (elem->type == tpReference) {
        expr = new dbExprNode(dbvmIntToReference, expr);
        cop = dbvmEqReference;
    } else if (elem->type == tpInteger) { 
        cop = dbvmEqInt;
    }
    } else if (expr->type == tpReal) {
    if (elem->type == tpReal) { 
        cop = dbvmEqReal;
    } else if (elem->type == tpInteger) { 
        cop = dbvmEqReal;
        elem = int2real(elem);
    }
    } else if (expr->type == tpString && elem->type == tpString) {
    cop = dbvmEqString;
    } else if (expr->type == tpReference && (elem->type == tpReference || elem->type == tpInteger)) {
    if (elem->type == tpInteger) {
        elem = new dbExprNode(dbvmIntToReference, elem);
    }
    cop = dbvmEqReference;
    } else if (expr->type == tpBoolean && elem->type == tpBoolean) {
    cop = dbvmEqBool;
    } else if (expr->type == tpRawBinary && elem->type == tpRawBinary) {
    cop = dbvmEqBinary;
    if ((expr->cop == dbvmLoadSelfRawBinary || expr->cop == dbvmLoadRawBinary) 
        && expr->ref.field != NULL)
    {
        rawBinarySize = expr->ref.field->dbsSize;
        rawBinaryComparator = (void*)expr->ref.field->comparator;
    } else if ((elem->cop == dbvmLoadSelfRawBinary || elem->cop == dbvmLoadRawBinary) 
        && elem->ref.field != NULL)
    {
        rawBinarySize = elem->ref.field->dbsSize;
        rawBinaryComparator = (void*)elem->ref.field->comparator;
    } else { 
        error("Operations with raw binary types should include at least one record field");
    }
    }
    if (cop == dbvmVoid) { 
    char buf[256];
    sprintf(buf, "Expression %d in right part of IN operator have "
        "incompatible type", n);
    error(buf);
    } else { 
    list->type = tpBoolean;
    if (list->operand[0] != NULL) { 
        expr = new dbExprNode(cop, expr, elem, rawBinarySize);
        expr->func.fptr = rawBinaryComparator;
        list->operand[1] = expr;
        list->cop = dbvmOrBool;
    } else { 
        list->operand[0] = expr;
        list->cop = cop;
        list->offs = rawBinarySize;
        list->func.fptr = rawBinaryComparator;
    }
    }
    return ++n;
}

dbExprNode* dbCompiler::comparison()
{
    int leftPos = currPos;
    dbExprNode *left, *right;
    left = addition();
    int cop = lex;
    if (cop == tkn_eq || cop == tkn_ne || cop == tkn_gt || cop == tkn_ge
    || cop == tkn_le || cop == tkn_lt || cop == tkn_between 
    || cop == tkn_like || cop == tkn_not || cop == tkn_is || cop == tkn_in)
    {
    int rightPos = currPos;
    bool notOp = false;
    if (cop == tkn_not) { 
        notOp = true;
        cop = scan();
        if (cop != tkn_like && cop != tkn_between && cop != tkn_in) { 
        error("LIKE, BETWEEN or IN expected", rightPos);
        } 
        rightPos = currPos;
    } else if (cop == tkn_is) {
        if (left->type != tpReference) { 
        error("IS [NOT] NULL predicate can be applied only to "
              "references", rightPos);
        } 
        rightPos = currPos;
        if ((cop = scan()) == tkn_null) { 
        left = new dbExprNode(dbvmIsNull, left);
        } else if (cop == tkn_not) { 
        rightPos = currPos;
        if (scan() == tkn_null) { 
            left = new dbExprNode(dbvmNotBool, 
                      new dbExprNode(dbvmIsNull, left));
        } else { 
            error("NULL expected", rightPos);
        }
        } else { 
        error("[NOT] NULL expected", rightPos);
        } 
        lex = scan();
        return left;
    }   
    right = addition();
    if (cop == tkn_in) { 
        int type;
        switch (right->type) {
          case tpArray:
        type = (right->cop == dbvmLoadVarArray || right->cop == dbvmLoadVarArrayPtr)
             ? dbField::tpReference 
             : right->ref.field->components->type;
        if ((left->type == tpBoolean && type == dbField::tpBool) 
            || (left->type == tpInteger 
            && (type == dbField::tpInt1 
                || type == dbField::tpInt2
                || type == dbField::tpInt4 
                || type == dbField::tpInt8))
            || (left->type == tpReal 
            && (type == dbField::tpReal4
                || type == dbField::tpReal8))
            || (left->type == tpString && type == dbField::tpString)
            || ((left->type == tpReference || left->type == tpInteger)
                        && type == dbField::tpReference))
        {
            if (left->type == tpInteger && type == dbField::tpReference) { 
            left = new dbExprNode(dbvmIntToReference, left);
            }
            left = new dbExprNode(dbvmInArrayBool + type, left, right);
        } else {
            error("Type of selective expression of IN operator doesn't"
              " match type of the array");
        }
        break;
          case tpString:
        if (left->type == tpString) { 
            left = new dbExprNode(dbvmInString, left, right);
        } else { 
            error("Left operand of IN expression hasn't string type");
        }
        break;
          case tpList:
        compare(left, right);
        left = right;
        break;
          default:
        error("List of expressions or array expected", rightPos);
        }
    } else if (cop == tkn_between) { 
        int andPos = currPos;
        if (lex != tkn_and) { 
        error("AND expected");
        }
        dbExprNode* right2 = addition();
        if (left->type == tpReal 
        || right->type == tpReal || right2->type == tpReal)
        {
        if (left->type == tpInteger) { 
            left = int2real(left);
        } else if (left->type != tpReal) { 
            error("operand of BETWEEN operator should be of "
              "integer, real or string type", leftPos);
        }
        if (right->type == tpInteger) {
            right = int2real(right);
        } else if (right->type != tpReal) { 
            error("operand of BETWEEN operator should be of "
              "integer, real or string type", rightPos);
        }
        if (right2->type == tpInteger) {
            right2 = int2real(right2);
        } else if (right2->type != tpReal) { 
            error("operand of BETWEEN operator should be of "
              "integer, real or string type", andPos);
        }
        left = new dbExprNode(dbvmBetweenReal, left, right, right2);
        } 
        else if (left->type == tpInteger && right->type == tpInteger 
             && right2->type == tpInteger)
        {           
        left = new dbExprNode(dbvmBetweenInt, left, right, right2);
        }
        else if (left->type == tpString && right->type == tpString 
             && right2->type == tpString)
        {
        left = new dbExprNode(dbvmBetweenString, left, right, right2);
        }
        else if (left->type == tpRawBinary && right->type == tpRawBinary
             && right2->type == tpRawBinary)
        {
        int rawBinarySize = 0;
        void* rawBinaryComparator = NULL;
        if ((left->cop == dbvmLoadSelfRawBinary || left->cop == dbvmLoadRawBinary) 
            && left->ref.field != NULL)
        {
            rawBinarySize = left->ref.field->dbsSize;
            rawBinaryComparator = (void*)left->ref.field->comparator;
        } else { 
            error("Operations with raw binary types should include at least one record field");
        }
        left = new dbExprNode(dbvmBetweenBinary, left, right, right2);
        left->offs = rawBinarySize;
        left->func.fptr = rawBinaryComparator;
        }
        else { 
        error("operands of BETWEEN operator should be of "
              "integer, real or string type", rightPos);
        }
    } else if (cop == tkn_like) {  
        if (left->type != tpString || right->type != tpString) { 
        error("operands of LIKE operator should be of "
              "string type", rightPos);
        }
        if (lex == tkn_escape) { 
        rightPos = currPos;
        if (scan() != tkn_sconst) { 
            error("String literal espected after ESCAPE", rightPos);
        }
        lex = scan();
        left = new dbExprNode(dbvmLikeEscapeString, left, right,
               new dbExprNode(dbvmLoadStringConstant, svalue));
        } else { 
        left = new dbExprNode(dbvmLikeString, left, right);
        }
    } else { 
        if (left->type == tpReal || right->type == tpReal) { 
        if (left->type == tpInteger) { 
            left = int2real(left);
        } else if (left->type != tpReal) { 
            error("operands of relation operator should be of "
              "intger, real or string type", leftPos);
        }
        if (right->type == tpInteger) { 
            right = int2real(right);
        } else if (right->type != tpReal) { 
            error("operands of relation operator should be of "
              "intger, real or string type", rightPos);
        }
        left = new dbExprNode(dbvmEqReal + cop - tkn_eq, left, right);
        } else if (left->type == tpInteger && right->type == tpInteger) { 
        left = new dbExprNode(dbvmEqInt + cop - tkn_eq, left, right);
        } else if (left->type == tpString && right->type == tpString) { 
        left = new dbExprNode(dbvmEqString + cop-tkn_eq, left, right);
        } else if ((left->type == tpReference || left->type == tpInteger) 
               && (right->type == tpReference || right->type == tpInteger)) 
        {
        if (cop != tkn_eq && cop != tkn_ne) {
            error("References can be checked only for equality",
              rightPos);
        }
        if (left->type == tpInteger) { 
            left = new dbExprNode(dbvmIntToReference, left);
        } else if (right->type == tpInteger) { 
            right = new dbExprNode(dbvmIntToReference, right);
        }
        left = new dbExprNode(dbvmEqReference+cop-tkn_eq, left, right);
        } else if (left->type == tpBoolean && right->type == tpBoolean) { 
        if (cop != tkn_eq && cop != tkn_ne) { 
            error("Boolean variables can be checked only for equality",
              rightPos);
        }
        left = new dbExprNode(dbvmEqBool + cop - tkn_eq, left, right);
        } else if (left->type == tpRawBinary && right->type == tpRawBinary) {
        int rawBinarySize = 0;
        void* rawBinaryComparator = NULL;
        if ((left->cop == dbvmLoadSelfRawBinary || left->cop == dbvmLoadRawBinary) 
            && left->ref.field != NULL)
        {
            rawBinarySize = left->ref.field->dbsSize;
            rawBinaryComparator = (void*)left->ref.field->comparator;
        } else if ((right->cop == dbvmLoadSelfRawBinary || right->cop == dbvmLoadRawBinary) 
               && right->ref.field != NULL)
        {
            rawBinarySize = right->ref.field->dbsSize;
            rawBinaryComparator = (void*)right->ref.field->comparator;
        } else { 
            error("Operations with raw binary types should include at least one record field");
        }
        left = new dbExprNode(dbvmEqBinary + cop - tkn_eq, left, right, rawBinarySize);
        left->func.fptr = rawBinaryComparator;
        } else { 
        error("operands of relation operator should be of "
              "integer, real or string type", rightPos);
        }
        //
        // Optimization for applying indices: if operation is 
        // commuatative and left operand is constant then swap operands
        //
        if (IS_CONSTANT(left->operand[0]->cop)) { 
        right = left->operand[1];
        left->operand[1] = left->operand[0];
        left->operand[0] = right;
        left->cop = dbExprNode::commutativeOperator[left->cop];
        }   
    }
    if (notOp) { 
        left = new dbExprNode(dbvmNotBool, left);
    }
    }
    return left;
}

dbExprNode* dbCompiler::addition() 
{ 
    int leftPos = currPos;
    dbExprNode* left = multiplication();
    while (lex == tkn_add || lex == tkn_sub) { 
    int cop = lex;
    int rightPos = currPos;
    dbExprNode* right = multiplication();
    if (left->type == tpReal || right->type == tpReal) { 
        if (left->type == tpInteger) { 
        left = int2real(left);
        } else if (left->type != tpReal) { 
        error("operands of arithmetic operators should be of "
              "integer or real type", leftPos);
        }
        if (right->type == tpInteger) { 
        right = int2real(right);
        } else if (right->type != tpReal) { 
        error("operands of arithmetic operator should be of "
              "integer or real type", rightPos);
        }
        left = new dbExprNode(cop==tkn_add ? dbvmAddReal : dbvmSubReal,
                  left, right);
    } else if (left->type == tpInteger && right->type == tpInteger) { 
        left = new dbExprNode(cop==tkn_add ? dbvmAddInt : dbvmSubInt,
                  left, right);
    } else if (left->type == tpString && right->type == tpString) { 
        if (cop == tkn_add) { 
        left = new dbExprNode(dbvmStringConcat, left, right);
        } else { 
        error("Operation - is not defined for strings", rightPos);
        }
    } else { 
        error("operands of arithmentic operator should be of "
          "integer or real type", rightPos);
    }
    leftPos = rightPos;
    }
    return left;
}


dbExprNode* dbCompiler::multiplication() 
{ 
    int leftPos = currPos;
    dbExprNode* left = power();
    while (lex == tkn_mul || lex == tkn_div) { 
    int cop = lex;
    int rightPos = currPos;
    dbExprNode* right = power();
    if (left->type == tpReal || right->type == tpReal) { 
        if (left->type == tpInteger) { 
        left = int2real(left);
        } else if (left->type != tpReal) { 
        error("operands of arithmetic operators should be of "
              "integer or real type", leftPos);
        }
        if (right->type == tpInteger) { 
        right = int2real(right);
        } else if (right->type != tpReal) { 
            error("operands of arithmetic operator should be of "
              "integer or real type", rightPos);
        }
        left = new dbExprNode(cop==tkn_mul ? dbvmMulReal : dbvmDivReal,
                  left, right);
    } else if (left->type == tpInteger && right->type == tpInteger) { 
        left = new dbExprNode(cop==tkn_mul ? dbvmMulInt : dbvmDivInt,
                  left, right);
    } else { 
        error("operands of arithmentic operator should be of "
          "integer or real type", rightPos);
    }
    leftPos = rightPos;
    }
    return left;
}

dbExprNode* dbCompiler::power() 
{ 
    int leftPos = currPos;
    dbExprNode* left = userDefinedOperator();
    if (lex == tkn_power) { 
    int rightPos = currPos;
    dbExprNode* right = power();
    if (left->type == tpReal || right->type == tpReal) { 
        int cop = dbvmPowerReal;
        if (left->type == tpInteger) { 
        left = int2real(left);
        } else if (left->type != tpReal) { 
        error("operands of arithmetic operators should be of "
              "integer or real type", leftPos);
        }
        if (right->type == tpInteger) { 
        cop = dbvmPowerRealInt;
        } else if (right->type != tpReal) { 
            error("operands of arithmetic operator should be of "
              "integer or real type", rightPos);
        }
        left = new dbExprNode(cop, left, right);
    } else if (left->type == tpInteger && right->type == tpInteger) { 
        left = new dbExprNode(dbvmPowerInt, left, right);
    } else { 
        error("operands of arithmentic operator should be of "
          "integer or real type", rightPos);
    }
    }    
    return left;
}


dbExprNode* dbCompiler::userDefinedOperator() 
{ 
    dbExprNode* left = term();
    while (lex == tkn_ident) { 
    dbUserFunction* func = dbUserFunction::find(name);
    if (func != NULL) { 
        int nParams =  func->getNumberOfParameters();
        if (nParams != 2) { 
        error("Only function with two arguments can be used as operator", currPos);
        }
        int rightPos = currPos;
        dbExprNode* right = term();
        if ((left->type != tpInteger && left->type != tpReal && left->type != tpString
         && left->type != tpReference && left->type != tpRawBinary && left->type != tpBoolean)
        || (right->type != tpInteger && right->type != tpReal && right->type != tpString
            && right->type != tpReference && right->type != tpRawBinary && right->type != tpBoolean))
        {
        
        error("User function should receive parameter of boolean, integer, real, string, reference or user defined type", rightPos);
        } 
        left = new dbExprNode(dbvmFuncInt2Bool + func->type, func->fptr, left, right);
    } else { 
        break;
    }
    }
    return left;
}

dbExprNode* dbCompiler::field(dbExprNode* expr, dbTableDescriptor* refTable,
                  dbFieldDescriptor* fd) 
{
    int pos;

    while (true) { 
    switch (lex) {
      case tkn_dot: 
        pos = currPos;
        if (scan() != tkn_ident) { 
        error("identifier expected", pos);
        }
        if (fd != NULL && fd->type == dbField::tpStructure) { 
        if ((fd = fd->find(name)) == NULL) { 
            error("Field not found");
        }
        } else { 
        assert(expr != NULL);
        if (expr->type != tpReference) { 
            error("Left operand of '.' should be "
              "structure or reference", pos);
        }
        if (refTable == NULL) { 
            refTable = fd->refTable;
        }
        if (refTable == NULL) {
            refTable = fd->refTable;
        }
        if (refTable == NULL || (fd = refTable->findSymbol(name)) == NULL) { 
            error("Field not found");
        }
            refTable = NULL;
        expr = new dbExprNode(dbvmDeref, expr);
        }
        break;
      case tkn_lbr:
        if (expr == NULL || 
        (expr->type != tpArray && expr->type != tpString))
        { 
        error("Index can be applied only to arrays");
        } else { 
        dbExprNode* index = disjunction();
        if (lex != tkn_rbr) { 
            error("']' expected");
        }
        if (index->type != tpInteger && index->type != tpFreeVar) {
            error("Index should have integer type");
        }
        
        if (expr->type == tpString) { 
            lex = scan();
            return new dbExprNode(dbvmCharAt, expr, index);
        }
        if (fd == NULL) { 
            // variable of array of reference type
            expr = new dbExprNode(dbvmGetAt,expr,index,sizeof(oid_t));
        } else { 
                    if (refTable == NULL) { 
                        refTable = fd->refTable;
                    }
            fd = fd->components;
            expr = new dbExprNode(dbvmGetAt, expr, index, fd->dbsSize);
        }
        }
        break;
      default:
        if (expr == NULL) { 
        error("'.' expected");
        }
        return expr;
    }   
    if (fd == NULL) { 
        expr = new dbExprNode(dbvmLoadReference, expr, 0); 
    } else if (fd->type == dbField::tpRawBinary) {
        expr = new dbExprNode(expr != NULL ? dbvmLoadRawBinary : dbvmLoadSelfRawBinary, 
                  fd, expr);
    } else if (fd->type != dbField::tpStructure) { 
        expr = new dbExprNode((expr != NULL 
                   ? dbvmLoadBool : dbvmLoadSelfBool) 
                  + fd->type, fd, expr);
    }
    lex = scan();
    }
}



dbExprNode* dbCompiler::term() 
{
    dbFieldDescriptor* fd;
    dbTableDescriptor* refTable;
    int cop;
    int pos = currPos;
    dbBinding* bp;
    dbExprNode* expr = NULL;
    dbUserFunction* func;

    if ((cop = scan()) == tkn_where) { 
    if (firstPos == 0) { 
        firstPos = currPos;
    }
    cop = scan(); // just ignore 'where' keyword
    }

    switch (cop) { 
      case tkn_eof:
      case tkn_order:
      case tkn_start:
    lex = cop;
    return new dbExprNode(dbvmVoid);
      case tkn_current:
    lex = scan();
    return field(new dbExprNode(dbvmCurrent), table, NULL);
      case tkn_ident:
    for (bp = bindings; bp != NULL; bp = bp->next) { 
        if (name == bp->name) { 
        bp->used = true;
        lex = scan();
        return new dbExprNode(dbvmVariable, (dbExprNode*)0, bp->index);
        }
    }
        if ((fd = table->findSymbol(name)) == NULL) { 
        if ((func = dbUserFunction::find(name)) != NULL) { 
        pos = currPos;
        expr = term();
        int argType = func->getParameterType();
        int nParams =  func->getNumberOfParameters();
        if (nParams > 1) { 
            if (expr->type != tpList) {
            error("Too few paramters for user defined function");
            }
            dbExprNode* params[3];
            do { 
            if (nParams == 0) { 
                error("Too many parameters for user defined function");
            }   
            dbExprNode* param = expr->operand[1];
            if (param->type != tpInteger && param->type != tpReal && param->type != tpString
                && param->type != tpReference && param->type != tpRawBinary && param->type != tpBoolean)
            {
                error("User function should receive parameters of boolean, integer, real, string, reference or user defined type", pos);
            } 
            params[--nParams] = param;
            expr = expr->operand[0];
            } while (expr != NULL);

            if (nParams != 0) { 
            error("Too few parameters for user defined function");
            }   
            expr = new dbExprNode(dbvmFuncInt2Bool + func->type, func->fptr, params[0], params[1], params[2]);
        } else { 
            if (argType == tpReal && expr->type == tpInteger) { 
            expr = int2real(expr);
            } else if (argType == tpList) {
            if (expr->type != tpInteger && expr->type != tpReal && expr->type != tpString
                && expr->type != tpReference && expr->type != tpRawBinary && expr->type != tpBoolean)
            {
                error("User function should receive parameter of boolean, integer, real, string, reference or user defined type", pos);
            } 
            } else if (argType != expr->type) { 
            error("Incompatible function argument", pos);
            }
            expr = new dbExprNode(dbvmFuncInt2Bool + func->type, func->fptr, expr);
        }
        return field(expr, NULL, NULL);
        }
        error("Field not found", pos);
    }
    if (fd->type == dbField::tpRawBinary) { 
        expr = new dbExprNode(dbvmLoadSelfRawBinary, fd);
    } else if (fd->type != dbField::tpStructure) {
        expr = new dbExprNode(dbvmLoadSelfBool + fd->type, fd);
    } 
    lex = scan();
    return field(expr, NULL, fd);
      case tkn_exists:
    if (scan() == tkn_ident) { 
        dbBinding var;
        var.next = bindings;
        var.name = name;
        var.used = false;
        var.index = nFreeVars++;
        bindings = &var;
        if (nFreeVars >= maxFreeVars) { 
        error("Too many nested EXISTS clauses\n");
        }
        pos = currPos;
        if (scan() != tkn_col) { 
        error("':' expected", pos);
        }
        expr = term();
        if (expr->type != tpBoolean) { 
        error("Expresion in EXISTS clause should be of boolean type");
        }
        if (var.used) { 
        expr = new dbExprNode(dbvmExists, expr, nFreeVars-1);
        }
        nFreeVars -= 1;     
        assert(bindings == &var);
        bindings = var.next;
        return expr;
    } else { 
        error("Free variable name expected");
    }   
    break;
      case tkn_first:
    lex = scan();
    return field(new dbExprNode(dbvmFirst), table, NULL);
      case tkn_last:
    lex = scan();
    return field(new dbExprNode(dbvmLast), table, NULL);
      case tkn_false:
    expr = new dbExprNode(dbvmLoadFalse);
    break;
      case tkn_true:
    expr = new dbExprNode(dbvmLoadTrue);
    break;
      case tkn_null:
    expr = new dbExprNode(dbvmLoadNull);
    break;
      case tkn_iconst:
    expr = new dbExprNode(dbvmLoadIntConstant, ivalue);
    break;
      case tkn_fconst:
    expr = new dbExprNode(dbvmLoadRealConstant, fvalue);
    break;
      case tkn_sconst:
    expr = new dbExprNode(dbvmLoadStringConstant, svalue); 
    lex = scan();
    return field(expr, NULL, NULL);
      case tkn_var:
    expr = new dbExprNode(dbvmLoadVarBool + varType - 
                  dbQueryElement::qVarBool, varPtr);
    refTable = varRefTable;
    lex = scan();
    return field(expr, refTable, NULL);
      case tkn_abs:
      case tkn_length:
      case tkn_lower:
      case tkn_upper:
      case tkn_integer:
      case tkn_real:
      case tkn_string:
    pos = currPos;
    expr = term();
    switch (cop) {
      case tkn_abs:
        if (expr->type == tpInteger) { 
        cop = dbvmAbsInt;
        } else if (expr->type == tpReal) { 
        cop = dbvmAbsReal;
        } else { 
        error("ABS function can be applied only "
              "to integer or real expression", pos);
        }
        break;
      case tkn_length:
        if (expr->type == tpArray) { 
        cop = dbvmLength;
        } else if (expr->type == tpString) { 
        cop = dbvmStringLength;
        } else { 
        error("LENGTH function is defined only for arrays and strings",
              pos);
        } 
        break;
      case tkn_integer:
        if (expr->type == tpReal) { 
        cop = dbvmRealToInt;
        } else { 
        error("INTEGER function can be applied only to "
              "expression of real type", pos);
        }
        break;
      case tkn_real:
        if (expr->type == tpInteger) { 
        cop = dbvmIntToReal;
        } else { 
        error("REAL function can be applied only to "
              "expression of integer type", pos);
        }
        break;
      case tkn_string:
        if (expr->type == tpInteger) { 
        cop = dbvmIntToString;
        } else if (expr->type == tpReal) { 
        cop = dbvmRealToString;
        } else { 
        error("STRING function can be applied only "
              "to integer or real expression", pos);
        }
        break;
      case tkn_lower:
        if (expr->type != tpString) { 
        error("LOWER function can be applied only to string argument",
              pos);
        } else { 
        cop = dbvmLowerString;
        }
        break;
      case tkn_upper:
        if (expr->type != tpString) { 
        error("UPPER function can be applied only to string argument",
              pos);
        } else { 
        cop = dbvmUpperString;
        }
        break;
      default:
        assert(false);
    }
    return field(new dbExprNode(cop, expr), NULL, NULL);
      case tkn_lpar:
        {
        expr = disjunction();
        dbExprNode* list = NULL;
        while (lex == tkn_comma) { 
        list = new dbExprNode(dbvmList, list, expr);
        expr = disjunction();
        }
        if (lex != tkn_rpar) { 
        error("')' expected");
        }
        if (list != NULL) { 
        expr = new dbExprNode(dbvmList, list, expr);
        }
    }
    break;
      case tkn_not:
    pos = currPos;
    expr = comparison();
    if (expr->type == tpInteger) { 
        if (expr->cop == dbvmLoadIntConstant) { 
        expr->ivalue = ~expr->ivalue;
        } else {
        expr = new dbExprNode(dbvmNotInt, expr);
        } 
        return expr;
    } else if (expr->type == tpBoolean) { 
        return new dbExprNode(dbvmNotBool, expr);
    } else { 
        error("NOT operator can be applied only to "
          "integer or boolean expressions", pos);
    }
    break;
      case tkn_add:
    error("Using of unary plus operator has no sence");
    break;
      case tkn_sub:
    pos = currPos;
    expr = term();
    if (expr->type == tpInteger) { 
        if (expr->cop == dbvmLoadIntConstant) { 
        expr->ivalue = -expr->ivalue;
        } else { 
        expr = new dbExprNode(dbvmNegInt, expr);
        }
        return expr;
    } else if (expr->type == tpReal) { 
        if (expr->cop == dbvmLoadRealConstant) { 
        expr->fvalue = -expr->fvalue;
        } else { 
        expr = new dbExprNode(dbvmNegReal, expr);
        }
        return expr;
    } else { 
        error("Unary minus can be applied only to "
          "integer or real expressions", pos);
    }
      default:
    error("operand expected");
    }
    lex = scan();
    return expr;
}

void dbCompiler::error(const char* msg, int pos) 
{ 
    if (pos < 0) { 
    if ((pos = currPos-1) < 0) { 
        pos = 0;
    }
    } else if (pos < firstPos) { 
    pos = firstPos;
    }
    if (pos + offsetWithinStatement >= 0) { 
    pos += offsetWithinStatement;
    }
    table->db->handleError(dbDatabase::QueryError, msg, pos);
    longjmp(abortCompilation, dbDatabase::QueryError);
}

void dbCompiler::compileStartFollowPart(dbQuery& query)
{
    if (lex != tkn_start) { 
    return;
    }
    int pos = currPos;
    if (scan() != tkn_from) { 
    error("FROM expected after START", pos);
    }
    pos = currPos;
    switch (scan()) { 
      case tkn_first:
    query.startFrom = dbCompiledQuery::StartFromFirst; 
    break;
      case tkn_last:
    query.startFrom = dbCompiledQuery::StartFromLast; 
    break;
      case tkn_var:
    if (varType == dbQueryElement::qVarReference) { 
        if (varRefTable != table) { 
        error("Incompatiable type of reference variable");
        }
        query.startFrom = dbCompiledQuery::StartFromRef; 
    } else if (varType == dbQueryElement::qVarArrayOfRef) {
        if (varRefTable != table) { 
        error("Incompatiable type of array of reference variable");
        }
        query.startFrom = dbCompiledQuery::StartFromArray; 
    } else if (varType == dbQueryElement::qVarArrayOfRefPtr) {
        if (varRefTable != table) { 
        error("Incompatiable type of array of reference variable");
        }
        query.startFrom = dbCompiledQuery::StartFromArrayPtr; 
    } else { 
        error("Reference or array of reference variable expected");
    }
    query.root = varPtr;
    break;
      default:
    error("FIRST, LAST or reference varaible expected", pos); 
    }
    if ((lex = scan()) == tkn_follow) { 
    pos = currPos;
    if (scan() != tkn_by) { 
        error("BY expected after FOLLOW", pos);
    }
    do { 
        pos = currPos;
        if (scan() != tkn_ident) { 
        error("Field name expected", pos);
        }
        dbFieldDescriptor* fd;
        if ((fd = table->findSymbol(name)) == NULL) { 
        error("Field not found");
        }
        while (fd->type == dbField::tpStructure) { 
        pos = currPos;
        if (scan() != tkn_dot) { 
            error("'.' expected", pos);
        }
        pos = currPos;
        if (scan() != tkn_ident) { 
            error("Field name expected", pos);
        }
        if ((fd = fd->find(name)) == NULL) { 
            error("Field not found");
        }
        }
        if (!(fd->type == dbField::tpReference 
          && fd->refTable == table) &&
        !(fd->type == dbField::tpArray 
          && fd->components->type == dbField::tpReference
          && fd->components->refTable == table)) 
        { 
        error("Follow field should be of compatibale reference "
              "or array of reference type");
        }
        dbFollowByNode* node = new dbFollowByNode;
        node->field = fd;
        node->next = query.follow; // list should be inverted
        query.follow = node;
    } while ((lex = scan()) == tkn_comma); 
    }   
}

void dbCompiler::compileOrderByPart(dbQuery& query)
{
    if (lex == tkn_order) { 
        dbOrderByNode** opp = &query.order;
    int pos = currPos;
    if (scan() != tkn_by) {
        error("BY expected after ORDER", pos);
    }
        int parentheses = 0;
        do {
            pos = currPos;
        int tkn = scan();
            if (tkn == tkn_lpar) {
        parentheses += 1;
            } else {
                ungetToken(tkn);
            }
            dbExprNode* expr = disjunction();
            dbOrderByNode* node = new dbOrderByNode;
            switch (expr->cop) { 
              case dbvmLoadSelfBool:
              case dbvmLoadSelfInt1:
              case dbvmLoadSelfInt2:
              case dbvmLoadSelfInt4:
              case dbvmLoadSelfInt8:
              case dbvmLoadSelfReal4:
              case dbvmLoadSelfReal8:
              case dbvmLoadSelfString:
              case dbvmLoadSelfArray:
              case dbvmLoadSelfReference:
              case dbvmLoadSelfRawBinary:
                assert(expr->ref.field != NULL);
                node->field = expr->ref.field;
                node->expr = NULL;
                break;
              case dbvmLength:
                if (expr->operand[0]->cop == dbvmLoadSelfArray) { 
                    node->field = expr->operand[0]->ref.field;
                    node->expr = NULL;
                    break;                    
                }
                // no break
              default:
                if (expr->type > tpReference) { 
                    error("Expressions in ORDER BY part should have scalar type", pos);
                } 
                node->field = NULL;
                node->expr = expr;
                node->table = table;
            }
            node->ascent = true;
            *opp = node;
            opp = &node->next;
            *opp = NULL;
            if (lex == tkn_desc) {
                node->ascent = false;
                lex = scan();
            } else if (lex == tkn_asc) {
                lex = scan();
            } 
            if (lex == tkn_rpar) { 
        if (--parentheses < 0) { 
            error("Unbalanced parentheses ");
        }
                lex = scan();
            }
        } while (lex == tkn_comma);
    }
}


dbExprNode* dbCompiler::compileExpression(dbTableDescriptor* table, char const* expr, int startPos)
{
    TRACE_MSG(("Compile expression %s for table %s\n", table->name));    
    if (setjmp(abortCompilation) == 0) { 
    this->table = table;
    bindings = NULL;
    nFreeVars = 0;
    dbQueryElement elem(dbQueryElement::qExpression, expr, NULL); 
    queryElement = &elem;
    currPos = firstPos = 0;
    offsetWithinStatement = startPos;
    return disjunction(); 
    } else {
    return NULL;
    }
}

bool dbCompiler::compile(dbTableDescriptor* table, dbQuery& query)
{
    TRACE_MSG(("Compile query for table %s\n", table->name));
    query.destroy(); 
    if (setjmp(abortCompilation) == 0) { 
    this->table = table;
    bindings = NULL;
    nFreeVars = 0;
    queryElement = query.elements;
    currPos = firstPos = 0;
        hasToken  = false;
    offsetWithinStatement = query.pos;
    dbExprNode* expr = disjunction(); 
    if (expr->type != tpBoolean && expr->type != tpVoid) { 
        table->db->handleError(dbDatabase::QueryError, "Conditional "
                   "expression should have boolean type\n", 0);
        delete expr;
        return false;
    }
    compileStartFollowPart(query); 
    compileOrderByPart(query); 
    query.tree  = expr;
    query.table = table;
    return true;
    } else { 
    for (dbOrderByNode *op = query.order, *nop; op != NULL; op = nop) {
        nop = op->next;
        delete op;
    }
    for (dbFollowByNode *fp = query.follow, *nfp; fp != NULL; fp = nfp) {
        nfp = fp->next;
        delete fp;
    }
    return false;
    }
}

dbCompiler::dbCompiler() {
    static struct { 
    const char* name;
    int   tag;
    } keywords[] = { 
    {"all",     tkn_all},
    {"abs",     tkn_abs},
    {"and",     tkn_and},
    {"asc",     tkn_asc},
    {"between", tkn_between},
    {"by",      tkn_by},
    {"current", tkn_current},
    {"desc",    tkn_desc},
    {"escape",  tkn_escape},
    {"exists",  tkn_exists},
    {"first",   tkn_first},
    {"false",   tkn_false},
    {"follow",  tkn_follow},
    {"from",    tkn_from},
    {"in",      tkn_in},
    {"is",      tkn_is},
    {"integer", tkn_integer},
    {"insert",  tkn_insert},
    {"into",    tkn_into},
    {"last",    tkn_last},
    {"length",  tkn_length},
    {"like",    tkn_like},
    {"lower",   tkn_lower},
    {"not",     tkn_not},
    {"null",    tkn_null},
    {"or",      tkn_or},
    {"order",   tkn_order},
    {"real",    tkn_real},
    {"select",  tkn_select},
    {"start",   tkn_start},
    {"string",  tkn_string},
    {"table",   tkn_table},
    {"true",    tkn_true},
    {"upper",   tkn_upper},
    {"where",   tkn_where}
    };
    if (!initialized) { 
    for (unsigned i = 0; i < itemsof(keywords); i++) { 
        dbSymbolTable::add((char*&)keywords[i].name, keywords[i].tag, FASTDB_CLONE_ANY_IDENTIFIER);    
    }
    initialized = true;
    }
}

void dbInheritedAttribute::removeTemporaries()
{
    for (dbStringValue *next, *s = tempStrings; s != NULL; s = next) { 
    next = s->next;
    delete s;
    }
}


static void stderrTrace(char* msg)
{
    fputs(msg, stderr);
}

dbTraceFunctionPtr dbTraceFunction = stderrTrace;

void dbTrace(char* message, ...) 
{
    va_list args;
    va_start (args, message);
    char buffer[1024];
    vsprintf(buffer, message, args);
    (*dbTraceFunction)(buffer);
    va_end(args);
}


byte* dbMalloc(size_t size)
{
#ifdef __BORLANDC__ 
    // malloc from Borland runtime library returns value aligned only on 4
    // And GigaBASE needs alignment on maximal size of scalar field = 8
    byte* p = (byte*)malloc(size+8);
    byte* q = (byte*)(DOALIGN((long)p + 4, 8));
    *((int*)q-1) = q - p;
    return q;
#else
    byte* p = (byte*)malloc(size);
    assert(((long)p & 7) == 0);
    return p;
#endif
}

void  dbFree(void* p)
{
    if (p != NULL) { 
#ifdef __BORLANDC__ 
        free((byte*)p - *((int*)p-1));
#else
        free(p);
#endif
    }
}


syntax highlighted by Code2HTML, v. 0.9.1