Icontem

File: lib-js/xbseparser.js

Recommend this page to a friend!
  Classes of Alexey Znaev  >  XBSDB  >  lib-js/xbseparser.js  >  Download  
File: lib-js/xbseparser.js
Role: Class source
Content type: text/plain
Description: Expression parser
Class: XBSDB
Manipulate arrays with an SQL-like language
Author: By
Last change: changed Listing priority
Date: 8 years ago
Size: 32,581 bytes
 

Contents

Class file image Download
///////////////////////////////////////////////////////////////////////////
//
//    XBSDB - Cross-Browser JavaScript Database library
//    Copyright (C) 2010 Alexey A.Znayev
//
//    This file is part of XBSDB.
//
//    This program is free software: you can redistribute it and/or modify
//    it under the terms of the GNU General Public License as published by
//    the Free Software Foundation, either version 3 of the License, or
//    (at your option) any later version.
//
//    This program is distributed in the hope that it will be useful,
//    but WITHOUT ANY WARRANTY; without even the implied warranty of
//    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//    GNU General Public License for more details.
//
//    You should have received a copy of the GNU General Public License
//    along with this program.  If not, see <http://www.gnu.org/licenses/>.
//
//    Alexey A.Znayev, znaeff@mail.ru
//
///////////////////////////////////////////////////////////////////////////

// This flie contains private class(es) and/or method(s) and may be changed any time

function XBSEParser(){
 this.sResultCode = 'OK';
 this.oResult;
}

function XBSEParserResult(){
// types:

// arrays
//array_OR (OR array)
//array_AND (AND array)

// 'WHERE' parsing objects
//object_calc_scalar (scalar calculation object)
//object_calc_logical (logical calculation object) - not done yet
//object_calc_string (string calculation object) - not done yet

// 'KEY' parsing objects
//object_key_scalar (scalar key object)
//object_key_logical (logical key object) - not done yet
//object_key_string (string key object) - not done yet

 this.sType; // element type

 this.aOR;   // if Type == 'array_OR' - array of something binded by OR
 this.aAND;  // if Type == 'array_AND' - array of something binded by AND

 this.sExprFields;  // if Type == 'object_calc_scalar','object_calc_logical','object_key_scalar' - string of expression with field names
 this.sExprFieldsX; // if Type == 'object_calc_scalar','object_calc_logical','object_key_scalar' - string of expression with field names extended by strings before filed name and after one
 this.sConstant;    // if Type == 'object_calc_scalar' - string of constant
 this.sOperation;   // if Type == 'object_calc_scalar' - string of logical operation between sExprFields (left side) and sConstant (right side)
 this.oFields = {}; // if Type == 'object_calc_scalar','object_calc_logical','object_key_scalar' - hash of used field names ('field' : true);
}

XBSEParser.prototype.Parse = function(sExpr,oOptions){
 this.sResultCode = 'OK';
 if(typeof sExpr == 'string' && sExpr != ''){
  this.oResult = new XBSEParserResult();
  this._aFieldNames = [];
  this._sFieldBefore = '';
  this._sFieldAfter  = '';
  this._sExprType    = 'OR';
  this._sExprSource = sExpr;
  this._aPSyn = []; // syntax
  this._oPSem = {}; // semantics
  this._oPCon = {}; // context
  
  if(oOptions && typeof oOptions == 'object'){
   if(Object.prototype.toString.call(oOptions.aFieldNames) === '[object Array]'){
    this._aFieldNames = oOptions.aFieldNames; 
   }
   if(oOptions.sFieldBefore && typeof oOptions.sFieldBefore == 'string'){
    this._sFieldBefore = oOptions.sFieldBefore;
   }
   if(oOptions.sFieldAfter && typeof oOptions.sFieldAfter == 'string'){
    this._sFieldAfter  = oOptions.sFieldAfter;
   }
   if(oOptions.sExprType && typeof oOptions.sExprType == 'string'){
    switch(oOptions.sExprType.toUpperCase()){
     case 'OR' :
     case 'AND' :
     case 'WHERE' :
      this._sExprType  = 'WHERE';
      break;
     case 'KEY':
      this._sExprType  = 'KEY';
      break;
    }
   }
  }
  if(this._ParseSyn()){
   if(this._ParseSem()){
    if(this._ParseCon()){
     this.oResult.sType        = this._oPCon.sType;
     this.oResult.aOR          = this._oPCon.aOR;
     this.oResult.aAND         = this._oPCon.aAND;
     this.oResult.sExprFields  = this._oPCon.sExprFields;
     this.oResult.sExprFieldsX = this._oPCon.sExprFieldsX;
     this.oResult.sConstant    = this._oPCon.sConstant;
     this.oResult.sOperation   = this._oPCon.sOperation;
    }
   }
  }

  delete this._aFieldNames;
  delete this._sFieldBefore;
  delete this._sFieldAfter;
  delete this._sExprSource;
  delete this._aPSyn;
  delete this._oPSem;
  delete this._oPCon;
 }else{
  this.sResultCode = 'PARSER_SYN_EXPR_BAD';
 }

 if(this.sResultCode == 'OK'){
  return this.oResult;
 }else{
  return false;
 }
}

