| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,342 @@ | ||
| /*************************************************************************** | ||
| qgsexpression.h | ||
| ------------------- | ||
| begin : August 2011 | ||
| copyright : (C) 2011 Martin Dobias | ||
| email : wonder.sk at gmail dot com | ||
| *************************************************************************** | ||
| * * | ||
| * This program is free software; you can redistribute it and/or modify * | ||
| * it under the terms of the GNU General Public License as published by * | ||
| * the Free Software Foundation; either version 2 of the License, or * | ||
| * (at your option) any later version. * | ||
| * * | ||
| ***************************************************************************/ | ||
|
|
||
| #ifndef QGSEXPRESSION_H | ||
| #define QGSEXPRESSION_H | ||
|
|
||
| #include <QStringList> | ||
| #include <QVariant> | ||
|
|
||
| #include "qgsfield.h" | ||
|
|
||
| class QgsDistanceArea; | ||
| class QgsFeature; | ||
|
|
||
| /** | ||
| Class for parsing and evaluation of expressions (formerly called "search strings"). | ||
| The expressions try to follow both syntax and semantics of SQL expressions. | ||
| Usage: | ||
| QgsExpression exp("gid*2 > 10 and type not in ('D','F'); | ||
| if (exp.hasParserError()) | ||
| { | ||
| // show error message with parserErrorString() and exit | ||
| } | ||
| QVariant result = exp.evaluate(feature, fields); | ||
| if (exp.hasEvalError()) | ||
| { | ||
| // show error message with evalErrorString() | ||
| } | ||
| else | ||
| { | ||
| // examine the result | ||
| } | ||
| Possible QVariant value types: | ||
| - invalid (null) | ||
| - int | ||
| - double | ||
| - string | ||
| Similarly to SQL, this class supports three-value logic: true/false/unknown. | ||
| Unknown value may be a result of operations with missing data (NULL). Please note | ||
| that NULL is different value than zero or an empty string. For example | ||
| 3 > NULL returns unknown. | ||
| There is no special (three-value) 'boolean' type: true/false is represented as | ||
| 1/0 integer, unknown value is represented the same way as NULL values: invalid QVariant. | ||
| For better performance with many evaluations you may first call prepare(fields) function | ||
| to find out indices of columns and then repeatedly call evaluate(feature). | ||
| Type conversion: operators and functions that expect arguments to be of particular | ||
| type automatically convert the arguments to that type, e.g. sin('2.1') will convert | ||
| the argument to a double, length(123) will first convert the number to a string. | ||
| Explicit conversion can be achieved with toint, toreal, tostring functions. | ||
| If implicit or explicit conversion is invalid, the evaluation returns an error. | ||
| Comparison operators do numeric comparison in case both operators are numeric (int/double) | ||
| or they can be converted to numeric types. | ||
| Arithmetic operators do integer arithmetics if both operands are integer. That is | ||
| 2+2 yields integer 4, but 2.0+2 returns real number 4.0. There are also two versions of | ||
| division and modulo operators: 1.0/2 returns 0.5 while 1/2 returns 0. | ||
| @note added in 2.0 | ||
| */ | ||
| class CORE_EXPORT QgsExpression | ||
| { | ||
| public: | ||
| QgsExpression( const QString& expr ); | ||
| ~QgsExpression(); | ||
|
|
||
| //! Returns true if an error occurred when parsing the input expression | ||
| bool hasParserError() const { return !mParserErrorString.isNull(); } | ||
| //! Returns parser error | ||
| QString parserErrorString() const { return mParserErrorString; } | ||
|
|
||
| //! Get the expression ready for evaluation - find out column indexes. | ||
| bool prepare( const QgsFieldMap& fields ); | ||
|
|
||
| //! Get list of columns referenced by the expression | ||
| QStringList referencedColumns(); | ||
| //! Returns true if the expression uses feature geometry for some computation | ||
| bool needsGeometry(); | ||
|
|
||
| // evaluation | ||
|
|
||
| //! Evaluate the feature and return the result | ||
| //! @note prepare() should be called before calling this method | ||
| QVariant evaluate( QgsFeature* f = NULL ); | ||
|
|
||
| //! Evaluate the feature and return the result | ||
| //! @note this method does not expect that prepare() has been called on this instance | ||
| QVariant evaluate( QgsFeature* f, const QgsFieldMap& fields ); | ||
|
|
||
| //! Returns true if an error occurred when evaluating last input | ||
| bool hasEvalError() const { return !mEvalErrorString.isNull(); } | ||
| //! Returns evaluation error | ||
| QString evalErrorString() const { return mEvalErrorString; } | ||
| //! Set evaluation error (used internally by evaluation functions) | ||
| void setEvalErrorString( QString str ) { mEvalErrorString = str; } | ||
|
|
||
| //! Set the number for $rownum special column | ||
| void setCurrentRowNumber( int rowNumber ) { mRowNumber = rowNumber; } | ||
| //! Return the number used for $rownum special column | ||
| int currentRowNumber() { return mRowNumber; } | ||
|
|
||
| //! Return the parsed expression as a string - useful for debugging | ||
| QString dump() const; | ||
|
|
||
| //! Return calculator used for distance and area calculations | ||
| //! (used by internal functions) | ||
| QgsDistanceArea* geomCalculator() { if ( mCalc == NULL ) initGeomCalculator(); return mCalc; } | ||
|
|
||
| // | ||
|
|
||
| enum UnaryOperator | ||
| { | ||
| uoNot, | ||
| uoMinus, | ||
| }; | ||
| enum BinaryOperator | ||
| { | ||
| // logical | ||
| boOr, | ||
| boAnd, | ||
|
|
||
| // comparison | ||
| boEQ, // = | ||
| boNE, // <> | ||
| boLE, // <= | ||
| boGE, // >= | ||
| boLT, // < | ||
| boGT, // > | ||
| boRegexp, | ||
| boLike, | ||
| boILike, | ||
| boIs, | ||
| boIsNot, | ||
|
|
||
| // math | ||
| boPlus, | ||
| boMinus, | ||
| boMul, | ||
| boDiv, | ||
| boMod, | ||
| boPow, | ||
|
|
||
| // strings | ||
| boConcat, | ||
| }; | ||
|
|
||
| static const char* BinaryOperatorText[]; | ||
| static const char* UnaryOperatorText[]; | ||
|
|
||
|
|
||
| typedef QVariant( *FcnEval )( const QVariantList& values, QgsFeature* f, QgsExpression* parent ); | ||
|
|
||
| struct FunctionDef | ||
| { | ||
| FunctionDef( QString fnname, int params, FcnEval fcn, bool usesGeometry = false ) | ||
| : mName( fnname ), mParams( params ), mFcn( fcn ), mUsesGeometry( usesGeometry ) {} | ||
| QString mName; | ||
| int mParams; | ||
| FcnEval mFcn; | ||
| bool mUsesGeometry; | ||
| }; | ||
|
|
||
| static FunctionDef BuiltinFunctions[]; | ||
|
|
||
| // tells whether the identifier is a name of existing function | ||
| static bool isFunctionName( QString name ); | ||
|
|
||
| // return index of the function in BuiltinFunctions array | ||
| static int functionIndex( QString name ); | ||
|
|
||
| //! return quoted column reference (in double quotes) | ||
| static QString quotedColumnRef( QString name ) { return QString( "\"%1\"" ).arg( name.replace( "\"", "\"\"" ) ); } | ||
|
|
||
| ////// | ||
|
|
||
| class Node | ||
| { | ||
| public: | ||
| virtual ~Node() {} | ||
| // abstract virtual eval function | ||
| // errors are reported to the parent | ||
| virtual QVariant eval( QgsExpression* parent, QgsFeature* f ) = 0; | ||
|
|
||
| // abstract virtual preparation function | ||
| // errors are reported to the parent | ||
| virtual bool prepare( QgsExpression* parent, const QgsFieldMap& fields ) = 0; | ||
|
|
||
| virtual QString dump() const = 0; | ||
|
|
||
| virtual QStringList referencedColumns() const = 0; | ||
| virtual bool needsGeometry() const = 0; | ||
| }; | ||
|
|
||
| class NodeList | ||
| { | ||
| public: | ||
| NodeList() {} | ||
| ~NodeList() { foreach( Node* n, mList ) delete n; } | ||
| void append( Node* node ) { mList.append( node ); } | ||
| int count() { return mList.count(); } | ||
| QList<Node*> list() { return mList; } | ||
|
|
||
| virtual QString dump() const; | ||
| protected: | ||
| QList<Node*> mList; | ||
| }; | ||
|
|
||
| class NodeUnaryOperator : public Node | ||
| { | ||
| public: | ||
| NodeUnaryOperator( UnaryOperator op, Node* operand ) : mOp( op ), mOperand( operand ) {} | ||
| ~NodeUnaryOperator() { delete mOperand; } | ||
|
|
||
| virtual bool prepare( QgsExpression* parent, const QgsFieldMap& fields ); | ||
| virtual QVariant eval( QgsExpression* parent, QgsFeature* f ); | ||
| virtual QString dump() const; | ||
| virtual QStringList referencedColumns() const { return mOperand->referencedColumns(); } | ||
| virtual bool needsGeometry() const { return mOperand->needsGeometry(); } | ||
| protected: | ||
| UnaryOperator mOp; | ||
| Node* mOperand; | ||
| }; | ||
|
|
||
| class NodeBinaryOperator : public Node | ||
| { | ||
| public: | ||
| NodeBinaryOperator( BinaryOperator op, Node* opLeft, Node* opRight ) : mOp( op ), mOpLeft( opLeft ), mOpRight( opRight ) {} | ||
| ~NodeBinaryOperator() { delete mOpLeft; delete mOpRight; } | ||
|
|
||
| virtual bool prepare( QgsExpression* parent, const QgsFieldMap& fields ); | ||
| virtual QVariant eval( QgsExpression* parent, QgsFeature* f ); | ||
| virtual QString dump() const; | ||
| virtual QStringList referencedColumns() const { return mOpLeft->referencedColumns() + mOpRight->referencedColumns(); } | ||
| virtual bool needsGeometry() const { return mOpLeft->needsGeometry() || mOpRight->needsGeometry(); } | ||
| protected: | ||
| bool compare( double diff ); | ||
| int computeInt( int x, int y ); | ||
| double computeDouble( double x, double y ); | ||
|
|
||
| BinaryOperator mOp; | ||
| Node* mOpLeft; | ||
| Node* mOpRight; | ||
| }; | ||
|
|
||
| class NodeInOperator : public Node | ||
| { | ||
| public: | ||
| NodeInOperator( Node* node, NodeList* list, bool notin = false ) : mNode( node ), mList( list ), mNotIn( notin ) {} | ||
| ~NodeInOperator() { delete mNode; delete mList; } | ||
|
|
||
| virtual bool prepare( QgsExpression* parent, const QgsFieldMap& fields ); | ||
| virtual QVariant eval( QgsExpression* parent, QgsFeature* f ); | ||
| virtual QString dump() const; | ||
| virtual QStringList referencedColumns() const { QStringList lst( mNode->referencedColumns() ); foreach( Node* n, mList->list() ) lst.append( n->referencedColumns() ); return lst; } | ||
| virtual bool needsGeometry() const { bool needs = false; foreach( Node* n, mList->list() ) needs |= n->needsGeometry(); return needs; } | ||
| protected: | ||
| Node* mNode; | ||
| NodeList* mList; | ||
| bool mNotIn; | ||
| }; | ||
|
|
||
| class NodeFunction : public Node | ||
| { | ||
| public: | ||
| NodeFunction( int fnIndex, NodeList* args ): mFnIndex( fnIndex ), mArgs( args ) {} | ||
| //NodeFunction( QString name, NodeList* args ) : mName(name), mArgs(args) {} | ||
| ~NodeFunction() { delete mArgs; } | ||
|
|
||
| virtual bool prepare( QgsExpression* parent, const QgsFieldMap& fields ); | ||
| virtual QVariant eval( QgsExpression* parent, QgsFeature* f ); | ||
| virtual QString dump() const; | ||
| virtual QStringList referencedColumns() const { QStringList lst; if ( !mArgs ) return lst; foreach( Node* n, mArgs->list() ) lst.append( n->referencedColumns() ); return lst; } | ||
| virtual bool needsGeometry() const { return BuiltinFunctions[mFnIndex].mUsesGeometry; } | ||
| protected: | ||
| //QString mName; | ||
| int mFnIndex; | ||
| NodeList* mArgs; | ||
| }; | ||
|
|
||
| class NodeLiteral : public Node | ||
| { | ||
| public: | ||
| NodeLiteral( QVariant value ) : mValue( value ) {} | ||
|
|
||
| virtual bool prepare( QgsExpression* parent, const QgsFieldMap& fields ); | ||
| virtual QVariant eval( QgsExpression* parent, QgsFeature* f ); | ||
| virtual QString dump() const; | ||
| virtual QStringList referencedColumns() const { return QStringList(); } | ||
| virtual bool needsGeometry() const { return false; } | ||
| protected: | ||
| QVariant mValue; | ||
| }; | ||
|
|
||
| class NodeColumnRef : public Node | ||
| { | ||
| public: | ||
| NodeColumnRef( QString name ) : mName( name ), mIndex( -1 ) {} | ||
|
|
||
| virtual bool prepare( QgsExpression* parent, const QgsFieldMap& fields ); | ||
| virtual QVariant eval( QgsExpression* parent, QgsFeature* f ); | ||
| virtual QString dump() const; | ||
| virtual QStringList referencedColumns() const { return QStringList( mName ); } | ||
| virtual bool needsGeometry() const { return false; } | ||
| protected: | ||
| QString mName; | ||
| int mIndex; | ||
| }; | ||
|
|
||
|
|
||
| protected: | ||
|
|
||
| QString mExpression; | ||
| Node* mRootNode; | ||
|
|
||
| QString mParserErrorString; | ||
| QString mEvalErrorString; | ||
|
|
||
| int mRowNumber; | ||
|
|
||
| void initGeomCalculator(); | ||
| QgsDistanceArea* mCalc; | ||
| }; | ||
|
|
||
| #endif // QGSEXPRESSION_H |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,171 @@ | ||
| /*************************************************************************** | ||
| qgsexpressionlexer.ll | ||
| -------------------- | ||
| begin : August 2011 | ||
| copyright : (C) 2011 by Martin Dobias | ||
| email : wonder.sk at gmail dot com | ||
| *************************************************************************** | ||
| * * | ||
| * This program is free software; you can redistribute it and/or modify * | ||
| * it under the terms of the GNU General Public License as published by * | ||
| * the Free Software Foundation; either version 2 of the License, or * | ||
| * (at your option) any later version. * | ||
| * * | ||
| ***************************************************************************/ | ||
|
|
||
| %option noyywrap | ||
| %option case-insensitive | ||
| %option never-interactive | ||
| %option nounput | ||
| %option prefix="exp_" | ||
|
|
||
| // ensure that lexer will be 8-bit (and not just 7-bit) | ||
| %option 8bit | ||
|
|
||
| %{ | ||
|
|
||
| #include <stdlib.h> // atof() | ||
|
|
||
| #include "qgsexpression.h" | ||
| #include "qgsexpressionparser.hpp" | ||
| #include <QRegExp> | ||
|
|
||
|
|
||
| // if not defined, searches for isatty() | ||
| // which doesn't in MSVC compiler | ||
| #define YY_NEVER_INTERACTIVE 1 | ||
|
|
||
| #ifndef YY_NO_UNPUT | ||
| #define YY_NO_UNPUT // unused | ||
| #endif | ||
|
|
||
| #ifdef _MSC_VER | ||
| #define YY_NO_UNISTD_H | ||
| #endif | ||
|
|
||
| #define B_OP(x) exp_lval.b_op = QgsExpression::x | ||
| #define U_OP(x) exp_lval.u_op = QgsExpression::x | ||
| //#define TEXT exp_lval.text = new QString(); *exp_lval.text = QString::fromUtf8(yytext); | ||
| #define TEXT_FILTER(filter_fn) exp_lval.text = new QString(); *exp_lval.text = filter_fn( QString::fromUtf8(yytext) ); | ||
| #define TEXT TEXT_FILTER() | ||
|
|
||
|
|
||
| static QString stripText(QString text) | ||
| { | ||
| // strip single quotes on start,end | ||
| text = text.mid( 1, text.length() - 2 ); | ||
|
|
||
| // make single "single quotes" from double "single quotes" | ||
| text.replace( QRegExp( "''" ), "'" ); | ||
|
|
||
| // strip \n \' etc. | ||
| int index = 0; | ||
| while (( index = text.indexOf( '\\', index ) ) != -1 ) | ||
| { | ||
| text.remove( index, 1 ); // delete backslash | ||
| QChar chr; | ||
| switch ( text[index].toLatin1() ) // evaluate backslashed character | ||
| { | ||
| case 'n': chr = '\n'; break; | ||
| case 't': chr = '\t'; break; | ||
| case '\\': chr = '\\'; break; | ||
| case '\'': chr = '\''; break; | ||
| default: chr = '?'; break; | ||
| } | ||
| text[index++] = chr; // set new character and push index +1 | ||
| } | ||
| return text; | ||
| } | ||
|
|
||
| static QString stripColumnRef(QString text) | ||
| { | ||
| // strip double quotes on start,end | ||
| text = text.mid( 1, text.length() - 2 ); | ||
|
|
||
| // make single "double quotes" from double "double quotes" | ||
| text.replace( QRegExp( "\"\"" ), "\"" ); | ||
| return text; | ||
| } | ||
|
|
||
|
|
||
| %} | ||
|
|
||
| white [ \t\r\n]+ | ||
|
|
||
| non_ascii [\x80-\xFF] | ||
|
|
||
| col_first [A-Za-z_]|{non_ascii} | ||
| col_next [A-Za-z0-9_]|{non_ascii} | ||
| column_ref {col_first}{col_next}* | ||
|
|
||
| special_col "$"{column_ref} | ||
|
|
||
| col_str_char "\"\""|[^\"] | ||
| column_ref_quoted "\""{col_str_char}*"\"" | ||
| dig [0-9] | ||
| num_int {dig}+ | ||
| num_float {dig}*\.{dig}+([eE][-+]?{dig}+)? | ||
| str_char ('')|(\\.)|[^'\\] | ||
| string "'"{str_char}*"'" | ||
| %% | ||
| "NOT" { U_OP(uoNot); return NOT; } | ||
| "AND" { B_OP(boAnd); return AND; } | ||
| "OR" { B_OP(boOr); return OR; } | ||
| "=" { B_OP(boEQ); return EQ; } | ||
| "!=" { B_OP(boNE); return NE; } | ||
| "<=" { B_OP(boLE); return LE; } | ||
| ">=" { B_OP(boGE); return GE; } | ||
| "<>" { B_OP(boNE); return NE; } | ||
| "<" { B_OP(boLT); return LT; } | ||
| ">" { B_OP(boGT); return GT; } | ||
| "~" { B_OP(boRegexp); return REGEXP; } | ||
| "LIKE" { B_OP(boLike); return LIKE; } | ||
| "ILIKE" { B_OP(boILike); return ILIKE; } | ||
| "IS" { B_OP(boIs); return IS; } | ||
| "IS NOT" { B_OP(boIsNot); return ISNOT; } | ||
| "||" { B_OP(boConcat); return CONCAT; } | ||
| "+" { B_OP(boPlus); return PLUS; } | ||
| "-" { B_OP(boMinus); return MINUS; } | ||
| "*" { B_OP(boMul); return MUL; } | ||
| "/" { B_OP(boDiv); return DIV; } | ||
| "%" { B_OP(boMod); return MOD; } | ||
| "^" { B_OP(boPow); return POW; } | ||
| "IN" { return IN; } | ||
| "NULL" { return NULLVALUE; } | ||
| [()] { return yytext[0]; } | ||
| "," { return COMMA; } | ||
| {num_float} { exp_lval.numberFloat = atof(yytext); return NUMBER_FLOAT; } | ||
| {num_int} { exp_lval.numberInt = atoi(yytext); return NUMBER_INT; } | ||
| {string} { TEXT_FILTER(stripText); return STRING; } | ||
| {special_col} { TEXT; return SPECIAL_COL; } | ||
| {column_ref} { TEXT; return QgsExpression::isFunctionName(*exp_lval.text) ? FUNCTION : COLUMN_REF; } | ||
| {column_ref_quoted} { TEXT_FILTER(stripColumnRef); return COLUMN_REF; } | ||
| {white} /* skip blanks and tabs */ | ||
| . { return Unknown_CHARACTER; } | ||
| %% | ||
| void exp_set_input_buffer(const char* buffer) | ||
| { | ||
| exp__scan_string(buffer); | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,232 @@ | ||
| /*************************************************************************** | ||
| qgsexpressionparser.yy | ||
| -------------------- | ||
| begin : August 2011 | ||
| copyright : (C) 2011 by Martin Dobias | ||
| email : wonder.sk at gmail dot com | ||
| *************************************************************************** | ||
| * * | ||
| * This program is free software; you can redistribute it and/or modify * | ||
| * it under the terms of the GNU General Public License as published by * | ||
| * the Free Software Foundation; either version 2 of the License, or * | ||
| * (at your option) any later version. * | ||
| * * | ||
| ***************************************************************************/ | ||
|
|
||
| %{ | ||
| #include <qglobal.h> | ||
| #include <QList> | ||
| #include <cstdlib> | ||
| #include "qgsexpression.h" | ||
|
|
||
| #ifdef _MSC_VER | ||
| # pragma warning( disable: 4065 ) // switch statement contains 'default' but no 'case' labels | ||
| # pragma warning( disable: 4702 ) // unreachable code | ||
| #endif | ||
|
|
||
| // don't redeclare malloc/free | ||
| #define YYINCLUDED_STDLIB_H 1 | ||
|
|
||
| //! from lexer | ||
| extern int exp_lex(); | ||
| extern void exp_set_input_buffer(const char* buffer); | ||
|
|
||
| /** returns parsed tree, otherwise returns NULL and sets parserErrorMsg | ||
| (interface function to be called from QgsExpression) | ||
| */ | ||
| QgsExpression::Node* parseExpression(const QString& str, QString& parserErrorMsg); | ||
|
|
||
| /** error handler for bison */ | ||
| void exp_error(const char* msg); | ||
|
|
||
| //! varible where the parser error will be stored | ||
| QString gExpParserErrorMsg; | ||
| QgsExpression::Node* gExpParserRootNode; | ||
|
|
||
|
|
||
| // we want verbose error messages | ||
| #define YYERROR_VERBOSE 1 | ||
|
|
||
| #define BINOP(x, y, z) new QgsExpression::NodeBinaryOperator(x, y, z) | ||
|
|
||
| %} | ||
|
|
||
| %name-prefix "exp_" | ||
|
|
||
| %union | ||
| { | ||
| QgsExpression::Node* node; | ||
| QgsExpression::NodeList* nodelist; | ||
| double numberFloat; | ||
| int numberInt; | ||
| QString* text; | ||
| QgsExpression::BinaryOperator b_op; | ||
| QgsExpression::UnaryOperator u_op; | ||
| } | ||
|
|
||
| %start root | ||
|
|
||
|
|
||
| // | ||
| // token definitions | ||
| // | ||
|
|
||
| // operator tokens | ||
| %token <b_op> OR AND EQ NE LE GE LT GT REGEXP LIKE ILIKE IS ISNOT PLUS MINUS MUL DIV MOD CONCAT POW | ||
| %token <u_op> NOT | ||
| %token IN | ||
|
|
||
| // literals | ||
| %token <numberFloat> NUMBER_FLOAT | ||
| %token <numberInt> NUMBER_INT | ||
| %token NULLVALUE | ||
|
|
||
| %token <text> STRING COLUMN_REF FUNCTION SPECIAL_COL | ||
|
|
||
| %token COMMA | ||
|
|
||
| %token Unknown_CHARACTER | ||
|
|
||
| // | ||
| // definition of non-terminal types | ||
| // | ||
|
|
||
| %type <node> expression | ||
| %type <nodelist> exp_list | ||
|
|
||
| // debugging | ||
| %error-verbose | ||
|
|
||
| // | ||
| // operator precedence | ||
| // | ||
|
|
||
| // left associativity means that 1+2+3 translates to (1+2)+3 | ||
| // the order of operators here determines their precedence | ||
|
|
||
| %left OR | ||
| %left AND | ||
| %right NOT | ||
| %left EQ NE LE GE LT GT REGEXP LIKE ILIKE IS ISNOT IN | ||
| %left PLUS MINUS | ||
| %left MUL DIV MOD | ||
| %right POW | ||
| %left CONCAT | ||
|
|
||
| %right UMINUS // fictitious symbol (for unary minus) | ||
|
|
||
| %left COMMA | ||
|
|
||
| %destructor { delete $$; } <node> | ||
| %destructor { delete $$; } <nodelist> | ||
| %destructor { delete $$; } <text> | ||
|
|
||
| %% | ||
|
|
||
| root: expression { gExpParserRootNode = $1; } | ||
| ; | ||
|
|
||
| expression: | ||
| expression AND expression { $$ = BINOP($2, $1, $3); } | ||
| | expression OR expression { $$ = BINOP($2, $1, $3); } | ||
| | expression EQ expression { $$ = BINOP($2, $1, $3); } | ||
| | expression NE expression { $$ = BINOP($2, $1, $3); } | ||
| | expression LE expression { $$ = BINOP($2, $1, $3); } | ||
| | expression GE expression { $$ = BINOP($2, $1, $3); } | ||
| | expression LT expression { $$ = BINOP($2, $1, $3); } | ||
| | expression GT expression { $$ = BINOP($2, $1, $3); } | ||
| | expression REGEXP expression { $$ = BINOP($2, $1, $3); } | ||
| | expression LIKE expression { $$ = BINOP($2, $1, $3); } | ||
| | expression ILIKE expression { $$ = BINOP($2, $1, $3); } | ||
| | expression IS expression { $$ = BINOP($2, $1, $3); } | ||
| | expression ISNOT expression { $$ = BINOP($2, $1, $3); } | ||
| | expression PLUS expression { $$ = BINOP($2, $1, $3); } | ||
| | expression MINUS expression { $$ = BINOP($2, $1, $3); } | ||
| | expression MUL expression { $$ = BINOP($2, $1, $3); } | ||
| | expression DIV expression { $$ = BINOP($2, $1, $3); } | ||
| | expression MOD expression { $$ = BINOP($2, $1, $3); } | ||
| | expression POW expression { $$ = BINOP($2, $1, $3); } | ||
| | expression CONCAT expression { $$ = BINOP($2, $1, $3); } | ||
| | NOT expression { $$ = new QgsExpression::NodeUnaryOperator($1, $2); } | ||
| | '(' expression ')' { $$ = $2; } | ||
|
|
||
| | FUNCTION '(' exp_list ')' | ||
| { | ||
| int fnIndex = QgsExpression::functionIndex(*$1); | ||
| if (fnIndex == -1) | ||
| { | ||
| // this should not actually happen because already in lexer we check whether an identifier is a known function | ||
| // (if the name is not known the token is parsed as a column) | ||
| exp_error("Function is not known"); | ||
| YYERROR; | ||
| } | ||
| if (QgsExpression::BuiltinFunctions[fnIndex].mParams != $3->count()) | ||
| { | ||
| exp_error("Function is called with wrong number of arguments"); | ||
| YYERROR; | ||
| } | ||
| $$ = new QgsExpression::NodeFunction(fnIndex, $3); | ||
| delete $1; | ||
| } | ||
|
|
||
| | expression IN '(' exp_list ')' { $$ = new QgsExpression::NodeInOperator($1, $4, false); } | ||
| | expression NOT IN '(' exp_list ')' { $$ = new QgsExpression::NodeInOperator($1, $5, true); } | ||
|
|
||
| | PLUS expression %prec UMINUS { $$ = $2; } | ||
| | MINUS expression %prec UMINUS { $$ = new QgsExpression::NodeUnaryOperator( QgsExpression::uoMinus, $2); } | ||
|
|
||
| // columns | ||
| | COLUMN_REF { $$ = new QgsExpression::NodeColumnRef( *$1 ); delete $1; } | ||
|
|
||
| // special columns (actually functions with no arguments) | ||
| | SPECIAL_COL | ||
| { | ||
| int fnIndex = QgsExpression::functionIndex(*$1); | ||
| if (fnIndex == -1) | ||
| { | ||
| exp_error("Special column is not known"); | ||
| YYERROR; | ||
| } | ||
| $$ = new QgsExpression::NodeFunction( fnIndex, NULL ); | ||
| delete $1; | ||
| } | ||
|
|
||
| // literals | ||
| | NUMBER_FLOAT { $$ = new QgsExpression::NodeLiteral( QVariant($1) ); } | ||
| | NUMBER_INT { $$ = new QgsExpression::NodeLiteral( QVariant($1) ); } | ||
| | STRING { $$ = new QgsExpression::NodeLiteral( QVariant(*$1) ); delete $1; } | ||
| | NULLVALUE { $$ = new QgsExpression::NodeLiteral( QVariant() ); } | ||
| ; | ||
|
|
||
| exp_list: | ||
| exp_list COMMA expression { $$ = $1; $1->append($3); } | ||
| | expression { $$ = new QgsExpression::NodeList(); $$->append($1); } | ||
| ; | ||
|
|
||
| %% | ||
|
|
||
| // returns parsed tree, otherwise returns NULL and sets parserErrorMsg | ||
| QgsExpression::Node* parseExpression(const QString& str, QString& parserErrorMsg) | ||
| { | ||
| gExpParserRootNode = NULL; | ||
| exp_set_input_buffer(str.toUtf8().constData()); | ||
| int res = exp_parse(); | ||
|
|
||
| // list should be empty when parsing was OK | ||
| if (res == 0) // success? | ||
| { | ||
| return gExpParserRootNode; | ||
| } | ||
| else // error? | ||
| { | ||
| parserErrorMsg = gExpParserErrorMsg; | ||
| return NULL; | ||
| } | ||
| } | ||
|
|
||
|
|
||
| void exp_error(const char* msg) | ||
| { | ||
| gExpParserErrorMsg = msg; | ||
| } | ||
|
|