From 4875a3dd9b183dcd2256e2abfc4ccf7484c233b4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Frings-F=C3=BCrst?= Date: Wed, 7 Dec 2022 13:17:14 +0100 Subject: New upstream version 4.0.2 --- src/core/xbexp.cpp | 2643 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 2643 insertions(+) create mode 100755 src/core/xbexp.cpp (limited to 'src/core/xbexp.cpp') diff --git a/src/core/xbexp.cpp b/src/core/xbexp.cpp new file mode 100755 index 0000000..d3e8ca8 --- /dev/null +++ b/src/core/xbexp.cpp @@ -0,0 +1,2643 @@ +/* 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 = 10; + 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 = 20; + 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 = 30; + 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 = 40; + 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 = 50; + 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 = 100; + 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 = 10; + iRc = XB_UNBALANCED_PARENS; + throw iRc; + } + if( bInQuotes ){ + iErrorStop = 20; + 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 = 10; + throw iRc; + } + } + // Check for parens + else if( t.sExpression[1] == '(' || t.sExpression[1] == '{' ){ + if(( iRc = GetTokenParen( t )) != XB_NO_ERROR ){ + iErrorStop = 20; + throw iRc; + } + } + // Check for a char constant + else if( t.sExpression[1] == '"' || t.sExpression[1] == '\'' ){ + if(( iRc = GetTokenCharConstant( t )) != XB_NO_ERROR ){ + iErrorStop = 30; + iRc = XB_PARSE_ERROR; + throw iRc; + } + } + // Check for logical constant + else if( IsLogicalConstant( t.sExpression )){ + if(( iRc = GetTokenLogicalConstant( t )) != XB_NO_ERROR ){ + iErrorStop = 40; + 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 = 50; + iRc = XB_PARSE_ERROR; + throw iRc; + } + } + // check for operator + else if( IsOperator( t.sExpression )){ + if(( iRc = GetTokenOperator( t )) != XB_NO_ERROR ){ + iErrorStop = 60; + iRc = XB_PARSE_ERROR; + throw iRc; + } + } + // check for function + else if( IsFunction( t.sExpression, t.cReturnType )){ + if(( iRc = GetTokenFunction( t )) != XB_NO_ERROR ){ + iErrorStop = 70; + iRc = XB_PARSE_ERROR; + throw iRc; + } + } + else if(( iRc = GetTokenDatabaseField( t )) != XB_NO_ERROR ){ + iErrorStop = 80; + iRc = XB_PARSE_ERROR; + throw iRc; + } + } + + catch (xbInt16 iRc ){ + xbString sMsg; + + //t.sExpression; + // std::cout << ">>>>>>>>>" << sMsg.Str() << "\n"; + // sMsg.Sprintf( "[%d][%s]", iErrorStop, t.sExpression.Str() ); + // sMsg.Sprintf( "xbexp::GetNextToken() Exception Caught. Error Stop = [%d] %s", iErrorStop, 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() );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 = 10; + 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 = 10; + 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 = 20; + 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 field name from before the -> + if( strncmp( s, "->", 2 ) == 0 ){ + 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 = 10; + 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 = 10; + 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 = 20; + 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 = 30; + throw iRc; + } + break; + + case XB_EXP_FUNCTION: + n = new xbExpNode( t.cNodeType ); + if(( iRc = ParseExpressionFunction( t, n, iWeight )) != XB_NO_ERROR ){ + iErrorStop = 40; + throw iRc; + } + break; + + case XB_EXP_FIELD: + n = new xbExpNode( t.cNodeType ); + if(( iRc = ParseExpressionField( t, n )) != XB_NO_ERROR ){ + iErrorStop = 50; + 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 = 60; + throw iRc; + } + break; + + default: + iErrorStop = 70; + 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 = 80; + 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 = 90; + 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 = 100; + iRc = XB_PARSE_ERROR; + throw iRc; + } + if( nWork->GetChildCnt() < 2 ){ + iErrorStop = 110; + 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 = 120; + iRc = XB_PARSE_ERROR; + throw iRc; + // break; + } + } + if( nWork->IsUnaryOperator() ){ + if( nWork->GetChildCnt() != 1 ){ + iErrorStop = 130; + iRc = XB_PARSE_ERROR; + throw iRc; + } + } else if( nWork->IsOperator() && nWork->GetChildCnt() != 2 ){ + iErrorStop = 140; + 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 ); + } else { + // table name is not part of the token + pDbf = dbf; + sFieldName = t.sToken; + } + if( !pDbf ){ + iErrorStop = 10; + iRc = XB_INVALID_FIELD; + throw iRc; + } + xbInt16 iFieldNo = 0; + + if(( iRc = pDbf->GetFieldNo( sFieldName, iFieldNo )) != XB_NO_ERROR ){ + iErrorStop = 20; + throw iRc; + } + char cFieldType; + if(( iRc = pDbf->GetFieldType( iFieldNo, cFieldType )) != XB_NO_ERROR ){ + iErrorStop = 20; + 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 = 30; + iRc = XB_PARSE_ERROR; + throw iRc; + // break; + } + n->SetNodeText( sFieldName ); + xbInt16 iResultLen = 0; + if(( iRc = pDbf->GetFieldLen( iFieldNo, iResultLen )) != XB_NO_ERROR ){ + iErrorStop = 30; + 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 = 10; + 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 = 20; + 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 = 30; + 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 = 40; + 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 = 40; + 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 = 50; + 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 = 10; + 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 = 20; + 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 = 30; + 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 = 40; + throw iRc; + } + nWork->SetResult( sWork1 ); + break; + + case XB_EXP_DATE: + + if(( iRc = dbf->GetField( nWork->GetFieldNo(), sWork1, iRecBufSw )) < XB_NO_ERROR ){ + iErrorStop = 50; + 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 = 60; + throw iRc; + } + nWork->SetResult( bWork ); + break; + + case XB_EXP_NUMERIC: + if(( iRc = dbf->GetDoubleField( nWork->GetFieldNo(), dWork, iRecBufSw )) != XB_NO_ERROR ){ + iErrorStop = 70; + throw iRc; + } + nWork->SetResult( dWork ); + break; + + default: + iErrorStop = 80; + iRc = XB_PARSE_ERROR; + throw iRc; + // break; + } + break; + + + case XB_EXP_OPERATOR: + if(( iRc = ProcessExpressionOperator( nWork )) != XB_NO_ERROR ){ + iErrorStop = 300; + throw iRc; + } + + break; + + case XB_EXP_FUNCTION: + if(( iRc = ProcessExpressionFunction( nWork, iRecBufSw )) != XB_NO_ERROR ){ + iErrorStop = 400; + throw iRc; + } + break; + + default: + iErrorStop = 500; + 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 +/*************************************************************************/ -- cgit v1.2.3