//////////////////////////////////////////////////////////////////////////////////
// syntax

function XBSEPSynItem(Value,sType,nPos,nLevel){
// types: 
//const_string (string constant)
//const_numeric (numeric constant)
//bracket_left (left bracket)
//bracket_right (right bracket)
//oper_comparsion (comparsion operation)
//oper_scalar (scalar operation)
//oper_bind_AND (bind operation AND)
//oper_bind_OR (bind operation OR)
//field (field)
 this.Value  = Value;  // element value (strings in initial quotes!)
 this.sType  = sType;  // element type
 this.nPos   = nPos;   // element position
 this.nLevel = nLevel; // element brackets level
}

XBSEParser.prototype._ParseSyn = function(){
 var rSymbols = /./ig;
 var rOpersComp = /[<>\=\!]/;
 var rOpersScal = /[\+\-\*\/]/;
 var sKav = '';     // current symbol of quote (and flag of string value) (',")
 var nBrackets = 0; // number of open brackets
 var sItemValue = '';
 var sItemType = '';
 var nItemPos = 0;

 var aExpr = this._sExprSource.match(rSymbols), i;

 try{

 for(i = 0; i < aExpr.length; i++){ // solid
  if(sItemType != 'const_string'){ // not string
   if(aExpr[i] == "'" || aExpr[i] == '"'){
    if(sItemValue != ''){
     if(!this._AddPSynItem(sItemValue,sItemType,nItemPos)){
      this.sResultCode = 'PARSER_SYN_ELEMENT_UN';
      break;
     }
    }
    sKav = aExpr[i];
    sItemValue = aExpr[i];
    sItemType = 'const_string';
    nItemPos = i;
   }else if(rOpersComp.test(aExpr[i])){
    if(sItemType == 'oper_comparsion' && aExpr[i] == '=' && sItemValue.length == 1){
     sItemValue += aExpr[i];
    }else{
     if(sItemValue != ''){
      if(!this._AddPSynItem(sItemValue,sItemType,nItemPos)){
       this.sResultCode = 'PARSER_SYN_ELEMENT_UN';
       break;
      }
     }
     sItemValue = aExpr[i];
     sItemType = 'oper_comparsion';
     nItemPos = i;
    }
   }else if(rOpersScal.test(aExpr[i])){
    if(sItemValue != ''){
     if(!this._AddPSynItem(sItemValue,sItemType,nItemPos)){
      this.sResultCode = 'PARSER_SYN_ELEMENT_UN';
      break;
     }
    }
    sItemValue = aExpr[i];
    sItemType = 'oper_scalar';
    nItemPos = i;
   }else if(aExpr[i] == '('){
    if(sItemValue != ''){
     if(!this._AddPSynItem(sItemValue,sItemType,nItemPos)){
      this.sResultCode = 'PARSER_SYN_ELEMENT_UN';
      break;
     }
    }
    sItemValue = aExpr[i];
    sItemType = 'bracket_left';
    nItemPos = i;
    nBrackets ++;
   }else if(aExpr[i] == ')'){
    if(sItemValue != ''){
     if(!this._AddPSynItem(sItemValue,sItemType,nItemPos)){
      this.sResultCode = 'PARSER_SYN_ELEMENT_UN';
      break;
     }
    }
    if(nBrackets > 0){
     sItemValue = aExpr[i];
     sItemType = 'bracket_right';
     nItemPos = i;
     nBrackets --;
    }else{
     this.sResultCode = 'PARSER_SYN_BRACKETS_R';
     break;
    }
   }else if(aExpr[i] == ' '){
    if(sItemValue != ''){
     if(!this._AddPSynItem(sItemValue,sItemType,nItemPos)){
      this.sResultCode = 'PARSER_SYN_ELEMENT_UN';
      break;
     }
    }
    sItemValue = '';
    sItemType = '';
    nItemPos = i;
   }else{
    if(sItemType != ''){
     if(sItemValue != ''){
      if(!this._AddPSynItem(sItemValue,sItemType,nItemPos)){
       this.sResultCode = 'PARSER_SYN_ELEMENT_UN';
       break;
      }
     }
     sItemValue = aExpr[i];
     sItemType = '';
     nItemPos = i;
    }else{
     if(sItemValue != ''){
      sItemValue += aExpr[i];
     }else{
      sItemValue = aExpr[i];
      sItemType = '';
      nItemPos = i;
     }
    }
   }
  }else{
   if(aExpr[i] != sKav){
    sItemValue += aExpr[i];
   }else{
    sItemValue += aExpr[i];
    if(!this._AddPSynItem(sItemValue,sItemType,nItemPos)){
     this.sResultCode = 'PARSER_SYN_ELEMENT_UN';
     break;
    }
    sKav = '';
    sItemValue = '';
    sItemType = '';
    nItemPos = i;
   }
  }
 } //for
 
 if(this.sResultCode == 'OK'){
  if(sKav != ''){
   this.sResultCode = 'PARSER_SYN_EOS_MISSING';
   nItemPos = i;
  }else if(nBrackets != 0){
   this.sResultCode = 'PARSER_SYN_BRACKETS_L';
  }else{
   if(sItemValue != ''){
    if(!this._AddPSynItem(sItemValue,sItemType,nItemPos)){
     this.sResultCode = 'PARSER_SYN_ELEMENT_UN';
    }
   }
  }
 }
 if(this.sResultCode == 'OK'){
  var iPrev2 = -1, iPrev1 = -1;
  for(i = 0; i < this._aPSyn.length; i++){ // negative numbers, solid
   if(this._aPSyn[i]){
    if(iPrev1 >= 0 && i > iPrev1 &&
      this._aPSyn[i].sType == 'const_numeric' &&
      this._aPSyn[iPrev1].sType == 'oper_scalar' && 
      this._aPSyn[iPrev1].Value == '-' && 
      this._aPSyn[i].nPos - this._aPSyn[iPrev1].nPos == 1 &&
      (iPrev2 == -1 || iPrev2 >=0 && (this._aPSyn[iPrev2].sType == 'bracket_left' || this._aPSyn[iPrev2].sType == 'oper_comparsion' || this._aPSyn[iPrev1].nPos - this._aPSyn[iPrev2].nPos - this._aPSyn[iPrev2].Value.length == 1))
    ){
     this._aPSyn[i].Value = '-' + this._aPSyn[i].Value;
     delete this._aPSyn[iPrev1];
     iPrev1 = -1;
    }
    iPrev2 = iPrev1;
    iPrev1 = i;
   }
  } //for
  
  var aBuff = []; // solidization after deleting of negative numbers minuses
  for(i = 0; i < this._aPSyn.length; i++){ // not solid
   if(this._aPSyn[i]){
    aBuff.push(this._aPSyn[i]);
   }
  } //for
  this._aPSyn = aBuff; // solid now

  var nBN1, nBN2, nBQ=0, aBracketsOdd = [], j, k, l;
  for(i = 0; i < this._aPSyn.length; i++){ // brackets levels parsing, solid
   if(this._aPSyn[i]){
    if(this._aPSyn[i].sType == 'bracket_left'){
     nBN1 = i;
     nBQ = 0;
     for(j = i+1; j < this._aPSyn.length; j++){ // solid
      if(this._aPSyn[j].sType == 'bracket_right'){
       if(nBQ != 0){
        nBQ--;
       }else{
        nBN2 = j;
        break;
       }
      }
      if(this._aPSyn[j].sType == 'bracket_left'){
       nBQ++;
      }
     } //for j
     var bIsLevelUp = false;
     for(k = nBN1; k <= nBN2; k++){
      if(this._aPSyn[k].sType == 'oper_bind_AND' || this._aPSyn[k].sType == 'oper_bind_OR'){ // increase level when AND or OR exists only
       bIsLevelUp = true;
       break;
      }
     } //for k
     if(bIsLevelUp){
      for(k = nBN1; k <= nBN2; k++){ // level increasing
       this._aPSyn[k].nLevel++;
      } //for k
     }else{
      aBracketsOdd.push(nBN1); // store redundant brackets
      aBracketsOdd.push(nBN2);
     }
    }
   } //if

   if(aBracketsOdd.length > 0){ // process redundant brackets
    this.sResultCode = 'PARSER_SYN_BRACKETS_ODD PARSER_AT_POS ';
    for(l = 0; l < aBracketsOdd.length; l++){ // solid
     this.sResultCode += this._aPSyn[aBracketsOdd[l]].nPos + ' ';
    }
   }

  } //for i
 } //if

 }catch(e){
  this.sResultCode = 'PARSER_SYN_ERROR_UN';
 }
 
 switch(this.sResultCode){
  case 'PARSER_SYN_ELEMENT_UN' :
   this.sResultCode += ' PARSER_AT_POS ' + nItemPos + ' (' + sItemValue + ')';
   break;
  case 'PARSER_SYN_EOS_MISSING' :
   this.sResultCode += ' PARSER_AT_POS ' + nItemPos;
   break;
  case 'PARSER_SYN_BRACKETS_R' :
   this.sResultCode += ' PARSER_AT_POS ' + nItemPos;
   break;
  case 'PARSER_SYN_BRACKETS_L' :
   this.sResultCode += ' PARSER_AT_POS ' + nItemPos;
   break;
 }
 
 if(this.sResultCode == 'OK'){
  return true;
 }else{
  return false;
 }
}

