/*************************************************************************** qgssearchstringparser.yy Grammar rules for search string used by Bison -------------------- begin : 2005-07-26 copyright : (C) 2005 by Martin Dobias email : won.der at centrum.sk ***************************************************************************/ /*************************************************************************** * * * 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. * * * ***************************************************************************/ /* $Id$ */ %{ #include #if QT_VERSION < 0x040000 #include #else #include #endif #include "qgssearchtreenode.h" /** returns parsed tree, otherwise returns NULL and sets parserErrorMsg (interface function to be called from QgsSearchString) */ QgsSearchTreeNode* parseSearchString(const QString& str, QString& parserErrorMsg); //! from lex.yy.c extern int yylex(); extern char* yytext; extern void set_input_buffer(const char* buffer); //! varible where the parser error will be stored QString gParserErrorMsg; //! sets gParserErrorMsg void yyerror(const char* msg); //! temporary list for nodes without parent (if parsing fails these nodes are removed) #if QT_VERSION < 0x040000 QPtrList gTmpNodes; #else QList gTmpNodes; #endif void joinTmpNodes(QgsSearchTreeNode* parent, QgsSearchTreeNode* left, QgsSearchTreeNode* right); void addToTmpNodes(QgsSearchTreeNode* node); // we want verbose error messages #define YYERROR_VERBOSE 1 %} %union { QgsSearchTreeNode* node; double number; QgsSearchTreeNode::Operator op; } %start root /* http://docs.openlinksw.com/virtuoso/GRAMMAR.html */ %token NUMBER %token COMPARISON %token STRING %token COLUMN_REF %token UNKNOWN_CHARACTER %token NOT %token AND %token OR %type root %type search_cond %type predicate %type comp_predicate %type scalar_exp // debugging //%error-verbose // operator precedence // all operators have left associativity // (right associtativity is used for assigment) %left '*' '/' %left '+' '-' %left UMINUS // fictious symbol (for unary minus) %left COMPARISON %left AND %left OR %left NOT %% root: search_cond { /*gParserRootNode = $1;*/ } ; search_cond: search_cond OR search_cond { $$ = new QgsSearchTreeNode(QgsSearchTreeNode::opOR, $1, $3); joinTmpNodes($$,$1,$3); } | search_cond AND search_cond { $$ = new QgsSearchTreeNode(QgsSearchTreeNode::opAND, $1, $3); joinTmpNodes($$,$1,$3); } | NOT search_cond { $$ = new QgsSearchTreeNode(QgsSearchTreeNode::opNOT, $2, 0); joinTmpNodes($$,$2, 0); } | '(' search_cond ')' { $$ = $2; } | predicate; ; // more predicates to come predicate: comp_predicate ; comp_predicate: scalar_exp COMPARISON scalar_exp { $$ = new QgsSearchTreeNode($2, $1, $3); joinTmpNodes($$,$1,$3); } ; scalar_exp: scalar_exp '*' scalar_exp { $$ = new QgsSearchTreeNode(QgsSearchTreeNode::opMUL, $1, $3); joinTmpNodes($$,$1,$3); } | scalar_exp '/' scalar_exp { $$ = new QgsSearchTreeNode(QgsSearchTreeNode::opDIV, $1, $3); joinTmpNodes($$,$1,$3); } | scalar_exp '+' scalar_exp { $$ = new QgsSearchTreeNode(QgsSearchTreeNode::opPLUS, $1, $3); joinTmpNodes($$,$1,$3); } | scalar_exp '-' scalar_exp { $$ = new QgsSearchTreeNode(QgsSearchTreeNode::opMINUS,$1, $3); joinTmpNodes($$,$1,$3); } | '(' scalar_exp ')' { $$ = $2; } | '+' scalar_exp %prec UMINUS { $$ = $2; } | '-' scalar_exp %prec UMINUS { $$ = $2; if ($$->type() == QgsSearchTreeNode::tNumber) $$->setNumber(- $$->number()); } | NUMBER { $$ = new QgsSearchTreeNode($1); addToTmpNodes($$); } | STRING { $$ = new QgsSearchTreeNode(yytext, 0); addToTmpNodes($$); } | COLUMN_REF { $$ = new QgsSearchTreeNode(yytext, 1); addToTmpNodes($$); } ; %% void addToTmpNodes(QgsSearchTreeNode* node) { #if QT_VERSION < 0x040000 gTmpNodes.append(node); #else gTmpNodes.append(node); #endif } void joinTmpNodes(QgsSearchTreeNode* parent, QgsSearchTreeNode* left, QgsSearchTreeNode* right) { bool res; if (left) { #if QT_VERSION < 0x040000 res = gTmpNodes.removeRef(left); #else res = gTmpNodes.removeAll(left); #endif Q_ASSERT(res); } if (right) { #if QT_VERSION < 0x040000 res = gTmpNodes.removeRef(right); #else res = gTmpNodes.removeAll(right); #endif Q_ASSERT(res); } #if QT_VERSION < 0x040000 gTmpNodes.append(parent); #else gTmpNodes.append(parent); #endif } // returns parsed tree, otherwise returns NULL and sets parserErrorMsg QgsSearchTreeNode* parseSearchString(const QString& str, QString& parserErrorMsg) { // list should be empty when starting Q_ASSERT(gTmpNodes.count() == 0); set_input_buffer((const char*)str); int res = yyparse(); // list should be empty when parsing was OK if (res == 0) // success? { Q_ASSERT(gTmpNodes.count() == 1); #if QT_VERSION < 0x040000 return gTmpNodes.take(0); #else return gTmpNodes.takeFirst(); #endif } else // error? { parserErrorMsg = gParserErrorMsg; // remove nodes without parents - to prevent memory leaks #if QT_VERSION < 0x040000 while (gTmpNodes.first()) delete gTmpNodes.take(); #else while (gTmpNodes.size() > 0) delete gTmpNodes.takeFirst(); #endif return NULL; } } void yyerror(const char* msg) { gParserErrorMsg = msg; }