diff options
author | Andrew Stitcher <astitcher@apache.org> | 2013-03-18 18:43:07 +0000 |
---|---|---|
committer | Andrew Stitcher <astitcher@apache.org> | 2013-03-18 18:43:07 +0000 |
commit | a1a2ba2cf8663e3b4cade0e5f439e03f1e5da76b (patch) | |
tree | 0aa0c1be8a4dd759fe63eb1acca145b6ed1714f8 | |
parent | 592669bad63f5579d3516f41ea143c9a549a261a (diff) | |
download | qpid-python-a1a2ba2cf8663e3b4cade0e5f439e03f1e5da76b.tar.gz |
QPID-4621: Arithmetic operations for C++ broker selectors
- Rearranged Syntax and parser to support arithmetic
- Implemented arithmetic syntax
- Implemented arithmetic operators
- Added some tests for arithmetic operations
- Added explicit lexical tokens for all the comparison and arithmetic operations
- Added the missing ',' token for us in the 'IN' comparison
- Rearranged the lexer so that everything is not uniformly inside the
hand crafted state machine.
- Moved a bunch of testing code from the tokeniser to the unit test
(much of this code might actually not be very useful anymore)
git-svn-id: https://svn.apache.org/repos/asf/qpid/trunk@1457912 13f79535-47bb-0310-9956-ffa450edef68
-rw-r--r-- | qpid/cpp/src/qpid/broker/SelectorExpression.cpp | 412 | ||||
-rw-r--r-- | qpid/cpp/src/qpid/broker/SelectorToken.cpp | 173 | ||||
-rw-r--r-- | qpid/cpp/src/qpid/broker/SelectorToken.h | 25 | ||||
-rw-r--r-- | qpid/cpp/src/qpid/broker/SelectorValue.cpp | 13 | ||||
-rw-r--r-- | qpid/cpp/src/qpid/broker/SelectorValue.h | 1 | ||||
-rw-r--r-- | qpid/cpp/src/tests/Selector.cpp | 221 |
6 files changed, 561 insertions, 284 deletions
diff --git a/qpid/cpp/src/qpid/broker/SelectorExpression.cpp b/qpid/cpp/src/qpid/broker/SelectorExpression.cpp index 8c6d82cc6f..58ab756a12 100644 --- a/qpid/cpp/src/qpid/broker/SelectorExpression.cpp +++ b/qpid/cpp/src/qpid/broker/SelectorExpression.cpp @@ -63,21 +63,32 @@ * * OrExpression ::= AndExpression ( "OR" AndExpression )* * - * AndExpression :: = EqualityExpression ( "AND" EqualityExpression )* + * AndExpression :: = ComparisonExpression ( "AND" ComparisonExpression )* * - * ComparisonExpression ::= PrimaryExpression "IS" "NULL" | - * PrimaryExpression "IS" "NOT" "NULL" | - * PrimaryExpression "LIKE" LiteralString [ "ESCAPE" LiteralString ] | - * PrimaryExpression "NOT" "LIKE" LiteralString [ "ESCAPE" LiteralString ] | - * PrimaryExpression "BETWEEN" PrimaryExpression "AND" PrimaryExpression | - * PrimaryExpression "NOT" "BETWEEN" PrimaryExpression "AND" PrimaryExpression | - * PrimaryExpression ComparisonOpsOps PrimaryExpression | + * ComparisonExpression ::= AddExpression "IS" "NULL" | + * AddExpression "IS" "NOT" "NULL" | + * AddExpression "LIKE" LiteralString [ "ESCAPE" LiteralString ] | + * AddExpression "NOT" "LIKE" LiteralString [ "ESCAPE" LiteralString ] | + * AddExpression "BETWEEN" AddExpression "AND" AddExpression | + * AddExpression "NOT" "BETWEEN" AddExpression "AND" AddExpression | + * AddExpression ComparisonOps AddExpression | * "NOT" ComparisonExpression | - * "(" OrExpression ")" + * AddExpression + * + * AddOps ::= "+" | "-" + * + * MultiplyOps ::= "*" | "-" + * + * AddExpression :: = MultiplyExpression ( AddOps MultiplyExpression )* + * + * MultiplyExpression :: = UnaryArithExpression ( MultiplyOps UnaryArithExpression )* + * + * UnaryArithExpression ::= AddOps AddExpression | + * "(" OrExpression ")" | + * PrimaryExpression * * PrimaryExpression :: = Identifier | * Literal - * */ @@ -92,6 +103,12 @@ public: virtual ~Expression() {} virtual void repr(std::ostream&) const = 0; virtual Value eval(const SelectorEnv&) const = 0; + + virtual BoolOrNone eval_bool(const SelectorEnv& env) const { + Value v = eval(env); + if (v.type==Value::T_BOOL) return BoolOrNone(v.b); + else return BN_UNKNOWN; + } }; class BoolExpression : public Expression { @@ -122,6 +139,20 @@ public: virtual BoolOrNone eval(T&, const SelectorEnv&) const = 0; }; +class ArithmeticOperator { +public: + virtual ~ArithmeticOperator() {} + virtual void repr(ostream&) const = 0; + virtual Value eval(Expression&, Expression&, const SelectorEnv&) const = 0; +}; + +class UnaryArithmeticOperator { +public: + virtual ~UnaryArithmeticOperator() {} + virtual void repr(ostream&) const = 0; + virtual Value eval(Expression&, const SelectorEnv&) const = 0; +}; + //////////////////////////////////////////////////// // Convenience outputters @@ -145,6 +176,18 @@ ostream& operator<<(ostream& os, const UnaryBooleanOperator<T>& e) return os; } +ostream& operator<<(ostream& os, const ArithmeticOperator& e) +{ + e.repr(os); + return os; +} + +ostream& operator<<(ostream& os, const UnaryArithmeticOperator& e) +{ + e.repr(os); + return os; +} + // Boolean Expression types... class ComparisonExpression : public BoolExpression { @@ -169,11 +212,11 @@ public: }; class OrExpression : public BoolExpression { - boost::scoped_ptr<BoolExpression> e1; - boost::scoped_ptr<BoolExpression> e2; + boost::scoped_ptr<Expression> e1; + boost::scoped_ptr<Expression> e2; public: - OrExpression(BoolExpression* e, BoolExpression* e_): + OrExpression(Expression* e, Expression* e_): e1(e), e2(e_) {} @@ -193,11 +236,11 @@ public: }; class AndExpression : public BoolExpression { - boost::scoped_ptr<BoolExpression> e1; - boost::scoped_ptr<BoolExpression> e2; + boost::scoped_ptr<Expression> e1; + boost::scoped_ptr<Expression> e2; public: - AndExpression(BoolExpression* e, BoolExpression* e_): + AndExpression(Expression* e, Expression* e_): e1(e), e2(e_) {} @@ -336,6 +379,48 @@ public: } }; +// Arithmetic Expression types + +class ArithmeticExpression : public Expression { + ArithmeticOperator* op; + boost::scoped_ptr<Expression> e1; + boost::scoped_ptr<Expression> e2; + +public: + ArithmeticExpression(ArithmeticOperator* o, Expression* e, Expression* e_): + op(o), + e1(e), + e2(e_) + {} + + void repr(ostream& os) const { + os << "(" << *e1 << *op << *e2 << ")"; + } + + Value eval(const SelectorEnv& env) const { + return op->eval(*e1, *e2, env); + } +}; + +class UnaryArithExpression : public Expression { + UnaryArithmeticOperator* op; + boost::scoped_ptr<Expression> e1; + +public: + UnaryArithExpression(UnaryArithmeticOperator* o, Expression* e) : + op(o), + e1(e) + {} + + void repr(ostream& os) const { + os << *op << "(" << *e1 << ")"; + } + + Value eval(const SelectorEnv& env) const { + return op->eval(*e1, env); + } +}; + // Expression types... class Literal : public Expression { @@ -496,18 +581,68 @@ class IsNonNull : public UnaryBooleanOperator<Expression> { }; // "NOT" -class Not : public UnaryBooleanOperator<BoolExpression> { +class Not : public UnaryBooleanOperator<Expression> { void repr(ostream& os) const { os << "NOT"; } - BoolOrNone eval(BoolExpression& e, const SelectorEnv& env) const { + BoolOrNone eval(Expression& e, const SelectorEnv& env) const { BoolOrNone bn = e.eval_bool(env); if (bn==BN_UNKNOWN) return bn; else return BoolOrNone(!bn); } }; +class Negate : public UnaryArithmeticOperator { + void repr(ostream& os) const { + os << "-"; + } + + Value eval(Expression& e, const SelectorEnv& env) const { + return -e.eval(env); + } +}; + +class Add : public ArithmeticOperator { + void repr(ostream& os) const { + os << "+"; + } + + Value eval(Expression& e1, Expression& e2, const SelectorEnv& env) const { + return e1.eval(env)+e2.eval(env); + } +}; + +class Sub : public ArithmeticOperator { + void repr(ostream& os) const { + os << "-"; + } + + Value eval(Expression& e1, Expression& e2, const SelectorEnv& env) const { + return e1.eval(env)-e2.eval(env); + } +}; + +class Mult : public ArithmeticOperator { + void repr(ostream& os) const { + os << "*"; + } + + Value eval(Expression& e1, Expression& e2, const SelectorEnv& env) const { + return e1.eval(env)*e2.eval(env); + } +}; + +class Div : public ArithmeticOperator { + void repr(ostream& os) const { + os << "/"; + } + + Value eval(Expression& e1, Expression& e2, const SelectorEnv& env) const { + return e1.eval(env)/e2.eval(env); + } +}; + Eq eqOp; Neq neqOp; Ls lsOp; @@ -518,16 +653,25 @@ IsNull isNullOp; IsNonNull isNonNullOp; Not notOp; +Negate negate; +Add add; +Sub sub; +Mult mult; +Div div; + //////////////////////////////////////////////////// -BoolExpression* parseOrExpression(Tokeniser&); -BoolExpression* parseAndExpression(Tokeniser&); -BoolExpression* parseComparisonExpression(Tokeniser&); +Expression* parseOrExpression(Tokeniser&); +Expression* parseAndExpression(Tokeniser&); +Expression* parseComparisonExpression(Tokeniser&); +Expression* parseAddExpression(Tokeniser&); +Expression* parseMultiplyExpression(Tokeniser&); +Expression* parseUnaryArithExpression(Tokeniser&); Expression* parsePrimaryExpression(Tokeniser&); // Top level parser class TopBoolExpression : public TopExpression { - boost::scoped_ptr<BoolExpression> expression; + boost::scoped_ptr<Expression> expression; void repr(ostream& os) const { expression->repr(os); @@ -540,7 +684,7 @@ class TopBoolExpression : public TopExpression { } public: - TopBoolExpression(BoolExpression* be) : + TopBoolExpression(Expression* be) : expression(be) {} }; @@ -550,19 +694,19 @@ TopExpression* TopExpression::parse(const string& exp) string::const_iterator s = exp.begin(); string::const_iterator e = exp.end(); Tokeniser tokeniser(s,e); - std::auto_ptr<BoolExpression> b(parseOrExpression(tokeniser)); + std::auto_ptr<Expression> b(parseOrExpression(tokeniser)); if (!b.get()) throw std::range_error("Illegal selector: couldn't parse"); if (tokeniser.nextToken().type != T_EOS) throw std::range_error("Illegal selector: too much input"); return new TopBoolExpression(b.release()); } -BoolExpression* parseOrExpression(Tokeniser& tokeniser) +Expression* parseOrExpression(Tokeniser& tokeniser) { - std::auto_ptr<BoolExpression> e(parseAndExpression(tokeniser)); + std::auto_ptr<Expression> e(parseAndExpression(tokeniser)); if (!e.get()) return 0; while ( tokeniser.nextToken().type==T_OR ) { - std::auto_ptr<BoolExpression> e1(e); - std::auto_ptr<BoolExpression> e2(parseAndExpression(tokeniser)); + std::auto_ptr<Expression> e1(e); + std::auto_ptr<Expression> e2(parseAndExpression(tokeniser)); if (!e2.get()) return 0; e.reset(new OrExpression(e1.release(), e2.release())); } @@ -570,13 +714,13 @@ BoolExpression* parseOrExpression(Tokeniser& tokeniser) return e.release(); } -BoolExpression* parseAndExpression(Tokeniser& tokeniser) +Expression* parseAndExpression(Tokeniser& tokeniser) { - std::auto_ptr<BoolExpression> e(parseComparisonExpression(tokeniser)); + std::auto_ptr<Expression> e(parseComparisonExpression(tokeniser)); if (!e.get()) return 0; while ( tokeniser.nextToken().type==T_AND ) { - std::auto_ptr<BoolExpression> e1(e); - std::auto_ptr<BoolExpression> e2(parseComparisonExpression(tokeniser)); + std::auto_ptr<Expression> e1(e); + std::auto_ptr<Expression> e2(parseComparisonExpression(tokeniser)); if (!e2.get()) return 0; e.reset(new AndExpression(e1.release(), e2.release())); } @@ -586,94 +730,166 @@ BoolExpression* parseAndExpression(Tokeniser& tokeniser) BoolExpression* parseSpecialComparisons(Tokeniser& tokeniser, std::auto_ptr<Expression> e1) { switch (tokeniser.nextToken().type) { - case T_LIKE: { - const Token t = tokeniser.nextToken(); - if ( t.type!=T_STRING ) return 0; - // Check for "ESCAPE" - if ( tokeniser.nextToken().type==T_ESCAPE ) { - const Token e = tokeniser.nextToken(); - if ( e.type!=T_STRING ) return 0; - return new LikeExpression(e1.release(), t.val, e.val); - } else { - tokeniser.returnTokens(); - return new LikeExpression(e1.release(), t.val); - } - } - case T_BETWEEN: { - std::auto_ptr<Expression> lower(parsePrimaryExpression(tokeniser)); - if ( !lower.get() ) return 0; - if ( tokeniser.nextToken().type!=T_AND ) return 0; - std::auto_ptr<Expression> upper(parsePrimaryExpression(tokeniser)); - if ( !upper.get() ) return 0; - return new BetweenExpression(e1.release(), lower.release(), upper.release()); + case T_LIKE: { + const Token t = tokeniser.nextToken(); + if ( t.type!=T_STRING ) return 0; + // Check for "ESCAPE" + if ( tokeniser.nextToken().type==T_ESCAPE ) { + const Token e = tokeniser.nextToken(); + if ( e.type!=T_STRING ) return 0; + return new LikeExpression(e1.release(), t.val, e.val); + } else { + tokeniser.returnTokens(); + return new LikeExpression(e1.release(), t.val); } - default: - return 0; + } + case T_BETWEEN: { + std::auto_ptr<Expression> lower(parseAddExpression(tokeniser)); + if ( !lower.get() ) return 0; + if ( tokeniser.nextToken().type!=T_AND ) return 0; + std::auto_ptr<Expression> upper(parseAddExpression(tokeniser)); + if ( !upper.get() ) return 0; + return new BetweenExpression(e1.release(), lower.release(), upper.release()); + } + default: + return 0; } } -BoolExpression* parseComparisonExpression(Tokeniser& tokeniser) +Expression* parseComparisonExpression(Tokeniser& tokeniser) { const Token t = tokeniser.nextToken(); - if ( t.type==T_LPAREN ) { - std::auto_ptr<BoolExpression> e(parseOrExpression(tokeniser)); - if (!e.get()) return 0; - if ( tokeniser.nextToken().type!=T_RPAREN ) return 0; - return e.release(); - } else if ( t.type==T_NOT ) { - std::auto_ptr<BoolExpression> e(parseComparisonExpression(tokeniser)); + if ( t.type==T_NOT ) { + std::auto_ptr<Expression> e(parseComparisonExpression(tokeniser)); if (!e.get()) return 0; - return new UnaryBooleanExpression<BoolExpression>(¬Op, e.release()); + return new UnaryBooleanExpression<Expression>(¬Op, e.release()); } tokeniser.returnTokens(); - std::auto_ptr<Expression> e1(parsePrimaryExpression(tokeniser)); + std::auto_ptr<Expression> e1(parseAddExpression(tokeniser)); if (!e1.get()) return 0; switch (tokeniser.nextToken().type) { - // Check for "IS NULL" and "IS NOT NULL" - case T_IS: - // The rest must be T_NULL or T_NOT, T_NULL - switch (tokeniser.nextToken().type) { - case T_NULL: - return new UnaryBooleanExpression<Expression>(&isNullOp, e1.release()); - case T_NOT: - if ( tokeniser.nextToken().type == T_NULL) - return new UnaryBooleanExpression<Expression>(&isNonNullOp, e1.release()); - default: - return 0; - } - case T_NOT: { - std::auto_ptr<BoolExpression> e(parseSpecialComparisons(tokeniser, e1)); - if (!e.get()) return 0; - return new UnaryBooleanExpression<BoolExpression>(¬Op, e.release()); - } - case T_BETWEEN: - case T_LIKE: { - tokeniser.returnTokens(); - return parseSpecialComparisons(tokeniser, e1); + // Check for "IS NULL" and "IS NOT NULL" + case T_IS: + // The rest must be T_NULL or T_NOT, T_NULL + switch (tokeniser.nextToken().type) { + case T_NULL: + return new UnaryBooleanExpression<Expression>(&isNullOp, e1.release()); + case T_NOT: + if ( tokeniser.nextToken().type == T_NULL) + return new UnaryBooleanExpression<Expression>(&isNonNullOp, e1.release()); + default: + return 0; } + case T_NOT: { + std::auto_ptr<BoolExpression> e(parseSpecialComparisons(tokeniser, e1)); + if (!e.get()) return 0; + return new UnaryBooleanExpression<Expression>(¬Op, e.release()); + } + case T_BETWEEN: + case T_LIKE: { + tokeniser.returnTokens(); + return parseSpecialComparisons(tokeniser, e1); + } + default: + break; + } + tokeniser.returnTokens(); + + ComparisonOperator* op; + switch (tokeniser.nextToken().type) { + case T_EQUAL: op = &eqOp; break; + case T_NEQ: op = &neqOp; break; + case T_LESS: op = &lsOp; break; + case T_GRT: op = &grOp; break; + case T_LSEQ: op = &lseqOp; break; + case T_GREQ: op = &greqOp; break; + default: + tokeniser.returnTokens(); + return e1.release(); + } + + std::auto_ptr<Expression> e2(parseAddExpression(tokeniser)); + if (!e2.get()) return 0; + + return new ComparisonExpression(op, e1.release(), e2.release()); +} + +Expression* parseAddExpression(Tokeniser& tokeniser) +{ + std::auto_ptr<Expression> e(parseMultiplyExpression(tokeniser)); + if (!e.get()) return 0; + + Token t = tokeniser.nextToken(); + while (t.type==T_PLUS || t.type==T_MINUS ) { + ArithmeticOperator* op; + switch (t.type) { + case T_PLUS: op = &add; break; + case T_MINUS: op = ⊂ break; default: - break; + return 0; + } + std::auto_ptr<Expression> e1(e); + std::auto_ptr<Expression> e2(parseMultiplyExpression(tokeniser)); + if (!e2.get()) return 0; + e.reset(new ArithmeticExpression(op, e1.release(), e2.release())); + t = tokeniser.nextToken(); } + tokeniser.returnTokens(); + return e.release(); +} - const Token op = tokeniser.nextToken(); - if (op.type != T_OPERATOR) { - return 0; +Expression* parseMultiplyExpression(Tokeniser& tokeniser) +{ + std::auto_ptr<Expression> e(parseUnaryArithExpression(tokeniser)); + if (!e.get()) return 0; + + Token t = tokeniser.nextToken(); + while (t.type==T_MULT || t.type==T_DIV ) { + ArithmeticOperator* op; + switch (t.type) { + case T_MULT: op = &mult; break; + case T_DIV: op = ÷ break; + default: + return 0; + } + std::auto_ptr<Expression> e1(e); + std::auto_ptr<Expression> e2(parseMultiplyExpression(tokeniser)); + if (!e2.get()) return 0; + e.reset(new ArithmeticExpression(op, e1.release(), e2.release())); + t = tokeniser.nextToken(); } - std::auto_ptr<Expression> e2(parsePrimaryExpression(tokeniser)); - if (!e2.get()) return 0; + tokeniser.returnTokens(); + return e.release(); +} - if (op.val == "=") return new ComparisonExpression(&eqOp, e1.release(), e2.release()); - if (op.val == "<>") return new ComparisonExpression(&neqOp, e1.release(), e2.release()); - if (op.val == "<") return new ComparisonExpression(&lsOp, e1.release(), e2.release()); - if (op.val == ">") return new ComparisonExpression(&grOp, e1.release(), e2.release()); - if (op.val == "<=") return new ComparisonExpression(&lseqOp, e1.release(), e2.release()); - if (op.val == ">=") return new ComparisonExpression(&greqOp, e1.release(), e2.release()); +Expression* parseUnaryArithExpression(Tokeniser& tokeniser) +{ + const Token t = tokeniser.nextToken(); + switch (t.type) { + case T_LPAREN: { + std::auto_ptr<Expression> e(parseOrExpression(tokeniser)); + if (!e.get()) return 0; + if ( tokeniser.nextToken().type!=T_RPAREN ) return 0; + return e.release(); + } + case T_PLUS: + break; // Unary + is no op + case T_MINUS: { + std::auto_ptr<Expression> e(parseUnaryArithExpression(tokeniser)); + if (!e.get()) return 0; + return new UnaryArithExpression(&negate, e.release()); + } + default: + break; + } - return 0; + tokeniser.returnTokens(); + std::auto_ptr<Expression> e(parsePrimaryExpression(tokeniser)); + return e.release(); } Expression* parsePrimaryExpression(Tokeniser& tokeniser) diff --git a/qpid/cpp/src/qpid/broker/SelectorToken.cpp b/qpid/cpp/src/qpid/broker/SelectorToken.cpp index 3540ee158f..1e84834e18 100644 --- a/qpid/cpp/src/qpid/broker/SelectorToken.cpp +++ b/qpid/cpp/src/qpid/broker/SelectorToken.cpp @@ -52,38 +52,6 @@ void skipWS(std::string::const_iterator& s, std::string::const_iterator& e) } } -bool tokeniseEos(std::string::const_iterator& s, std::string::const_iterator& e, Token& tok) -{ - if ( s!=e ) return false; - - tok = Token(T_EOS, ""); - return true; -} - -inline bool isIdentifierStart(char c) -{ - return std::isalpha(c) || c=='_' || c=='$'; -} - -inline bool isIdentifierPart(char c) -{ - return std::isalnum(c) || c=='_' || c=='$' || c=='.'; -} - -bool tokeniseIdentifier(std::string::const_iterator& s, std::string::const_iterator& e, Token& tok) -{ - // Be sure that first char is alphanumeric or _ or $ - if ( s==e || !isIdentifierStart(*s) ) return false; - - std::string::const_iterator t = s; - - while ( ++s!=e && isIdentifierPart(*s) ); - - tok = Token(T_IDENTIFIER, t, s); - - return true; -} - // Lexically, reserved words are a subset of identifiers // so we parse an identifier first then check if it is a reserved word and // convert it if it is a reserved word @@ -145,27 +113,11 @@ bool tokeniseReservedWord(Token& tok) return true; } -// This is really only used for testing -bool tokeniseReservedWord(std::string::const_iterator& s, std::string::const_iterator& e, Token& tok) -{ - std::string::const_iterator p = s; - bool r = tokeniseIdentifier(p, e, tok) && tokeniseReservedWord(tok); - if (r) s = p; - return r; -} - -bool tokeniseIdentifierOrReservedWord(std::string::const_iterator& s, std::string::const_iterator& e, Token& tok) -{ - bool r = tokeniseIdentifier(s, e, tok); - if (r) (void) tokeniseReservedWord(tok); - return r; -} - // parsing strings is complicated by the need to allow "''" as an embedded single quote -bool tokeniseString(std::string::const_iterator& s, std::string::const_iterator& e, Token& tok) +bool processString(std::string::const_iterator& s, std::string::const_iterator& e, Token& tok) { - if ( s==e || *s != '\'' ) return false; - + // We only get here once the tokeniser recognises the initial quote for a string + // so we don't need to check for it again. std::string::const_iterator q = std::find(s+1, e, '\''); if ( q==e ) return false; @@ -185,42 +137,17 @@ bool tokeniseString(std::string::const_iterator& s, std::string::const_iterator& return true; } -bool tokeniseParens(std::string::const_iterator& s, std::string::const_iterator& e, Token& tok) -{ - if ( s==e) return false; - if ( *s=='(' ) { - tok = Token (T_LPAREN, s, s+1); - ++s; - return true; - } - if ( *s==')' ) { - tok = Token (T_RPAREN, s, s+1); - ++s; - return true; - } - return false; -} - -inline bool isOperatorPart(char c) +inline bool isIdentifierStart(char c) { - return !std::isalnum(c) && !std::isspace(c) && c!='_' && c!='$' && c!='(' && c!=')' && c!= '\''; + return std::isalpha(c) || c=='_' || c=='$'; } -// These lexical tokens contain no alphanumerics - this is broader than actual operators but -// works. -bool tokeniseOperator(std::string::const_iterator& s, std::string::const_iterator& e, Token& tok) +inline bool isIdentifierPart(char c) { - if ( s==e || !isOperatorPart(*s) ) return false; - - std::string::const_iterator t = s; - - while (++s!=e && isOperatorPart(*s)); - - tok = Token(T_OPERATOR, t, s); - return true; + return std::isalnum(c) || c=='_' || c=='$' || c=='.'; } -bool tokeniseNumeric(std::string::const_iterator& s, std::string::const_iterator& e, Token& tok) +bool tokenise(std::string::const_iterator& s, std::string::const_iterator& e, Token& tok) { std::string::const_iterator t = s; @@ -228,65 +155,102 @@ bool tokeniseNumeric(std::string::const_iterator& s, std::string::const_iterator enum { START, REJECT, + IDENTIFIER, DIGIT, DECIMAL_START, DECIMAL, EXPONENT_SIGN, EXPONENT_START, EXPONENT, - ACCEPT_EXACT, - ACCEPT_INEXACT + ACCEPT_IDENTIFIER, + ACCEPT_INC, + ACCEPT_NOINC } state = START; + TokenType tokType = T_EOS; while (true) switch (state) { case START: - if (t==e) {state = REJECT;} + if (t==e) {tok = Token(T_EOS, ""); return true;} + else switch (*t) { + case '(': tokType = T_LPAREN; state = ACCEPT_INC; continue; + case ')': tokType = T_RPAREN; state = ACCEPT_INC; continue; + case ',': tokType = T_COMMA; state = ACCEPT_INC; continue; + case '+': tokType = T_PLUS; state = ACCEPT_INC; continue; + case '-': tokType = T_MINUS; state = ACCEPT_INC; continue; + case '*': tokType = T_MULT; state = ACCEPT_INC; continue; + case '/': tokType = T_DIV; state = ACCEPT_INC; continue; + case '=': tokType = T_EQUAL; state = ACCEPT_INC; continue; + case '<': + ++t; + if (t==e || (*t!='>' && *t!='=')) + {tokType = T_LESS; state = ACCEPT_NOINC; continue; } + else + {tokType = (*t=='>') ? T_NEQ : T_LSEQ; state = ACCEPT_INC; continue; } + case '>': + ++t; + if (t==e || *t!='=') + {tokType = T_GRT; state = ACCEPT_NOINC; continue;} + else + {tokType = T_GREQ; state = ACCEPT_INC; continue;} + default: + break; + } + if (isIdentifierStart(*t)) {++t; state = IDENTIFIER;} + else if (*t=='\'') {return processString(s, e, tok);} else if (std::isdigit(*t)) {++t; state = DIGIT;} else if (*t=='.') {++t; state = DECIMAL_START;} else state = REJECT; - break; + continue; + case IDENTIFIER: + if (t==e) {state = ACCEPT_IDENTIFIER;} + else if (isIdentifierPart(*t)) {++t; state = IDENTIFIER;} + else state = ACCEPT_IDENTIFIER; + continue; case DECIMAL_START: if (t==e) {state = REJECT;} else if (std::isdigit(*t)) {++t; state = DECIMAL;} else state = REJECT; - break; + continue; case EXPONENT_SIGN: if (t==e) {state = REJECT;} else if (*t=='-' || *t=='+') {++t; state = EXPONENT_START;} else if (std::isdigit(*t)) {++t; state = EXPONENT;} else state = REJECT; - break; + continue; case EXPONENT_START: if (t==e) {state = REJECT;} else if (std::isdigit(*t)) {++t; state = EXPONENT;} else state = REJECT; - break; + continue; case DIGIT: - if (t==e) {state = ACCEPT_EXACT;} + if (t==e) {tokType = T_NUMERIC_EXACT; state = ACCEPT_NOINC;} else if (std::isdigit(*t)) {++t; state = DIGIT;} else if (*t=='.') {++t; state = DECIMAL;} else if (*t=='e' || *t=='E') {++t; state = EXPONENT_SIGN;} - else state = ACCEPT_EXACT; - break; + else {tokType = T_NUMERIC_EXACT; state = ACCEPT_NOINC;} + continue; case DECIMAL: - if (t==e) {state = ACCEPT_INEXACT;} + if (t==e) {tokType = T_NUMERIC_APPROX; state = ACCEPT_NOINC;} else if (std::isdigit(*t)) {++t; state = DECIMAL;} else if (*t=='e' || *t=='E') {++t; state = EXPONENT_SIGN;} - else state = ACCEPT_INEXACT; - break; + else {tokType = T_NUMERIC_APPROX; state = ACCEPT_NOINC;} + continue; case EXPONENT: - if (t==e) {state = ACCEPT_INEXACT;} + if (t==e) {tokType = T_NUMERIC_APPROX; state = ACCEPT_NOINC;} else if (std::isdigit(*t)) {++t; state = EXPONENT;} - else state = ACCEPT_INEXACT; - break; - case ACCEPT_EXACT: - tok = Token(T_NUMERIC_EXACT, s, t); + else {tokType = T_NUMERIC_APPROX; state = ACCEPT_NOINC;} + continue; + case ACCEPT_INC: + ++t; + case ACCEPT_NOINC: + tok = Token(tokType, s, t); s = t; return true; - case ACCEPT_INEXACT: - tok = Token(T_NUMERIC_APPROX, s, t); + case ACCEPT_IDENTIFIER: + tok = Token(T_IDENTIFIER, s, t); s = t; + tokeniseReservedWord(tok); return true; case REJECT: return false; @@ -319,12 +283,7 @@ const Token& Tokeniser::nextToken() tokens.push_back(Token()); Token& tok = tokens[tokp++]; - if (tokeniseEos(inp, inEnd, tok)) return tok; - if (tokeniseIdentifierOrReservedWord(inp, inEnd, tok)) return tok; - if (tokeniseNumeric(inp, inEnd, tok)) return tok; - if (tokeniseString(inp, inEnd, tok)) return tok; - if (tokeniseParens(inp, inEnd, tok)) return tok; - if (tokeniseOperator(inp, inEnd, tok)) return tok; + if (tokenise(inp, inEnd, tok)) return tok; throw TokenException("Found illegal character"); } diff --git a/qpid/cpp/src/qpid/broker/SelectorToken.h b/qpid/cpp/src/qpid/broker/SelectorToken.h index 62f6d78fdd..91fb1d5c36 100644 --- a/qpid/cpp/src/qpid/broker/SelectorToken.h +++ b/qpid/cpp/src/qpid/broker/SelectorToken.h @@ -51,7 +51,17 @@ typedef enum { T_NUMERIC_APPROX, T_LPAREN, T_RPAREN, - T_OPERATOR + T_COMMA, + T_PLUS, + T_MINUS, + T_MULT, + T_DIV, + T_EQUAL, + T_NEQ, + T_LESS, + T_GRT, + T_LSEQ, + T_GREQ } TokenType; struct Token { @@ -67,8 +77,8 @@ struct Token { {} Token(TokenType t, const std::string::const_iterator& s, const std::string::const_iterator& e) : - type(t), - val(std::string(s,e)) + type(t), + val(std::string(s,e)) {} bool operator==(const Token& r) const @@ -84,14 +94,7 @@ public: TokenException(const std::string&); }; -QPID_BROKER_EXTERN bool tokeniseEos(std::string::const_iterator& s, std::string::const_iterator& e, Token& tok); -QPID_BROKER_EXTERN bool tokeniseIdentifier(std::string::const_iterator& s, std::string::const_iterator& e, Token& tok); -QPID_BROKER_EXTERN bool tokeniseReservedWord(std::string::const_iterator& s, std::string::const_iterator& e, Token& tok); -QPID_BROKER_EXTERN bool tokeniseIdentifierOrReservedWord(std::string::const_iterator& s, std::string::const_iterator& e, Token& tok); -QPID_BROKER_EXTERN bool tokeniseString(std::string::const_iterator& s, std::string::const_iterator& e, Token& tok); -QPID_BROKER_EXTERN bool tokeniseParens(std::string::const_iterator& s, std::string::const_iterator& e, Token& tok); -QPID_BROKER_EXTERN bool tokeniseOperator(std::string::const_iterator& s, std::string::const_iterator& e, Token& tok); -QPID_BROKER_EXTERN bool tokeniseNumeric(std::string::const_iterator& s, std::string::const_iterator& e, Token& tok); +QPID_BROKER_EXTERN bool tokenise(std::string::const_iterator& s, std::string::const_iterator& e, Token& tok); class Tokeniser { std::vector<Token> tokens; diff --git a/qpid/cpp/src/qpid/broker/SelectorValue.cpp b/qpid/cpp/src/qpid/broker/SelectorValue.cpp index 83a9240dcb..de097b8969 100644 --- a/qpid/cpp/src/qpid/broker/SelectorValue.cpp +++ b/qpid/cpp/src/qpid/broker/SelectorValue.cpp @@ -194,4 +194,17 @@ Value operator/(const Value& v1, const Value& v2) return Value(); } +Value operator-(const Value& v) +{ + switch (v.type) { + case Value::T_EXACT: + return -v.i; + case Value::T_INEXACT: + return -v.x; + default: + break; + } + return Value(); +} + }} diff --git a/qpid/cpp/src/qpid/broker/SelectorValue.h b/qpid/cpp/src/qpid/broker/SelectorValue.h index 2bf4168a5a..3a731fd000 100644 --- a/qpid/cpp/src/qpid/broker/SelectorValue.h +++ b/qpid/cpp/src/qpid/broker/SelectorValue.h @@ -114,6 +114,7 @@ Value operator+(const Value&, const Value&); Value operator-(const Value&, const Value&); Value operator*(const Value&, const Value&); Value operator/(const Value&, const Value&); +Value operator-(const Value&); }} diff --git a/qpid/cpp/src/tests/Selector.cpp b/qpid/cpp/src/tests/Selector.cpp index 7d36cfe308..7758e3e5b0 100644 --- a/qpid/cpp/src/tests/Selector.cpp +++ b/qpid/cpp/src/tests/Selector.cpp @@ -38,14 +38,7 @@ namespace qb = qpid::broker; using qpid::broker::Token; using qpid::broker::TokenType; using qpid::broker::Tokeniser; -using qpid::broker::tokeniseEos; -using qpid::broker::tokeniseIdentifier; -using qpid::broker::tokeniseIdentifierOrReservedWord; -using qpid::broker::tokeniseReservedWord; -using qpid::broker::tokeniseOperator; -using qpid::broker::tokeniseParens; -using qpid::broker::tokeniseNumeric; -using qpid::broker::tokeniseString; +using qpid::broker::tokenise; namespace qpid { namespace tests { @@ -54,6 +47,88 @@ QPID_AUTO_TEST_SUITE(SelectorSuite) typedef bool (*TokeniseF)(string::const_iterator&,string::const_iterator&,Token&); +bool tokeniseEos(std::string::const_iterator& s, std::string::const_iterator& e, Token& tok) +{ + Token t1; + std::string::const_iterator t = s; + bool r = tokenise(t, e, t1); + if (r && (t1.type==qb::T_EOS)) {tok = t1; s = t; return true;} + return false; +} + +bool tokeniseParens(std::string::const_iterator& s, std::string::const_iterator& e, Token& tok) +{ + Token t1; + std::string::const_iterator t = s; + bool r = tokenise(t, e, t1); + if (r && (t1.type==qb::T_LPAREN || t1.type==qb::T_RPAREN)) {tok = t1; s = t; return true;} + return false; +} + +bool tokeniseOperator(std::string::const_iterator& s, std::string::const_iterator& e, Token& tok) +{ + Token t1; + std::string::const_iterator t = s; + bool r = tokenise(t, e, t1); + if (r && (t1.type>=qb::T_PLUS && t1.type<=qb::T_GREQ)) {tok = t1; s = t; return true;} + return false; +} + +bool tokeniseString(std::string::const_iterator& s, std::string::const_iterator& e, Token& tok) +{ + Token t1; + std::string::const_iterator t = s; + bool r = tokenise(t, e, t1); + if (r && (t1.type==qb::T_STRING)) {tok = t1; s = t; return true;} + return false; +} + +bool tokeniseIdentifier(std::string::const_iterator& s, std::string::const_iterator& e, Token& tok) +{ + Token t1; + std::string::const_iterator t = s; + bool r = tokenise(t, e, t1); + if (r && (t1.type==qb::T_IDENTIFIER)) {tok = t1; s = t; return true;} + return false; +} + +bool tokeniseReservedWord(std::string::const_iterator& s, std::string::const_iterator& e, Token& tok) +{ + std::string::const_iterator t = s; + Token t1; + if (tokenise(t, e, t1)) { + switch (t1.type) { + case qb::T_AND: + case qb::T_BETWEEN: + case qb::T_ESCAPE: + case qb::T_FALSE: + case qb::T_IN: + case qb::T_IS: + case qb::T_LIKE: + case qb::T_NOT: + case qb::T_NULL: + case qb::T_OR: + case qb::T_TRUE: + tok = t1; + s = t; + return true; + default: + break; + } + } + return false; +} + +bool tokeniseNumeric(std::string::const_iterator& s, std::string::const_iterator& e, Token& tok) +{ + Token t1; + std::string::const_iterator t = s; + bool r = tokenise(t, e, t1); + if (r && (t1.type==qb::T_NUMERIC_EXACT || t1.type==qb::T_NUMERIC_APPROX)) {tok = t1; s = t; return true;} + return false; +} + + void verifyTokeniserSuccess(TokeniseF t, const char* ss, TokenType tt, const char* tv, const char* fs) { Token tok; string s(ss); @@ -75,29 +150,29 @@ void verifyTokeniserFail(TokeniseF t, const char* c) { QPID_AUTO_TEST_CASE(tokeniseSuccess) { - verifyTokeniserSuccess(&tokeniseEos, "", qb::T_EOS, "", ""); - verifyTokeniserSuccess(&tokeniseIdentifier, "null_123+blah", qb::T_IDENTIFIER, "null_123", "+blah"); - verifyTokeniserSuccess(&tokeniseIdentifierOrReservedWord, "null_123+blah", qb::T_IDENTIFIER, "null_123", "+blah"); - verifyTokeniserSuccess(&tokeniseIdentifierOrReservedWord, "null+blah", qb::T_NULL, "null", "+blah"); - verifyTokeniserSuccess(&tokeniseIdentifierOrReservedWord, "null+blah", qb::T_NULL, "null", "+blah"); - verifyTokeniserSuccess(&tokeniseIdentifierOrReservedWord, "Is nOt null", qb::T_IS, "Is", " nOt null"); - verifyTokeniserSuccess(&tokeniseIdentifierOrReservedWord, "nOt null", qb::T_NOT, "nOt", " null"); - verifyTokeniserSuccess(&tokeniseIdentifierOrReservedWord, "Is nOt null", qb::T_IS, "Is", " nOt null"); - verifyTokeniserSuccess(&tokeniseString, "'Hello World'", qb::T_STRING, "Hello World", ""); - verifyTokeniserSuccess(&tokeniseString, "'Hello World''s end'a bit more", qb::T_STRING, "Hello World's end", "a bit more"); - verifyTokeniserSuccess(&tokeniseOperator, "=blah", qb::T_OPERATOR, "=", "blah"); - verifyTokeniserSuccess(&tokeniseOperator, "<> Identifier", qb::T_OPERATOR, "<>", " Identifier"); - verifyTokeniserSuccess(&tokeniseParens, "(a and b) not c", qb::T_LPAREN, "(", "a and b) not c"); - verifyTokeniserSuccess(&tokeniseParens, ") not c", qb::T_RPAREN, ")", " not c"); - verifyTokeniserSuccess(&tokeniseNumeric, "019kill", qb::T_NUMERIC_EXACT, "019", "kill"); - verifyTokeniserSuccess(&tokeniseNumeric, "0kill", qb::T_NUMERIC_EXACT, "0", "kill"); - verifyTokeniserSuccess(&tokeniseNumeric, "0.kill", qb::T_NUMERIC_APPROX, "0.", "kill"); - verifyTokeniserSuccess(&tokeniseNumeric, "3.1415=pi", qb::T_NUMERIC_APPROX, "3.1415", "=pi"); - verifyTokeniserSuccess(&tokeniseNumeric, ".25.kill", qb::T_NUMERIC_APPROX, ".25", ".kill"); - verifyTokeniserSuccess(&tokeniseNumeric, "2e5.kill", qb::T_NUMERIC_APPROX, "2e5", ".kill"); - verifyTokeniserSuccess(&tokeniseNumeric, "3.e50easy to kill", qb::T_NUMERIC_APPROX, "3.e50", "easy to kill"); - verifyTokeniserSuccess(&tokeniseNumeric, "34.25e+50easy to kill", qb::T_NUMERIC_APPROX, "34.25e+50", "easy to kill"); - verifyTokeniserSuccess(&tokeniseNumeric, "34.e-50easy to kill", qb::T_NUMERIC_APPROX, "34.e-50", "easy to kill"); + verifyTokeniserSuccess(&tokenise, "", qb::T_EOS, "", ""); + verifyTokeniserSuccess(&tokenise, "null_123+blah", qb::T_IDENTIFIER, "null_123", "+blah"); + verifyTokeniserSuccess(&tokenise, "null_123+blah", qb::T_IDENTIFIER, "null_123", "+blah"); + verifyTokeniserSuccess(&tokenise, "null+blah", qb::T_NULL, "null", "+blah"); + verifyTokeniserSuccess(&tokenise, "null+blah", qb::T_NULL, "null", "+blah"); + verifyTokeniserSuccess(&tokenise, "Is nOt null", qb::T_IS, "Is", " nOt null"); + verifyTokeniserSuccess(&tokenise, "nOt null", qb::T_NOT, "nOt", " null"); + verifyTokeniserSuccess(&tokenise, "Is nOt null", qb::T_IS, "Is", " nOt null"); + verifyTokeniserSuccess(&tokenise, "'Hello World'", qb::T_STRING, "Hello World", ""); + verifyTokeniserSuccess(&tokenise, "'Hello World''s end'a bit more", qb::T_STRING, "Hello World's end", "a bit more"); + verifyTokeniserSuccess(&tokenise, "=blah", qb::T_EQUAL, "=", "blah"); + verifyTokeniserSuccess(&tokenise, "<> Identifier", qb::T_NEQ, "<>", " Identifier"); + verifyTokeniserSuccess(&tokenise, "(a and b) not c", qb::T_LPAREN, "(", "a and b) not c"); + verifyTokeniserSuccess(&tokenise, ") not c", qb::T_RPAREN, ")", " not c"); + verifyTokeniserSuccess(&tokenise, "019kill", qb::T_NUMERIC_EXACT, "019", "kill"); + verifyTokeniserSuccess(&tokenise, "0kill", qb::T_NUMERIC_EXACT, "0", "kill"); + verifyTokeniserSuccess(&tokenise, "0.kill", qb::T_NUMERIC_APPROX, "0.", "kill"); + verifyTokeniserSuccess(&tokenise, "3.1415=pi", qb::T_NUMERIC_APPROX, "3.1415", "=pi"); + verifyTokeniserSuccess(&tokenise, ".25.kill", qb::T_NUMERIC_APPROX, ".25", ".kill"); + verifyTokeniserSuccess(&tokenise, "2e5.kill", qb::T_NUMERIC_APPROX, "2e5", ".kill"); + verifyTokeniserSuccess(&tokenise, "3.e50easy to kill", qb::T_NUMERIC_APPROX, "3.e50", "easy to kill"); + verifyTokeniserSuccess(&tokenise, "34.25e+50easy to kill", qb::T_NUMERIC_APPROX, "34.25e+50", "easy to kill"); + verifyTokeniserSuccess(&tokenise, "34.e-50easy to kill", qb::T_NUMERIC_APPROX, "34.e-50", "easy to kill"); } QPID_AUTO_TEST_CASE(tokeniseFailure) @@ -135,7 +210,7 @@ QPID_AUTO_TEST_CASE(tokenString) Tokeniser t(s, e); BOOST_CHECK_EQUAL(t.nextToken(), Token(qb::T_IDENTIFIER, "a")); - BOOST_CHECK_EQUAL(t.nextToken(), Token(qb::T_OPERATOR, "=")); + BOOST_CHECK_EQUAL(t.nextToken(), Token(qb::T_EQUAL, "=")); BOOST_CHECK_EQUAL(t.nextToken(), Token(qb::T_IDENTIFIER, "b")); BOOST_CHECK_EQUAL(t.nextToken(), Token(qb::T_EOS, "")); @@ -146,7 +221,7 @@ QPID_AUTO_TEST_CASE(tokenString) BOOST_CHECK_EQUAL(u.nextToken(), Token(qb::T_NOT, "not")); BOOST_CHECK_EQUAL(u.nextToken(), Token(qb::T_STRING, "hello kitty's friend")); - BOOST_CHECK_EQUAL(u.nextToken(), Token(qb::T_OPERATOR, "=")); + BOOST_CHECK_EQUAL(u.nextToken(), Token(qb::T_EQUAL, "=")); BOOST_CHECK_EQUAL(u.nextToken(), Token(qb::T_IS, "Is")); BOOST_CHECK_EQUAL(u.nextToken(), Token(qb::T_NULL, "null")); BOOST_CHECK_EQUAL(u.nextToken(), Token(qb::T_EOS, "")); @@ -165,12 +240,12 @@ QPID_AUTO_TEST_CASE(tokenString) BOOST_CHECK_EQUAL(v.nextToken(), Token(qb::T_LPAREN, "(")); BOOST_CHECK_EQUAL(v.nextToken(), Token(qb::T_IDENTIFIER, "a")); - BOOST_CHECK_EQUAL(v.nextToken(), Token(qb::T_OPERATOR, "+")); + BOOST_CHECK_EQUAL(v.nextToken(), Token(qb::T_PLUS, "+")); BOOST_CHECK_EQUAL(v.nextToken(), Token(qb::T_NUMERIC_EXACT, "6")); BOOST_CHECK_EQUAL(v.nextToken(), Token(qb::T_RPAREN, ")")); - BOOST_CHECK_EQUAL(v.nextToken(), Token(qb::T_OPERATOR, "*")); + BOOST_CHECK_EQUAL(v.nextToken(), Token(qb::T_MULT, "*")); BOOST_CHECK_EQUAL(v.nextToken(), Token(qb::T_NUMERIC_APPROX, "7.5")); - BOOST_CHECK_EQUAL(v.nextToken(), Token(qb::T_OPERATOR, "/")); + BOOST_CHECK_EQUAL(v.nextToken(), Token(qb::T_DIV, "/")); BOOST_CHECK_EQUAL(v.nextToken(), Token(qb::T_NUMERIC_APPROX, "1e6")); } @@ -179,7 +254,6 @@ QPID_AUTO_TEST_CASE(parseStringFail) BOOST_CHECK_THROW(qb::Selector e("A is null not"), std::range_error); BOOST_CHECK_THROW(qb::Selector e("A is null or not"), std::range_error); BOOST_CHECK_THROW(qb::Selector e("A is null or and"), std::range_error); - BOOST_CHECK_THROW(qb::Selector e("A is null and 'hello out there'"), std::range_error); BOOST_CHECK_THROW(qb::Selector e("A is null and (B='hello out there'"), std::range_error); BOOST_CHECK_THROW(qb::Selector e("in='hello kitty'"), std::range_error); BOOST_CHECK_THROW(qb::Selector e("A like 234"), std::range_error); @@ -187,38 +261,9 @@ QPID_AUTO_TEST_CASE(parseStringFail) BOOST_CHECK_THROW(qb::Selector e("A not like 'eclecti_' escape 'happy'"), std::range_error); BOOST_CHECK_THROW(qb::Selector e("A not like 'eclecti_' escape happy"), std::range_error); BOOST_CHECK_THROW(qb::Selector e("A BETWEEN AND 'true'"), std::range_error); - BOOST_CHECK_THROW(qb::Selector e("A NOT BETWEEN (X=Y) AND 3.9"), std::range_error); BOOST_CHECK_THROW(qb::Selector e("A NOT BETWEEN 34 OR 3.9"), std::range_error); } -class TestSelectorEnv : public qpid::broker::SelectorEnv { - mutable map<string, qb::Value> values; - boost::ptr_vector<string> strings; - static const qb::Value EMPTY; - - const qb::Value& value(const string& v) const { - const qb::Value& r = values.find(v)!=values.end() ? values[v] : EMPTY; - return r; - } - -public: - void set(const string& id, const char* value) { - strings.push_back(new string(value)); - values[id] = strings[strings.size()-1]; - } - - void set(const string& id, const qb::Value& value) { - if (value.type==qb::Value::T_STRING) { - strings.push_back(new string(*value.s)); - values[id] = strings[strings.size()-1]; - } else { - values[id] = value; - } - } -}; - -const qb::Value TestSelectorEnv::EMPTY; - QPID_AUTO_TEST_CASE(parseString) { BOOST_CHECK_NO_THROW(qb::Selector e("'Daft' is not null")); @@ -245,8 +290,43 @@ QPID_AUTO_TEST_CASE(parseString) BOOST_CHECK_NO_THROW(qb::Selector e("A LIKE 'excep%ional' EScape '\'")); BOOST_CHECK_NO_THROW(qb::Selector e("A BETWEEN 13 AND 'true'")); BOOST_CHECK_NO_THROW(qb::Selector e("A NOT BETWEEN 100 AND 3.9")); + BOOST_CHECK_NO_THROW(qb::Selector e("true")); + BOOST_CHECK_NO_THROW(qb::Selector e("-354")); + BOOST_CHECK_NO_THROW(qb::Selector e("-(X or Y)")); + BOOST_CHECK_NO_THROW(qb::Selector e("-687 or 567")); + BOOST_CHECK_NO_THROW(qb::Selector e("(354.6)")); + BOOST_CHECK_NO_THROW(qb::Selector e("A is null and 'hello out there'")); + BOOST_CHECK_NO_THROW(qb::Selector e("17/4>4")); } +class TestSelectorEnv : public qpid::broker::SelectorEnv { + mutable map<string, qb::Value> values; + boost::ptr_vector<string> strings; + static const qb::Value EMPTY; + + const qb::Value& value(const string& v) const { + const qb::Value& r = values.find(v)!=values.end() ? values[v] : EMPTY; + return r; + } + +public: + void set(const string& id, const char* value) { + strings.push_back(new string(value)); + values[id] = strings[strings.size()-1]; + } + + void set(const string& id, const qb::Value& value) { + if (value.type==qb::Value::T_STRING) { + strings.push_back(new string(*value.s)); + values[id] = strings[strings.size()-1]; + } else { + values[id] = value; + } + } +}; + +const qb::Value TestSelectorEnv::EMPTY; + QPID_AUTO_TEST_CASE(simpleEval) { TestSelectorEnv env; @@ -296,13 +376,18 @@ QPID_AUTO_TEST_CASE(numericEval) BOOST_CHECK(qb::Selector("B=39.0").eval(env)); BOOST_CHECK(qb::Selector("Not A=17 or B=5.6").eval(env)); BOOST_CHECK(!qb::Selector("A<>17 and B=5.6e17").eval(env)); + BOOST_CHECK(qb::Selector("3 BETWEEN -17 and 98.5").eval(env)); BOOST_CHECK(qb::Selector("A BETWEEN B and 98.5").eval(env)); BOOST_CHECK(!qb::Selector("B NOT BETWEEN 35 AND 100").eval(env)); BOOST_CHECK(!qb::Selector("A BETWEEN B and 40").eval(env)); BOOST_CHECK(!qb::Selector("A BETWEEN C and 40").eval(env)); BOOST_CHECK(!qb::Selector("A BETWEEN 45 and C").eval(env)); - BOOST_CHECK(!qb::Selector("A BETWEEN 40 and C OR A NOT BETWEEN 40 and C").eval(env)); - BOOST_CHECK(!qb::Selector("A BETWEEN C and 45 OR A NOT BETWEEN C and 45").eval(env)); + BOOST_CHECK(qb::Selector("(A BETWEEN 40 and C) IS NULL").eval(env)); + BOOST_CHECK(qb::Selector("(A BETWEEN C and 45) IS NULL").eval(env)); + BOOST_CHECK(qb::Selector("17/4=4").eval(env)); + BOOST_CHECK(!qb::Selector("A/0=0").eval(env)); + BOOST_CHECK(qb::Selector("A*B+19<A*(B+19)").eval(env)); + BOOST_CHECK(qb::Selector("-A=0-A").eval(env)); } QPID_AUTO_TEST_CASE(comparisonEval) |