Skip to content

Commit

Permalink
More square brackets
Browse files Browse the repository at this point in the history
  • Loading branch information
nyalldawson committed Dec 18, 2018
1 parent 3490217 commit 06d5f92
Show file tree
Hide file tree
Showing 12 changed files with 299 additions and 13 deletions.
Expand Up @@ -60,7 +60,8 @@ Abstract base class for all nodes that can appear in an expression.
ntFunction,
ntLiteral,
ntColumnRef,
ntCondition
ntCondition,
ntIndexOperator,
};


Expand Down
57 changes: 57 additions & 0 deletions python/core/auto_generated/expression/qgsexpressionnodeimpl.sip.in
Expand Up @@ -179,6 +179,63 @@ Returns a the name of this operator without the operands.
I.e. "AND", "OR", ...
%End

};

class QgsExpressionNodeIndexOperator : QgsExpressionNode
{
%Docstring
A indexing expression operator, which allows use of square brackets [] to reference map and array items.

.. versionadded:: 3.6
%End

%TypeHeaderCode
#include "qgsexpressionnodeimpl.h"
%End
public:

QgsExpressionNodeIndexOperator( QgsExpressionNode *container /Transfer/, QgsExpressionNode *index /Transfer/ );
%Docstring
Constructor for QgsExpressionNodeIndexOperator.
%End
~QgsExpressionNodeIndexOperator();

QgsExpressionNode *container() const;
%Docstring
Returns the container node, representing an array or map value.

.. seealso:: :py:func:`index`
%End

QgsExpressionNode *index() const;
%Docstring
Returns the index node, representing an array element index or map key.

.. seealso:: :py:func:`container`
%End

virtual QgsExpressionNode::NodeType nodeType() const;

virtual bool prepareNode( QgsExpression *parent, const QgsExpressionContext *context );

virtual QVariant evalNode( QgsExpression *parent, const QgsExpressionContext *context );

virtual QString dump() const;


virtual QSet<QString> referencedColumns() const;

virtual QSet<QString> referencedVariables() const;

virtual QSet<QString> referencedFunctions() const;

virtual bool needsGeometry() const;

virtual QgsExpressionNode *clone() const /Factory/;

virtual bool isStatic( QgsExpression *parent, const QgsExpressionContext *context ) const;


};

class QgsExpressionNodeInOperator : QgsExpressionNode
Expand Down
15 changes: 15 additions & 0 deletions resources/function_help/json/op_index
@@ -0,0 +1,15 @@
{
"name": "[]",
"type": "operator",
"description": "Index operator. Returns an element from an array or map value.",
"arguments": [
{ "arg": "index", "description": "array index or map key value" }
],
"examples": [
{ "expression":"array(1,2,3)[0]", "returns":"1"},
{ "expression":"array(1,2,3)[2]", "returns":"3"},
{ "expression":"array(1,2,3)[-1]", "returns":"3"},
{ "expression":"map('a',1,'b',2)['a']", "returns":"1"},
{ "expression":"map('a',1,'b',2)['b']", "returns":"2"}
]
}
3 changes: 2 additions & 1 deletion src/core/expression/qgsexpressionnode.h
Expand Up @@ -79,7 +79,8 @@ class CORE_EXPORT QgsExpressionNode SIP_ABSTRACT
ntFunction, //!< \see QgsExpression::Node::NodeFunction
ntLiteral, //!< \see QgsExpression::Node::NodeLiteral
ntColumnRef, //!< \see QgsExpression::Node::NodeColumnRef
ntCondition //!< \see QgsExpression::Node::NodeCondition
ntCondition, //!< \see QgsExpression::Node::NodeCondition
ntIndexOperator, //!< Index operator
};


Expand Down
94 changes: 94 additions & 0 deletions src/core/expression/qgsexpressionnodeimpl.cpp
Expand Up @@ -1548,3 +1548,97 @@ QString QgsExpressionNodeBinaryOperator::text() const
return BINARY_OPERATOR_TEXT[mOp];
}

//

QVariant QgsExpressionNodeIndexOperator::evalNode( QgsExpression *parent, const QgsExpressionContext *context )
{
const QVariant container = mContainer->eval( parent, context );
ENSURE_NO_EVAL_ERROR;
const QVariant index = mIndex->eval( parent, context );
ENSURE_NO_EVAL_ERROR;

switch ( container.type() )
{
case QVariant::Map:
return QgsExpressionUtils::getMapValue( container, parent ).value( index.toString() );

case QVariant::List:
case QVariant::StringList:
{
const QVariantList list = QgsExpressionUtils::getListValue( container, parent );
qlonglong pos = QgsExpressionUtils::getIntValue( index, parent );
if ( pos >= list.length() || pos < -list.length() )
{
return QVariant();
}
if ( pos < 0 )
{
// negative indices are from back of list
pos += list.length();
}

return list.at( pos );
}

default:
parent->setEvalErrorString( tr( "[] can only be used with map or array values, not %1" ).arg( QMetaType::typeName( container.type() ) ) );
return QVariant();
}
}