XBSEParser.prototype._AddPSynItem = function(sItemValue,sItemType,nItemPos){
 if(sItemType == ''){
  var sBuf = sItemValue.toLowerCase(), rNumber = /^\d+(\.\d*)?$/, i;
  for(i = 0; i <  this._aFieldNames.length; i++){ //solid
   if(this._aFieldNames[i]){
    if(sBuf == this._aFieldNames[i]){
     sItemValue = sBuf;
     sItemType = 'field';
     this.oResult.oFields[sItemValue] = true;
     break;
    }
   }
  }//for
  if(sItemType == ''){
   if(sBuf == 'and'){
    sItemValue = sBuf.toUpperCase();
    sItemType = 'oper_bind_AND';
   }else if(sBuf == 'or'){
    sItemValue = sBuf.toUpperCase();
    sItemType = 'oper_bind_OR';
   }else if(rNumber.test(sBuf)){
    sItemType = 'const_numeric';
   }
  }
 }
 if(sItemType == 'oper_comparsion' && sItemValue == '='){
  sItemValue = '==';
 }
 if(sItemType != ''){
  this._aPSyn.push(new XBSEPSynItem(sItemValue,sItemType,nItemPos,0));
  return true;
 }else{
  return false;
 }
}

//////////////////////////////////////////////////////////////////////////////////
// semantics
function XBSEPSemItem(Value,sType){
// types
//array_undef (array of something)
//array_OR (OR array)
//array_AND (AND array)
//array_calculation (comparsion array)
//array_expression (expression array)
//array_expression_fields (expression array with field names)
//array_expression_const (expression array with constants only)
//link2syn (link to syntax array element)
 
 this.Value = Value; // element value (array or number)
 if(sType != ''){
  this.sType = sType; // element type
 }else{
  this.sType = '';
 }
}

