From 4ec7a8e5a1d7039d2e8c436e177001ce57fdbcfe Mon Sep 17 00:00:00 2001 From: stv Date: Fri, 25 May 2007 08:57:40 +0000 Subject: [PATCH] no message --- src/Qtx/QtxEvalExpr.cxx | 1930 +++++++++++++++++++++++++++++++++++++++ src/Qtx/QtxEvalExpr.h | 376 ++++++++ 2 files changed, 2306 insertions(+) create mode 100644 src/Qtx/QtxEvalExpr.cxx create mode 100644 src/Qtx/QtxEvalExpr.h diff --git a/src/Qtx/QtxEvalExpr.cxx b/src/Qtx/QtxEvalExpr.cxx new file mode 100644 index 000000000..c245b3c8b --- /dev/null +++ b/src/Qtx/QtxEvalExpr.cxx @@ -0,0 +1,1930 @@ +// Copyright (C) 2005 OPEN CASCADE, CEA/DEN, EDF R&D, PRINCIPIA R&D +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 2.1 of the License. +// +// This library is distributed in the hope that it will be useful +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +// +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com +// + +#include "QtxEvalExpr.h" + +#include + +/*! + \class QtxEvalExpr + Evaluate string expression. +*/ + +/*! + Constructor +*/ +QtxEvalExpr::QtxEvalExpr( const QString& expr ) +{ + intialize( true, expr ); +} + +/*! + Constructor +*/ +QtxEvalExpr::QtxEvalExpr( const bool stdSets, const QString& expr ) +{ + intialize( stdSets, expr ); +} + +/*! + Destructor +*/ +QtxEvalExpr::~QtxEvalExpr() +{ + delete myParser; +} + +void QtxEvalExpr::intialize( const bool stdSets, const QString& expr ) +{ + myParser = new QtxEvalParser(); + if ( stdSets ) + { + myParser->setAutoDeleteOperationSets( true ); + myParser->insertOperationSet( new QtxEvalSetArithmetic() ); + myParser->insertOperationSet( new QtxEvalSetLogic() ); + myParser->insertOperationSet( new QtxEvalSetString() ); + myParser->insertOperationSet( new QtxEvalSetMath() ); + myParser->insertOperationSet( new QtxEvalSetSets() ); + } + setExpression( expr ); +} + +/*! + Calculates the expression +*/ +QVariant QtxEvalExpr::calculate( const QString& expr ) +{ + if ( !expr.isEmpty() ) + setExpression( expr ); + return myParser->calculate(); +} + +/*! + Returns the expression string +*/ +QString QtxEvalExpr::expression() const +{ + return myExpr; +} + +/*! + Sets the expression string +*/ +void QtxEvalExpr::setExpression( const QString& expr ) +{ + if ( expr == expression() ) + return; + + myExpr = expr; + myParser->setExpression( myExpr ); +} + +/*! + Returns the code of the last error +*/ +QtxEvalExpr::Error QtxEvalExpr::error() const +{ + return myParser->error(); +} + +/*! + Returns parser +*/ +QtxEvalParser* QtxEvalExpr::parser() const +{ + return myParser; +} + +QList QtxEvalExpr::operationSets() const +{ + return myParser->operationSets(); +} + +void QtxEvalExpr::insertOperationSet( QtxEvalSet* set, const int idx ) +{ + myParser->insertOperationSet( set, idx ); +} + +void QtxEvalExpr::removeOperationSet( QtxEvalSet* set ) +{ + myParser->removeOperationSet( set ); +} + +QtxEvalSet* QtxEvalExpr::operationSet( const QString& name ) const +{ + return myParser->operationSet( name ); +} + +/*! + \class QtxEvalParser + Expression parser. +*/ + +/*! + Constructor +*/ +QtxEvalParser::QtxEvalParser() +: myAutoDel( false ) +{ + setError( QtxEvalExpr::OK ); +} + +/*! + Destructor +*/ +QtxEvalParser::~QtxEvalParser() +{ + if ( autoDeleteOperationSets() ) + qDeleteAll( mySets ); +} + +QList QtxEvalParser::operationSets() const +{ + return mySets; +} + +QtxEvalSet* QtxEvalParser::operationSet( const QString& name ) const +{ + QtxEvalSet* set = 0; + for ( SetList::const_iterator it = mySets.begin(); it != mySets.end() && !set; ++it ) + { + if ( (*it)->name() == name ) + set = *it; + } + return set; +} + +void QtxEvalParser::insertOperationSet( QtxEvalSet* set, const int idx ) +{ + if ( mySets.contains( set ) ) + return; + + int index = idx < 0 ? mySets.count() - 1 : idx; + index = qMin( index, mySets.count() - 1 ); + mySets.insert( index, set ); +} + +void QtxEvalParser::removeOperationSet( QtxEvalSet* set ) +{ + mySets.removeAll( set ); +} + +bool QtxEvalParser::autoDeleteOperationSets() const +{ + return myAutoDel; +} + +void QtxEvalParser::setAutoDeleteOperationSets( const bool on ) +{ + myAutoDel = on; +} + +/*! + Search elements of list as substrings starting on 'offset' + \returns the least position of substrings inside string + \param list - list of substrings + \param str - string where search + \param offset - starting index for search + \param matchLen - the length of appropriate substring + \param listind - list index of appropriate substring +*/ +int QtxEvalParser::search( const QStringList& list, const QString& str, + int offset, int& matchLen, int& listind ) +{ + int min = -1; + QStringList::const_iterator anIt = list.begin(), aLast = list.end(); + for ( int ind = 0; anIt != aLast; anIt++, ind++ ) + { + int pos = str.indexOf( *anIt, offset ); + if ( pos >= 0 && ( min < 0 || min > pos || + ( min == pos && matchLen < (int)(*anIt).length() ) ) ) + { + min = pos; + listind = ind; + matchLen = (*anIt).length(); + } + } + if ( min < 0 ) + matchLen = 0; + return min; +} + +/*! + \return substring + \param str - string + \param pos - start position of substring + \param len - length of substring +*/ +QString QtxEvalParser::note( const QString& str, int pos, int len ) +{ + return str.mid( pos, len ).trimmed(); +} + +/*! + First step of parsing: finding tokens, determining its types and creating of unsorted pseudo-postfix (with brackets) + \param expr - string expression + \param post - postfix to be created +*/ +bool QtxEvalParser::prepare( const QString& expr, Postfix& post ) +{ + int pos = 0; + int len = expr.length(); + QStack aBracketStack; + QStringList anOpers, anOpenBr, aCloseBr; + if ( !checkOperations() ) + return false; + + bracketsList( anOpenBr, true ); + bracketsList( aCloseBr, false ); + operationList( anOpers ); + + while ( pos < len && error() == QtxEvalExpr::OK ) + { + PostfixItem item; + while ( expr[pos].isSpace() && pos < len ) + pos++; + if ( pos >= len ) + break; + + int mBrLen = 0, mLen = 0, br_ind = -1, op_ind = -1; + int oPos = search( anOpenBr, expr, pos, mBrLen, br_ind ); + int cPos = oPos == pos ? -1 : search( aCloseBr, expr, pos, mBrLen, br_ind ); + int opPos = search( anOpers, expr, pos, mLen, op_ind ); + + if ( expr[pos] == QChar( '\'' ) ) + { + int vpos = pos + 1; + while ( vpos < (int)expr.length() && expr[vpos] != QChar( '\'' ) ) + vpos++; + + mLen = vpos - pos + 1; + + int res = createValue( note( expr, pos, mLen ), item.myValue ); + item.myType = res ? Value : Param; + post.append( item ); + pos = vpos + 1; + continue; + } + + if ( oPos == pos ) + { + aBracketStack.push( br_ind ); + item.myValue = note( expr, pos, mBrLen ); + item.myType = Open; + post.append( item ); + } + else if ( cPos == pos ) + { + if ( aBracketStack.count() == 0 ) + { + setError( QtxEvalExpr::ExcessClose ); + break; + } + if ( br_ind != aBracketStack.top() ) + { + setError( QtxEvalExpr::BracketsNotMatch ); + break; + } + else + { + aBracketStack.pop(); + item.myValue = note( expr, pos, mBrLen ); + item.myType = Close; + post.append( item ); + } + } + else + mBrLen = 0; + + if ( opPos == pos ) + { + mBrLen = 0; + item.myValue = note( expr, pos, mLen ); + item.myType = Binary; + + if ( oPos == pos ) + post.insert( post.count() - 1, item ); + else + post.append( item ); + } + else + { + mLen = 0; + if ( oPos != pos && cPos != pos ) + { + int i; + for ( i = pos + 1; i < (int)expr.length(); i++ ) + { + if ( expr[i].isSpace() ) + break; + } + + int vpos = i; + if ( oPos >= 0 && oPos < vpos ) + vpos = oPos; + if ( cPos >= 0 && cPos < vpos ) + vpos = cPos; + if ( opPos >= 0 && opPos < vpos ) + vpos = opPos; + + while( vpos < (int)expr.length() && + ( expr[vpos].isLetter() || expr[vpos].isDigit() || expr[vpos]=='_' ) ) + vpos++; + + mLen = vpos - pos; + bool res = createValue( note( expr, pos, mLen ), item.myValue ); + item.myType = res ? Value : Param; + post.append( item ); + } + } + + pos += mBrLen + mLen; + } + + //Bracket checking + int brValue = 0; + for ( Postfix::iterator anIt = post.begin(); anIt != post.end(); ++anIt ) + { + if ( (*anIt).myType == Open ) + brValue++; + else if ( (*anIt).myType == Close ) + { + if ( brValue > 0 ) + brValue--; + else + { + setError( QtxEvalExpr::ExcessClose ); + break; + } + } + } + + if ( brValue > 0 ) + setError( QtxEvalExpr::CloseExpected ); + + return error() == QtxEvalExpr::OK; +} + +/*! + Second step of parsing: determining types of operations + \param post - unsorted postfix +*/ +bool QtxEvalParser::setOperationTypes( Postfix& post ) +{ + if ( !checkOperations() ) + return false; + + QStringList anOpen, aClose; + bracketsList( anOpen, true ); + bracketsList( aClose, false ); + + Postfix::iterator aPrev, aNext; + for ( Postfix::iterator anIt = post.begin(); anIt != post.end(); ++anIt ) + { + aPrev = anIt; + aPrev--; + aNext = anIt; + aNext++; + if ( (*anIt).myType != Binary ) + continue; + + if ( ( anIt == post.begin() || (*aPrev).myType == Open || + (*aPrev).myType == Pre || (*aPrev).myType == Binary ) && aNext != post.end() && + ( (*aNext).myType == Value || (*aNext).myType == Param || + (*aNext).myType == Open || (*aNext).myType == Binary ) ) + (*anIt).myType = Pre; + else if ( anIt != post.begin() && ( (*aPrev).myType == Close || (*aPrev).myType == Param || + (*aPrev).myType == Value || (*aPrev).myType == Pre || + (*aPrev).myType == Post || (*aPrev).myType == Binary ) && + ( aNext == post.end() || (*aNext).myType == Close ) ) + (*anIt).myType = Post; + + if ( anOpen.contains( (*anIt).myValue.toString() ) ) + (*anIt).myType = Pre; + else if ( aClose.contains( (*anIt).myValue.toString() ) ) + (*anIt).myType = Post; + } + + return error() == QtxEvalExpr::OK; +} + +/*! + \return how many global brackets there is (for example '((2+3))' has 2 global brackets) + \param post - postfix to be checked + \param f - start index to search + \param l - last index to search +*/ +int QtxEvalParser::globalBrackets( const QtxEvalParser::Postfix& post, int f, int l ) +{ + int i, + start_br = 0, + fin_br = 0, + br = 0, + br_num = 0, + min_br_num = (l-f+1)*5; + + for( i=f; i<=l; i++ ) + if( post[ i ].myType==QtxEvalParser::Open ) + start_br++; + else + break; + for( i=l; i>=f; i-- ) + if( post[ i ].myType==QtxEvalParser::Close ) + fin_br++; + else + break; + + br = start_br= 0 ) + res.append( post[f] ); + + if ( l <= f ) + return true; + + if ( !checkOperations() ) + return false; + + int min = -1; + QIntList argmin; + QList min_types; + + for ( int i = 0, j = f; j <= l; i++, j++ ) + { + const PostfixItem& item = post[j]; + PostfixItemType tt = item.myType; + if ( tt == Binary || tt == Pre || tt == Post ) + { + int cur_pr = priority( item.myValue.toString(), tt == Binary ); + if ( cur_pr > 0 ) + { + if ( min < 0 || min >= cur_pr ) + { + if ( min == cur_pr ) + { + argmin.append( f + i ); + min_types.append( tt ); + } + else + { + min = cur_pr; + argmin.clear(); + argmin.append( f + i ); + min_types.clear(); min_types.append( tt ); + } + } + } + else + { + setError( QtxEvalExpr::InvalidOperation ); + break; + } + } + else if ( tt == Open ) + { + QString opBr = item.myValue.toString(); + int ind = anOpen.indexOf( opBr ), brValue = 0; + while ( j <= l ) + { + const PostfixItem& anItem = post[j]; + if ( anItem.myType == Open ) + brValue++; + + if ( anItem.myType == Close ) + { + brValue--; + QString clBr = anItem.myValue.toString(); + if ( aClose.indexOf( clBr ) == ind && brValue == 0 ) + break; + } + i++; j++; + } + + if ( brValue > 0 ) + { + setError( QtxEvalExpr::CloseExpected ); + break; + } + } + } + + if ( error() == QtxEvalExpr::OK ) + { + if ( min >= 0 ) + { + Postfix one; + QList parts; + QIntList::const_iterator anIt = argmin.begin(), aLast = argmin.end(); + bool ok = sort( post, one, anOpen, aClose, f, *anIt - 1 ); + parts.append( one ); + one.clear(); + for ( ; anIt != aLast && ok; anIt++ ) + { + QIntList::const_iterator aNext = anIt; aNext++; + ok = sort( post, one, anOpen, aClose, *anIt + 1, aNext == aLast ? l : *aNext - 1 ); + parts.append( one ); + one.clear(); + } + + if ( !ok ) + return false; + + QStack aStack; + QList::const_iterator aPIt = parts.begin(); + QList::const_iterator aTIt = min_types.begin(); + res += (*aPIt); + aPIt++; + anIt = argmin.begin(); + for ( ; anIt != aLast; anIt++, aPIt++, aTIt++ ) + { + if ( *aTIt == Pre ) + { + if ( anOpen.contains( post[*anIt].myValue.toString() ) == 0 ) + { + res += (*aPIt); + aStack.push( post[ *anIt ] ); + } + else + { + res.append( post[*anIt] ); + res += *aPIt; + } + } + else + { + res += *aPIt; + while ( !aStack.isEmpty() ) + { + res.append( aStack.top() ); + aStack.pop(); + } + res.append( post[*anIt] ); + } + } + while ( !aStack.isEmpty() ) + { + res.append( aStack.top() ); + aStack.pop(); + } + } + else + { //there are no operations + for ( int k = f; k <= l; k++ ) + { + if ( post.at( k ).myType==Value || post.at( k ).myType == Param ) + res.append( post.at( k ) ); + } + } + } + + return error() == QtxEvalExpr::OK; +} + +/*! + Build posfix by expression + \param expr - string expression +*/ +bool QtxEvalParser::parse( const QString& expr ) +{ + myPostfix.clear(); + + if ( !checkOperations() ) + return false; + + Postfix p; + QStringList opens, closes; + + setError( QtxEvalExpr::OK ); + bracketsList( opens, true ); + bracketsList( closes, false ); + + return prepare( expr, p ) && setOperationTypes( p ) && sort( p, myPostfix, opens, closes ); +} + +/*! + Calculate operation + \param op - operation name + \param v1 - first argument (it is not valid for unary prefix operations and it is used to store result) + \param v2 - second argument (it is not valid for unary postfix operations) +*/ +bool QtxEvalParser::calculate( const QString& op, QVariant& v1, QVariant& v2 ) +{ + QtxEvalExpr::Error err = isValid( op, v1.type(), v2.type() ); + if ( err == QtxEvalExpr::OK ) + setError( calculation( op, v1, v2 ) ); + else + setError( err ); + + return error() == QtxEvalExpr::OK; +} + +/*! + Calculates expression without postfix rebuilding + \return QVariant as result (it is invalid if there were errors during calculation) +*/ +QVariant QtxEvalParser::calculate() +{ + if ( !checkOperations() ) + return QVariant(); + + setError( QtxEvalExpr::OK ); + + QStringList anOpen, aClose; + bracketsList( anOpen, true ); + bracketsList( aClose, false ); + + QStack aStack; + Postfix::iterator anIt = myPostfix.begin(), aLast = myPostfix.end(); + for ( ; anIt != aLast && error() == QtxEvalExpr::OK; anIt++ ) + { + QString nn = (*anIt).myValue.toString(); + if ( (*anIt).myType == Param ) + { + if ( hasParameter( nn ) ) + { + QVariant& v = myParams[nn]; + if ( v.isValid() ) + aStack.push( v ); + else + setError( QtxEvalExpr::InvalidToken ); + } + else + setError( QtxEvalExpr::InvalidToken ); + } + else if ( (*anIt).myType == Value ) + aStack.push( (*anIt).myValue ); + else if ( (*anIt).myType == Pre || (*anIt).myType == Post ) + { + if ( anOpen.contains( nn ) ) + { + QVariant inv; + if ( calculate( nn, inv, inv ) ) + aStack.push( QVariant() ); + } + else if ( aClose.contains( nn ) ) + { + QList set; + while ( true ) + { + if ( aStack.isEmpty() ) + { + setError( QtxEvalExpr::StackUnderflow ); + break; + } + if ( aStack.top().isValid() ) + { + set.append( aStack.top() ); + aStack.pop(); + } + else + { + aStack.pop(); + break; + } + } + + QVariant qSet = set, inv; + if ( calculate( nn, qSet, inv ) ) + aStack.push( set ); + } + else if ( aStack.count() >= 1 ) + { + QVariant inv; + QVariant* v1 = &aStack.top(), *v2 = &inv; //"post-" case + if ( (*anIt).myType == Pre ) + { + v2 = &aStack.top(); + v1 = &inv; + } + calculate( nn, *v1, *v2 ); + } + else + setError( QtxEvalExpr::StackUnderflow ); + } + else if ( (*anIt).myType == Binary ) + { + if ( aStack.count() >= 2 ) + { + QVariant v2 = aStack.top(); + aStack.pop(); + calculate( nn, aStack.top(), v2 ); + } + else + setError( QtxEvalExpr::StackUnderflow ); + } + } + + QVariant res; + if ( error() == QtxEvalExpr::OK ) + { + int count = aStack.count(); + if ( count == 0 ) + setError( QtxEvalExpr::StackUnderflow ); + else if( count == 1 ) + res = aStack.top(); + else + setError( QtxEvalExpr::ExcessData ); + } + return res; +} + +/*! + Change expression, rebuild postfix and calculate it + \return QVariant as result (it is invalid if there were errors during calculation) +*/ +QVariant QtxEvalParser::calculate( const QString& expr ) +{ + setExpression( expr ); + return calculate(); +} + +/*! + Change expression and rebuild postfix +*/ +bool QtxEvalParser::setExpression( const QString& expr ) +{ + return parse( expr ); +} + +/*! + \return true, if parser contain parameter + \param name - name of parameter +*/ +bool QtxEvalParser::hasParameter( const QString& name ) const +{ + return myParams.contains( name.trimmed() ); +} + +/*! + Sets parameter value + \param name - name of parameter + \param value - value of parameter +*/ +void QtxEvalParser::setParameter( const QString& name, const QVariant& value ) +{ + myParams.insert( name.trimmed(), value ); +} + +/*! + Removes parameter + \param name - name of parameter +*/ +bool QtxEvalParser::removeParameter( const QString& name ) +{ + return myParams.remove( name.trimmed() ); +} + +/*! + \return value of parameter (result is invalid if there is no such parameter) + \param name - name of parameter +*/ +QVariant QtxEvalParser::parameter( const QString& name ) const +{ + QVariant res; + if ( myParams.contains( name.trimmed() ) ) + res = myParams[name.trimmed()].toString(); + return res; +} + +/*! + Searches first parameter with assigned invalid QVariant + \return true if it is found + \param name - variable to return name of parameter +*/ +bool QtxEvalParser::firstInvalid( QString& name ) const +{ + for ( ParamMap::const_iterator anIt = myParams.begin(); anIt != myParams.end(); anIt++ ) + { + if ( !anIt.value().isValid() ) + { + name = anIt.key(); + return true; + } + } + return false; +} + +/*! + Removes all parameters with assigned invalid QVariants +*/ +void QtxEvalParser::removeInvalids() +{ + QStringList toDelete; + for ( ParamMap::const_iterator anIt = myParams.begin(); anIt != myParams.end(); anIt++ ) + { + if ( !anIt.value().isValid() ) + toDelete.append( anIt.key() ); + } + + for ( QStringList::const_iterator aLIt = toDelete.begin(); aLIt != toDelete.end(); aLIt++ ) + myParams.remove( *aLIt ); +} + +/*! + \return last error occured during parsing +*/ +QtxEvalExpr::Error QtxEvalParser::error() const +{ + return myError; +} + +/*! + Sets last error occured during parsing (for internal using only) +*/ +void QtxEvalParser::setError( QtxEvalExpr::Error err ) +{ + myError = err; +} + +/*! + \return string dump of internal parser postfix +*/ +QString QtxEvalParser::dump() const +{ + return dump( myPostfix ); +} + +/*! + \return string dump of postfix + \param post - postfix to be dumped +*/ +QString QtxEvalParser::dump( const Postfix& post ) const +{ + QString res; + + if ( !checkOperations() ) + return res; + + for ( Postfix::const_iterator anIt = post.begin(); anIt != post.end(); anIt++ ) + { + if ( (*anIt).myType == Value && (*anIt).myValue.type() == QVariant::String ) + res += "'" + (*anIt).myValue.toString() + "'"; + else + res += (*anIt).myValue.toString(); + + if ( (*anIt).myType == Pre ) + res += "(pre)"; + else if ( (*anIt).myType == Post ) + res += "(post)"; + else if ( (*anIt).myType == Binary ) + res += "(bin)"; + + res += "_"; + } + + return res; +} + +/*! + Fills and returns list with names of parameters +*/ +QStringList QtxEvalParser::parameters() const +{ + QStringList lst; + for ( Postfix::const_iterator anIt = myPostfix.begin(); anIt != myPostfix.end(); anIt++ ) + { + if ( (*anIt).myType == Param ) + { + QString name = (*anIt).myValue.toString(); + if ( !lst.contains( name ) ) + lst.append( name ); + } + } + return lst; +} + +/*! + Removes all parameters +*/ +void QtxEvalParser::clearParameters() +{ + myParams.clear(); +} + +/*! + \return string representation for list of QVariants + \param list - list to be converted +*/ +QString QtxEvalParser::toString( const QList& list ) +{ + QString res = "set : [ "; + for ( QList::const_iterator anIt = list.begin(); anIt != list.end(); anIt++ ) + res += (*anIt).toString() + " "; + res += "]"; + return res; +} + +void QtxEvalParser::operationList( QStringList& list ) const +{ + for ( SetList::const_iterator it = mySets.begin(); it != mySets.end(); ++it ) + { + QStringList custom; + QtxEvalSet* set = *it; + set->operationList( custom ); + for ( QStringList::const_iterator sIt = custom.begin(); sIt != custom.end(); ++sIt ) + { + if ( !list.contains( *sIt ) ) + list.append( *sIt ); + } + } +} + +void QtxEvalParser::bracketsList( QStringList& list, bool open ) const +{ + for ( SetList::const_iterator it = mySets.begin(); it != mySets.end(); ++it ) + { + QStringList custom; + QtxEvalSet* set = *it; + set->bracketsList( custom, open ); + for ( QStringList::const_iterator sIt = custom.begin(); sIt != custom.end(); ++sIt ) + { + if ( !list.contains( *sIt ) ) + list.append( *sIt ); + } + } +} + +bool QtxEvalParser::createValue( const QString& str, QVariant& val ) const +{ + bool ok = false; + for ( SetList::const_iterator it = mySets.begin(); it != mySets.end() && !ok; ++it ) + ok = (*it)->createValue( str, val ); + return ok; +} + +int QtxEvalParser::priority( const QString& op, bool isBin ) const +{ + int i = 0; + int priority = 0; + for ( SetList::const_iterator it = mySets.begin(); it != mySets.end() && priority <= 0; ++it, i++ ) + priority = (*it)->priority( op, isBin ); + + return priority > 0 ? priority + i * 10 : 0; +} + +QtxEvalExpr::Error QtxEvalParser::isValid( const QString& op, + const QVariant::Type t1, const QVariant::Type t2 ) const +{ + QtxEvalExpr::Error err = QtxEvalExpr::OK; + for ( SetList::const_iterator it = mySets.begin(); it != mySets.end(); ++it ) + { + err = (*it)->isValid( op, t1, t2 ); + if ( err == QtxEvalExpr::OK ) + break; + } + return err; +} + +QtxEvalExpr::Error QtxEvalParser::calculation( const QString& op, QVariant& v1, QVariant& v2 ) const +{ + QVariant nv1, nv2; + for ( SetList::const_iterator it = mySets.begin(); it != mySets.end(); ++it ) + { + nv1 = v1; + nv2 = v2; + if ( (*it)->isValid( op, v1.type(), v2.type() ) == QtxEvalExpr::OK ) + { + QtxEvalExpr::Error err = (*it)->calculate( op, nv1, nv2 ); + if ( err == QtxEvalExpr::OK || err == QtxEvalExpr::InvalidResult ) + { + v1 = nv1; + v2 = nv2; + return err; + } + } + } + return QtxEvalExpr::InvalidOperation; +} + +bool QtxEvalParser::checkOperations() const +{ + if ( !mySets.isEmpty() ) + return true; + + QtxEvalParser* that = (QtxEvalParser*)this; + that->setError( QtxEvalExpr::OperationsNull ); + return false; +} + +/*! + \class QtxEvalSet + Set of operation for expression. +*/ + +/*! + Constructor +*/ +QtxEvalSet::QtxEvalSet() +{ +} + +/*! + Destructor +*/ +QtxEvalSet::~QtxEvalSet() +{ +} + +/*! + Creates QVariant by it's string representation +*/ +bool QtxEvalSet::createValue( const QString& str, QVariant& v ) const +{ + v = str; + return false; +} + +// ------------------------------- Standard operation sets ------------------------------ + +/*! + \class QtxEvalSetBase + Provides base functionality for standard sets of operations for parser +*/ + +/*! + Default constructor +*/ +QtxEvalSetBase::QtxEvalSetBase() +{ +} + +/*! + Destructor +*/ +QtxEvalSetBase::~QtxEvalSetBase() +{ +} + +/*! + Fills list of brackets treated as open (when 'open' is 'true') or close ('open' is 'false') +*/ +void QtxEvalSetBase::bracketsList( QStringList& list, bool open ) const +{ + list.append( open ? "(" : ")" ); +} + +/*! + Fills list with operation names by copying of internal list of operations +*/ +void QtxEvalSetBase::operationList( QStringList& list ) const +{ + list += myOpers; +} + +/*! + Add operation names from list to internal list of operations +*/ +void QtxEvalSetBase::addOperations( const QStringList& list ) +{ + for ( QStringList::const_iterator anIt = list.begin(); anIt != list.end(); ++anIt ) + { + if ( !myOpers.contains( *anIt ) ) + myOpers.append( *anIt ); + } +} + +/*! + Append operation names from 'list' to internal list of operations +*/ +void QtxEvalSetBase::addTypes( const ListOfTypes& list ) +{ + for ( ListOfTypes::const_iterator anIt = list.begin(); anIt != list.end(); ++anIt ) + { + if ( !myTypes.contains( *anIt ) ) + myTypes.append( *anIt ); + } +} + +/*! + \return whether values with passed types are valid for arguments of operation + \param op - name of operation + \param t1 - type of first argument + \param t2 - type of second argument +*/ +QtxEvalExpr::Error QtxEvalSetBase::isValid( const QString& op, + const QVariant::Type t1, const QVariant::Type t2 ) const +{ + if ( ( t1 == QVariant::Invalid || myTypes.contains( t1 ) ) && + ( t2 == QVariant::Invalid || myTypes.contains( t2 ) ) && + ( t1 != QVariant::Invalid || t2 != QVariant::Invalid ) ) + { + if ( priority( op, t1 != QVariant::Invalid && t2 != QVariant::Invalid ) > 0 ) + return QtxEvalExpr::OK; + else + return QtxEvalExpr::InvalidOperation; + } + else + return QtxEvalExpr::OperandsNotMatch; +} + +/*! + \class QtxEvalSetArithmetic + Provides set of arithmetic operations for parser +*/ + +/*! + Default constructor +*/ +QtxEvalSetArithmetic::QtxEvalSetArithmetic() +: QtxEvalSetBase() +{ + addOperations( QString( "+;-;*;/;=;<;>;<=;>=;<>;!=" ).split( ";" ) ); + + ListOfTypes aTypes; + aTypes.append( QVariant::Int ); + aTypes.append( QVariant::UInt ); + aTypes.append( QVariant::Double ); + addTypes( aTypes ); +} + +/*! + Destructor +*/ +QtxEvalSetArithmetic::~QtxEvalSetArithmetic() +{ +} + +QString QtxEvalSetArithmetic::Name() +{ + return "Arithmetic"; +} + +QString QtxEvalSetArithmetic::name() const +{ + return Name(); +} + +/*! + Creates numbers by it's string representation [redefined virtual] +*/ +bool QtxEvalSetArithmetic::createValue( const QString& str, QVariant& v ) const +{ + bool ok = false; + v = str.toInt( &ok ); + + if ( !ok ) + { + v = str.toDouble( &ok ); + if ( !ok ) + ok = QtxEvalSetBase::createValue( str, v ); + } + return ok; +} + +/*! + \return priority of arithmetic operation 'op'. + \param isBin indicate whether the operation is binary +*/ +int QtxEvalSetArithmetic::priority( const QString& op, bool isBin ) const +{ + if ( isBin ) + { + if ( op == "<" || op == ">" || op == "=" || + op == "<=" || op == ">=" || op == "<>" || op == "!=" ) + return 1; + else if ( op == "+" || op == "-" ) + return 2; + else if( op == "*" || op == "/" ) + return 3; + else + return 0; + } + else if ( op == "+" || op == "-" ) + return 5; + else + return 0; +} +/* +void set( QVariant& v1, bool v2 ) +{ + v1 = QVariant( v2 ); +} +*/ +/*! + Calculates result of operation + \return one of error states + \param op - name of operation + \param v1 - first operation argument (must be used also to store result) + \param v2 - second operation argument +*/ +QtxEvalExpr::Error QtxEvalSetArithmetic::calculate( const QString& op, QVariant& v1, QVariant& v2 ) const +{ + QtxEvalExpr::Error err = QtxEvalExpr::OK; + + if ( v1.isValid() && v2.isValid() ) + { + // binary operations + if ( ( v1.type() == QVariant::Int || v1.type() == QVariant::UInt ) && + ( v2.type() == QVariant::Int || v2.type() == QVariant::UInt ) ) + { + int _v1 = v1.toInt(); + int _v2 = v2.toInt(); + + if ( op == "+" ) + v1 = _v1 + _v2; + else if ( op == "-" ) + v1 = _v1 - _v2; + else if ( op == "*" ) + v1 = _v1 * _v2; + else if ( op == "/" ) + { + if ( _v2 != 0 ) + { + if ( _v1 % _v2 == 0 ) + v1 = _v1 / _v2; + else + v1 = double( _v1 ) / double( _v2 ); + } + else + err = QtxEvalExpr::InvalidResult; + } + else if ( op == "<" ) + v1 = _v1 < _v2; + else if ( op == ">" ) + v1 = _v1 > _v2; + else if ( op == "=" ) + v1 = _v1 == _v2; + else if ( op == "<=" ) + v1 = _v1 <= _v2; + else if ( op == ">=" ) + v1 = _v1 >= _v2; + else if ( op == "<>" || op == "!=" ) + v1 = _v1 != _v2; + } + else if ( ( v1.type() == QVariant::Int || v1.type() == QVariant::Double ) && + ( v2.type() == QVariant::Int || v2.type() == QVariant::Double ) ) + { + double _v1 = v1.toDouble(); + double _v2 = v2.toDouble(); + + if ( op == "+" ) + v1 = _v1 + _v2; + else if ( op == "-" ) + v1 = _v1 - _v2; + else if ( op == "*" ) + v1 = _v1 * _v2; + else if ( op == "/" ) + { + if ( _v2 != 0 ) + v1 = _v1 / _v2; + else + err = QtxEvalExpr::InvalidResult; + } + else if ( op == "<" ) + v1 = _v1 < _v2; + else if ( op == ">" ) + v1 = _v1 > _v2; + else if ( op == "=" ) + v1 = _v1 == _v2; + else if ( op == "<=" ) + v1 = _v1 <= _v2; + else if ( op == ">=" ) + v1 = _v1 >= _v2; + else if ( op == "<>" || op == "!=" ) + v1 = _v1 != _v2; + } + else // prefix operations + { + if ( op == "-" ) + { + if ( v2.type() == QVariant::Int ) + v2 = -v2.toInt(); + else if ( v2.type() == QVariant::Double ) + v2 = -v2.toDouble(); + } + } + } + + return err; +} + +/*! + \class QtxEvalSetLogic + Provides set of logic operations for parser +*/ + +/*! + Default constructor +*/ +QtxEvalSetLogic::QtxEvalSetLogic() +: QtxEvalSetBase() +{ + addOperations( QString( "and;&&;or;||;xor;not;!;imp;=" ).split( ";" ) ); + + ListOfTypes aTypes; + aTypes.append( QVariant::Bool ); + aTypes.append( QVariant::Int ); + aTypes.append( QVariant::UInt ); + addTypes( aTypes ); +} + +/*! + Destructor +*/ +QtxEvalSetLogic::~QtxEvalSetLogic() +{ +} + +QString QtxEvalSetLogic::Name() +{ + return "Logic"; +} + +QString QtxEvalSetLogic::name() const +{ + return Name(); +} + +/*! + Creates value 'true' or 'false' it's string representation [redefined virtual] +*/ +bool QtxEvalSetLogic::createValue( const QString& str, QVariant& v ) const +{ + bool ok = true; + QString valStr = str.toLower(); + if ( valStr == "true" || valStr == "yes" ) + v = QVariant( true ); + else if ( valStr == "false" || valStr == "no" ) + v = QVariant( false ); + else + ok = QtxEvalSetBase::createValue( str, v ); + + return ok; +} + +/*! + \return priority of arithmetic operation 'op'. + \param isBin indicate whether the operation is binary +*/ +int QtxEvalSetLogic::priority( const QString& op, bool isBin ) const +{ + if ( isBin ) + { + if ( op == "and" || op == "or" || op == "xor" || op == "&&" || op == "||" || op == "imp" ) + return 1; + else if ( op == "=" ) + return 2; + else + return 0; + } + else if ( op == "not" || op == "!" ) + return 5; + else + return 0; +} + +/*! + Calculates result of operation + \return one of error states + \param op - name of operation + \param v1 - first operation argument (must be used also to store result) + \param v2 - second operation argument +*/ +QtxEvalExpr::Error QtxEvalSetLogic::calculate( const QString& op, QVariant& v1, QVariant& v2 ) const +{ + QtxEvalExpr::Error err = QtxEvalExpr::OK; + bool val1 = booleanValue( v1 ); + bool val2 = booleanValue( v2 ); + if ( v1.isValid() && v2.isValid() ) + { + if ( op == "and" || op == "&&" ) + v1 = val1 && val2; + else if ( op == "or" || op == "||" ) + v1 = val1 || val2; + else if ( op == "xor" ) + v1 = ( !val1 && val2 ) || ( val1 && !val2 ); + else if ( op == "imp" ) + v1 = !val1 || val2; + else if ( op == "=" ) + v1 = val1 == val2; + } + else if ( op == "not" || op == "!" ) + v2 = !val2; + + return err; +} + +bool QtxEvalSetLogic::booleanValue( const QVariant& v ) const +{ + bool res = false; + switch ( v.type() ) + { + case QVariant::Bool: + res = v.toBool(); + break; + case QVariant::Int: + res = v.toInt() != 0; + break; + case QVariant::UInt: + res = v.toUInt() != 0; + break; + default: + res = false; + break; + } + return res; +} + +/*! + \class QtxEvalSetMath + Provides set of more complex operations (mathematics functions) for parser (sqrt, sin, cos, etc) +*/ + +/*! + Default constructor +*/ +QtxEvalSetMath::QtxEvalSetMath() +: QtxEvalSetBase() +{ + addOperations( QString( "sqrt;abs;sin;cos;rad2grad;grad2rad" ).split( ";" ) ); + + ListOfTypes aTypes; + aTypes.append( QVariant::Int ); + aTypes.append( QVariant::Double ); + addTypes( aTypes ); +} + +/*! + Destructor +*/ +QtxEvalSetMath::~QtxEvalSetMath() +{ +} + +QString QtxEvalSetMath::Name() +{ + return "Math"; +} + +QString QtxEvalSetMath::name() const +{ + return Name(); +} + +/*! + Creates numbers by it's string representation [redefined virtual] +*/ +bool QtxEvalSetMath::createValue( const QString& str, QVariant& v ) const +{ + bool ok = false; + v = str.toInt( &ok ); + + if ( !ok ) + { + v = str.toDouble( &ok ); + if ( !ok ) + ok = QtxEvalSetBase::createValue( str, v ); + } + return ok; +} + +/*! + \return priority of arithmetic operation 'op'. + \param isBin indicate whether the operation is binary +*/ +int QtxEvalSetMath::priority( const QString& op, bool isBin ) const +{ + if ( isBin ) + return 0; + else if ( op == "sqrt" || op == "abs" || op == "sin" || + op == "cos" || op == "rad2grad" || op == "grad2rad" ) + return 1; + else + return 0; +} + +/*! + Calculates result of operation + \return one of error states + \param op - name of operation + \param v1 - first operation argument (must be used also to store result) + \param v2 - second operation argument +*/ +QtxEvalExpr::Error QtxEvalSetMath::calculate( const QString& op, QVariant& v1, QVariant& v2 ) const +{ + QtxEvalExpr::Error err = QtxEvalExpr::OK; + double val = v2.toDouble(); + if ( op == "sqrt" ) + { + if ( val >= 0 ) + v2 = sqrt( val ); + else + err = QtxEvalExpr::InvalidResult; + } + else if ( op == "abs" ) + { + if ( v2.type() == QVariant::Int ) + v2 = abs( v2.toInt() ); + else + v2 = fabs( v2.toDouble() ); + } + else if ( op == "sin" ) + v2 = sin( val ); + else if ( op == "cos" ) + v2 = cos( val ); + else if ( op == "grad2rad" ) + v2 = val * 3.14159256 / 180.0; + else if ( op == "rad2grad" ) + v2 = val * 180.0 / 3.14159256; + + return err; +} + +/*! + \class QtxEvalSetString + Provides set of string operations for parser +*/ + +/*! + Default constructor +*/ +QtxEvalSetString::QtxEvalSetString() +: QtxEvalSetBase() +{ + addOperations( QString( "+;=;<;>;<=;>=;<>;!=;length;lower;upper" ).split( ";" ) ); + + ListOfTypes aTypes; + aTypes.append( QVariant::Int ); + aTypes.append( QVariant::Double ); + aTypes.append( QVariant::String ); + addTypes( aTypes ); +} + +/*! + Destructor +*/ +QtxEvalSetString::~QtxEvalSetString() +{ +} + +QString QtxEvalSetString::Name() +{ + return "String"; +} + +QString QtxEvalSetString::name() const +{ + return Name(); +} + +/*! + Creates string QVariant by it's Qt string representation [redefined virtual] +*/ +bool QtxEvalSetString::createValue( const QString& str, QVariant& v ) const +{ + bool ok = false; + if ( str.length() > 1 && str[0] == '\'' && str[str.length() - 1] == '\'' ) + { + v = str.mid( 1, str.length() - 2 ); + ok = true; + } + else + ok = QtxEvalSetBase::createValue( str, v ); + return ok; +} + +/*! + \return priority of arithmetic operation 'op'. + \param isBin indicate whether the operation is binary +*/ +int QtxEvalSetString::priority( const QString& op, bool isBin ) const +{ + if ( isBin ) + { + if ( op == "+" ) + return 2; + else if ( op == "=" || op == "<" || op == ">" || + op == "<=" || op == ">=" || op == "<>" || op == "!=" ) + return 1; + else + return 0; + } + else if ( op == "length" || op == "lower" || op=="upper" ) + return 5; + else + return 0; +} + +/*! + Calculates result of operation + \return one of error states + \param op - name of operation + \param v1 - first operation argument (must be used also to store result) + \param v2 - second operation argument +*/ +QtxEvalExpr::Error QtxEvalSetString::calculate( const QString& op, QVariant& v1, QVariant& v2 ) const +{ + QtxEvalExpr::Error err = QtxEvalExpr::OK; + if ( v1.isValid() && v2.isValid() ) + { + QString _v1 = v1.toString(); + QString _v2 = v2.toString(); + if ( op == "+" ) + v1 = _v1 + _v2; + else if ( op == "=" ) + v1 = _v1 ==_v2; + else if ( op == "<" ) + v1 = _v1 < _v2; + else if ( op == ">" ) + v1 = _v1 > _v2; + else if ( op == "<>" || op == "!=" ) + v1 = _v1 != _v2; + else if ( op == "<=" ) + v1 = _v1 < _v2 || _v1 == _v2; + else if ( op == ">=" ) + v1 = _v1 > _v2 || _v1 == _v2; + } + else if ( !v1.isValid() && v2.isValid() ) + { + QString val = v2.toString(); + if ( op == "length" ) + v2 = (int)val.length(); + else if ( op == "lower" ) + v2 = val.toLower(); + else if ( op == "upper" ) + v2 = val.toUpper(); + } + return err; +} + +/*! + \class QtxEvalSetSets + Provides set of operations with sets for parser +*/ + +/*! + Default constructor +*/ +QtxEvalSetSets::QtxEvalSetSets() +{ + addOperations( QString( "{;};=;<>;!=;+;-;*;in;count" ).split( ";" ) ); + + ListOfTypes aTypes; + aTypes.append( QVariant::List ); + addTypes( aTypes ); +} + +/*! + Destructor +*/ +QtxEvalSetSets::~QtxEvalSetSets() +{ +} + +QString QtxEvalSetSets::Name() +{ + return "Sets"; +} + +QString QtxEvalSetSets::name() const +{ + return Name(); +} + +/*! + Fills list of brackets treated as open (when 'open' is 'true') or close ('open' is 'false') +*/ +void QtxEvalSetSets::bracketsList( QStringList& list, bool open ) const +{ + list.append( open ? "{" : "}" ); + QtxEvalSetBase::bracketsList( list, open ); +} + +/*! + \return priority of arithmetic operation 'op'. + \param isBin indicate whether the operation is binary +*/ +int QtxEvalSetSets::priority( const QString& op, bool isBin ) const +{ + if ( isBin ) + { + if ( op == "=" || op == "<>" || op == "!=" ) + return 1; + else if ( op == "+" || op == "-" || op == "*" ) + return 2; + else if ( op == "in" ) + return 3; + else + return 0; + } + else if ( op == "{" || op == "}" ) + return 5; + else if ( op == "count" ) + return 4; + else + return 0; +} + +/*! + \return whether values with passed types are valid for arguments of operation + \param op - name of operation + \param t1 - type of first argument + \param t2 - type of second argument +*/ +QtxEvalExpr::Error QtxEvalSetSets::isValid( const QString& op, + const QVariant::Type t1, const QVariant::Type t2 ) const +{ + if ( op == "{" ) + return QtxEvalExpr::OK; + + if ( op != "in" ) + return QtxEvalSetBase::isValid( op, t1, t2 ); + + if ( t1 != QVariant::Invalid && t2 == QVariant::List ) + return QtxEvalExpr::OK; + else + return QtxEvalExpr::OperandsNotMatch; +} + +/*! + Adds new value 'v' to set 'set' [static] +*/ +void QtxEvalSetSets::add( ValueSet& set, const QVariant& v ) +{ + if ( v.isValid() && !set.contains( v ) ) + set.append( v ); +} + +/*! + Adds values from set 's2' to set 's1' +*/ +void QtxEvalSetSets::add( ValueSet& s1, const ValueSet& s2 ) +{ + for ( ValueSet::const_iterator anIt = s2.begin(); anIt != s2.end(); ++anIt ) + add( s1, *anIt ); +} + +/*! + Removes value 'v' from set 'set' +*/ +void QtxEvalSetSets::remove( ValueSet& set, const QVariant& v ) +{ + set.removeAll( v ); +} + +/*! + Removes values of set 's2' from set 's1' +*/ +void QtxEvalSetSets::remove( ValueSet& s1, const ValueSet& s2 ) +{ + for ( ValueSet::const_iterator anIt = s2.begin(); anIt != s2.end(); ++anIt ) + s1.removeAll( *anIt ); +} + +/*! + Calculates result of operation + \return one of error states + \param op - name of operation + \param v1 - first operation argument (must be used also to store result) + \param v2 - second operation argument +*/ +QtxEvalExpr::Error QtxEvalSetSets::calculate( const QString& op, QVariant& v1, QVariant& v2 ) const +{ + QtxEvalExpr::Error err = QtxEvalExpr::OK; + + if ( op != "{" ) + { + if ( op == "}" ) + { + ValueSet aNewList; + add( aNewList, v1.toList() ); + v1 = aNewList; + } + else if ( op == "=" || op == "<>" || op == "!=" || op == "+" || op == "-" || op == "*" ) + { + ValueSet aNewList; + add( aNewList, v1.toList() ); + if ( op == "=" || op == "<>" || op == "!=" || op == "-" ) + { + remove( aNewList, v2.toList() ); + if ( op == "=" ) + v1 = aNewList.isEmpty() && v1.toList().count() == v2.toList().count(); + else if ( op == "<>" || op == "!=" ) + v1 = !aNewList.isEmpty() || v1.toList().count() != v2.toList().count(); + else + v1 = aNewList; + } + else if ( op == "+" ) + { + add( aNewList, v2.toList() ); + v1 = aNewList; + } + else if ( op == "*" ) + { + ValueSet toDelete; + add( toDelete, aNewList ); + remove( toDelete, v2.toList() ); + remove( aNewList, toDelete ); + v1 = aNewList; + } + } + else if ( op== "count" ) + v2 = (int)v2.toList().count(); + else if ( op == "in" ) + { + if ( v1.type() == QVariant::List ) + { + bool res = true; + ValueSet lst1 = v1.toList(); + ValueSet lst2 = v2.toList(); + for ( ValueSet::const_iterator anIt = lst1.begin(); anIt != lst1.end() && res; ++anIt ) + res = lst2.contains( *anIt ); + + v1 = res; + } + else + v1 = QVariant( v2.toList().contains( v1 ) ); + } + } + return err; +} + +/*! + \class QtxEvalSetConst + Provides different standard constants +*/ +QtxEvalSetConst::QtxEvalSetConst() +{ +} + +QtxEvalSetConst::~QtxEvalSetConst() +{ +} + +QString QtxEvalSetConst::Name() +{ + return "Const"; +} + +QString QtxEvalSetConst::name() const +{ + return Name(); +} + +bool QtxEvalSetConst::createValue( const QString& str, QVariant& val ) const +{ + bool ok = true; + if ( str == "pi" ) // PI number + val = QVariant( 3.141593 ); + else if ( str == "exp" ) // Exponent (e) + val = QVariant( 2.718282 ); + else if ( str == "g" ) // Free falling acceleration (g) + val = QVariant( 9.80665 ); + else + ok = false; + + return ok; +} diff --git a/src/Qtx/QtxEvalExpr.h b/src/Qtx/QtxEvalExpr.h new file mode 100644 index 000000000..ac186f486 --- /dev/null +++ b/src/Qtx/QtxEvalExpr.h @@ -0,0 +1,376 @@ +// Copyright (C) 2005 OPEN CASCADE, CEA/DEN, EDF R&D, PRINCIPIA R&D +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 2.1 of the License. +// +// This library is distributed in the hope that it will be useful +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +// +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com +// +// File: QtxEvalExpr.h +// Author: Alexander SOLOVYOV + +#ifndef QTX_EVALEXPR_H +#define QTX_EVALEXPR_H + +#include "Qtx.h" + +#include +#include +#include + +#ifdef WIN32 +#pragma warning( disable:4251 ) +#endif + +class QtxEvalSet; +class QtxEvalParser; + +class QTX_EXPORT QtxEvalExpr +{ +public: + /*! + \enum Error + \brief Errors during parsing + */ + typedef enum + { + OK, /*! \var All right */ + OperandsNotMatch, /*! \var Types of arguments are invalid for this operation */ + InvalidResult, /*! \var Operation cannot find result (for example, division by zero) */ + InvalidOperation, /*! \var Name of operation is unknown */ + OperationsNull, /*! \var Internal operations pointer of parser is null */ + InvalidToken, /*! \var It isn't operation, parameter of value */ + CloseExpected, /*! \var Close bracket is expected */ + ExcessClose, /*! \var The one of close bracket is excess */ + BracketsNotMatch, /*! \var Last open and this close bracket are different, for example [) */ + StackUnderflow, /*! \var There is no arguments in stack for operation */ + ExcessData /*! \var The parsing is finished, but there is more then one value in stack */ + } Error; + +public: + QtxEvalExpr( const QString& = QString() ); + QtxEvalExpr( const bool, const QString& = QString() ); + ~QtxEvalExpr(); + + QVariant calculate( const QString& = QString() ); + + QString expression() const; + void setExpression( const QString& ); + + QtxEvalExpr::Error error() const; + QtxEvalParser* parser() const; + + QList operationSets() const; + QtxEvalSet* operationSet( const QString& ) const; + void removeOperationSet( QtxEvalSet* ); + void insertOperationSet( QtxEvalSet*, const int = -1 ); + +private: + void intialize( const bool, const QString& ); + +private: + QString myExpr; + QtxEvalParser* myParser; +}; + +/*! + \class QtxEvalParser + + This class allows to calculate values of expressions using different set of operations. + It is provided some of standard set of operations (arithmetics, logic, strings, etc). + This parser allows to use parameters with help of methods has(), set(), remove(), value(). It uses + postfix representation of expressions and uses class QtxOperations in order to make certain operation + Every instance of parser contains only one postfix, so that if expression has been changed, then postfix + must be rebuilt. In order to increase performance of frequent calculation for many of expressions it is + recommended to use different instances of parser for expressions + +*/ +class QTX_EXPORT QtxEvalParser +{ +public: + QtxEvalParser(); + virtual ~QtxEvalParser(); + + QVariant calculate(); + QVariant calculate( const QString& ); + bool setExpression( const QString& ); + + QList operationSets() const; + QtxEvalSet* operationSet( const QString& ) const; + void removeOperationSet( QtxEvalSet* ); + void insertOperationSet( QtxEvalSet*, const int = -1 ); + + bool autoDeleteOperationSets() const; + void setAutoDeleteOperationSets( const bool ); + + virtual void clearParameters(); + virtual bool removeParameter( const QString& name ); + virtual QVariant parameter( const QString& name ) const; + virtual bool hasParameter( const QString& name ) const; + virtual void setParameter( const QString& name, const QVariant& value ); + QStringList parameters() const; + + QtxEvalExpr::Error error() const; + + bool firstInvalid( QString& ) const; + void removeInvalids(); + QString dump() const; + + static QString toString( const QList& ); + +protected: + /*! + \enum PostfixItemType + \brief Types of postfix representation elements + */ + typedef enum + { + Value, /*! \var Value (number, string, etc.)*/ + Param, /*! \var Parameter */ + Open, /*! \var Open bracket */ + Close, /*! \var Close bracket */ + Pre, /*! \var Unary prefix operation */ + Post, /*! \var Unary postfix operation */ + Binary /*! \var Binary operation */ + } PostfixItemType; + + /*! \var postfix representation element */ + typedef struct + { + QVariant myValue; + PostfixItemType myType; + } PostfixItem; + + /*! \var postfix representation */ + typedef QList Postfix; + typedef QList SetList; + typedef QMap ParamMap; + +protected: + QString dump( const Postfix& ) const; + virtual bool prepare( const QString&, Postfix& ); + virtual bool setOperationTypes( Postfix& ); + virtual bool sort( const Postfix&, Postfix&, const QStringList&, + const QStringList&, int f = -1, int l = -1 ); + + virtual bool parse( const QString& ); + virtual void setError( const QtxEvalExpr::Error ); + + bool calculate( const QString&, QVariant&, QVariant& ); + + static int search( const QStringList&, const QString&, + int offset, int& matchLen, int& listind ); + static QString note( const QString& str, int pos, int len ); + static int globalBrackets( const Postfix&, int, int ); + +private: + void operationList( QStringList& ) const; + void bracketsList( QStringList&, bool ) const; + bool createValue( const QString&, QVariant& ) const; + int priority( const QString&, bool isBin ) const; + QtxEvalExpr::Error isValid( const QString&, + const QVariant::Type, const QVariant::Type ) const; + QtxEvalExpr::Error calculation( const QString&, QVariant&, QVariant& ) const; + + bool checkOperations() const; + +private: + SetList mySets; + QtxEvalExpr::Error myError; + ParamMap myParams; + Postfix myPostfix; + bool myAutoDel; +}; + +class QTX_EXPORT QtxEvalSet +{ +public: + QtxEvalSet(); + virtual ~QtxEvalSet(); + + virtual QString name() const = 0; + + virtual void operationList( QStringList& ) const = 0; + //list of possible operations + + virtual void bracketsList( QStringList&, bool open ) const = 0; + //list of open/close brackets + + virtual bool createValue( const QString&, QVariant& ) const; + //by default, the String value will be set, it corresponds to parameter + //base method returns false (always parameter) + //successor's method returns true if it has created custom value + //or call base if it hasn't + + virtual int priority( const QString&, bool isBin ) const = 0; + //returns prioritet of operation; + //if operation is impossible, it must return 0 or less + + virtual QtxEvalExpr::Error isValid( const QString&, const QVariant::Type, + const QVariant::Type ) const = 0; + //return OK if this parameter types is valid for operation + //return OperandsNotMatch or InvalidOperation otherwise + + virtual QtxEvalExpr::Error calculate( const QString&, QVariant&, QVariant& ) const = 0; + //process binary operation with values + //for unary operation the second QVariant will be passed as invalid +}; + +// ------------------------------- Standard operation sets ------------------------------ + +/*! + \class QtxEvalSetBase + Provides base functionality for standard sets of operations for parser +*/ +class QTX_EXPORT QtxEvalSetBase : public QtxEvalSet +{ +public: + QtxEvalSetBase(); + virtual ~QtxEvalSetBase(); + + virtual void operationList( QStringList& ) const; + virtual void bracketsList( QStringList&, bool open ) const; + + virtual QtxEvalExpr::Error isValid( const QString&, const QVariant::Type, + const QVariant::Type ) const; +protected: + typedef QList ListOfTypes; + + void addTypes( const ListOfTypes& ); + void addOperations( const QStringList& ); + +private: + QStringList myOpers; + ListOfTypes myTypes; +}; + +/*! + \class QtxEvalSetArithmetic + Provides set of arithmetic operations for parser +*/ +class QTX_EXPORT QtxEvalSetArithmetic : public QtxEvalSetBase +{ +public: + QtxEvalSetArithmetic(); + virtual ~QtxEvalSetArithmetic(); + + virtual bool createValue( const QString&, QVariant& ) const; + virtual int priority( const QString&, bool isBin ) const; + virtual QtxEvalExpr::Error calculate( const QString&, QVariant&, QVariant& ) const; + + static QString Name(); + virtual QString name() const; +}; + +/*! + \class QtxEvalSetLogic + Provides set of logic operations for parser +*/ +class QTX_EXPORT QtxEvalSetLogic : public QtxEvalSetBase +{ +public: + QtxEvalSetLogic(); + virtual ~QtxEvalSetLogic(); + + virtual bool createValue( const QString&, QVariant& ) const; + virtual int priority( const QString&, bool isBin ) const; + virtual QtxEvalExpr::Error calculate( const QString&, QVariant&, QVariant& ) const; + + static QString Name(); + virtual QString name() const; + +private: + bool booleanValue( const QVariant& v ) const; +}; + +/*! + \class QtxEvalSetMath + Provides set of more complex operations (mathematics functions) for parser (sqrt, sin, cos, etc) +*/ +class QTX_EXPORT QtxEvalSetMath : public QtxEvalSetBase +{ +public: + QtxEvalSetMath(); + virtual ~QtxEvalSetMath(); + + virtual bool createValue( const QString&, QVariant& ) const; + virtual int priority( const QString&, bool isBin ) const; + virtual QtxEvalExpr::Error calculate( const QString&, QVariant&, QVariant& ) const; + + static QString Name(); + virtual QString name() const; +}; + +/*! + \class QtxEvalSetString + Provides set of string operations for parser +*/ +class QTX_EXPORT QtxEvalSetString : public QtxEvalSetBase +{ +public: + QtxEvalSetString(); + virtual ~QtxEvalSetString(); + + virtual bool createValue( const QString&, QVariant& ) const; + virtual int priority( const QString&, bool isBin ) const; + virtual QtxEvalExpr::Error calculate( const QString&, QVariant&, QVariant& ) const; + + static QString Name(); + virtual QString name() const; +}; + +/*! + \class QtxEvalSetSets + Provides set of operations with sets for parser +*/ +class QTX_EXPORT QtxEvalSetSets : public QtxEvalSetBase +{ +public: + typedef QList ValueSet; + +public: + QtxEvalSetSets(); + virtual ~QtxEvalSetSets(); + + virtual void bracketsList( QStringList&, bool open ) const; + virtual int priority( const QString&, bool isBin ) const; + virtual QtxEvalExpr::Error isValid( const QString&, const QVariant::Type, + const QVariant::Type ) const; + virtual QtxEvalExpr::Error calculate( const QString&, QVariant&, QVariant& ) const; + + static void add( ValueSet&, const QVariant& ); + static void add( ValueSet&, const ValueSet& ); + static void remove( ValueSet&, const QVariant& ); + static void remove( ValueSet&, const ValueSet& ); + + static QString Name(); + virtual QString name() const; +}; + +/*! + \class QtxEvalSetConst + Provides different standard constants +*/ +class QTX_EXPORT QtxEvalSetConst : public QtxEvalSet +{ +public: + QtxEvalSetConst(); + virtual ~QtxEvalSetConst(); + + static QString Name(); + virtual QString name() const; + + virtual bool createValue( const QString&, QVariant& ) const; +}; + +#endif -- 2.39.2