QgsExpressionNode::NodeType QgsExpressionNodeIndexOperator::nodeType() const
{
return ntIndexOperator;
}

bool QgsExpressionNodeIndexOperator::prepareNode( QgsExpression *parent, const QgsExpressionContext *context )
{
bool resC = mContainer->prepare( parent, context );
bool resV = mIndex->prepare( parent, context );
return resC && resV;
}

QString QgsExpressionNodeIndexOperator::dump() const
{
return QStringLiteral( "%1[%2]" ).arg( mContainer->dump(), mIndex->dump() );
}

QSet<QString> QgsExpressionNodeIndexOperator::referencedColumns() const
{
return mContainer->referencedColumns() + mIndex->referencedColumns();
}

QSet<QString> QgsExpressionNodeIndexOperator::referencedVariables() const
{
return mContainer->referencedVariables() + mIndex->referencedVariables();
}

QSet<QString> QgsExpressionNodeIndexOperator::referencedFunctions() const
{
return mContainer->referencedFunctions() + mIndex->referencedFunctions();
}

QList<const QgsExpressionNode *> QgsExpressionNodeIndexOperator::nodes() const
{
QList<const QgsExpressionNode *> lst;
lst << this;
lst += mContainer->nodes() + mIndex->nodes();
return lst;
}

bool QgsExpressionNodeIndexOperator::needsGeometry() const
{
return mContainer->needsGeometry() || mIndex->needsGeometry();
}

QgsExpressionNode *QgsExpressionNodeIndexOperator::clone() const
{
QgsExpressionNodeIndexOperator *copy = new QgsExpressionNodeIndexOperator( mContainer->clone(), mIndex->clone() );
cloneTo( copy );
return copy;
}

bool QgsExpressionNodeIndexOperator::isStatic( QgsExpression *parent, const QgsExpressionContext *context ) const
{
return mContainer->isStatic( parent, context ) && mIndex->isStatic( parent, context );
}
51 changes: 51 additions & 0 deletions src/core/expression/qgsexpressionnodeimpl.h
Expand Up @@ -206,6 +206,57 @@ class CORE_EXPORT QgsExpressionNodeBinaryOperator : public QgsExpressionNode
static const char *BINARY_OPERATOR_TEXT[];
};

/**
* A indexing expression operator, which allows use of square brackets [] to reference map and array items.
* \ingroup core
* \since QGIS 3.6
*/
class CORE_EXPORT QgsExpressionNodeIndexOperator : public QgsExpressionNode
{
public:

/**
* Constructor for QgsExpressionNodeIndexOperator.
*/
QgsExpressionNodeIndexOperator( QgsExpressionNode *container SIP_TRANSFER, QgsExpressionNode *index SIP_TRANSFER )
: mContainer( container )
, mIndex( index )
{}
~QgsExpressionNodeIndexOperator() override { delete mContainer; delete mIndex; }

/**
* Returns the container node, representing an array or map value.
* \see index()
*/
QgsExpressionNode *container() const { return mContainer; }

/**
* Returns the index node, representing an array element index or map key.
* \see container()
*/
QgsExpressionNode *index() const { return mIndex; }

QgsExpressionNode::NodeType nodeType() const override;
bool prepareNode( QgsExpression *parent, const QgsExpressionContext *context ) override;
QVariant evalNode( QgsExpression *parent, const QgsExpressionContext *context ) override;
QString dump() const override;

QSet<QString> referencedColumns() const override;
QSet<QString> referencedVariables() const override;
QSet<QString> referencedFunctions() const override;
QList<const QgsExpressionNode *> nodes( ) const override; SIP_SKIP

bool needsGeometry() const override;
QgsExpressionNode *clone() const override SIP_FACTORY;
bool isStatic( QgsExpression *parent, const QgsExpressionContext *context ) const override;

private:

QgsExpressionNode *mContainer = nullptr;
QgsExpressionNode *mIndex = nullptr;

};