XBSEParser.prototype._ParseSem = function(){
 this._oPSemBuf = new XBSEPSemItem([],'array_undef'); // root element XBSEPSemItem, type 'array_undef'

 for(var i = 0; i < this._aPSyn.length; i++){ //solid
  if(this._aPSyn[i]){
   this._oPSemBuf.Value.push(new XBSEPSemItem(i,'link2syn'));
  }
 }
 try{
  switch(this._sExprType){
   case 'WHERE' :
   if(!this._ParseSemBR(this._oPSemBuf)){
    return false;
   }
   this._ParseSemOR(this._oPSemBuf);
   this._ParseSemAND(this._oPSemBuf);
   this._ParseSemComparsion(this._oPSemBuf);
  case 'KEY' :
   this._ParseSemCalculation(this._oPSemBuf);
  }
 }catch(e){
  this.sResultCode = 'PARSER_SEM_ERROR_UN';
 }

 if(this.sResultCode == 'OK'){
  this._oPSem = this._oPSemBuf;
  delete this._oPSemBuf;
  return true;
 }else{
  delete this._oPSemBuf;
  return false;
 }
}

// parsing of arrays with 'array_undef' type  to levels
// solid sequences of array with level upper than given move to nested arrays type 'array_undef'
XBSEParser.prototype._ParseSemBR = function(oPA){
 if(oPA.sType == 'array_undef'){
  var aArrayNested = [], aNewValue = [], nLowestLevel = 65535, i;
  for(i = 0; i < oPA.Value.length; i++){ //lowest level definition, solid
   if(oPA.Value[i] && typeof oPA.Value[i] == 'object' && oPA.Value[i].sType == 'link2syn' && nLowestLevel > this._aPSyn[oPA.Value[i].Value].nLevel){
    nLowestLevel = this._aPSyn[oPA.Value[i].Value].nLevel;
   }
  }//for
  for(i = 0; i < oPA.Value.length; i++){ //moving of upped levels elements to nested arrays except first and last (brackets), solid
   if(oPA.Value[i] && typeof oPA.Value[i] == 'object'){
    if(oPA.Value[i].sType == 'link2syn'){
     if(this._aPSyn[oPA.Value[i].Value].nLevel == nLowestLevel){
      if(aArrayNested.length >= 3){
       aNewValue.push(new XBSEPSemItem(aArrayNested.slice(1,aArrayNested.length-1),'array_undef'));
       aArrayNested = [];
      }else if(aArrayNested.length > 0 && aArrayNested.length < 3){
       this.sResultCode = 'PARSER_SEM_UPPER_ARRAY_MUST_3';
      }
      aNewValue.push(oPA.Value[i]);
     }else{
      aArrayNested.push(oPA.Value[i]);
     }
    }else{
     aNewValue.push(oPA.Value[i]);
    }//if
   }//if
  }//for
  if(aArrayNested.length >= 3){
   aNewValue.push(new XBSEPSemItem(aArrayNested.slice(1,aArrayNested.length-1),'array_undef'));
   aArrayNested = [];
  }else if(aArrayNested.length > 0 && aArrayNested.length < 3){
   this.sResultCode = 'PARSER_SEM_UPPER_ARRAY_MUST_3';
  }
  oPA.Value = aNewValue;
  for(i = 0; i < oPA.Value.length; i++){ //recursive call for nested arrays, solid
   if(oPA.Value[i] && typeof oPA.Value[i] == 'object' && oPA.Value[i].sType == 'array_undef'){
    this._ParseSemBR(oPA.Value[i]);
   }
  }//for
 }//if
 return true;
}

