diff --git a/src/DataFilter.cpp b/src/DataFilter.cpp index 7c6e682ea..ba575fec8 100644 --- a/src/DataFilter.cpp +++ b/src/DataFilter.cpp @@ -327,8 +327,10 @@ QStringList DataFilter::parseFilter(QString query, QStringList *list) foreach(RideItem *item, context->athlete->rideCache->rides()) { // evaluate each ride... - if(treeRoot->eval(context, this, treeRoot, item)) + Result result = treeRoot->eval(context, this, treeRoot, item); + if (result.isNumber && result.number) { filenames << item->fileName; + } } emit results(filenames); if (list) *list = filenames; @@ -351,7 +353,8 @@ DataFilter::dynamicParse() foreach(RideItem *item, context->athlete->rideCache->rides()) { // evaluate each ride... - if(treeRoot->eval(context, this, treeRoot, item)) + Result result = treeRoot->eval(context, this, treeRoot, item); + if (result.isNumber && result.number) filenames << item->fileName; } emit results(filenames); @@ -400,18 +403,35 @@ void DataFilter::configChanged(qint32) } -double Leaf::eval(Context *context, DataFilter *df, Leaf *leaf, RideItem *m) +Result Leaf::eval(Context *context, DataFilter *df, Leaf *leaf, RideItem *m) { switch(leaf->type) { + // + // LOGICAL EXPRESSION + // case Leaf::Logical : { switch (leaf->op) { case AND : - return (eval(context, df, leaf->lvalue.l, m) && eval(context, df, leaf->rvalue.l, m)); - + { + Result left = eval(context, df, leaf->lvalue.l, m); + if (left.isNumber && left.number) { + Result right = eval(context, df, leaf->rvalue.l, m); + if (right.isNumber && right.number) return Result(true); + } + return Result(false); + } case OR : - return (eval(context, df, leaf->lvalue.l, m) || eval(context, df, leaf->rvalue.l, m)); + { + Result left = eval(context, df, leaf->lvalue.l, m); + if (left.isNumber && left.number) return Result(true); + + Result right = eval(context, df, leaf->rvalue.l, m); + if (right.isNumber && right.number) return Result(true); + + return Result(false); + } default : // parenthesis return (eval(context, df, leaf->lvalue.l, m)); @@ -419,6 +439,9 @@ double Leaf::eval(Context *context, DataFilter *df, Leaf *leaf, RideItem *m) } break; + // + // FUNCTIONS + // case Leaf::Function : { double duration; @@ -431,20 +454,19 @@ double Leaf::eval(Context *context, DataFilter *df, Leaf *leaf, RideItem *m) QString lookup = df->lookupMap.value(symbol, ""); PMCData *pmcData = context->athlete->getPMCFor(lookup); - if (leaf->function == "sts") return pmcData->sts(m->dateTime.date()); - if (leaf->function == "lts") return pmcData->lts(m->dateTime.date()); - if (leaf->function == "sb") return pmcData->sb(m->dateTime.date()); - if (leaf->function == "rr") return pmcData->rr(m->dateTime.date()); + if (leaf->function == "sts") return Result(pmcData->sts(m->dateTime.date())); + if (leaf->function == "lts") return Result(pmcData->lts(m->dateTime.date())); + if (leaf->function == "sb") return Result(pmcData->sb(m->dateTime.date())); + if (leaf->function == "rr") return Result(pmcData->rr(m->dateTime.date())); } - // GET LHS VALUE switch (leaf->lvalue.l->type) { default: case Leaf::Function : { - duration = eval(context, df, leaf->lvalue.l, m); // duration + duration = eval(context, df, leaf->lvalue.l, m).number; // duration will be zero if string } break; @@ -477,339 +499,218 @@ double Leaf::eval(Context *context, DataFilter *df, Leaf *leaf, RideItem *m) } if (leaf->function == "best") - return RideFileCache::best(df->context, m->fileName, leaf->seriesType, duration); + return Result(RideFileCache::best(df->context, m->fileName, leaf->seriesType, duration)); if (leaf->function == "tiz") // duration is really zone number - return RideFileCache::tiz(df->context, m->fileName, leaf->seriesType, duration); + return Result(RideFileCache::tiz(df->context, m->fileName, leaf->seriesType, duration)); // unknown function!? - return 0 ; + return Result(0) ; } break; + // + // SYMBOLS + // + case Leaf::Symbol : + { + double lhsdouble=0.0f; + bool lhsisNumber=false; + QString lhsstring; + QString rename; + QString symbol = *(leaf->lvalue.n); + + // is it isRun ? + if (symbol == "isRun") { + lhsdouble = m->isRun ? 1 : 0; + lhsisNumber = true; + + } else if (symbol == "isSwim") { + lhsdouble = m->isSwim ? 1 : 0; + lhsisNumber = true; + + } else if (!symbol.compare("Current", Qt::CaseInsensitive)) { + + if (context->currentRideItem()) + lhsdouble = QDate(1900,01,01). + daysTo(context->currentRideItem()->dateTime.date()); + else + lhsdouble = 0; + lhsisNumber = true; + + } else if (!symbol.compare("Today", Qt::CaseInsensitive)) { + + lhsdouble = QDate(1900,01,01).daysTo(QDate::currentDate()); + lhsisNumber = true; + + } else if (!symbol.compare("Date", Qt::CaseInsensitive)) { + + lhsdouble = QDate(1900,01,01).daysTo(m->dateTime.date()); + lhsisNumber = true; + + } else if (isCoggan(symbol)) { + // a coggan PMC metric + PMCData *pmcData = context->athlete->getPMCFor("coggan_tss"); + if (!symbol.compare("ctl", Qt::CaseInsensitive)) lhsdouble = pmcData->lts(m->dateTime.date()); + if (!symbol.compare("atl", Qt::CaseInsensitive)) lhsdouble = pmcData->sts(m->dateTime.date()); + if (!symbol.compare("tsb", Qt::CaseInsensitive)) lhsdouble = pmcData->sb(m->dateTime.date()); + lhsisNumber = true; + + } else if ((lhsisNumber = df->lookupType.value(*(leaf->lvalue.n))) == true) { + // get symbol value + // check metadata string to number first ... + QString meta = m->getText(rename=df->lookupMap.value(symbol,""), "unknown"); + if (meta == "unknown") + lhsdouble = m->getForSymbol(rename=df->lookupMap.value(symbol,"")); + else + lhsdouble = meta.toDouble(); + lhsisNumber = true; + + //qDebug()<<"symbol" << *(lvalue.n) << "is" << lhsdouble << "via" << rename; + } else { + // string symbol will evaluate to zero as unary expression + lhsstring = m->getText(rename=df->lookupMap.value(symbol,""), ""); + //qDebug()<<"symbol" << *(lvalue.n) << "is" << lhsstring << "via" << rename; + } + if (lhsisNumber) return Result(lhsdouble); + else return Result(lhsstring); + } + break; + + // + // LITERALS + // + case Leaf::Float : + { + return Result(leaf->lvalue.f); + } + break; + + case Leaf::Integer : + { + return Result(leaf->lvalue.i); + } + break; + + case Leaf::String : + { + QString string = *(leaf->lvalue.s); + + // dates are returned as numbers + QDate date = QDate::fromString(string, "yyyy/MM/dd"); + if (date.isValid()) return Result(QDate(1900,01,01).daysTo(date)); + else return Result(string); + } + break; + + // + // BINARY EXPRESSION + // case Leaf::BinaryOperation : case Leaf::Operation : { - double lhsdouble=0.00, rhsdouble=0.00; - QString lhsstring, rhsstring; - bool lhsisNumber=false, rhsisNumber=false; - - // GET LHS VALUE - switch (leaf->lvalue.l->type) { - - default: - case Leaf::Function : - { - lhsdouble = eval(context, df, leaf->lvalue.l, m); // duration - lhsisNumber=true; - } - break; - - case Leaf::Symbol : - { - QString rename; - QString symbol = *(leaf->lvalue.l->lvalue.n); - - // is it isRun ? - if (symbol == "isRun") { - lhsdouble = m->isRun ? 1 : 0; - lhsisNumber = true; - - } else if (symbol == "isSwim") { - lhsdouble = m->isSwim ? 1 : 0; - lhsisNumber = true; - - } else if (!symbol.compare("Current", Qt::CaseInsensitive)) { - - if (context->currentRideItem()) - lhsdouble = QDate(1900,01,01). - daysTo(context->currentRideItem()->dateTime.date()); - else - lhsdouble = 0; - lhsisNumber = true; - - } else if (!symbol.compare("Today", Qt::CaseInsensitive)) { - - lhsdouble = QDate(1900,01,01).daysTo(QDate::currentDate()); - lhsisNumber = true; - - } else if (!symbol.compare("Date", Qt::CaseInsensitive)) { - - lhsdouble = QDate(1900,01,01).daysTo(m->dateTime.date()); - lhsisNumber = true; - - } else if (isCoggan(symbol)) { - // a coggan PMC metric - PMCData *pmcData = context->athlete->getPMCFor("coggan_tss"); - if (!symbol.compare("ctl", Qt::CaseInsensitive)) lhsdouble = pmcData->lts(m->dateTime.date()); - if (!symbol.compare("atl", Qt::CaseInsensitive)) lhsdouble = pmcData->sts(m->dateTime.date()); - if (!symbol.compare("tsb", Qt::CaseInsensitive)) lhsdouble = pmcData->sb(m->dateTime.date()); - lhsisNumber = true; - - } else if ((lhsisNumber = df->lookupType.value(*(leaf->lvalue.l->lvalue.n))) == true) { - // get symbol value - // check metadata string to number first ... - QString meta = m->getText(rename=df->lookupMap.value(*(leaf->lvalue.l->lvalue.n),""), "unknown"); - if (meta == "unknown") - lhsdouble = m->getForSymbol(rename=df->lookupMap.value(*(leaf->lvalue.l->lvalue.n),"")); - else - lhsdouble = meta.toDouble(); - - //qDebug()<<"symbol" << *(leaf->lvalue.l->lvalue.n) << "is" << lhsdouble << "via" << rename; - } else { - // string - lhsstring = m->getText(rename=df->lookupMap.value(*(leaf->lvalue.l->lvalue.n),""), ""); - //qDebug()<<"symbol" << *(leaf->lvalue.l->lvalue.n) << "is" << lhsstring << "via" << rename; - } - } - break; - - case Leaf::Float : - lhsisNumber = true; - lhsdouble = leaf->lvalue.l->lvalue.f; - break; - - case Leaf::Integer : - lhsisNumber = true; - lhsdouble = leaf->lvalue.l->lvalue.i; - break; - - case Leaf::String : - QString string = *(leaf->lvalue.l->lvalue.s); - QDate date = QDate::fromString(string, "yyyy/MM/dd"); - if (date.isValid()) { - lhsdouble = QDate(1900,01,01).daysTo(date); - lhsisNumber = true; - } else { - lhsstring = string; - lhsisNumber = false; - } - break; - - break; - } - - // GET RHS VALUE -- BUT NOT FOR FUNCTIONS - switch (leaf->rvalue.l->type) { - - default: - case Leaf::Function : - { - rhsdouble = eval(context, df, leaf->rvalue.l, m); - rhsisNumber=true; - } - break; - case Leaf::Symbol : - { - QString rename; - QString symbol = *(leaf->rvalue.l->lvalue.n); - - // is it isRun ? - if (symbol == "isRun") { - - rhsdouble = m->isRun ? 1 : 0; - rhsisNumber = true; - - } else if (symbol == "isSwim") { - - rhsdouble = m->isSwim ? 1 : 0; - rhsisNumber = true; - - } else if (!symbol.compare("Current", Qt::CaseInsensitive)) { - - if (context->currentRideItem()) - rhsdouble = QDate(1900,01,01). - daysTo(context->currentRideItem()->dateTime.date()); - else - rhsdouble = 0; - rhsisNumber = true; - - } else if (!symbol.compare("Today", Qt::CaseInsensitive)) { - - rhsdouble = QDate(1900,01,01).daysTo(QDate::currentDate()); - rhsisNumber = true; - - } else if (!symbol.compare("Date", Qt::CaseInsensitive)) { - - rhsdouble = QDate(1900,01,01).daysTo(m->dateTime.date()); - rhsisNumber = true; - - } else if (isCoggan(symbol)) { - - // a coggan PMC metric - PMCData *pmcData = context->athlete->getPMCFor("coggan_tss"); - if (!symbol.compare("ctl", Qt::CaseInsensitive)) rhsdouble = pmcData->lts(m->dateTime.date()); - if (!symbol.compare("atl", Qt::CaseInsensitive)) rhsdouble = pmcData->sts(m->dateTime.date()); - if (!symbol.compare("tsb", Qt::CaseInsensitive)) rhsdouble = pmcData->sb(m->dateTime.date()); - rhsisNumber = true; - - // get symbol value - } else if ((rhsisNumber=df->lookupType.value(*(leaf->rvalue.l->lvalue.n))) == true) { - // numeric - QString meta = m->getText(rename=df->lookupMap.value(*(leaf->rvalue.l->lvalue.n),""), "unknown"); - if (meta == "unknown") - rhsdouble = m->getForSymbol(rename=df->lookupMap.value(*(leaf->rvalue.l->lvalue.n),"")); - else - rhsdouble = meta.toDouble(); - //qDebug()<<"symbol" << *(leaf->rvalue.l->lvalue.n) << "is" << rhsdouble << "via" << rename; - } else { - // string - rhsstring = m->getText(rename=df->lookupMap.value(*(leaf->rvalue.l->lvalue.n),""), "notfound"); - //qDebug()<<"symbol" << *(leaf->rvalue.l->lvalue.n) << "is" << rhsstring << "via" << rename; - } - } - break; - - case Leaf::Float : - rhsisNumber = true; - rhsdouble = leaf->rvalue.l->lvalue.f; - break; - - case Leaf::Integer : - rhsisNumber = true; - rhsdouble = leaf->rvalue.l->lvalue.i; - break; - - case Leaf::String : - QString string = *(leaf->rvalue.l->lvalue.s); - QDate date = QDate::fromString(string, "yyyy/MM/dd"); - if (date.isValid()) { - rhsdouble = QDate(1900,01,01).daysTo(date); - rhsisNumber = true; - } else { - rhsstring = string; - rhsisNumber = false; - } - break; - - break; - } + // lhs and rhs + Result lhs = eval(context, df, leaf->lvalue.l, m); + Result rhs = eval(context, df, leaf->rvalue.l, m); // NOW PERFORM OPERATION - //qDebug()<<"lhs="<op) { case ADD: { - if (lhsisNumber) { - return lhsdouble + rhsdouble; - } else { - return 0; - } + if (lhs.isNumber) return Result(lhs.number + rhs.number); + else return Result(0); } break; case SUBTRACT: { - if (lhsisNumber) { - return lhsdouble - rhsdouble; - } else { - return 0; - } + if (lhs.isNumber) return Result(lhs.number - rhs.number); + else return Result(0); } break; case DIVIDE: { - if (lhsisNumber && rhsdouble) { // avoid divide by zero - return lhsdouble / rhsdouble; - } else { - return 0; - } + // avoid divide by zero + if (lhs.isNumber && rhs.number) return Result(lhs.number / rhs.number); + else return Result(0); } break; case MULTIPLY: { - if (lhsisNumber) { - return lhsdouble * rhsdouble; - } else { - return 0; - } + if (lhs.isNumber) return Result(lhs.number * rhs.number); + else return Result(0); } break; case POW: { - if (lhsisNumber && rhsdouble) { - return pow(lhsdouble,rhsdouble); - } else { - return 0; - } + if (lhs.isNumber && rhs.number) return Result(pow(lhs.number,rhs.number)); + else return Result(0); } break; case EQ: { - if (lhsisNumber) { - return lhsdouble == rhsdouble; - } else { - return lhsstring == rhsstring; - } + if (lhs.isNumber) return Result(lhs.number == rhs.number); + else return Result(lhs.string == rhs.string); } break; case NEQ: { - if (lhsisNumber) { - return lhsdouble != rhsdouble; - } else { - return lhsstring != rhsstring; - } + if (lhs.isNumber) return Result(lhs.number != rhs.number); + else return Result(lhs.string != rhs.string); } break; case LT: { - if (lhsisNumber) { - return lhsdouble < rhsdouble; - } else { - return lhsstring < rhsstring; - } + if (lhs.isNumber) return Result(lhs.number < rhs.number); + else return Result(lhs.string < rhs.string); } break; case LTE: { - if (lhsisNumber) { - return lhsdouble <= rhsdouble; - } else { - return lhsstring <= rhsstring; - } + if (lhs.isNumber) return Result(lhs.number <= rhs.number); + else return Result(lhs.string <= rhs.string); } break; case GT: { - if (lhsisNumber) { - return lhsdouble > rhsdouble; - } else { - return lhsstring > rhsstring; - } + if (lhs.isNumber) return Result(lhs.number > rhs.number); + else return Result(lhs.string > rhs.string); } break; case GTE: { - if (lhsisNumber) { - return lhsdouble >= rhsdouble; - } else { - return lhsstring >= rhsstring; - } + if (lhs.isNumber) return Result(lhs.number >= rhs.number); + else return Result(lhs.string >= rhs.string); } break; case MATCHES: - return QRegExp(rhsstring).exactMatch(lhsstring); + if (!lhs.isNumber && !rhs.isNumber) return Result(QRegExp(rhs.string).exactMatch(lhs.string)); + else return Result(false); break; case ENDSWITH: - return lhsstring.endsWith(rhsstring); + if (!lhs.isNumber && !rhs.isNumber) return Result(lhs.string.endsWith(rhs.string)); + else return Result(false); break; case BEGINSWITH: - return lhsstring.startsWith(rhsstring); + if (!lhs.isNumber && !rhs.isNumber) return Result(lhs.string.startsWith(rhs.string)); + else return Result(false); break; case CONTAINS: - return lhsstring.contains(rhsstring) ? true : false; + { + if (!lhs.isNumber && !rhs.isNumber) return Result(lhs.string.contains(rhs.string) ? true : false); + else return Result(false); + } break; default: @@ -821,5 +722,5 @@ double Leaf::eval(Context *context, DataFilter *df, Leaf *leaf, RideItem *m) default: // we don't need to evaluate any lower - they are leaf nodes handled above break; } - return false; + return Result(false); } diff --git a/src/DataFilter.h b/src/DataFilter.h index 57a677da6..6634e31ef 100644 --- a/src/DataFilter.h +++ b/src/DataFilter.h @@ -33,6 +33,19 @@ class RideMetric; class FieldDefinition; class DataFilter; +class Result { + public: + + // construct a result + Result (double value) : isNumber(true), string(""), number(value) {} + Result (QString value) : isNumber(false), string(value), number(0.0f) {} + + // we can't use QString with union + bool isNumber; // if true, value is numeric + QString string; + double number; +}; + class Leaf { public: @@ -40,7 +53,7 @@ class Leaf { Leaf() : type(none),op(0),series(NULL),dynamic(false) { } // evaluate against a RideItem - double eval(Context *context, DataFilter *df, Leaf *, RideItem *m); + Result eval(Context *context, DataFilter *df, Leaf *, RideItem *m); // tree traversal etc void print(Leaf *, int level); // print leaf and all children diff --git a/src/DataFilter.y b/src/DataFilter.y index 4212a01d0..041bdfae5 100644 --- a/src/DataFilter.y +++ b/src/DataFilter.y @@ -1,202 +1,202 @@ -%{ -/* - * Copyright (c) 2012 Mark Liversedge (liversedge@gmail.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. - * - * This program 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 General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., 51 - * Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ - -// This grammar should work with yacc and bison, but has -// only been tested with bison. In addition, since qmake -// uses the -p flag to rename all the yy functions to -// enable multiple grammars in a single executable you -// should make sure you use the very latest bison since it -// has been known to be problematic in the past. It is -// know to work well with bison v2.4.1. -// -// To make the grammar readable I have placed the code -// for each nterm at column 40, this source file is best -// edited / viewed in an editor which is at least 120 -// columns wide (e.g. vi in xterm of 120x40) -// -// - -#include "DataFilter.h" - -extern int DataFilterlex(); // the lexer aka yylex() -extern char *DataFiltertext; // set by the lexer aka yytext - -extern void DataFilter_setString(QString); -extern void DataFilter_clearString(); - -// PARSER STATE VARIABLES -extern QStringList DataFiltererrors; -static void DataFiltererror(const char *error) { DataFiltererrors << QString(error);} -extern int DataFilterparse(); - -extern Leaf *root; // root node for parsed statement - -%} - -// Symbol can be meta or metric name -%token SYMBOL - -// Constants can be a string or a number -%token DF_STRING DF_INTEGER DF_FLOAT -%token BEST TIZ STS LTS SB RR - -// comparative operators -%token EQ NEQ LT LTE GT GTE -%token ADD SUBTRACT DIVIDE MULTIPLY POW -%token MATCHES ENDSWITH BEGINSWITH CONTAINS - -// logical operators -%token AND OR - -%union { - Leaf *leaf; - int op; - char function[32]; -} - -%type symbol value lexpr expr; -%type lop cop bop; - -%left ADD SUBTRACT DIVIDE MULTIPLY POW -%left EQ NEQ LT LTE GT GTE MATCHES ENDSWITH CONTAINS -%left AND OR - -%start filter; -%% - -filter: lexpr { root = $1; } - ; - -lexpr : expr lop expr { $$ = new Leaf(); - $$->type = Leaf::Logical; - $$->lvalue.l = $1; - $$->op = $2; - $$->rvalue.l = $3; } - | lexpr lop lexpr { $$ = new Leaf(); - $$->type = Leaf::Logical; - $$->lvalue.l = $1; - $$->op = $2; - $$->rvalue.l = $3; } - | '(' expr ')' { $$ = new Leaf(); - $$->type = Leaf::Logical; - $$->lvalue.l = $2; - $$->op = 0; } - | expr - ; - - -expr : '(' expr ')' { $$ = new Leaf(); - $$->type = Leaf::Logical; - $$->lvalue.l = $2; - $$->op = 0; } - - | expr cop expr { $$ = new Leaf(); - $$->type = Leaf::Operation; - $$->lvalue.l = $1; - $$->op = $2; - $$->rvalue.l = $3; } - - | expr bop expr { $$ = new Leaf(); - $$->type = Leaf::BinaryOperation; - $$->lvalue.l = $1; - $$->op = $2; - $$->rvalue.l = $3; } - - | value { $$ = $1; } - - ; - -cop : EQ - | NEQ - | LT - | LTE - | GT - | GTE - | MATCHES - | ENDSWITH - | BEGINSWITH - | CONTAINS - ; - -lop : AND - | OR - ; - -bop : ADD - | SUBTRACT - | DIVIDE - | MULTIPLY - | POW - ; - -symbol : SYMBOL { $$ = new Leaf(); $$->type = Leaf::Symbol; - if (QString(DataFiltertext) == "BikeScore") - $$->lvalue.n = new QString("BikeScore™"); - else - $$->lvalue.n = new QString(DataFiltertext); - } - ; - -value : symbol { $$ = $1; } - - | DF_STRING { $$ = new Leaf(); $$->type = Leaf::String; - QString s2(DataFiltertext); - $$->lvalue.s = new QString(s2.mid(1,s2.length()-2)); } - | DF_FLOAT { $$ = new Leaf(); $$->type = Leaf::Float; - $$->lvalue.f = QString(DataFiltertext).toFloat(); } - | DF_INTEGER { $$ = new Leaf(); $$->type = Leaf::Integer; - $$->lvalue.i = QString(DataFiltertext).toInt(); } - - | BEST '(' symbol ',' lexpr ')' { $$ = new Leaf(); $$->type = Leaf::Function; - $$->function = QString($1); - $$->series = $3; - $$->lvalue.l = $5; - } - - - | TIZ '(' symbol ',' lexpr ')' { $$ = new Leaf(); $$->type = Leaf::Function; - $$->function = QString($1); - $$->series = $3; - $$->lvalue.l = $5; - } - - | LTS '(' symbol ')' { $$ = new Leaf(); $$->type = Leaf::Function; - $$->function = QString($1); - $$->series = $3; - $$->lvalue.l = NULL; - } - | STS '(' symbol ')' { $$ = new Leaf(); $$->type = Leaf::Function; - $$->function = QString($1); - $$->series = $3; - $$->lvalue.l = NULL; - } - | RR '(' symbol ')' { $$ = new Leaf(); $$->type = Leaf::Function; - $$->function = QString($1); - $$->series = $3; - $$->lvalue.l = NULL; - } - | SB '(' symbol ')' { $$ = new Leaf(); $$->type = Leaf::Function; - $$->function = QString($1); - $$->series = $3; - $$->lvalue.l = NULL; - } - ; - -%% - +%{ +/* + * Copyright (c) 2012 Mark Liversedge (liversedge@gmail.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. + * + * This program 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 General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., 51 + * Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +// This grammar should work with yacc and bison, but has +// only been tested with bison. In addition, since qmake +// uses the -p flag to rename all the yy functions to +// enable multiple grammars in a single executable you +// should make sure you use the very latest bison since it +// has been known to be problematic in the past. It is +// know to work well with bison v2.4.1. +// +// To make the grammar readable I have placed the code +// for each nterm at column 40, this source file is best +// edited / viewed in an editor which is at least 120 +// columns wide (e.g. vi in xterm of 120x40) +// +// + +#include "DataFilter.h" + +extern int DataFilterlex(); // the lexer aka yylex() +extern char *DataFiltertext; // set by the lexer aka yytext + +extern void DataFilter_setString(QString); +extern void DataFilter_clearString(); + +// PARSER STATE VARIABLES +extern QStringList DataFiltererrors; +static void DataFiltererror(const char *error) { DataFiltererrors << QString(error);} +extern int DataFilterparse(); + +extern Leaf *root; // root node for parsed statement + +%} + +// Symbol can be meta or metric name +%token SYMBOL + +// Constants can be a string or a number +%token DF_STRING DF_INTEGER DF_FLOAT +%token BEST TIZ STS LTS SB RR + +// comparative operators +%token EQ NEQ LT LTE GT GTE +%token ADD SUBTRACT DIVIDE MULTIPLY POW +%token MATCHES ENDSWITH BEGINSWITH CONTAINS + +// logical operators +%token AND OR + +%union { + Leaf *leaf; + int op; + char function[32]; +} + +%type symbol value lexpr expr; +%type lop cop bop; + +%left ADD SUBTRACT DIVIDE MULTIPLY POW +%left EQ NEQ LT LTE GT GTE MATCHES ENDSWITH CONTAINS +%left AND OR + +%start filter; +%% + +filter: lexpr { root = $1; } + ; + +lexpr : expr lop expr { $$ = new Leaf(); + $$->type = Leaf::Logical; + $$->lvalue.l = $1; + $$->op = $2; + $$->rvalue.l = $3; } + | lexpr lop lexpr { $$ = new Leaf(); + $$->type = Leaf::Logical; + $$->lvalue.l = $1; + $$->op = $2; + $$->rvalue.l = $3; } + | '(' expr ')' { $$ = new Leaf(); + $$->type = Leaf::Logical; + $$->lvalue.l = $2; + $$->op = 0; } + | expr + ; + + +expr : '(' expr ')' { $$ = new Leaf(); + $$->type = Leaf::Logical; + $$->lvalue.l = $2; + $$->op = 0; } + + | expr cop expr { $$ = new Leaf(); + $$->type = Leaf::Operation; + $$->lvalue.l = $1; + $$->op = $2; + $$->rvalue.l = $3; } + + | expr bop expr { $$ = new Leaf(); + $$->type = Leaf::BinaryOperation; + $$->lvalue.l = $1; + $$->op = $2; + $$->rvalue.l = $3; } + + | value { $$ = $1; } + + ; + +cop : EQ + | NEQ + | LT + | LTE + | GT + | GTE + | MATCHES + | ENDSWITH + | BEGINSWITH + | CONTAINS + ; + +lop : AND + | OR + ; + +bop : ADD + | SUBTRACT + | DIVIDE + | MULTIPLY + | POW + ; + +symbol : SYMBOL { $$ = new Leaf(); $$->type = Leaf::Symbol; + if (QString(DataFiltertext) == "BikeScore") + $$->lvalue.n = new QString("BikeScore™"); + else + $$->lvalue.n = new QString(DataFiltertext); + } + ; + +value : symbol { $$ = $1; } + + | DF_STRING { $$ = new Leaf(); $$->type = Leaf::String; + QString s2(DataFiltertext); + $$->lvalue.s = new QString(s2.mid(1,s2.length()-2)); } + | DF_FLOAT { $$ = new Leaf(); $$->type = Leaf::Float; + $$->lvalue.f = QString(DataFiltertext).toFloat(); } + | DF_INTEGER { $$ = new Leaf(); $$->type = Leaf::Integer; + $$->lvalue.i = QString(DataFiltertext).toInt(); } + + | BEST '(' symbol ',' lexpr ')' { $$ = new Leaf(); $$->type = Leaf::Function; + $$->function = QString($1); + $$->series = $3; + $$->lvalue.l = $5; + } + + + | TIZ '(' symbol ',' lexpr ')' { $$ = new Leaf(); $$->type = Leaf::Function; + $$->function = QString($1); + $$->series = $3; + $$->lvalue.l = $5; + } + + | LTS '(' symbol ')' { $$ = new Leaf(); $$->type = Leaf::Function; + $$->function = QString($1); + $$->series = $3; + $$->lvalue.l = NULL; + } + | STS '(' symbol ')' { $$ = new Leaf(); $$->type = Leaf::Function; + $$->function = QString($1); + $$->series = $3; + $$->lvalue.l = NULL; + } + | RR '(' symbol ')' { $$ = new Leaf(); $$->type = Leaf::Function; + $$->function = QString($1); + $$->series = $3; + $$->lvalue.l = NULL; + } + | SB '(' symbol ')' { $$ = new Leaf(); $$->type = Leaf::Function; + $$->function = QString($1); + $$->series = $3; + $$->lvalue.l = NULL; + } + ; + +%% +