/* xbexp.cpp
XBase64 Software Library
Copyright (c) 1997,2003,2014,2017,2021,2022 Gary A Kunkel
The xb64 software library is covered under
the terms of the GPL Version 3, 2007 license.
Email Contact:
XDB-devel@lists.sourceforge.net
XDB-users@lists.sourceforge.net
This module is part of the expression logic and has the code
for parsing various tokens out of an expression
*/
#include "xbase.h"
#ifdef XB_EXPRESSION_SUPPORT
namespace xb{
/*************************************************************************/
//! Constructor
/*!
\param x Pointer to xbXBase instance.
*/
xbExp::xbExp( xbXBase *x ){
xbase = x;
dbf = NULL;
nTree = NULL;
}
/*************************************************************************/
//! Constructor
/*!
\param x Pointer to xbXBase instance.
\param d Pointer to xbDbf instance.
*/
xbExp::xbExp( xbXBase *x, xbDbf *d ){
xbase = x;
dbf = d;
nTree = NULL;
}
/*************************************************************************/
//! Deconstrucor.
xbExp::~xbExp() {
if( nTree )
delete nTree;
}
/*************************************************************************/
//! Calulate expression return length
/*!
This function returns the maximum possible length of an expression
The create index functions use this for determining the fixed length keys
It sets the return length field in the node.
\param n Start node
\returns Return Codes
*/
xbInt16 xbExp::CalcFunctionResultLen( xbExpNode * n ) const{
xbInt16 iRc = XB_NO_ERROR;
xbInt16 iErrorStop = 0;
xbInt16 iReturnLenCalc = 0;;
xbInt32 lReturnLenVal = 0;
xbString sNodeText;
try{
n->GetNodeText( sNodeText );
char cReturnType = 0;
if(( iRc = xbase->GetFunctionInfo( sNodeText, cReturnType, iReturnLenCalc, lReturnLenVal )) != XB_NO_ERROR ){
iErrorStop = 100;
throw iRc;
}
if( iReturnLenCalc == 1 ){
// use the value from iReturnLenVal
n->SetResultLen( (xbUInt32) lReturnLenVal );
}
else if( iReturnLenCalc == 2 ){
// use the length from the child node identified in lReturnLenVal
xbExpNode *nChild = n->GetChild( (xbUInt32) lReturnLenVal - 1 );
if( !nChild ){
iErrorStop = 110;
iRc = XB_PARSE_ERROR;
throw iRc;
}
n->SetResultLen( nChild->GetResultLen());
}
else if( iReturnLenCalc == 3 ){
// use the length from the child node identified in lReturnLenVal
xbExpNode *nChild = n->GetChild( (xbUInt32) lReturnLenVal - 1 );
if( !nChild ){
iErrorStop = 120;
iRc = XB_PARSE_ERROR;
throw iRc;
}
n->SetResultLen( (xbUInt32) nChild->GetNumericResult());
}
else if( iReturnLenCalc == 4 ){
// use the value from the length in parm 1 multiplied by the value in parm 2 (REPLICATE)
xbExpNode *nChild1 = n->GetChild( 0 );
xbExpNode *nChild2 = n->GetChild( 1 );
if( !nChild1 || !nChild2 ){
iErrorStop = 130;
iRc = XB_PARSE_ERROR;
throw iRc;
}
n->SetResultLen( nChild1->GetResultLen() * (xbUInt32) nChild2->GetNumericResult());
}
else if( iReturnLenCalc == 5 ){
// use the larger of the length of the value in parm2 or parm 3 (IIF statement)
xbExpNode *nChild2 = n->GetChild( 1 );
xbExpNode *nChild3 = n->GetChild( 2 );
if( !nChild2 || !nChild3 ){
iErrorStop = 140;
iRc = XB_PARSE_ERROR;
throw iRc;
}
if( nChild2->GetResultLen() >= nChild3->GetResultLen())
n->SetResultLen( nChild2->GetResultLen());
else
n->SetResultLen( nChild3->GetResultLen());
}
else if( iReturnLenCalc == 6 ){
if( n->GetChildCnt() >= 2 ){
xbExpNode *nChild2 = n->GetChild( 1 );
n->SetResultLen( (xbUInt32) nChild2->GetNumericResult());
} else {
n->SetResultLen( (xbUInt32) lReturnLenVal );
}
} else {
iErrorStop = 150;
iRc = XB_PARSE_ERROR;
throw iRc;
}
}
catch (xbInt16 iRc ){
xbString sMsg;
sMsg.Sprintf( "xbexp::GetFunctionResultLen() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc );
xbase->WriteLogMessage( sMsg.Str() );
}
return iRc;
}
/*************************************************************************/
//! Check parens and quotes
/*!
This routine looks for unbalanced parens and quotes
\param sExpression Expression to examine.
\returns Return Codes
*/
xbInt16 xbExp::CheckParensAndQuotes( const xbString &sExpression ){
xbInt16 iRc = XB_NO_ERROR;
xbInt16 iErrorStop = 0;
xbBool bInQuotes = xbFalse;
xbInt16 iLparenCtr = 0;
xbInt16 iRparenCtr = 0;
xbInt16 iQuoteType = 0;
const char *s = sExpression;
try{
while( *s ){
if( !bInQuotes ){
if( *s == '(' ){
iLparenCtr++;
} else if( *s == ')' ){
iRparenCtr++;
} else if( *s == '\'' ){
bInQuotes++;
iQuoteType = 0;
} else if( *s == '"' ){
bInQuotes++;
iQuoteType = 1;
}
} else {
if(( *s == '\'' && iQuoteType == 0 ) || (*s == '"' && iQuoteType == 1 ))
bInQuotes--;
}
s++;
}
if( iLparenCtr != iRparenCtr ){
iErrorStop = 100;
iRc = XB_UNBALANCED_PARENS;
throw iRc;
}
if( bInQuotes ){
iErrorStop = 110;
iRc = XB_UNBALANCED_QUOTES;
throw iRc;
}
}
catch (xbInt16 iRc ){
xbString sMsg;
sMsg.Sprintf( "xbexp::CheckParensAndQuots() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc );
xbase->WriteLogMessage( sMsg.Str() );
xbase->WriteLogMessage( sExpression );
}
return iRc;
}
/*************************************************************************/
//! Clear tree handle.
/*!
This routine clears the expression tree and frees any associated memory.
\returns void.
*/
void xbExp::ClearTreeHandle(){
if( nTree ){
nTree = NULL;
}
}
/*************************************************************************/
#ifdef XB_DEBUG_SUPPORT
//! Dump the tree.
/*!
\param iOption - Output opton.
\returns void.
*/
void xbExp::DumpTree( xbInt16 iOption ){
nTree->DumpNode( iOption );
}
//! Dump token
/*!
\param iOption - Output opton.
\returns void.
*/
void xbExp::DumpToken( xbExpToken &t, xbInt16 iOption ){
xbString sMsg;
sMsg = "Processing Token";
xbase->WriteLogMessage( sMsg.Str(), iOption );
sMsg.Sprintf( "Expression = [%s]", t.sExpression.Str());
xbase->WriteLogMessage( sMsg.Str(), iOption );
sMsg.Sprintf( "Token = [%s]", t.sToken.Str());
xbase->WriteLogMessage( sMsg.Str(), iOption );
sMsg.Sprintf( "NodeType = [%c]", t.cNodeType );
xbase->WriteLogMessage( sMsg.Str(), iOption );
sMsg.Sprintf( "ReturnType = [%c]", t.cReturnType );
xbase->WriteLogMessage( sMsg.Str(), iOption );
sMsg.Sprintf( "Sts = [%d]", t.iSts );
xbase->WriteLogMessage( sMsg.Str(), iOption );
sMsg.Sprintf( "PrevNodeType = [%c]", t.cPrevNodeType );
xbase->WriteLogMessage( sMsg.Str(), iOption );
sMsg.Sprintf( "PrevReturnType = [%c]", t.cPrevReturnType );
xbase->WriteLogMessage( sMsg.Str(), iOption );
}
#endif
/*************************************************************************/
//! Get date result.
/*!
If the expression generates a date return type, this method retrieves the date value.
\param dtResult - Output date value.
\returns Return Codes
*/
xbInt16 xbExp::GetDateResult( xbDate &dtResult ){
if( nTree ){
dtResult.JulToDate8( (xbInt32) nTree->GetNumericResult() );
return XB_NO_ERROR;
}
else{
//dtResult = ?;
return XB_PARSE_ERROR;
}
}
/*************************************************************************/
//! Get bool result.
/*!
If the expression generates a boolean return type, this method retrieves the boolean value.
\param bResult - Output boolean value.
\returns Return Codes
*/
xbInt16 xbExp::GetBoolResult( xbBool &bResult){
if( nTree ){
bResult = nTree->GetBoolResult();
return XB_NO_ERROR;
}
else{
return XB_PARSE_ERROR;
}
}
/*************************************************************************/
//! Get the next node in the tree.
/*!
\param n Node to starting point. To get the first node of the entire tree, set n = NULL
\returns Pointer to next node.
*/
xbExpNode *xbExp::GetNextNode( xbExpNode * n ) const {
// to get the first node of the entire tree, set n = NULL
// std::cout << "In GetNextNode\n";
if( n == nTree )
return NULL;
else if( !n ){
if( !nTree )
return NULL;
else
return nTree->GetFirstNode();
}
return n->GetNextNode();
}
/*************************************************************************/
//! GetNextToken
/*! This method returns the next token in an expression of one or more tokens
\param t Token
\returns Return Codes
*/
xbInt16 xbExp::GetNextToken( xbExpToken &t ){
xbInt16 iRc = XB_NO_ERROR;
xbInt16 iErrorStop = 0;
try{
t.iSts = XB_NO_ERROR;
t.sExpression.Ltrim();
if( t.sExpression.Len() == 0 ){
t.iSts = XB_END_OF_EXPRESSION;
return XB_NO_ERROR;
}
// Check for date constant
if((t.sExpression.Len() >= 10 && t.sExpression[1] == '{' && t.sExpression[4] == '/' && t.sExpression[7] == '/') &&
(t.sExpression[10] == '}' || (t.sExpression.Len() >= 12 && t.sExpression[12] == '}'))){
if(( iRc = GetTokenDateConstant( t )) != XB_NO_ERROR ){
iErrorStop = 100;
throw iRc;
}
}
// Check for parens
else if( t.sExpression[1] == '(' || t.sExpression[1] == '{' ){
if(( iRc = GetTokenParen( t )) != XB_NO_ERROR ){
iErrorStop = 110;
throw iRc;
}
}
// Check for a char constant
else if( t.sExpression[1] == '"' || t.sExpression[1] == '\'' ){
if(( iRc = GetTokenCharConstant( t )) != XB_NO_ERROR ){
iErrorStop = 120;
iRc = XB_PARSE_ERROR;
throw iRc;
}
}
// Check for logical constant
else if( IsLogicalConstant( t.sExpression )){
if(( iRc = GetTokenLogicalConstant( t )) != XB_NO_ERROR ){
iErrorStop = 130;
iRc = XB_PARSE_ERROR;
throw iRc;
}
}
// check for numeric constant
else if( IsNumericConstant( t.sExpression, t.cPrevNodeType )){
if(( iRc = GetTokenNumericConstant( t )) != XB_NO_ERROR ){
iErrorStop = 140;
iRc = XB_PARSE_ERROR;
throw iRc;
}
}
// check for operator
else if( IsOperator( t.sExpression )){
if(( iRc = GetTokenOperator( t )) != XB_NO_ERROR ){
iErrorStop = 150;
iRc = XB_PARSE_ERROR;
throw iRc;
}
}
// check for function
else if( IsFunction( t.sExpression, t.cReturnType )){
if(( iRc = GetTokenFunction( t )) != XB_NO_ERROR ){
iErrorStop = 160;
iRc = XB_PARSE_ERROR;
throw iRc;
}
}
else if(( iRc = GetTokenDatabaseField( t )) != XB_NO_ERROR ){
iErrorStop = 170;
iRc = XB_PARSE_ERROR;
throw iRc;
}
}
catch (xbInt16 iRc ){
xbString sMsg;
sMsg.Sprintf( "xbexp::GetNextToken() Exception Caught. Error Stop = [%d] iRc = [%d] Expression = [%s]", iErrorStop, iRc, t.sExpression.Str() );sMsg.Sprintf( "xbexp::GetNextToken() Exception Caught. Error Stop = [%d] iRc = [%d] Expression = [%s]", iErrorStop, iRc, t.sExpression.Str() );sMsg.Sprintf( "xbexp::GetNextToken() Exception Caught. Error Stop = [%d] iRc = [%d] Expression = [%s]", iErrorStop, iRc, t.sExpression.Str() );sMsg.Sprintf( "xbexp::GetNextToken() Exception Caught. Error Stop = [%d] iRc = [%d] Expression = [%s]", iErrorStop, iRc, t.sExpression.Str() );
xbase->WriteLogMessage( sMsg.Str() );
}
return iRc;
}
/*************************************************************************/
//! Get numeric result.
/*!
If the expression generates a numeric return type, this method retrieves the numeric value.
\param dResult - Output numeric value.
\returns Return Codes
*/
xbInt16 xbExp::GetNumericResult( xbDouble &dResult){
if( nTree ){
dResult = nTree->GetNumericResult();
return XB_NO_ERROR;
}
else{
dResult = 0;
return XB_PARSE_ERROR;
}
}
/*************************************************************************/
//! Get result length.
/*!
This routine returns the result length.
\returns Result length.
*/
xbInt16 xbExp::GetResultLen() const{
if( nTree )
return nTree->GetResultLen();
else
return 0;
}
/*************************************************************************/
//! Get return type.
/*!
\returns Expression return type.
*/
char xbExp::GetReturnType() const{
if( nTree )
return nTree->GetReturnType();
else
return ' ';
}
/*************************************************************************/
//! Get string result.
/*!
If the expression generates a string return type, this method retrieves the string value.
\param sResult - Output string value.
\returns Return Codes
*/
xbInt16 xbExp::GetStringResult( xbString &sResult){
if( nTree ){
sResult = nTree->GetStringResult();
return XB_NO_ERROR;
}
else{
sResult = "";
return XB_PARSE_ERROR;
}
}
/*************************************************************************/
//! Get string result.
/*!
If the expression generates a string return type, this method retrieves the string value.
\param vpResult - Pointer to user supplied buffer for result.
\param ulLen - Max size of buffer.
\returns Return Codes
*/
xbInt16 xbExp::GetStringResult( char * vpResult, xbUInt32 ulLen ){
if( nTree ){
nTree->GetStringResult().strncpy((char *) vpResult, ulLen );
return XB_NO_ERROR;
}
else{
return XB_PARSE_ERROR;
}
}
/*************************************************************************/
//! GetTokenCharConstant
/*! This method returns the character constant in a pair of quotes
This routine returns the tokens inside a set of matching quotes in sOutToken
If there is nothing between the quotes then sOutToken is returned empty
sOutRemainder contains whatever remains to the right of the right quote
\param t Token
\returns Return Codes
*/
xbInt16 xbExp::GetTokenCharConstant( xbExpToken &t ){
const char *s = t.sExpression;
const char *sToken; // pointer to beginning of token
xbInt16 iQuoteType;
xbUInt32 ulTokenLen = 0;
xbInt16 iRc = XB_NO_ERROR;
xbInt16 iErrorStop = 0;
xbBool bDone = xbFalse;
try{
if( *s == '"' )
iQuoteType = 0;
else
iQuoteType = 1;
s++;
sToken = s;
while( *s && !bDone ){
if(( *s == '"' && iQuoteType == 0 ) || (*s == '\'' && iQuoteType == 1 ))
bDone = xbTrue;
s++;
ulTokenLen++;
}
if( bDone ){ // found matching paren
t.cNodeType = XB_EXP_CONSTANT;
t.cReturnType = XB_EXP_CHAR;
t.sToken.Set( sToken, ulTokenLen - 1 );
t.sExpression.Ltrunc( ulTokenLen + 1 );
} else {
iRc = XB_PARSE_ERROR;
t.iSts = XB_UNBALANCED_QUOTES;
iErrorStop = 100;
throw iRc;
}
}
catch (xbInt16 iRc ){
xbString sMsg;
sMsg.Sprintf( "xbexp::GetTokenCharConstant() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc );
xbase->WriteLogMessage( sMsg.Str() );
}
return iRc;
}
/*************************************************************************/
//! GetTokenDateConstant
/*! This method returns the date constant in a pair of {}
Date format is one of {mm/dd/yy} or {mm/dd/yyyy}
\param t Token.
\returns Return Codes
*/
xbInt16 xbExp::GetTokenDateConstant( xbExpToken &t ){
xbInt16 iRc = XB_NO_ERROR;
xbInt16 iErrorStop = 0;
char wBuf[13];
xbDate dt;
try{
memset( wBuf, 0x00, 13 );
t.cNodeType = XB_EXP_CONSTANT;
t.cReturnType = XB_EXP_DATE;
if( t.sExpression[10] == '}' ){
for( xbInt16 i = 0; i < 8; i++ )
wBuf[i] = t.sExpression[i+2];
if(( iRc = dt.CTOD( wBuf )) != XB_NO_ERROR ){
iErrorStop = 100;
throw iRc;
}
t.sToken.Set( dt.Str() );
t.sExpression.Ltrunc( 10 );
} else if( t.sExpression[12] == '}' ){
wBuf[0] = t.sExpression[8];
wBuf[1] = t.sExpression[9];
wBuf[2] = t.sExpression[10];
wBuf[3] = t.sExpression[11];
wBuf[4] = t.sExpression[2];
wBuf[5] = t.sExpression[3];
wBuf[6] = t.sExpression[5];
wBuf[7] = t.sExpression[6];
t.sToken.Set( wBuf );
t.sExpression.Ltrunc( 12 );
} else {
iRc = XB_PARSE_ERROR;
t.iSts = XB_INVALID_DATE;
iErrorStop = 110;
throw iRc;
}
}
catch (xbInt16 iRc ){
xbString sMsg;
sMsg.Sprintf( "xbexp::GetTokenDateConstant() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc );
xbase->WriteLogMessage( sMsg.Str() );
}
return iRc;
}
/*************************************************************************/
//! GetTokenField
/*! This method gets a database field token
Looks for a xbase field in one of the following formats
FIELDNAME
or
TABLENAME->FIELDNAME
\param t Token.
\returns Return Codes
*/
xbInt16 xbExp::GetTokenDatabaseField( xbExpToken &t ){
const char *s = t.sExpression;
xbUInt32 ulTokenLen = 0;
xbUInt32 ulTokenLen2 = 0;
while( *s && !IsTokenSeparator( *s ) && !IsWhiteSpace( *s )) {
ulTokenLen++;
s++;
}
// go past any white space
while( *s && !IsTokenSeparator( *s ) && IsWhiteSpace( *s )) {
ulTokenLen2++;
s++;
}
// look for ->
// remove the table name from before the ->
if( strncmp( s, "->", 2 ) == 0 ){
ulTokenLen2+=2;
s+=2;
/*
if( strncmp( s, "->", 2 ) == 0 || strncmp( s, ".", 1 ) == 0){
if( *s == '.' ){
ulTokenLen2+=1;
s+=1;
} else {
ulTokenLen2+=2;
s+=2;
}
*/
// go past white space
while( *s && !IsTokenSeparator( *s ) && IsWhiteSpace( *s )) {
ulTokenLen2++;
s++;
}
// go to the end
while( *s && !IsTokenSeparator( *s ) && !IsWhiteSpace( *s )) {
ulTokenLen2++;
s++;
}
ulTokenLen += ulTokenLen2;
}
t.cNodeType = XB_EXP_FIELD;
t.cReturnType = XB_EXP_UNKNOWN;
t.sToken.Set( t.sExpression, ulTokenLen );
t.sExpression.Ltrunc( ulTokenLen );
return XB_NO_ERROR;
}
/*************************************************************************/
//! GetTokenFunction
/*!
This method gets a function and everything between the following quotes
\param t Token
\returns Return Codes
*/
xbInt16 xbExp::GetTokenFunction( xbExpToken &t ){
xbUInt32 lPos = t.sExpression.Pos( '(' );
if( lPos == 0 )
return XB_PARSE_ERROR;
xbBool bDone = xbFalse;
xbUInt32 lLen = t.sExpression.Len();
xbInt16 iDepthCtr = 1;
while( ++lPos <= lLen && !bDone ){
if( t.sExpression[lPos] == ')' ){
iDepthCtr--;
if( iDepthCtr == 0 )
bDone = xbTrue;
} else if( t.sExpression[lPos] == '(' ){
iDepthCtr++;
}
}
t.cNodeType = XB_EXP_FUNCTION;
t.sToken.Set( t.sExpression, lPos-1 );
t.sExpression.Ltrunc( lPos-1 );
// std::cout << "lPos = [" << lPos << "] done= [" << bDone << "][" << t.sExpression << "] len=[" << lLen << "] return type = [" << t.cReturnType << "]\n";
return XB_NO_ERROR;
}
/*************************************************************************/
//! GetTokenCharConstant
/*! This method returns the character constant in a pair of quotes
This routine returns the tokens inside a set of matching quotes in sOutToken
If there is nothing between the quotes then sOutToken is returned empty
sOutRemainder contains whatever remains to the right of the right quote
\param t Token
\returns Return Codes
*/
xbInt16 xbExp::GetTokenLogicalConstant( xbExpToken &t ){
t.cNodeType = XB_EXP_CONSTANT;
t.cReturnType = XB_EXP_LOGICAL;
t.sToken = t.sExpression[2];
if( t.sExpression[3] == '.' )
t.sExpression.Ltrunc( 3 );
else if( t.sExpression[6] == '.' )
t.sExpression.Ltrunc( 6 );
else if( t.sExpression[7] == '.' )
t.sExpression.Ltrunc( 7 );
return XB_NO_ERROR;
}
/*************************************************************************/
//! GetTokenNumericConstant
/*! This method returns a numeric constant in
This routine returns a numeric constant token
sOutRemainder contains whatever remains to the right of the right quote
\param t Token
\returns Return Codes
*/
xbInt16 xbExp::GetTokenNumericConstant( xbExpToken &t ){
const char * s = t.sExpression;
xbUInt32 ulTokenLen = 0;
t.sToken = "";
t.cNodeType = XB_EXP_CONSTANT;
t.cReturnType = XB_EXP_NUMERIC;
// capture the leading sign
if( *s == '-' || *s == '+' || *s == '.' ){
t.sToken = *s;
ulTokenLen++;
s++;
// go past any white space between sign and number
while( *s && IsWhiteSpace( *s )){
s++;
ulTokenLen++;
}
}
// add the number to the token
while( *s && (isdigit( *s ) || *s == '.' )){
t.sToken += *s;
s++;
ulTokenLen++;
}
t.sExpression.Ltrunc( ulTokenLen );
return XB_NO_ERROR;
}
/*************************************************************************/
//! GetTokenOperator
/*! This method returns the operator
\param t Token
\returns Return Codes
*/
xbInt16 xbExp::GetTokenOperator( xbExpToken &t ){
const char *s = t.sExpression;
// Logical operators
if((strncmp( s, "<>", 2 ) == 0 ) || (strncmp( s, "!=", 2 ) == 0 ) ||
(strncmp( s, "<=", 2 ) == 0 ) || (strncmp( s, ">=", 2 ) == 0 )){
t.cReturnType = XB_EXP_LOGICAL;
t.sToken.Assign( s, 1, 2 );
t.sExpression.Ltrunc( 2 );
t.cNodeType = XB_EXP_OPERATOR;
return XB_NO_ERROR;
}
if( *s == '=' || *s == '<' || *s == '>' || *s == '$' || *s == '#' ){
t.cReturnType = XB_EXP_LOGICAL;
t.sToken.Assign( s, 1, 1 );
t.sExpression.Ltrunc( 1 );
t.cNodeType = XB_EXP_OPERATOR;
return XB_NO_ERROR;
}
if( (strncmp( s, ".NOT.", 5 ) == 0 ) || (strncmp( s, ".AND.", 5 ) == 0 )){
t.cReturnType = XB_EXP_LOGICAL;
t.sToken.Assign( s, 1, 5 );
t.sExpression.Ltrunc( 5 );
t.cNodeType = XB_EXP_OPERATOR;
return XB_NO_ERROR;
}
if( (strncmp( s, "NOT ", 4 ) == 0 ) || (strncmp( s, "AND ", 4 ) == 0 )){
t.cReturnType = XB_EXP_LOGICAL;
t.sToken.Assign( s, 1, 3 );
t.sExpression.Ltrunc( 3 );
t.cNodeType = XB_EXP_OPERATOR;
return XB_NO_ERROR;
}
if( strncmp( s, ".OR.", 4 ) == 0 ) {
t.cReturnType = XB_EXP_LOGICAL;
t.sToken.Assign( s, 1, 4 );
t.sExpression.Ltrunc( 4 );
t.cNodeType = XB_EXP_OPERATOR;
return XB_NO_ERROR;
}
if( strncmp( s, "OR ", 3 ) == 0 ) {
t.cReturnType = XB_EXP_LOGICAL;
t.sToken.Assign( s, 1, 2 );
t.sExpression.Ltrunc( 2 );
t.cNodeType = XB_EXP_OPERATOR;
return XB_NO_ERROR;
}
// Numeric operators
if(( strncmp( s, "**", 2 ) == 0 ) || ( strncmp( s, "+=", 2 ) == 0 ) ||
( strncmp( s, "-=", 2 ) == 0 ) || ( strncmp( s, "*=", 2 ) == 0 ) || ( strncmp( s, "/=", 2 ) == 0 )){
t.cReturnType = XB_EXP_NUMERIC;
t.sToken.Assign( s, 1, 2 );
t.sExpression.Ltrunc( 2 );
t.cNodeType = XB_EXP_OPERATOR;
return XB_NO_ERROR;
}
// Pre/post increment/decrement operators ++ or --
if(( strncmp( s, "--", 2 ) == 0 ) || ( strncmp( s, "++", 2 ) == 0 )){
t.cReturnType = XB_EXP_NUMERIC;
t.sToken.Assign( s, 1, 2 );
t.sExpression.Ltrunc( 2 );
if( t.sExpression.Len() > 0 && (isdigit( t.sExpression[1] ) || isalpha( t.sExpression[1] )))
t.cNodeType = XB_EXP_PRE_OPERATOR;
else
t.cNodeType = XB_EXP_POST_OPERATOR;
return XB_NO_ERROR;
}
if( *s == '*' || *s == '/' || *s == '%' || *s == '^' ){
t.cReturnType = XB_EXP_NUMERIC;
t.sToken.Assign( s, 1, 1 );
t.sExpression.Ltrunc( 1 );
t.cNodeType = XB_EXP_OPERATOR;
return XB_NO_ERROR;
}
// multi return type operators
t.cReturnType = XB_EXP_UNKNOWN;
if( *s == '+' || *s == '-' ){
t.sToken.Assign( s, 1, 1 );
t.sExpression.Ltrunc( 1 );
t.cNodeType = XB_EXP_OPERATOR;
return XB_NO_ERROR;
}
return XB_PARSE_ERROR;
}
/*************************************************************************/
//! GetTokenParen
/*! This method returns the tokens in a pair of enclosed parens
This routine returns the tokens inside a set of matching parens in sOutToken
If there is nothing between the parens then sOutToken is returned empty
sOutRemainder contains whatever remains to the right of the right paren
\param t Token
\returns Return Codes
*/
xbInt16 xbExp::GetTokenParen( xbExpToken &t ){
const char * s = t.sExpression;
const char * sToken; // pointer to beginning of token
xbInt16 iParenType = 0;
xbInt16 iRc = XB_NO_ERROR;
xbInt16 iErrorStop = 0;
xbInt16 iDepthCtr = 0; // depth inside of nested parens
xbUInt32 ulTokenLen = 0;
xbBool bDone = xbFalse;
try{
if( *s == '{' )
iParenType = 0;
else
iParenType = 1;
iDepthCtr = 1;
s++;
sToken = s;
while( *s && !bDone ){
if(( *s == ')' && iParenType == 1 ) || (*s == '}' && iParenType == 0 )){
iDepthCtr--;
if( iDepthCtr == 0 )
bDone = xbTrue;
} else if(( *s == '(' && iParenType == 1 ) || (*s == '{' && iParenType == 0 )){
iDepthCtr++;
}
s++;
ulTokenLen++;
}
if( bDone ){ // found matching paren
t.cNodeType = XB_EXP_NOTROOT;
t.cReturnType = XB_EXP_UNKNOWN;
t.sToken.Set( sToken, ulTokenLen - 1 );
t.sExpression.Ltrunc( ulTokenLen + 1 );
} else {
t.sToken = "";
t.cNodeType = XB_EXP_NOTROOT;
t.cReturnType = XB_EXP_UNKNOWN;
t.iSts = XB_UNBALANCED_PARENS;
iRc = XB_PARSE_ERROR;
iErrorStop = 100;
throw iRc;
}
}
catch (xbInt16 iRc ){
xbString sMsg;
sMsg.Sprintf( "xbexp::GetTokenParen() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc );
xbase->WriteLogMessage( sMsg );
}
return iRc;
}
/*************************************************************************/
//! Get the expression tree handle.
/*!
\returns Pointer to the top most node in the expression tree.
*/
xbExpNode *xbExp::GetTreeHandle(){
return nTree;
}
/*************************************************************************/
//! Is Function
/*! This method determines if the next token is a function.
\param sExpression - String expression to be evaluated.
\param cReturnType Output - Return type.
\returns xbTrue - Is a function.
xbFalse - Is not a function.
*/
xbBool xbExp::IsFunction( const xbString & sExpression, char &cReturnType ){
xbInt16 i = 0;
xbInt32 l = 0;
if( sExpression.Pos( '(' ) > 0 ){
if( xbase->GetFunctionInfo( sExpression, cReturnType, i, l ) == XB_NO_ERROR )
return xbTrue;
}
return xbFalse;
}
/*************************************************************************/
//! Is Logical constant
/*! This method determines if the next token is a logical constant (T/F, etc).
\param sExpression - String expression to be evaluated.
\returns xbTrue - Is a logical constant.
xbFalse - Is not a logical constant.
*/
xbBool xbExp::IsLogicalConstant( const xbString & sExpression ){
const char *s = sExpression;
if(( strncmp( s, ".T.", 3 ) == 0 ) || ( strncmp( s, ".F.", 3 ) == 0 ))
return xbTrue;
else if( strncmp( s, ".TRUE.", 6 ) == 0 )
return xbTrue;
else if( strncmp( s, ".FALSE.", 7 ) == 0 )
return xbTrue;
return xbFalse;
}
/*************************************************************************/
//! Is Numeric constant
/*! This method determines if the next token is a numeric constant.
\param sExpression - String expression to be evaluated.
\param cPrevNodeType - Type of previous node.
\returns xbTrue - Is a numeric constant.
xbFalse - Is not a numeric constant.
*/
xbBool xbExp::IsNumericConstant( const xbString & sExpression, char cPrevNodeType ){
// check for positive, negative or decimal number constants
const char *s = sExpression;
if(( *s == '-' && ( cPrevNodeType == 'O' || cPrevNodeType == 0 )) ||
( *s == '+' && ( cPrevNodeType == 'O' || cPrevNodeType == 0 ))){
s++;
while( *s && IsWhiteSpace( *s ))
s++;
}
if( *s == '.' )
s++;
if( isdigit( *s ))
return xbTrue;
else
return xbFalse;
}
/*************************************************************************/
//! Is Operator.
/*! This method determines if the next token is an operator.
\param sExpression - String expression to be evaluated.
\returns xbTrue - Is an operator.
xbFalse - Is not an operator.
*/
xbBool xbExp::IsOperator( const xbString & sExpression ){
const char *s = sExpression;
if( *s == '+' || *s == '-' || *s == '/' || *s == '^' || *s == '=' || *s == '$' ||
*s == '#' || *s == '*' || *s == '<' || *s == '>' || *s == '%' )
return xbTrue;
if( strncmp( s, "!=", 2 ) == 0 )
return xbTrue;
if((strncmp( s, ".NOT.", 5 ) == 0 ) || (strncmp( s, ".OR.", 4 ) == 0 ) || (strncmp( s, ".AND.", 5 ) == 0 ))
return xbTrue;
if((strncmp( s, "NOT ", 4 ) == 0 ) || (strncmp( s, "OR ", 3 ) == 0 ) || (strncmp( s, "AND ", 4 ) == 0 ))
return xbTrue;
return xbFalse;
}
/*************************************************************************/
//! Is Token seperator
/*! This method determines if the next token is a seperator.
\param sExpression - String expression to be evaluated.
\returns xbTrue - Is a token seperator.
xbFalse - Is not a token seperator.
*/
char xbExp::IsTokenSeparator( char c ){
if( c == '-' || c == '+' || c == '*' || c == '/' || c == '$' || c == '#' ||
c == '<' || c == '>' || c == '^' || c == '=' || c == '.' || c == '!' )
return c;
else
return 0;
}
/*************************************************************************/
//! Is White space
/*! This method determines if a given character is white space.
\param c - Character to be evaluated.
\returns xbTrue - Is white space.
xbFalse - Is not white space.
*/
xbBool xbExp::IsWhiteSpace( char c ){
return(( c == 0x20 )? 1 : 0 );
}
/*************************************************************************/
//! Get operator weight.
/*!
This method determines the priority of an operator
Operator precendence
10 .AND. .OR. .NOT. (not really an operator)
9 > or < (includes <= or >=)
6 unary plus or minus (+,-) -- not passed this routine
5 prefix increment and/or decrement (++,--)
4 exponentiation ** or ^
3 multiplication,division or modulus (*,/,%)
2 Addition, subtraction (+,-)
1 Postfix increment and/or decrement (++,--)
\param sOper - Operator.
\returns Operator weight
*/
xbInt16 xbExp::OperatorWeight( const xbString &sOper ){
if( sOper == "" || sOper.Len() > 5 )
return 0;
else if( sOper == "--0" || sOper == "++0" ) // 0 is prefix
return 9;
else if( sOper == "**" || sOper == "^" )
return 8;
else if( sOper == "*" || sOper == "/" || sOper == "%" || sOper == "*=" || sOper == "/=" )
return 7;
else if( sOper == "+" || sOper == "-" || sOper == "+=" || sOper == "-=" )
return 6;
else if( sOper == "--1" || sOper == "++1" ) // 1 is post fix
return 5;
else if( sOper == ">" || sOper == ">=" || sOper == "<" || sOper == "<=" ||
sOper == "<>" || sOper == "!=" || sOper == "#" || sOper == "$" || sOper == "=" )
return 4;
else if( sOper == ".NOT." || sOper == "NOT" )
return 3;
else if( sOper == ".AND." || sOper == "AND" )
return 2;
else if( sOper == ".OR." || sOper == "OR" )
return 1;
return 0;
}
/*************************************************************************/
//! Parse expression.
/*!
\param sExpression - Expression to parse.
\returns Return Codes
*/
xbInt16 xbExp::ParseExpression( const xbString &sExpression ){
return ParseExpression( sExpression, (xbInt16) 0 );
}
/*************************************************************************/
//! Parse expression.
/*!
\param dbf - Pointer to xbDbf instance.
\param sExpression - Expression to parse.
\returns Return Codes
*/
xbInt16 xbExp::ParseExpression( xbDbf *dbf, const xbString &sExpression ){
this->dbf = dbf;
return ParseExpression( sExpression, (xbInt16) 0 );
}
/*************************************************************************/
//! Parse expression.
/*!
\param sExpression - Expression to parse.
\param iWeight.
\returns Return Codes
*/
xbInt16 xbExp::ParseExpression( const xbString &sExpression, xbInt16 iWeight ){
xbExpNode *n;
xbExpNode *nLastNode = NULL; // pointer to the last node processed
xbExpToken t;
xbInt16 iRc = XB_NO_ERROR;
xbInt16 iErrorStop = 0;
xbString s;
try {
if( nTree )
delete nTree;
if(( iRc = CheckParensAndQuotes( sExpression )) != XB_NO_ERROR ){
iErrorStop = 100;
throw iRc;
}
t.sExpression = sExpression;
xbString sOriginalExp;
while( t.iSts == XB_NO_ERROR && iRc == XB_NO_ERROR ){
sOriginalExp = t.sExpression; // test code
iRc = GetNextToken( t );
if( !iRc && !t.iSts ){
// comment / uncomment debug / live
// DumpToken( t, 0 );
if( t.cNodeType == XB_EXP_NOTROOT ){
xbExp enr( xbase, dbf );
if(( iRc = enr.ParseExpression( t.sToken, iWeight + 10 )) != XB_NO_ERROR ){
iErrorStop = 110;
throw iRc;
}
n = enr.GetTreeHandle();
enr.ClearTreeHandle();
} else {
switch( t.cNodeType ) {
case XB_EXP_CONSTANT:
n = new xbExpNode( t.sToken, t.cNodeType );
if(( iRc = ParseExpressionConstant( t, n )) != XB_NO_ERROR ){
iErrorStop = 120;
throw iRc;
}
break;
case XB_EXP_FUNCTION:
n = new xbExpNode( t.cNodeType );
if(( iRc = ParseExpressionFunction( t, n, iWeight )) != XB_NO_ERROR ){
iErrorStop = 130;
throw iRc;
}
break;
case XB_EXP_FIELD:
n = new xbExpNode( t.cNodeType );
if(( iRc = ParseExpressionField( t, n )) != XB_NO_ERROR ){
iErrorStop = 140;
throw iRc;
}
break;
case XB_EXP_OPERATOR:
case XB_EXP_PRE_OPERATOR:
case XB_EXP_POST_OPERATOR:
n = new xbExpNode( t.sToken, t.cNodeType );
if(( iRc = ParseExpressionOperator( t, n, iWeight )) != XB_NO_ERROR ){
iErrorStop = 150;
throw iRc;
}
break;
default:
iErrorStop = 160;
iRc = XB_PARSE_ERROR;
throw iRc;
// break;
}
}
t.cPrevNodeType = t.cNodeType;
t.cPrevReturnType = t.cReturnType;
// determine where in the expression tree to insert the latest node "n"
// Is this the first node to be added to the tree?
if( !nTree ){
nTree = n;
}
// else if last node was XB_EXB_PRE_OPERATOR then append this as child to last node
else if( nLastNode && nLastNode->GetNodeType() == XB_EXP_PRE_OPERATOR ){
n->SetParent( nLastNode );
nLastNode->AddChild( n );
}
// else if last node was XB_EXB_POST_OPERATOR then append this as child to last node
else if( nLastNode && n->GetNodeType() == XB_EXP_POST_OPERATOR ){
n->AddChild( nLastNode );
nLastNode->SetParent( n );
if( nLastNode == nTree ){
nTree = n;
} else {
nLastNode->GetParent()->RemoveLastChild();
nLastNode->GetParent()->AddChild( n );
n->SetParent( nLastNode->GetParent() );
}
}
else if( n->GetNodeType() == XB_EXP_OPERATOR ){
xbExpNode * nWorkNode = nLastNode;
while( nWorkNode && ( nWorkNode->GetNodeType() != XB_EXP_OPERATOR || n->GetWeight() <= nWorkNode->GetWeight())){
nWorkNode = nWorkNode->GetParent();
}
if( !nWorkNode ){ // we are at the top
nTree->SetParent( n );
n->AddChild( nTree );
nTree = n;
} else if( nWorkNode->GetChildCnt() == 1 ){
nWorkNode->AddChild( n );
n->SetParent( nWorkNode );
} else if( nWorkNode->GetChildCnt() == 2 ){
xbExpNode * nChild2 = nWorkNode->GetChild(1);
n->AddChild( nChild2 );
nWorkNode->RemoveLastChild();
nWorkNode->AddChild( n );
n->SetParent( nWorkNode );
} else{
// should not be stopping on anything but an operator node with one or two children
iErrorStop = 170;
iRc = XB_PARSE_ERROR;
throw iRc;
}
} else {
n->SetParent( nLastNode );
nLastNode->AddChild( n );
}
nLastNode = n;
n = NULL;
}
}
// for each node in the tree, calculate the length if it's not already set
xbExpNode * nWork = GetNextNode( NULL );
xbExpNode * nChild1;
xbExpNode * nChild2;
while( nWork ){
if( nWork->GetReturnType() == XB_EXP_UNKNOWN ){
nWork->GetNodeText( s );
// std::cout << "XB_EXP_UNKNOWN logic [" << s << "][" << nWork->GetChildCnt() << "]\n";
// if this is "-" and child 1 and child 2 are both dates, set this result type to numeric
if( s == "-" && nWork->GetChildCnt() == 2 &&
nWork->GetChild(0)->GetReturnType() == XB_EXP_DATE && nWork->GetChild(1)->GetReturnType() == XB_EXP_DATE )
nWork->SetReturnType( XB_EXP_NUMERIC );
else if( nWork->GetChildCnt() > 0 )
nWork->SetReturnType( nWork->GetChild(0)->GetReturnType());
else{
iErrorStop = 180;
iRc = XB_PARSE_ERROR;
throw iRc;
}
}
if( nWork->GetResultLen() == 0 ){
switch( nWork->GetReturnType() ){
case XB_EXP_NUMERIC:
nWork->SetResultLen( 4 );
break;
case XB_EXP_CHAR:
if( nWork->GetNodeType() != XB_EXP_OPERATOR ){
iErrorStop = 190;
iRc = XB_PARSE_ERROR;
throw iRc;
}
if( nWork->GetChildCnt() < 2 ){
iErrorStop = 200;
iRc = XB_PARSE_ERROR;
throw iRc;
}
nChild1 = nWork->GetChild( 0 );
nChild2 = nWork->GetChild( 1 );
nWork->SetResultLen( nChild1->GetResultLen() + nChild2->GetResultLen());
break;
case XB_EXP_DATE:
nWork->SetResultLen( 8 );
break;
case XB_EXP_LOGICAL:
nWork->SetResultLen( 1 );
break;
default:
iErrorStop = 210;
iRc = XB_PARSE_ERROR;
throw iRc;
// break;
}
}
if( nWork->IsUnaryOperator() ){
if( nWork->GetChildCnt() != 1 ){
iErrorStop = 220;
iRc = XB_PARSE_ERROR;
throw iRc;
}
} else if( nWork->IsOperator() && nWork->GetChildCnt() != 2 ){
iErrorStop = 230;
iRc = XB_PARSE_ERROR;
throw iRc;
}
nWork = GetNextNode( nWork );
}
}
catch (xbInt16 iRc ){
xbString sMsg;
sMsg.Sprintf( "xbexp::ParseExpression() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc );
xbase->WriteLogMessage( sMsg.Str() );
}
return iRc;
}
/*************************************************************************/
//! Parse expression constant.
/*!
\param t - Token.
\param n - Node.
\returns Return Codes
*/
xbInt16 xbExp::ParseExpressionConstant( xbExpToken &t, xbExpNode *n ){
xbDate dtWork;
n->SetReturnType( t.cReturnType );
// std::cout << "parse expression constant[" << t.sToken << "]\n";
switch( t.cReturnType ){
case XB_EXP_CHAR:
n->SetResultLen( t.sToken.Len() );
n->SetResult( t.sToken );
break;
case XB_EXP_DATE:
n->SetResultLen( 8 );
dtWork.Set( t.sToken );
n->SetResult( dtWork );
break;
case XB_EXP_LOGICAL:
n->SetResultLen( 1 );
if( strncmp( t.sToken, "T", 1 ) == 0 )
n->SetResult( (xbBool) xbTrue );
else
n->SetResult( (xbBool) xbFalse );
break;
case XB_EXP_NUMERIC:
n->SetResultLen( 4 );
n->SetResult( strtod( t.sToken, 0 ));
n->SetResult( t.sToken );
break;
default:
return XB_PARSE_ERROR;
// break;
}
return XB_NO_ERROR;
}
/*************************************************************************/
//! Parse expression field.
/*!
\param t - Token.
\param n - Node.
\returns Return Codes
*/
xbInt16 xbExp::ParseExpressionField( xbExpToken &t, xbExpNode *n ){
xbInt16 iRc = XB_NO_ERROR;
xbInt16 iErrorStop = 0;
xbDbf * pDbf;
xbString sFieldName;
// do the db lookup and set the field number for the field
try{
xbUInt32 lPos;
if(( lPos = t.sToken.Pos( "->" )) > 0 ){
// table name is part of the token
xbString sTableName = t.sToken;
sTableName.Left( lPos-1 );
sFieldName = t.sToken;
sFieldName.Mid( lPos + 2, t.sToken.Len() - lPos - 1 );
pDbf = (xbDbf *) xbase->GetDbfPtr( sTableName );
/*
// updated 1/2/23 to support either table.field or table->field
if((( lPos = t.sToken.Pos( "->" )) > 0) || (( lPos = t.sToken.Pos( "." )) > 0) ){
// table name is part of the token
xbString sTableName = t.sToken;
sTableName.Left( lPos-1 );
sFieldName = t.sToken;
if( t.sToken[lPos] == '.' )
sFieldName.Mid( lPos + 1, t.sToken.Len() - lPos );
else // ->
sFieldName.Mid( lPos + 2, t.sToken.Len() - lPos - 1 );
pDbf = (xbDbf *) xbase->GetDbfPtr( sTableName );
*/
} else {
// table name is not part of the token
pDbf = dbf;
sFieldName = t.sToken;
}
if( !pDbf ){
iErrorStop = 100;
iRc = XB_INVALID_FIELD;
throw iRc;
}
xbInt16 iFieldNo = 0;
if(( iRc = pDbf->GetFieldNo( sFieldName, iFieldNo )) != XB_NO_ERROR ){
iErrorStop = 110;
throw iRc;
}
char cFieldType;
if(( iRc = pDbf->GetFieldType( iFieldNo, cFieldType )) != XB_NO_ERROR ){
iErrorStop = 120;
throw iRc;
}
n->SetDbfInfo( pDbf, iFieldNo );
switch( cFieldType ){
case XB_CHAR_FLD:
n->SetReturnType( XB_EXP_CHAR );
break;
case XB_LOGICAL_FLD:
n->SetReturnType( XB_EXP_LOGICAL );
break;
case XB_NUMERIC_FLD:
case XB_FLOAT_FLD:
n->SetReturnType( XB_EXP_NUMERIC );
break;
case XB_DATE_FLD:
n->SetReturnType( XB_EXP_DATE );
break;
case XB_MEMO_FLD:
default:
iErrorStop = 130;
iRc = XB_PARSE_ERROR;
throw iRc;
// break;
}
n->SetNodeText( sFieldName );
xbInt16 iResultLen = 0;
if(( iRc = pDbf->GetFieldLen( iFieldNo, iResultLen )) != XB_NO_ERROR ){
iErrorStop = 140;
throw iRc;
}
n->SetResultLen( (xbUInt32) iResultLen );
}
catch (xbInt16 iRc ){
xbString sMsg;
sMsg.Sprintf( "xbexp::ParseExpressionField() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc );
xbase->WriteLogMessage( sMsg.Str() );
}
return iRc;
}
/*************************************************************************/
//! Parse expression function.
/*!
\param t - Token.
\param n - Node.
\param iWeight
\returns Return Codes
*/
xbInt16 xbExp::ParseExpressionFunction( xbExpToken &t, xbExpNode *n, xbInt16 iWeight ){
xbInt16 iRc = XB_NO_ERROR;
xbInt16 iErrorStop = 0;
try{
// find the first "("
xbUInt32 lPos = t.sToken.Pos( '(' );
if( lPos == 0 ){
iErrorStop = 100;
iRc = XB_INVALID_FUNCTION;
throw iRc;
}
// Get the function name and look it up in the table
xbString sFunc = t.sToken;
sFunc.Left( lPos - 1 ).Trim();
char cReturnType;
xbInt16 i = 0;
xbInt32 l = 0;
if(( iRc = xbase->GetFunctionInfo( sFunc, cReturnType, i, l )) != XB_NO_ERROR ){
iErrorStop = 110;
throw iRc;
}
n->SetNodeText( sFunc );
// Get the function parms
xbString sParms = t.sToken;
sParms.Mid( lPos+1, t.sToken.Len() - lPos );
lPos = sParms.GetLastPos( ')' );
if( lPos == 0 ){
iErrorStop = 120;
iRc = XB_INVALID_FUNCTION;
throw iRc;
}
// remove the trailing ")" paren
sParms.Left( lPos - 1 ).Trim();
// if this function has parms, put them in the tree
if( sParms.Len() > 0 ){
xbExp enr( xbase, dbf );
// create a linked list of parms
xbLinkList llParms;
if(( iRc = ParseExpressionFunctionParms( sParms, llParms )) != XB_NO_ERROR ){
iErrorStop = 130;
throw iRc;
}
// for each function parm, recursively process it
xbLinkListNode * llN = llParms.GetHeadNode();
xbString sParm;
while( llN ){
sParm = llN->GetKey();
if(( iRc = enr.ParseExpression( sParm, iWeight + 10 )) != XB_NO_ERROR ){
iErrorStop = 140;
throw iRc;
}
n->AddChild( enr.GetTreeHandle());
enr.ClearTreeHandle();
llN = llN->GetNextNode();
}
llParms.Clear();
}
if( cReturnType == '1' ){
if( n->GetChildCnt() > 0 ){
xbExpNode *n1 = n->GetChild( 0 );
n->SetReturnType( n1->GetReturnType());
}
} else {
n->SetReturnType( cReturnType );
}
if(( iRc = CalcFunctionResultLen( n )) != XB_NO_ERROR ){
iErrorStop = 150;
throw iRc;
}
}
catch (xbInt16 iRc ){
xbString sMsg;
sMsg.Sprintf( "xbexp::ParseExpressionFunction() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc );
xbase->WriteLogMessage( sMsg.Str() );
}
return iRc;
}
/*************************************************************************/
//! Parse expression function.
/*!
Creates a linked list of function parms as xbStrings
This function pulls out the parms and addresses embedded parens and quotes within the parms
\param sParms
\param lParms
\returns Return Codes
*/
xbInt16 xbExp::ParseExpressionFunctionParms( const xbString &sParms, xbLinkList & llParms ){
xbInt16 iRc = XB_NO_ERROR;
xbInt16 iErrorStop = 0;
xbInt16 iParenCtr = 0;
xbInt16 iInQuotes = 0;
xbInt16 iDoubleQuotes = 0;
xbInt16 iSingleQuotes = 0;
xbInt32 lStartPos = 0;
xbInt32 lParmLen = 0;
xbString sParm;
try{
const char *sp = sParms;
while( *sp ){
if( *sp == '(')
iParenCtr++;
else if( *sp == ')' )
iParenCtr--;
else if( !iInQuotes && *sp == '"' ){
iInQuotes++;
iDoubleQuotes++;
} else if( iInQuotes && *sp == '"' ){
iInQuotes--;
iDoubleQuotes--;
}
else if( !iInQuotes && *sp == '\'' ){
iInQuotes++;
iSingleQuotes++;
} else if( iInQuotes && *sp == '\'' ){
iInQuotes--;
iSingleQuotes--;
} else if( !iInQuotes && !iParenCtr && *sp == ',' ){
// found a valid comma - at the end of a parm
// add it to the end of the linked list
sParm = sParms;
sParm.Mid( (xbUInt32) lStartPos + 1, (xbUInt32) lParmLen ).Trim(); // mid is one based
llParms.InsertAtEnd( sParm );
// set the start pos for the next one on the list
lStartPos += lParmLen + 1;
lParmLen = -1;
// lParmLen = 0;
}
lParmLen++;
sp++;
}
if( lParmLen > 0 ){
// get the last parm, it didn't end with a comma
sParm = sParms;
sParm.Mid( (xbUInt32) lStartPos + 1, (xbUInt32) lParmLen ).Trim();
llParms.InsertAtEnd( sParm );
}
}
// try / catch not used in this method, structure added for potential future use
catch (xbInt16 iRc ){
xbString sMsg;
sMsg.Sprintf( "xbexp::ParseExpressionFunctionParms() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc );
xbase->WriteLogMessage( sMsg.Str() );
}
return iRc;
}
/*************************************************************************/
//! Parse expression operator.
/*!
\param t - Token.
\param n - Node.
\param iWeight
\returns Return Codes
*/
xbInt16 xbExp::ParseExpressionOperator( xbExpToken &t, xbExpNode *n, xbInt16 iWeight ){
n->SetResult( t.sToken );
n->SetWeight( iWeight + OperatorWeight( t.sToken) );
// std::cout << "ParseExpressionOperator [" << t.cPrevNodeType << "][" << t.sToken << "] Weight = [" << iWeight;
// std::cout << "] PrevReturnType [" << t.cPrevReturnType;
// std::cout << "] Operator weight [" << OperatorWeight( t.sToken ) << "] getweight [" << n->GetWeight() << "]\n";
if( t.sToken == "**" || t.sToken == "^" ||
t.sToken == "*" || t.sToken == "/" || t.sToken == "%" || t.sToken == "*=" || t.sToken == "/=" )
n->SetReturnType( XB_EXP_NUMERIC );
else if( t.sToken == "--" || t.sToken == "++" || t.sToken == "+=" || t.sToken == "-=" ) // could be date or numeric
n->SetReturnType( XB_EXP_UNKNOWN );
else if( t.cPrevReturnType == XB_EXP_CHAR && ( t.sToken == "+" || t.sToken == "-" ))
n->SetReturnType( XB_EXP_CHAR );
else if( t.sToken == ".AND." || t.sToken == ".OR." || t.sToken == ".NOT." ||
t.sToken == "AND" || t.sToken == "OR" || t.sToken == "NOT" ||
t.sToken == ">" || t.sToken == ">=" || t.sToken == "<" ||
t.sToken == "<=" || t.sToken == "<>" || t.sToken == "!=" ||
t.sToken == "$" || t.sToken == "#" || t.sToken == "=" )
n->SetReturnType( XB_EXP_LOGICAL );
else if( t.cPrevReturnType == XB_EXP_UNKNOWN )
n->SetReturnType( XB_EXP_UNKNOWN );
// added for date constant logic 10/28/17
else if(( t.sToken == "+" || t.sToken == "-" ) && t.cPrevReturnType == XB_EXP_DATE )
n->SetReturnType( XB_EXP_DATE );
else if( t.sToken == "+" || t.sToken == "-" )
n->SetReturnType( XB_EXP_NUMERIC );
return XB_NO_ERROR;
}
/************************************************************************/
//! ProcessExpression
/*! This method processes an expression tree leaving the result in the
root node of the tree
*/
xbInt16 xbExp::ProcessExpression(){
return ProcessExpression( 0 );
}
/************************************************************************/
//! ProcessExpression
/*! This method processes a parsed expression tree leaving the result in the
root node of the tree
\param iRecBufSw Record buffer to use when evaluating expression.
0 - Current record buffer.
1 - Original record buffer.
*/
xbInt16 xbExp::ProcessExpression( xbInt16 iRecBufSw ){
// iRecBufSw 0 - Record Buffer
// 1 - Original Record Buffer
xbInt16 iRc = XB_NO_ERROR;
xbInt16 iErrorStop = 0;
try{
xbExpNode * nWork = GetNextNode( NULL );
xbExpNode * nChild1;
xbDbf * dbf;
xbString sWork1;
xbString sWork2;
xbString sOperator;
xbDate dtWork1;
xbBool bWork;
xbDouble dWork;
while( nWork ){
switch( nWork->GetNodeType() ){
case XB_EXP_CONSTANT:
break;
case XB_EXP_PRE_OPERATOR: // increment value before setting in head node
if( nWork->GetChildCnt() != 1 ){
iErrorStop = 100;
iRc = XB_PARSE_ERROR;
throw iRc;
}
nChild1 = nWork->GetChild( 0 );
//if( nChild1->GetReturnType() == XB_EXP_DATE )
// nChild1->SetResult( (xbDouble) nChild1->GetDateResult().JulianDays());
nWork->GetNodeText( sWork1 );
if( sWork1 == "++" )
nChild1->SetResult( nChild1->GetNumericResult() + 1 );
else
nChild1->SetResult( nChild1->GetNumericResult() - 1 );
nWork->SetResult( nChild1->GetNumericResult());
//if( nChild1->GetReturnType() == XB_EXP_DATE ){
// dtWork1.JulToDate8( (xbInt32) nChild1->GetNumericResult());
// nChild1->SetResult( dtWork1 );
// nWork->SetResult( dtWork1 );
// }
break;
case XB_EXP_POST_OPERATOR: // increment value after setting in head node
if( nWork->GetChildCnt() != 1 ){
iErrorStop = 110;
iRc = XB_PARSE_ERROR;
throw iRc;
}
nChild1 = nWork->GetChild( 0 );
//if( nChild1->GetReturnType() == XB_EXP_DATE ){
// nWork->SetResult( nChild1->GetDateResult());
// nChild1->SetResult( (xbDouble) nChild1->GetDateResult().JulianDays());
//}
//else
nWork->SetResult( nChild1->GetNumericResult());
nWork->GetNodeText( sWork1 );
if( sWork1 == "++" )
nChild1->SetResult( nChild1->GetNumericResult() + 1 );
else
nChild1->SetResult( nChild1->GetNumericResult() - 1 );
//if( nChild1->GetReturnType() == XB_EXP_DATE ){
// dtWork1.JulToDate8( (xbInt32) nChild1->GetNumericResult());
// nChild1->SetResult( dtWork1 );
// }
break;
case XB_EXP_FIELD:
if(( dbf = nWork->GetDbf()) == NULL ){
iErrorStop = 120;
iRc = XB_PARSE_ERROR;
throw iRc;
}
switch( nWork->GetReturnType()){
case XB_EXP_CHAR:
if(( iRc = dbf->GetField( nWork->GetFieldNo(), sWork1, iRecBufSw )) < XB_NO_ERROR ){
iErrorStop = 130;
throw iRc;
}
nWork->SetResult( sWork1 );
break;
case XB_EXP_DATE:
if(( iRc = dbf->GetField( nWork->GetFieldNo(), sWork1, iRecBufSw )) < XB_NO_ERROR ){
iErrorStop = 140;
throw iRc;
}
if( sWork1 == " " ){
nWork->SetResult( (xbDouble) 21474835648 ); // dbase sets a date value in ndx to this if spaces on dbf record
} else {
dtWork1.Set( sWork1 );
nWork->SetResult( (xbDouble) dtWork1.JulianDays() );
}
break;
case XB_EXP_LOGICAL:
if(( iRc = dbf->GetLogicalField( nWork->GetFieldNo(), bWork, iRecBufSw )) != XB_NO_ERROR ){
iErrorStop = 150;
throw iRc;
}
nWork->SetResult( bWork );
break;
case XB_EXP_NUMERIC:
if(( iRc = dbf->GetDoubleField( nWork->GetFieldNo(), dWork, iRecBufSw )) != XB_NO_ERROR ){
iErrorStop = 160;
throw iRc;
}
nWork->SetResult( dWork );
break;
default:
iErrorStop = 170;
iRc = XB_PARSE_ERROR;
throw iRc;
// break;
}
break;
case XB_EXP_OPERATOR:
if(( iRc = ProcessExpressionOperator( nWork )) != XB_NO_ERROR ){
iErrorStop = 180;
throw iRc;
}
break;
case XB_EXP_FUNCTION:
if(( iRc = ProcessExpressionFunction( nWork, iRecBufSw )) != XB_NO_ERROR ){
iErrorStop = 190;
throw iRc;
}
break;
default:
iErrorStop = 200;
iRc = XB_PARSE_ERROR;
throw iRc;
// break;
}
nWork = GetNextNode( nWork );
}
}
catch (xbInt16 iRc ){
xbString sMsg;
sMsg.Sprintf( "xbexp::ProcessExpression() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc );
xbase->WriteLogMessage( sMsg.Str() );
}
return iRc;
}
/*************************************************************************/
//! ProcessExpression
/*! This method processes an expression tree for a given node.
*/
xbInt16 xbExp::ProcessExpressionFunction( xbExpNode * n, xbInt16 iRecBufSw ){
xbInt16 iRc = XB_NO_ERROR;
xbInt16 iErrorStop = 0;
try{
xbString sFunction;
xbString sResult;
xbDouble dResult;
xbDate dtResult;
xbBool bResult;
n->GetNodeText( sFunction );
// process functions with no children first
xbExpNode * nChild1;
if( n->GetChildCnt() == 0 ){
if( sFunction == "DATE" ){
if(( iRc = xbase->DATE( dtResult )) != XB_NO_ERROR ){
iErrorStop = 100;
throw iRc;
}
n->SetResult( dtResult );
} else if( sFunction == "DEL" ){
if(( iRc = xbase->DEL( dbf, sResult, iRecBufSw )) != XB_NO_ERROR ){
iErrorStop = 110;
throw iRc;
}
n->SetResult( sResult );
} else if( sFunction == "DELETED" ){
if(( iRc = xbase->DELETED( dbf, bResult, iRecBufSw )) != XB_NO_ERROR ){
iErrorStop = 120;
throw iRc;
}
n->SetResult( bResult );
} else if( sFunction == "RECCOUNT" ){
if(( iRc = xbase->RECCOUNT( dbf, dResult )) != XB_NO_ERROR ){
iErrorStop = 130;
throw iRc;
}
n->SetResult( dResult );
} else if( sFunction == "RECNO" ){
if(( iRc = xbase->RECNO( dbf, dResult )) != XB_NO_ERROR ){
iErrorStop = 140;
throw iRc;
}
n->SetResult( dResult );
}
// process functions with one child
} else if( n->GetChildCnt() == 1 ){
nChild1 = n->GetChild( 0 );
if( sFunction == "ABS" ){
if(( iRc = xbase->ABS( nChild1->GetNumericResult(), dResult )) != XB_NO_ERROR ){
iErrorStop = 200;
throw iRc;
}
n->SetResult( dResult );
} else if( sFunction == "ALLTRIM" ){
if(( iRc = xbase->ALLTRIM( nChild1->GetStringResult(), sResult )) != XB_NO_ERROR ){
iErrorStop = 210;
throw iRc;
}
n->SetResult( sResult );
} else if( sFunction == "ASC" ){
if(( iRc = xbase->ASC( nChild1->GetStringResult(), dResult )) != XB_NO_ERROR ){
iErrorStop = 220;
throw iRc;
}
n->SetResult( dResult );
} else if( sFunction == "CDOW" ){
xbDate d( (xbInt32) nChild1->GetNumericResult());
if(( iRc = xbase->CDOW( d, sResult )) != XB_NO_ERROR ){
iErrorStop = 230;
throw iRc;
}
n->SetResult( sResult );
} else if( sFunction == "CHR" ){
if(( iRc = xbase->CHR( nChild1->GetNumericResult(), sResult )) != XB_NO_ERROR ){
iErrorStop = 240;
throw iRc;
}
n->SetResult( sResult );
} else if( sFunction == "CMONTH" ){
xbDate d( (xbInt32) nChild1->GetNumericResult());
if(( iRc = xbase->CMONTH( d, sResult )) != XB_NO_ERROR ){
iErrorStop = 250;
throw iRc;
}
n->SetResult( sResult );
} else if( sFunction == "CTOD" ){
if(( iRc = xbase->CTOD( nChild1->GetStringResult(), dtResult )) != XB_NO_ERROR ){
iErrorStop = 260;
throw iRc;
}
n->SetResult( dtResult );
} else if( sFunction == "DAY" ){
xbDate d( (xbInt32) nChild1->GetNumericResult());
if(( iRc = xbase->DAY( d, dResult )) != XB_NO_ERROR ){
iErrorStop = 270;
throw iRc;
}
n->SetResult( dResult );
} else if( sFunction == "DESCEND" ){
if( n->GetReturnType() == XB_EXP_CHAR ){
if(( iRc = xbase->DESCEND( nChild1->GetStringResult(), sResult )) != XB_NO_ERROR ){
iErrorStop = 280;
throw iRc;
}
n->SetResult( sResult );
} else if( n->GetReturnType() == XB_EXP_DATE ){
xbDate d( (xbInt32) nChild1->GetNumericResult());
if(( iRc = xbase->DESCEND( d, dtResult )) != XB_NO_ERROR ){
iErrorStop = 290;
throw iRc;
}
n->SetResult( dtResult );
} else if( n->GetReturnType() == XB_EXP_NUMERIC ){
if(( iRc = xbase->DESCEND( nChild1->GetNumericResult(), dResult )) != XB_NO_ERROR ){
iErrorStop = 300;
throw iRc;
}
n->SetResult( dResult );
} else {
iErrorStop = 310;
iRc = XB_PARSE_ERROR;
throw iRc;
}
} else if( sFunction == "DOW" ){
xbDate d( (xbInt32) nChild1->GetNumericResult());
if(( iRc = xbase->DOW( d, dResult )) != XB_NO_ERROR ){
iErrorStop = 320;
throw iRc;
}
n->SetResult( dResult );
} else if( sFunction == "DTOC" ){
xbDate d( (xbInt32) nChild1->GetNumericResult());
if(( iRc = xbase->DTOC( d, sResult )) != XB_NO_ERROR ){
iErrorStop = 330;
throw iRc;
}
n->SetResult( sResult );
} else if( sFunction == "DTOS" ){
xbDate d( (xbInt32) nChild1->GetNumericResult());
if(( iRc = xbase->DTOS( d, sResult )) != XB_NO_ERROR ){
iErrorStop = 340;
throw iRc;
}
n->SetResult( sResult );
} else if( sFunction == "EXP" ){
if(( iRc = xbase->EXP( nChild1->GetNumericResult(), dResult )) != XB_NO_ERROR ){
iErrorStop = 350;
throw iRc;
}
n->SetResult( dResult );
} else if( sFunction == "INT" ){
if(( iRc = xbase->INT( nChild1->GetNumericResult(), dResult )) != XB_NO_ERROR ){
iErrorStop = 360;
throw iRc;
}
n->SetResult( dResult );
} else if( sFunction == "ISALPHA" ){
if(( iRc = xbase->ISALPHA( nChild1->GetStringResult(), bResult )) != XB_NO_ERROR ){
iErrorStop = 370;
throw iRc;
}
n->SetResult( bResult );
} else if( sFunction == "ISLOWER" ){
if(( iRc = xbase->ISLOWER( nChild1->GetStringResult(), bResult )) != XB_NO_ERROR ){
iErrorStop = 380;
throw iRc;
}
n->SetResult( bResult );
} else if( sFunction == "ISUPPER" ){
if(( iRc = xbase->ISUPPER( nChild1->GetStringResult(), bResult )) != XB_NO_ERROR ){
iErrorStop = 390;
throw iRc;
}
n->SetResult( bResult );
} else if( sFunction == "LEN" ){
if(( iRc = xbase->LEN( nChild1->GetStringResult(), dResult )) != XB_NO_ERROR ){
iErrorStop = 400;
throw iRc;
}
n->SetResult( dResult );
} else if( sFunction == "LOG" ){
if(( iRc = xbase->LOG( nChild1->GetNumericResult(), dResult )) != XB_NO_ERROR ){
iErrorStop = 410;
throw iRc;
}
n->SetResult( dResult );
} else if( sFunction == "LTRIM" ){
if(( iRc = xbase->LTRIM( nChild1->GetStringResult(), sResult )) != XB_NO_ERROR ){
iErrorStop = 420;
throw iRc;
}
n->SetResult( sResult );
} else if( sFunction == "LOWER" ){
if(( iRc = xbase->LOWER( nChild1->GetStringResult(), sResult )) != XB_NO_ERROR ){
iErrorStop = 430;
throw iRc;
}
n->SetResult( sResult );
} else if( sFunction == "MONTH" ){
xbDate d( (xbInt32) nChild1->GetNumericResult());
if(( iRc = xbase->MONTH( d, dResult )) != XB_NO_ERROR ){
iErrorStop = 440;
throw iRc;
}
n->SetResult( dResult );
} else if( sFunction == "RTRIM" ){
if(( iRc = xbase->RTRIM( nChild1->GetStringResult(), sResult )) != XB_NO_ERROR ){
iErrorStop = 450;
throw iRc;
}
n->SetResult( sResult );
} else if( sFunction == "STOD" ){
if(( iRc = xbase->STOD( nChild1->GetStringResult(), dtResult )) != XB_NO_ERROR ){
iErrorStop = 460;
throw iRc;
}
n->SetResult( dtResult );
} else if( sFunction == "SPACE" ){
if(( iRc = xbase->SPACE( (xbInt32) nChild1->GetNumericResult(), sResult )) != XB_NO_ERROR ){
iErrorStop = 470;
throw iRc;
}
n->SetResult( sResult );
} else if( sFunction == "SQRT" ){
if(( iRc = xbase->SQRT( nChild1->GetNumericResult(), dResult )) != XB_NO_ERROR ){
iErrorStop = 480;
throw iRc;
}
n->SetResult( dResult );
} else if( sFunction == "STR" ){
if(( iRc = xbase->STR( nChild1->GetNumericResult(), sResult )) != XB_NO_ERROR ){
iErrorStop = 490;
throw iRc;
}
n->SetResult( sResult );
} else if( sFunction == "TRIM" ){
if(( iRc = xbase->TRIM( nChild1->GetStringResult(), sResult )) != XB_NO_ERROR ){
iErrorStop = 500;
throw iRc;
}
n->SetResult( sResult );
} else if( sFunction == "UPPER" ){
if(( iRc = xbase->UPPER( nChild1->GetStringResult(), sResult )) != XB_NO_ERROR ){
iErrorStop = 510;
throw iRc;
}
n->SetResult( sResult );
} else if( sFunction == "VAL" ){
if(( iRc = xbase->VAL( nChild1->GetStringResult(), dResult )) != XB_NO_ERROR ){
iErrorStop = 520;
throw iRc;
}
n->SetResult( dResult );
} else if( sFunction == "YEAR" ){
xbDate d( (xbInt32) nChild1->GetNumericResult());
if(( iRc = xbase->YEAR( d, dResult )) != XB_NO_ERROR ){
iErrorStop = 530;
throw iRc;
}
n->SetResult( dResult );
} else {
iErrorStop = 540;
iRc = XB_PARSE_ERROR;
throw iRc;
}
} else if( n->GetChildCnt() == 2 ){
xbExpNode * nChild2;
nChild1 = n->GetChild( 0 );
nChild2 = n->GetChild( 1 );
if( sFunction == "AT" ){
if(( iRc = xbase->AT( nChild1->GetStringResult(), nChild2->GetStringResult(), dResult )) != XB_NO_ERROR ){
iErrorStop = 700;
throw iRc;
}
n->SetResult( dResult );
} else if( sFunction == "LEFT" ){
if(( iRc = xbase->LEFT( nChild1->GetStringResult(), (xbUInt32) nChild2->GetNumericResult(), sResult )) != XB_NO_ERROR ){
iErrorStop = 710;
throw iRc;
}
n->SetResult( sResult );
} else if( sFunction == "MAX" ){
if(( iRc = xbase->MAX( nChild1->GetNumericResult(), nChild2->GetNumericResult(), dResult )) != XB_NO_ERROR ){
iErrorStop = 720;
throw iRc;
}
n->SetResult( dResult );
} else if( sFunction == "MIN" ){
if(( iRc = xbase->MIN( nChild1->GetNumericResult(), nChild2->GetNumericResult(), dResult )) != XB_NO_ERROR ){
iErrorStop = 730;
throw iRc;
}
n->SetResult( dResult );
}
else if( sFunction == "REPLICATE" ){
if(( iRc = xbase->REPLICATE( nChild1->GetStringResult(), (xbUInt32) nChild2->GetNumericResult(), sResult )) != XB_NO_ERROR ){
iErrorStop = 800;
throw iRc;
}
n->SetResult( sResult );
} else if( sFunction == "RIGHT" ){
if(( iRc = xbase->RIGHT( nChild1->GetStringResult(), (xbUInt32) nChild2->GetNumericResult(), sResult )) != XB_NO_ERROR ){
iErrorStop = 810;
throw iRc;
}
n->SetResult( sResult );
} else if( sFunction == "STR" ){
if(( iRc = xbase->STR( nChild1->GetNumericResult(), (xbUInt32) nChild2->GetNumericResult(), sResult )) != XB_NO_ERROR ){
iErrorStop = 820;
throw iRc;
}
n->SetResult( sResult );
} else {
iErrorStop = 830;
iRc = XB_PARSE_ERROR;
throw iRc;
}
} else if( n->GetChildCnt() == 3 ){
xbExpNode * nChild2;
xbExpNode * nChild3;
nChild1 = n->GetChild( 0 );
nChild2 = n->GetChild( 1 );
nChild3 = n->GetChild( 2 );
if( sFunction == "IIF" ){
if(( iRc = xbase->IIF( nChild1->GetBoolResult(), nChild2->GetStringResult(), nChild3->GetStringResult(), sResult )) != XB_NO_ERROR ){
iErrorStop = 900;
throw iRc;
}
n->SetResult( sResult );
} else if( sFunction == "STR" ){
if(( iRc = xbase->STR( nChild1->GetNumericResult(), (xbUInt32) nChild2->GetNumericResult(), (xbUInt32) nChild3->GetNumericResult(), sResult )) != XB_NO_ERROR ){
iErrorStop = 910;
throw iRc;
}
n->SetResult( sResult );
} else if( sFunction == "STRZERO" ){
if(( iRc = xbase->STRZERO( nChild1->GetNumericResult(), (xbUInt32) nChild2->GetNumericResult(), (xbUInt32) nChild3->GetNumericResult(), sResult )) != XB_NO_ERROR ){
iErrorStop = 920;
throw iRc;
}
n->SetResult( sResult );
} else if( sFunction == "SUBSTR" ){
if(( iRc = xbase->SUBSTR( nChild1->GetStringResult(), (xbUInt32) nChild2->GetNumericResult(), (xbUInt32) nChild3->GetNumericResult(), sResult )) != XB_NO_ERROR ){
iErrorStop = 930;
throw iRc;
}
n->SetResult( sResult );
} else {
iErrorStop = 950;
iRc = XB_PARSE_ERROR;
throw iRc;
}
} else if( n->GetChildCnt() == 4 ){
xbExpNode * nChild2;
xbExpNode * nChild3;
xbExpNode * nChild4;
nChild1 = n->GetChild( 0 );
nChild2 = n->GetChild( 1 );
nChild3 = n->GetChild( 2 );
nChild4 = n->GetChild( 3 );
if( sFunction == "STR" ){
if(( iRc = xbase->STR( nChild1->GetNumericResult(), (xbUInt32) nChild2->GetNumericResult(),
(xbUInt32) nChild3->GetNumericResult(), nChild4->GetStringResult(), sResult )) != XB_NO_ERROR ){
iErrorStop = 1000;
throw iRc;
}
n->SetResult( sResult );
} else {
iErrorStop = 1010;
iRc = XB_PARSE_ERROR;
throw iRc;
}
} else {
iErrorStop = 2000;
iRc = XB_PARSE_ERROR;
throw iRc;
}
}
catch (xbInt16 iRc ){
xbString sMsg;
sMsg.Sprintf( "xbexp::ProcessExpressionFunction() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc );
xbase->WriteLogMessage( sMsg.Str() );
}
return iRc;
}
/*************************************************************************/
//! Process Expression Operator
/*! This method processes an expression operator for a given node.
*/
xbInt16 xbExp::ProcessExpressionOperator( xbExpNode * n ){
xbInt16 iRc = XB_NO_ERROR;
xbInt16 iErrorStop = 0;
xbExpNode * nChild1 = NULL;
xbExpNode * nChild2 = NULL;
xbString sOperator;
xbString sWork1;
xbString sWork2;
xbDate dtWork1;
xbString sMsg;
try{
n->GetNodeText( sOperator );
nChild1 = n->GetChild( 0 );
if( !n->IsUnaryOperator())
nChild2 = n->GetChild( 1 );
switch( n->GetReturnType()){
case XB_EXP_CHAR:
if( sOperator == "+" ){
sWork1 = nChild1->GetStringResult();
sWork1 += nChild2->GetStringResult();
n->SetResult( sWork1 );
} else if( sOperator == "-" ){
sWork1 = nChild1->GetStringResult();
sWork1.Rtrim();
sWork1 += nChild2->GetStringResult();
sWork1.PadRight( ' ', n->GetResultLen());
n->SetResult( sWork1 );
} else {
iErrorStop = 100;
iRc = XB_PARSE_ERROR;
throw iRc;
}
break;
case XB_EXP_NUMERIC:
if( sOperator == "+" )
n->SetResult( nChild1->GetNumericResult() + nChild2->GetNumericResult());
else if( sOperator == "-" ){
n->SetResult( nChild1->GetNumericResult() - nChild2->GetNumericResult());
}
else if( sOperator == "*" )
n->SetResult( nChild1->GetNumericResult() * nChild2->GetNumericResult());
else if( sOperator == "/" )
n->SetResult( nChild1->GetNumericResult() / nChild2->GetNumericResult());
else if( sOperator == "^" || sOperator == "**" )
n->SetResult( pow( nChild1->GetNumericResult(), nChild2->GetNumericResult()));
else if( sOperator == "+=" ){
n->SetResult( nChild1->GetNumericResult() + nChild2->GetNumericResult());
nChild1->SetResult( n->GetNumericResult() );
}
else if( sOperator == "-=" ){
n->SetResult( nChild1->GetNumericResult() - nChild2->GetNumericResult());
nChild1->SetResult( n->GetNumericResult() );
}
else if( sOperator == "*=" ){
n->SetResult( nChild1->GetNumericResult() * nChild2->GetNumericResult());
nChild1->SetResult( n->GetNumericResult() );
}
else if( sOperator == "/=" ){
n->SetResult( nChild1->GetNumericResult() / nChild2->GetNumericResult());
nChild1->SetResult( n->GetNumericResult() );
} else {
iErrorStop = 200;
iRc = XB_PARSE_ERROR;
throw iRc;
}
break;
case XB_EXP_DATE:
// if date values in the leaf nodes, convert to numeric for operator logic
if( sOperator == "+" )
n->SetResult( nChild1->GetNumericResult() + nChild2->GetNumericResult());
else if( sOperator == "-" ){
n->SetResult( nChild1->GetNumericResult() - nChild2->GetNumericResult());
xbDate d( (xbInt32) n->GetNumericResult());
}
else if( sOperator == "+=" ){
n->SetResult( nChild1->GetNumericResult() + nChild2->GetNumericResult());
nChild1->SetResult( n->GetNumericResult() );
}
else if( sOperator == "-=" ){
n->SetResult( nChild1->GetNumericResult() - nChild2->GetNumericResult());
nChild1->SetResult( n->GetNumericResult() );
} else {
iErrorStop = 300;
iRc = XB_PARSE_ERROR;
throw iRc;
}
break;
case XB_EXP_LOGICAL:
if( !n->IsUnaryOperator() && (nChild1->GetReturnType() != nChild2->GetReturnType())){
iErrorStop = 400;
iRc = XB_INCOMPATIBLE_OPERANDS;
throw iRc;
}
if( sOperator == ".AND." || sOperator == "AND" )
n->SetResult((xbBool) (nChild1->GetBoolResult() && nChild2->GetBoolResult()) );
else if( sOperator == ".OR." || sOperator == "OR" )
n->SetResult((xbBool) (nChild1->GetBoolResult() || nChild2->GetBoolResult()) );
else if( sOperator == ".NOT." || sOperator == "NOT" ){
if( nChild1->GetBoolResult())
n->SetResult((xbBool) xbFalse );
else
n->SetResult((xbBool) xbTrue );
}
else if( sOperator == ">" ){
if( nChild1->GetReturnType() == XB_EXP_CHAR )
n->SetResult((xbBool)(nChild1->GetStringResult() > nChild2->GetStringResult()));
else if( nChild1->GetReturnType() == XB_EXP_NUMERIC || nChild1->GetReturnType() == XB_EXP_DATE )
n->SetResult((xbBool)(nChild1->GetNumericResult() > nChild2->GetNumericResult()));
else {
iErrorStop = 410;
iRc = XB_PARSE_ERROR;
throw iRc;
}
}
else if( sOperator == ">=" ){
if( nChild1->GetReturnType() == XB_EXP_CHAR )
n->SetResult((xbBool)(nChild1->GetStringResult() >= nChild2->GetStringResult()));
else if( nChild1->GetReturnType() == XB_EXP_NUMERIC || nChild1->GetReturnType() == XB_EXP_DATE )
n->SetResult((xbBool)(nChild1->GetNumericResult() >= nChild2->GetNumericResult()));
else {
iErrorStop = 420;
iRc = XB_PARSE_ERROR;
throw iRc;
}
}
else if( sOperator == "<" ){
if( nChild1->GetReturnType() == XB_EXP_CHAR )
n->SetResult((xbBool)( nChild1->GetStringResult() < nChild2->GetStringResult()));
else if( nChild1->GetReturnType() == XB_EXP_NUMERIC || nChild1->GetReturnType() == XB_EXP_DATE )
n->SetResult((xbBool)( nChild1->GetNumericResult() < nChild2->GetNumericResult()));
else {
iErrorStop = 430;
iRc = XB_PARSE_ERROR;
throw iRc;
}
}
else if( sOperator == "<=" ){
if( nChild1->GetReturnType() == XB_EXP_CHAR )
n->SetResult((xbBool)( nChild1->GetStringResult() <= nChild2->GetStringResult()));
else if( nChild1->GetReturnType() == XB_EXP_NUMERIC || nChild1->GetReturnType() == XB_EXP_DATE )
n->SetResult((xbBool)( nChild1->GetNumericResult() <= nChild2->GetNumericResult()));
else {
iErrorStop = 440;
iRc = XB_PARSE_ERROR;
throw iRc;
}
}
else if( sOperator == "<>" || sOperator == "#" || sOperator == "!=" ){
if( nChild1->GetReturnType() == XB_EXP_CHAR )
n->SetResult((xbBool)( nChild1->GetStringResult() != nChild2->GetStringResult()));
else if( nChild1->GetReturnType() == XB_EXP_NUMERIC || nChild1->GetReturnType() == XB_EXP_DATE )
n->SetResult((xbBool)( nChild1->GetNumericResult() != nChild2->GetNumericResult()));
else {
iErrorStop = 450;
iRc = XB_PARSE_ERROR;
throw iRc;
}
}
else if( sOperator == "$" ){
if( nChild1->GetReturnType() == XB_EXP_CHAR )
if( nChild2->GetStringResult().Pos( nChild1->GetStringResult()) > 0 )
n->SetResult((xbBool) xbTrue );
else
n->SetResult((xbBool) xbFalse );
else {
iErrorStop = 460;
iRc = XB_INCOMPATIBLE_OPERANDS;
throw iRc;
}
}
else if( sOperator == "=" ){
if( nChild1->GetReturnType() == XB_EXP_CHAR ){
xbString sChld1 = nChild1->GetStringResult();
xbString sChld2 = nChild2->GetStringResult();
sChld1.Rtrim();
sChld2.Rtrim();
n->SetResult((xbBool)( sChld1 == sChld2 ));
} else if( nChild1->GetReturnType() == XB_EXP_NUMERIC || nChild1->GetReturnType() == XB_EXP_DATE )
n->SetResult((xbBool)( nChild1->GetNumericResult() == nChild2->GetNumericResult()));
else {
iErrorStop = 470;
iRc = XB_PARSE_ERROR;
throw iRc;
}
} else {
iErrorStop = 500;
iRc = XB_PARSE_ERROR;
throw iRc;
}
break;
default:
iErrorStop = 600;
iRc = XB_PARSE_ERROR;
throw iRc;
// break;
}
}
catch (xbInt16 iRc ){
xbString sMsg;
sMsg.Sprintf( "xbexp::ProcessExpressionOperator() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc );
xbase->WriteLogMessage( sMsg.Str() );
}
return iRc;
}
/*************************************************************************/
}; // namespace
#endif // XB_EXPRESSION_SUPPORT
/*************************************************************************/