// parsing of arrays with 'array_undef' type to 'oper_bind_OR' (OR) elements
// if array contains 'oper_bind_OR' 
// then its type becomes 'array_OR' and its elements move to nested arrays with 'array_undef' type
// else its type keeps 'array_undef', 'link2syn' elements keep
XBSEParser.prototype._ParseSemOR = function(oPA){
 if(oPA.sType == 'array_undef'){
  var aItemNested, aNewValue = [], i;
  for(i = 0; i < oPA.Value.length; i++){ //type defenition, solid
   if(oPA.Value[i] && typeof oPA.Value[i] == 'object' && oPA.Value[i].sType == 'link2syn' && this._aPSyn[oPA.Value[i].Value].sType == 'oper_bind_OR'){
    oPA.sType = 'array_OR';
    break;
   }
  }//for
  if(oPA.sType == 'array_OR'){ //move elements between 'oper_bind_OR' to nested arrays
   aItemNested = new XBSEPSemItem([],'array_undef');
   for(i = 0; i < oPA.Value.length; i++){ // solid
    if(oPA.Value[i] && typeof oPA.Value[i] == 'object'){
     if(oPA.Value[i].sType == 'link2syn' && this._aPSyn[oPA.Value[i].Value].sType == 'oper_bind_OR'){
      if(aItemNested.Value.length > 0){
       if(aItemNested.Value.length == 1 && (aItemNested.Value[0].sType == 'array_undef' || aItemNested.Value[0].sType == 'array_OR')){
        aNewValue.push(aItemNested.Value[0]);
       }else{
        aNewValue.push(aItemNested);
       }
       aItemNested = new XBSEPSemItem([],'array_undef');
      }
     }else{
      aItemNested.Value.push(oPA.Value[i]);
     }
    }
   }//for
   if(aItemNested.Value.length > 0){
    if(aItemNested.Value.length == 1 && (aItemNested.Value[0].sType == 'array_undef' || aItemNested.Value[0].sType == 'array_OR')){
     aNewValue.push(aItemNested.Value[0]);
    }else{
     aNewValue.push(aItemNested);
    }
   }
   oPA.Value = aNewValue;
  }//if
 }//if
 if(oPA.sType == 'array_undef' || oPA.sType == 'array_OR'){
  for(i = 0; i < oPA.Value.length; i++){ //recursive call for nested arrays, solid
   if(oPA.Value[i] && typeof oPA.Value[i] == 'object' && oPA.Value[i].sType == 'array_undef'){
    this._ParseSemOR(oPA.Value[i]);
   }
  }//for
 }//if
}

// parsing of arrays with 'array_undef' type to 'oper_bind_AND' (AND) elements
// if array contains 'oper_bind_AND' 
// then its type becomes 'array_AND' and its elements move to nested arrays with 'array_undef' type
// else its type keeps 'array_undef', 'link2syn' elements keep
XBSEParser.prototype._ParseSemAND = function(oPA){ 
 if(oPA.sType == 'array_undef'){
  var aItemNested, aNewValue = [], i;
  for(i = 0; i < oPA.Value.length; i++){ //type defenition, solid
   if(oPA.Value[i] && typeof oPA.Value[i] == 'object' && oPA.Value[i].sType == 'link2syn' && this._aPSyn[oPA.Value[i].Value].sType == 'oper_bind_AND'){
    oPA.sType = 'array_AND';
    break;
   }
  }//for
  if(oPA.sType == 'array_AND'){ //move elements between 'oper_bind_AND' to nested arrays
   aItemNested = new XBSEPSemItem([],'array_undef');
   for(i = 0; i < oPA.Value.length; i++){ // solid
    if(oPA.Value[i] && typeof oPA.Value[i] == 'object'){
     if(oPA.Value[i].sType == 'link2syn' && this._aPSyn[oPA.Value[i].Value].sType == 'oper_bind_AND'){
      if(aItemNested.Value.length > 0){
       if(aItemNested.Value.length == 1 && (aItemNested.Value[0].sType == 'array_undef' || aItemNested.Value[0].sType == 'array_OR' || aItemNested.Value[0].sType == 'array_AND')){
        aNewValue.push(aItemNested.Value[0]);
       }else{
        aNewValue.push(aItemNested);
       }
       aItemNested = new XBSEPSemItem([],'array_undef');
      }
     }else{
      aItemNested.Value.push(oPA.Value[i]);
     }
    }
   }//for
   if(aItemNested.Value.length > 0){
    if(aItemNested.Value.length == 1 && (aItemNested.Value[0].sType == 'array_undef' || aItemNested.Value[0].sType == 'array_OR' || aItemNested.Value[0].sType == 'array_AND')){
     aNewValue.push(aItemNested.Value[0]);
    }else{
     aNewValue.push(aItemNested);
    }
   }
   oPA.Value = aNewValue;
  }//if
 }//if
 if(oPA.sType == 'array_undef' || oPA.sType == 'array_OR' || oPA.sType == 'array_AND'){
  for(i = 0; i < oPA.Value.length; i++){ //recursive call for nested arrays, solid
   if(oPA.Value[i] && typeof oPA.Value[i] == 'object' && (oPA.Value[i].sType == 'array_undef' || oPA.Value[i].sType == 'array_OR')){
    this._ParseSemAND(oPA.Value[i]);
   }
  }//for
 }//if
}

