From a518e9f639eb2e40c17b18b7b60ebac0973855fb Mon Sep 17 00:00:00 2001 From: Denis Rouzaud Date: Thu, 11 Dec 2014 14:04:44 +0100 Subject: [PATCH] [fix #11475] expressions: add left and right associavity for dump of binary operators complete test for left associativity --- python/core/qgsexpression.sip | 2 + src/core/qgsexpression.cpp | 76 +++++++++++++++++++++++++++- src/core/qgsexpression.h | 2 + tests/src/core/testqgsexpression.cpp | 3 ++ 4 files changed, 81 insertions(+), 2 deletions(-) diff --git a/python/core/qgsexpression.sip b/python/core/qgsexpression.sip index 88138a50789..9333bec3932 100644 --- a/python/core/qgsexpression.sip +++ b/python/core/qgsexpression.sip @@ -378,6 +378,8 @@ class QgsExpression virtual void accept( QgsExpression::Visitor& v ) const; int precedence() const; + bool leftAssociative() const; + bool rightAssociative() const; }; class NodeInOperator : QgsExpression::Node diff --git a/src/core/qgsexpression.cpp b/src/core/qgsexpression.cpp index 92331502385..491cf56ade8 100644 --- a/src/core/qgsexpression.cpp +++ b/src/core/qgsexpression.cpp @@ -2540,15 +2540,87 @@ int QgsExpression::NodeBinaryOperator::precedence() const return -1; } +bool QgsExpression::NodeBinaryOperator::leftAssociative() const +{ + // see left/right in qgsexpressionparser.yy + switch ( mOp ) + { + case boOr: + case boAnd: + case boEQ: + case boNE: + case boLE: + case boGE: + case boLT: + case boGT: + case boRegexp: + case boLike: + case boILike: + case boNotLike: + case boNotILike: + case boIs: + case boIsNot: + case boPlus: + case boMinus: + case boMul: + case boDiv: + case boIntDiv: + case boMod: + case boConcat: + return true; + + case boPow: + return false; + } + Q_ASSERT( 0 && "unexpected binary operator" ); + return -1; +} + +bool QgsExpression::NodeBinaryOperator::rightAssociative() const +{ + // see left/right in qgsexpressionparser.yy + switch ( mOp ) + { + case boOr: + case boAnd: + case boEQ: + case boNE: + case boLE: + case boGE: + case boLT: + case boGT: + case boRegexp: + case boLike: + case boILike: + case boNotLike: + case boNotILike: + case boIs: + case boIsNot: + case boPlus: + case boMul: + case boMod: + case boConcat: + case boPow: + return true; + + case boMinus: + case boDiv: + case boIntDiv: + return false; + } + Q_ASSERT( 0 && "unexpected binary operator" ); + return -1; +} + QString QgsExpression::NodeBinaryOperator::dump() const { QgsExpression::NodeBinaryOperator *lOp = dynamic_cast( mOpLeft ); QgsExpression::NodeBinaryOperator *rOp = dynamic_cast( mOpRight ); QString fmt; - fmt += lOp && lOp->precedence() < precedence() ? "(%1)" : "%1"; + fmt += lOp && ( lOp->precedence() < precedence() || !lOp->leftAssociative() ) ? "(%1)" : "%1"; fmt += " %2 "; - fmt += rOp && rOp->precedence() <= precedence() ? "(%3)" : "%3"; + fmt += rOp && ( rOp->precedence() < precedence() || !rOp->rightAssociative() ) ? "(%3)" : "%3"; return fmt.arg( mOpLeft->dump() ).arg( BinaryOperatorText[mOp] ).arg( mOpRight->dump() ); } diff --git a/src/core/qgsexpression.h b/src/core/qgsexpression.h index 9f1c7d69393..066569f7fec 100644 --- a/src/core/qgsexpression.h +++ b/src/core/qgsexpression.h @@ -513,6 +513,8 @@ class CORE_EXPORT QgsExpression virtual void accept( Visitor& v ) const override { v.visit( *this ); } int precedence() const; + bool leftAssociative() const; + bool rightAssociative() const; protected: bool compare( double diff ); diff --git a/tests/src/core/testqgsexpression.cpp b/tests/src/core/testqgsexpression.cpp index d35936b03fe..d9882f0dd2a 100644 --- a/tests/src/core/testqgsexpression.cpp +++ b/tests/src/core/testqgsexpression.cpp @@ -523,6 +523,9 @@ class TestQgsExpression: public QObject QgsExpression e2( e1.dump() ); QCOMPARE( e2.evaluate().toInt(), 21 ); + + QgsExpression e3( "(2^3)^2" ); + QCOMPARE( QgsExpression( e3.dump() ).evaluate().toInt(), 64 ); } void eval_columns()