/**
* An expression node for value IN or NOT IN clauses.
* \ingroup core
Expand Down
14 changes: 4 additions & 10 deletions src/core/qgsexpressionparser.yy
Expand Up @@ -115,7 +115,7 @@ void addParserLocation(YYLTYPE* yyloc, QgsExpressionNode *node)
//

// operator tokens
%token <b_op> OR AND EQ NE LE GE LT GT REGEXP LIKE IS PLUS MINUS MUL DIV INTDIV MOD CONCAT POW SQUARE_BRAKET_OPENING SQUARE_BRAKET_CLOSING
%token <b_op> OR AND EQ NE LE GE LT GT REGEXP LIKE IS PLUS MINUS MUL DIV INTDIV MOD CONCAT POW
%token <u_op> NOT
%token IN

Expand Down Expand Up @@ -166,6 +166,7 @@ void addParserLocation(YYLTYPE* yyloc, QgsExpressionNode *node)
%right UMINUS // fictitious symbol (for unary minus)

%left COMMA
%left '['

%destructor { delete $$; } <node>
%destructor { delete $$; } <nodelist>
Expand Down Expand Up @@ -284,15 +285,8 @@ expression:
| expression IN '(' exp_list ')' { $$ = new QgsExpressionNodeInOperator($1, $4, false); }
| expression NOT IN '(' exp_list ')' { $$ = new QgsExpressionNodeInOperator($1, $5, true); }

| expression '[' expression ']'
{
QgsExpressionNode::NodeList* args = new QgsExpressionNode::NodeList();
args->append( $1 );
args->append( $3 );
QgsExpressionNodeFunction *f = new QgsExpressionNodeFunction( QgsExpression::functionIndex( "map_get" ), args );
$$ = f;
}

| expression '[' expression ']' { $$ = new QgsExpressionNodeIndexOperator( $1, $3 ); }

| PLUS expression %prec UMINUS { $$ = $2; }
| MINUS expression %prec UMINUS { $$ = new QgsExpressionNodeUnaryOperator( QgsExpressionNodeUnaryOperator::uoMinus, $2); }

Expand Down
3 changes: 3 additions & 0 deletions src/core/qgssqlexpressioncompiler.cpp
Expand Up @@ -401,6 +401,9 @@ QgsSqlExpressionCompiler::Result QgsSqlExpressionCompiler::compileNode( const Qg

case QgsExpressionNode::ntCondition:
break;

case QgsExpressionNode::ntIndexOperator:
break;
}

return Fail;
Expand Down
8 changes: 7 additions & 1 deletion src/gui/qgsexpressionbuilderwidget.cpp
Expand Up @@ -97,7 +97,8 @@ QgsExpressionBuilderWidget::QgsExpressionBuilderWidget( QWidget *parent )
connect( mShowHelpButton, &QPushButton::clicked, this, [ = ]()
{
functionsplit->setSizes( QList<int>( {mOperationListGroup->width() - mHelpAndValuesWidget->minimumWidth(),
mHelpAndValuesWidget->minimumWidth()} ) );
mHelpAndValuesWidget->minimumWidth()
} ) );
mShowHelpButton->setEnabled( false );
} );
connect( functionsplit, &QSplitter::splitterMoved, this, [ = ]( int, int )
Expand Down Expand Up @@ -608,6 +609,7 @@ void QgsExpressionBuilderWidget::updateFunctionTree()
registerItem( QStringLiteral( "Operators" ), QStringLiteral( "<>" ), QStringLiteral( " <> " ) );
registerItem( QStringLiteral( "Operators" ), QStringLiteral( "<=" ), QStringLiteral( " <= " ) );
registerItem( QStringLiteral( "Operators" ), QStringLiteral( ">=" ), QStringLiteral( " >= " ) );
registerItem( QStringLiteral( "Operators" ), QStringLiteral( "[]" ), QStringLiteral( "[ ]" ) );
registerItem( QStringLiteral( "Operators" ), QStringLiteral( "||" ), QStringLiteral( " || " ) );
registerItem( QStringLiteral( "Operators" ), QStringLiteral( "IN" ), QStringLiteral( " IN " ) );
registerItem( QStringLiteral( "Operators" ), QStringLiteral( "LIKE" ), QStringLiteral( " LIKE " ) );
Expand Down Expand Up @@ -957,6 +959,10 @@ void QgsExpressionBuilderWidget::createMarkers( const QgsExpressionNode *inNode
}
break;
}
case QgsExpressionNode::NodeType::ntIndexOperator:
{
break;
}
}
}

Expand Down
1 change: 1 addition & 0 deletions src/providers/ogr/qgsogrexpressioncompiler.cpp
Expand Up @@ -79,6 +79,7 @@ QgsSqlExpressionCompiler::Result QgsOgrExpressionCompiler::compileNode( const Qg
case QgsExpressionNode::ntColumnRef:
case QgsExpressionNode::ntInOperator:
case QgsExpressionNode::ntLiteral:
case QgsExpressionNode::ntIndexOperator:
break;
}

Expand Down

0 comments on commit 06d5f92

Please sign in to comment.