// parsing of arrays with 'array_undef' type to 'oper_comparsion'(><!=) elements
// if array contains 'oper_comparsion' 
// then its type becomes 'array_calculation' and its elements between 'oper_comparsion' move to nested arrays with 'array_undef' type
// 'oper_comparsion' element keeps
// else its type keeps 'array_undef', 'link2syn' elements keep
XBSEParser.prototype._ParseSemComparsion = function(oPA){ 
 if(oPA.sType == 'array_undef'){
  var aItemNested, aNewValue = [], i;
  for(i = 0; i < oPA.Value.length; i++){ //type defenition, solid
   if(oPA.Value[i] && typeof oPA.Value[i] == 'object' && oPA.Value[i].sType == 'link2syn' && this._aPSyn[oPA.Value[i].Value].sType == 'oper_comparsion'){
    oPA.sType = 'array_calculation';
    break;
   }
  }//for
  if(oPA.sType == 'array_calculation'){ //move elements between 'oper_comparsion' to nested 'array_undef' arrays
   aItemNested = new XBSEPSemItem([],'array_undef');
   for(i = 0; i < oPA.Value.length; i++){// solid
    if(oPA.Value[i] && typeof oPA.Value[i] == 'object'){
     if(oPA.Value[i].sType == 'link2syn' && this._aPSyn[oPA.Value[i].Value].sType == 'oper_comparsion'){
       aNewValue.push(aItemNested);
       aNewValue.push(oPA.Value[i]);
       aItemNested = new XBSEPSemItem([],'array_undef');
     }else{
       aItemNested.Value.push(oPA.Value[i]);
     }
    }
   }//for
   if(aItemNested.Value.length > 0){
     aNewValue.push(aItemNested);
   }
   oPA.Value = aNewValue;
  }//if
 }//if
 if(oPA.sType == 'array_undef' || oPA.sType == 'array_OR' || oPA.sType == 'array_AND' || oPA.sType == 'array_calculation'){
  for(i = 0; i < oPA.Value.length; i++){ //recursive call for nested arrays, solid
   if(oPA.Value[i] && typeof oPA.Value[i] == 'object' && (oPA.Value[i].sType == 'array_undef' || oPA.Value[i].sType == 'array_OR' || oPA.Value[i].sType == 'array_AND')){
    this._ParseSemComparsion(oPA.Value[i]);
   }
  }//for
 }//if
}

// parsing of arrays with 'array_undef' type to 'field', 'const_numeric', 'const_string' elements
// if array contains one 'field' 
// then its type becomes 'array_expression_fields'
// else its type becomes 'array_expression_const'
XBSEParser.prototype._ParseSemCalculation = function(oPA){
 if(oPA.sType == 'array_undef'){ //type defenition
  var oTypes = {}, i;
  for(i = 0; i < oPA.Value.length; i++){ //store all presented types to hash, solid
   if(oPA.Value[i] && typeof oPA.Value[i] == 'object'){
    if(oPA.Value[i].sType == 'link2syn'){ 
     oTypes[this._aPSyn[oPA.Value[i].Value].sType] = true;
    }else{
     oTypes[oPA.Value[i].sType] = true;
    }
   }
  }//for

  delete oTypes['bracket_left']; // check of types, allowed in key expression 
  delete oTypes['bracket_right'];
  delete oTypes['oper_scalar'];

  if(oTypes['const_string']){
   oPA.sType = 'array_expression_const';
   delete oTypes['const_string'];
  }
  if(oTypes['const_numeric']){
   oPA.sType = 'array_expression_const';
   delete oTypes['const_numeric'];
  }
  if(oTypes['field'] ){
   oPA.sType = 'array_expression_fields';
   delete oTypes['field'];
  }

  var bPACorrect = true; // is hash empty (more elegant solution?)
  for(i in oTypes){
   bPACorrect = false;
  }
  if(!bPACorrect){
   this.sResultCode = 'PARSER_SEM_KEY_EXPR_ITEM_UN : ' + oTypes.toString();
  }
 }
 if(oPA.sType == 'array_undef' || oPA.sType == 'array_OR' || oPA.sType == 'array_AND' || oPA.sType == 'array_calculation'){
  for(i = 0; i < oPA.Value.length; i++){ //recursive call for nested arrays, solid
   if(oPA.Value[i] && typeof oPA.Value[i] == 'object' && (oPA.Value[i].sType == 'array_undef' || oPA.Value[i].sType == 'array_OR' || oPA.Value[i].sType == 'array_AND' || oPA.Value[i].sType == 'array_calculation')){
    this._ParseSemCalculation(oPA.Value[i]);
   }
  }//for
 }//if
}

//////////////////////////////////////////////////////////////////////////////////
// context
function XBSEPConItem(sType){
// types:
// arrays
//array_OR (OR array)
//array_AND (AND array)
// WHERE parsing objects
//object_calc_scalar (scalar calculation object)
//object_calc_logical (logical calculation object) - not done yet
//object_calc_string (string calculation object) - not done yet
// KEY parsing objects
//object_key_scalar (scalar key object)
//object_key_logical (logical key object) - not done yet
//object_key_string (string key object) - not done yet

 if(sType != ''){
  this.sType = sType; // element type
 }else{
  this.sType = '';
 }
 this.aOR;
 this.aAND;

 this.sExprFields;
 this.sExprFieldsX;
 this.sConstant;
 this.sOperation;
}

XBSEParser.prototype._ParseCon = function(){
 this._oPConBuf = new XBSEPConItem(); // root XBSEPConItem element

 try{
  switch(this._sExprType){
  case 'WHERE' :
   this._ParseConAOAA(this._oPConBuf,this._oPSem);
   break;
  case 'KEY' :
   this._oPConBuf.sType = 'object_key_scalar';
   this._oPConBuf.sExprFields = '';
   this._oPConBuf.sExprFieldsX = '';
   this._ParseConAEF(this._oPConBuf,this._oPSem);
  }
 }catch(e){
  this.sResultCode = 'PARSER_CON_ERROR_UN';
 }

 if(this.sResultCode == 'OK'){
  this._oPCon = this._oPConBuf;
  delete this._oPConBuf;
  return true;
 }else{
  delete this._oPConBuf;
  return false;
 }
}

XBSEParser.prototype._ParseConAOAA = function(oRes,oPA){
// oRes has no type, determine here
// if array type is array_OR or array_AND
// then one cycle thru oPA.Value elements
//  for array_AND and array_OR elements : create untyped aCon elements, then recursive call for them
//  for array_calculation elements : create 'object_calc_scalar' aCon elements and fill them from 
//   array_expression_fields, array_expression_const and link2syn oPA-elements
 if(oPA.sType == 'array_OR' || oPA.sType == 'array_AND'){
  var aNewValue = [], oNewResItem, i;
  oRes.sType = oPA.sType;
  for(i = 0; i < oPA.Value.length; i++){// solid
   if(oPA.Value[i] && typeof oPA.Value[i] == 'object'){
    oNewResItem = new XBSEPConItem();
    if(oPA.Value[i].sType == 'array_calculation'){
     if(!this._ParseConAC(oNewResItem,oPA.Value[i])){
      return false;
     }
    }else if(oPA.Value[i].sType == 'array_OR' || oPA.Value[i].sType == 'array_AND'){
     if(!this._ParseConAOAA(oNewResItem,oPA.Value[i])){
      return false;
     }
    }else if(oPA.Value[i].sType == 'array_undef'){
     this.sResultCode = 'PARSER_CON_ARRAY_UN : ' + oPA.sType;
    }else{
     this.sResultCode = 'PARSER_CON_ELEMENT_UN : ' + oPA.sType;
    }
    aNewValue.push(oNewResItem);
   }
  }//for
  if(oPA.sType == 'array_OR'){
   oRes.aOR = aNewValue;
  }else{
   oRes.aAND = aNewValue;
  }
 }else if(oPA.sType == 'array_calculation'){
  if(!this._ParseConAC(oRes,oPA)){
   return false;
  }
 }else if(oPA.sType == 'array_undef'){
  this.sResultCode = 'PARSER_CON_ARRAY_UN : ' + oPA.sType;
 }else{
  this.sResultCode = 'PARSER_CON_ELEMENT_UN : ' + oPA.sType;
 }

 if(this.sResultCode == 'OK'){
  return true;
 }else{
  return false;
 }
}

XBSEParser.prototype._ParseConAC = function(oRes,oPA){
// ac (calculation array) parsing: array consists of field(s) object and constant obect divided by operation
 if(oPA.Value.length == 3){
  if(oPA.Value[1].sType == 'link2syn' && this._aPSyn[oPA.Value[1].Value].sType == 'oper_comparsion'){
   if(oPA.Value[0].sType == 'array_expression_fields' && oPA.Value[2].sType == 'array_expression_const' || oPA.Value[0].sType == 'array_expression_const' && oPA.Value[2].sType == 'array_expression_fields'){
    oRes.sType = 'object_calc_scalar';
    oRes.sExprFields = '';
    oRes.sExprFieldsX = '';
    oRes.sConstant = '';
    oRes.sOperation = this._aPSyn[oPA.Value[1].Value].Value;
    var aExprFields, aConstant;
    if(oPA.Value[0].sType == 'array_expression_fields'){
     aExprFields = oPA.Value[0];
     aConstant = oPA.Value[2];
    }else{
     aConstant = oPA.Value[0];
     aExprFields = oPA.Value[2];
     var rLess = /</, rMore = />/;
     if(rLess.test(oRes.sOperation)){
      oRes.sOperation = oRes.sOperation.replace(rLess, '>');
     }else if(rMore.test(oRes.sOperation)){
      oRes.sOperation = oRes.sOperation.replace(rMore, '<');
     }
    }
    if(!this._ParseConAEF(oRes,aExprFields)){
     return false;
    }
    for(var j = 0; j < aConstant.Value.length; j++){ // solid
     if(aConstant.Value[j] && typeof aConstant.Value[j] == 'object'){
      if(oRes.sConstant != ''){
       oRes.sConstant += ' ';
      }
      oRes.sConstant += this._aPSyn[aConstant.Value[j].Value].Value;
     }
    }//for
   }else if(oPA.Value[0].sType == 'array_expression_fields' && oPA.Value[2].sType == 'array_expression_fields'){
    oRes.sType = 'object_calc_logical';
    oRes.sExprFields = '';
    oRes.sExprFieldsX = '';
    if(!this._ParseConAEF(oRes,oPA)){
     return false;
    }
   }else{
    this.sResultCode = 'PARSER_CON_ARRAY_AC_NO_FIELDS';
   }
  }else{
   this.sResultCode = 'PARSER_CON_ARRAY_AC_2ND_NOT_LOGIC';
  }
 }else{
  this.sResultCode = 'PARSER_CON_ARRAY_AC_MUST_HAVE_3_EL';
 }

 if(this.sResultCode == 'OK'){
  return true;
 }else{
  return false;
 }
}

XBSEParser.prototype._ParseConAEF = function(oRes,oPA){
 var j;
 switch(oPA.sType){
  case 'array_expression_fields' :
//some checking needed mayby ?
   for(j = 0; j < oPA.Value.length; j++){ // solid
    if(oPA.Value[j] && typeof oPA.Value[j] == 'object'){
        if(oRes.sExprFields != ''){
         oRes.sExprFields += ' ';
        }
        oRes.sExprFields += this._aPSyn[oPA.Value[j].Value].Value;

        if(oRes.sExprFieldsX != ''){
         oRes.sExprFieldsX += ' ';
        }
        if(this._aPSyn[oPA.Value[j].Value].sType == 'field'){
         oRes.sExprFieldsX += this._sFieldBefore;
        }
        oRes.sExprFieldsX += this._aPSyn[oPA.Value[j].Value].Value;
        if(this._aPSyn[oPA.Value[j].Value].sType == 'field'){
         oRes.sExprFieldsX += this._sFieldAfter;
        }
    }
   } //for
   break;
  case 'array_calculation' :
   for(j = 0; j < oPA.Value[0].Value.length; j++){ // solid
    if(oPA.Value[0].Value[j] && typeof oPA.Value[0].Value[j] == 'object'){
        if(oRes.sExprFields != ''){
         oRes.sExprFields += ' ';
        }
        oRes.sExprFields += this._aPSyn[oPA.Value[0].Value[j].Value].Value;

        if(oRes.sExprFieldsX != ''){
         oRes.sExprFieldsX += ' ';
        }
        if(this._aPSyn[oPA.Value[0].Value[j].Value].sType == 'field'){
         oRes.sExprFieldsX += this._sFieldBefore;
        }
        oRes.sExprFieldsX += this._aPSyn[oPA.Value[0].Value[j].Value].Value;
        if(this._aPSyn[oPA.Value[0].Value[j].Value].sType == 'field'){
         oRes.sExprFieldsX += this._sFieldAfter;
        }
    }
   } //for
   oRes.sExprFields += ' ' + this._aPSyn[oPA.Value[1].Value].Value;
   oRes.sExprFieldsX += ' ' + this._aPSyn[oPA.Value[1].Value].Value;
   for(j = 0; j < oPA.Value[2].Value.length; j++){ // solid
    if(oPA.Value[2].Value[j] && typeof oPA.Value[2].Value[j] == 'object'){
        if(oRes.sExprFields != ''){
         oRes.sExprFields += ' ';
        }
        oRes.sExprFields += this._aPSyn[oPA.Value[2].Value[j].Value].Value;

        if(oRes.sExprFieldsX != ''){
         oRes.sExprFieldsX += ' ';
        }
        if(this._aPSyn[oPA.Value[2].Value[j].Value].sType == 'field'){
         oRes.sExprFieldsX += this._sFieldBefore;
        }
        oRes.sExprFieldsX += this._aPSyn[oPA.Value[2].Value[j].Value].Value;
        if(this._aPSyn[oPA.Value[2].Value[j].Value].sType == 'field'){
         oRes.sExprFieldsX += this._sFieldAfter;
        }
    }
   } //for
  default :
   this.sResultCode = 'PARSER_CON_ARRAY_UN : ' + oPA.sType;
 }

 if(this.sResultCode == 'OK'){
  return true;
 }else{
  return false;
 }
}