You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@qpid.apache.org by as...@apache.org on 2013/03/04 22:08:46 UTC
svn commit: r1452525 - in /qpid/trunk/qpid/cpp/src: ./ qpid/broker/ tests/
Author: astitcher
Date: Mon Mar 4 21:08:45 2013
New Revision: 1452525
URL: http://svn.apache.org/r1452525
Log:
QPID-4558: Selectors for C++ broker
- Added numeric and boolean values
* To literals and identifier values
* To the code that extracts values from message properties
- Added the full set of comparison operators
- Implemented full "unknown" semantics for all
operators.
- Implemented extended "is null" and "is not null" operators
that allow expressions as well as just identifiers.
Added:
qpid/trunk/qpid/cpp/src/qpid/broker/SelectorValue.cpp
qpid/trunk/qpid/cpp/src/qpid/broker/SelectorValue.h
Modified:
qpid/trunk/qpid/cpp/src/CMakeLists.txt
qpid/trunk/qpid/cpp/src/Makefile.am
qpid/trunk/qpid/cpp/src/qpid/broker/Selector.cpp
qpid/trunk/qpid/cpp/src/qpid/broker/Selector.h
qpid/trunk/qpid/cpp/src/qpid/broker/SelectorExpression.cpp
qpid/trunk/qpid/cpp/src/qpid/broker/SelectorExpression.h
qpid/trunk/qpid/cpp/src/qpid/broker/SelectorToken.cpp
qpid/trunk/qpid/cpp/src/qpid/broker/SelectorToken.h
qpid/trunk/qpid/cpp/src/tests/Selector.cpp
Modified: qpid/trunk/qpid/cpp/src/CMakeLists.txt
URL: http://svn.apache.org/viewvc/qpid/trunk/qpid/cpp/src/CMakeLists.txt?rev=1452525&r1=1452524&r2=1452525&view=diff
==============================================================================
--- qpid/trunk/qpid/cpp/src/CMakeLists.txt (original)
+++ qpid/trunk/qpid/cpp/src/CMakeLists.txt Mon Mar 4 21:08:45 2013
@@ -1211,6 +1211,8 @@ set (qpidbroker_SOURCES
qpid/broker/SelectorExpression.cpp
qpid/broker/SelectorToken.h
qpid/broker/SelectorToken.cpp
+ qpid/broker/SelectorValue.h
+ qpid/broker/SelectorValue.cpp
qpid/broker/SemanticState.h
qpid/broker/SemanticState.cpp
qpid/broker/SessionAdapter.cpp
Modified: qpid/trunk/qpid/cpp/src/Makefile.am
URL: http://svn.apache.org/viewvc/qpid/trunk/qpid/cpp/src/Makefile.am?rev=1452525&r1=1452524&r2=1452525&view=diff
==============================================================================
--- qpid/trunk/qpid/cpp/src/Makefile.am (original)
+++ qpid/trunk/qpid/cpp/src/Makefile.am Mon Mar 4 21:08:45 2013
@@ -719,6 +719,8 @@ libqpidbroker_la_SOURCES = \
qpid/broker/SelectorExpression.h \
qpid/broker/SelectorToken.cpp \
qpid/broker/SelectorToken.h \
+ qpid/broker/SelectorValue.cpp \
+ qpid/broker/SelectorValue.h \
qpid/broker/SemanticState.cpp \
qpid/broker/SemanticState.h \
qpid/broker/SessionAdapter.cpp \
Modified: qpid/trunk/qpid/cpp/src/qpid/broker/Selector.cpp
URL: http://svn.apache.org/viewvc/qpid/trunk/qpid/cpp/src/qpid/broker/Selector.cpp?rev=1452525&r1=1452524&r2=1452525&view=diff
==============================================================================
--- qpid/trunk/qpid/cpp/src/qpid/broker/Selector.cpp (original)
+++ qpid/trunk/qpid/cpp/src/qpid/broker/Selector.cpp Mon Mar 4 21:08:45 2013
@@ -23,56 +23,23 @@
#include "qpid/broker/Message.h"
#include "qpid/broker/SelectorExpression.h"
+#include "qpid/broker/SelectorValue.h"
#include "qpid/log/Statement.h"
+#include "qpid/types/Variant.h"
#include <string>
#include <sstream>
+#include "qpid/sys/unordered_map.h"
#include <boost/make_shared.hpp>
#include <boost/lexical_cast.hpp>
+#include <boost/ptr_container/ptr_vector.hpp>
namespace qpid {
namespace broker {
using std::string;
-
-Selector::Selector(const string& e) :
- parse(parseTopBoolExpression(e)),
- expression(e)
-{
- bool debugOut;
- QPID_LOG_TEST(debug, debugOut);
- if (debugOut) {
- std::stringstream ss;
- parse->repr(ss);
- QPID_LOG(debug, "Selector parsed[" << e << "] into: " << ss.str());
- }
-}
-
-Selector::~Selector()
-{
-}
-
-bool Selector::eval(const SelectorEnv& env)
-{
- return parse->eval(env);
-}
-
-bool Selector::filter(const Message& msg)
-{
- return eval(MessageSelectorEnv(msg));
-}
-
-MessageSelectorEnv::MessageSelectorEnv(const Message& m) :
- msg(m)
-{
-}
-
-bool MessageSelectorEnv::present(const string& identifier) const
-{
- // If the value we get is void then most likely the property wasn't present
- return !msg.getProperty(identifier).isVoid();
-}
+using qpid::sys::unordered_map;
/**
* Identifier (amqp.) | JMS... | amqp 1.0 equivalent
@@ -89,48 +56,124 @@ bool MessageSelectorEnv::present(const s
* creation_time | Timestamp | creation-time properties section
* jms_type | Type | jms-type message-annotations section
*/
+const string EMPTY;
+const string PERSISTENT("PERSISTENT");
+const string NON_PERSISTENT("NON_PERSISTENT");
-string specialValue(const Message& msg, const string& id)
+const Value specialValue(const Message& msg, const string& id)
{
// TODO: Just use a simple if chain for now - improve this later
if ( id=="delivery_mode" ) {
- return msg.getEncoding().isPersistent() ? "PERSISTENT" : "NON_PERSISTENT";
+ return msg.getEncoding().isPersistent() ? PERSISTENT : NON_PERSISTENT;
} else if ( id=="redelivered" ) {
- return msg.getDeliveryCount()>0 ? "TRUE" : "FALSE";
+ return msg.getDeliveryCount()>0 ? true : false;
} else if ( id=="priority" ) {
- return boost::lexical_cast<string>(static_cast<uint32_t>(msg.getEncoding().getPriority()));
+ return int64_t(msg.getPriority());
} else if ( id=="correlation_id" ) {
- return ""; // Needs an indirection in getEncoding().
+ return EMPTY; // Needs an indirection in getEncoding().
} else if ( id=="message_id" ) {
- return ""; // Needs an indirection in getEncoding().
+ return EMPTY; // Needs an indirection in getEncoding().
} else if ( id=="to" ) {
- return msg.getRoutingKey(); // This is good for 0-10, not sure about 1.0
+ return EMPTY; // This is good for 0-10, not sure about 1.0
} else if ( id=="reply_to" ) {
- return ""; // Needs an indirection in getEncoding().
+ return EMPTY; // Needs an indirection in getEncoding().
} else if ( id=="absolute_expiry_time" ) {
- return ""; // Needs an indirection in getEncoding().
+ return EMPTY; // Needs an indirection in getEncoding().
} else if ( id=="creation_time" ) {
- return ""; // Needs an indirection in getEncoding().
+ return EMPTY; // Needs an indirection in getEncoding().
} else if ( id=="jms_type" ) {
- return msg.getAnnotation("jms-type");
- } else return "";
+ return EMPTY;
+ } else return Value();
}
-string MessageSelectorEnv::value(const string& identifier) const
+class MessageSelectorEnv : public SelectorEnv {
+ const Message& msg;
+ mutable boost::ptr_vector<string> returnedStrings;
+ mutable unordered_map<string, Value> returnedValues;
+
+ const Value& value(const string&) const;
+
+public:
+ MessageSelectorEnv(const Message&);
+};
+
+MessageSelectorEnv::MessageSelectorEnv(const Message& m) :
+msg(m)
{
- string v;
+}
+
+const Value& MessageSelectorEnv::value(const string& identifier) const
+{
+ if (returnedValues.find(identifier)==returnedValues.end()) {
+ Value v;
+
+ // Check for amqp prefix and strip it if present
+ if (identifier.substr(0, 5) == "amqp.") {
+ v = specialValue(msg, identifier.substr(5));
+ } else {
+ // Just return property as string
+ //v = &msg.getPropertyAsString(identifier);
+ qpid::types::Variant var = msg.getProperty(identifier);
+ switch (var.getType()) {
+ case types::VAR_VOID:
+ v = Value(); break;
+ case types::VAR_STRING: {
+ string& s = var.getString();
+ returnedStrings.push_back(new string(s));
+ v = returnedStrings[returnedStrings.size()-1];
+ break;
+ }
+ case types::VAR_UINT64:
+ // TODO: Need to take care of values too high to be int64_t
+ case types::VAR_UINT32:
+ case types::VAR_UINT16:
+ case types::VAR_UINT8:
+ case types::VAR_INT64:
+ case types::VAR_INT32:
+ case types::VAR_INT16:
+ case types::VAR_INT8:
+ v = var.asInt64(); break;
+ case types::VAR_FLOAT:
+ case types::VAR_DOUBLE:
+ v = var.asDouble(); break;
+ case types::VAR_BOOL:
+ v = var.asBool(); break;
+ default:
+ v = Value(); break;
+ }
+ }
+ QPID_LOG(debug, "Selector identifier: " << identifier << "->" << v);
+ returnedValues[identifier] = v;
+ }
+ return returnedValues[identifier];
+}
- // Check for amqp prefix and strip it if present
- if (identifier.substr(0, 5) == "amqp.") {
- v = specialValue(msg, identifier.substr(5));
- } else {
- // Just return property as string
- v = msg.getPropertyAsString(identifier);
+Selector::Selector(const string& e) :
+ parse(TopExpression::parse(e)),
+ expression(e)
+{
+ bool debugOut;
+ QPID_LOG_TEST(debug, debugOut);
+ if (debugOut) {
+ std::stringstream ss;
+ parse->repr(ss);
+ QPID_LOG(debug, "Selector parsed[" << e << "] into: " << ss.str());
}
- QPID_LOG(debug, "Selector identifier: " << identifier << "->" << v);
- return v;
}
+Selector::~Selector()
+{
+}
+
+bool Selector::eval(const SelectorEnv& env)
+{
+ return parse->eval(env);
+}
+
+bool Selector::filter(const Message& msg)
+{
+ return eval(MessageSelectorEnv(msg));
+}
namespace {
const boost::shared_ptr<Selector> NULL_SELECTOR = boost::shared_ptr<Selector>();
Modified: qpid/trunk/qpid/cpp/src/qpid/broker/Selector.h
URL: http://svn.apache.org/viewvc/qpid/trunk/qpid/cpp/src/qpid/broker/Selector.h?rev=1452525&r1=1452524&r2=1452525&view=diff
==============================================================================
--- qpid/trunk/qpid/cpp/src/qpid/broker/Selector.h (original)
+++ qpid/trunk/qpid/cpp/src/qpid/broker/Selector.h Mon Mar 4 21:08:45 2013
@@ -31,7 +31,8 @@ namespace qpid {
namespace broker {
class Message;
-class BoolExpression;
+class Value;
+class TopExpression;
/**
* Interface to provide values to a Selector evaluation
@@ -42,22 +43,11 @@ class SelectorEnv {
public:
virtual ~SelectorEnv() {};
- virtual bool present(const std::string&) const = 0;
- virtual std::string value(const std::string&) const = 0;
-};
-
-class MessageSelectorEnv : public SelectorEnv {
- const Message& msg;
-
- bool present(const std::string&) const;
- std::string value(const std::string&) const;
-
-public:
- MessageSelectorEnv(const Message&);
+ virtual const Value& value(const std::string&) const = 0;
};
class Selector {
- boost::scoped_ptr<BoolExpression> parse;
+ boost::scoped_ptr<TopExpression> parse;
const std::string expression;
public:
Modified: qpid/trunk/qpid/cpp/src/qpid/broker/SelectorExpression.cpp
URL: http://svn.apache.org/viewvc/qpid/trunk/qpid/cpp/src/qpid/broker/SelectorExpression.cpp?rev=1452525&r1=1452524&r2=1452525&view=diff
==============================================================================
--- qpid/trunk/qpid/cpp/src/qpid/broker/SelectorExpression.cpp (original)
+++ qpid/trunk/qpid/cpp/src/qpid/broker/SelectorExpression.cpp Mon Mar 4 21:08:45 2013
@@ -23,12 +23,15 @@
#include "qpid/broker/Selector.h"
#include "qpid/broker/SelectorToken.h"
+#include "qpid/broker/SelectorValue.h"
+#include "qpid/sys/IntegerTypes.h"
#include <string>
#include <memory>
#include <ostream>
#include <boost/scoped_ptr.hpp>
+#include <boost/lexical_cast.hpp>
/*
* Syntax for JMS style selector expressions (informal):
@@ -42,17 +45,15 @@
*
* LiteralString ::= ("'" ~[']* "'")+ // Repeats to cope with embedded single quote
*
- * // Currently no numerics at all
- * //LiteralExactNumeric ::= Digit+
- * //LiteralApproxNumeric ::= ( Digit "." Digit* [ "E" LiteralExactNumeric ] ) |
- * // ( "." Digit+ [ "E" LiteralExactNumeric ] ) |
- * // ( Digit+ "E" LiteralExactNumeric )
- * //LiteralBool ::= "TRUE" | "FALSE"
- * //
+ * LiteralExactNumeric ::= Digit+
+ * Exponent ::= ['+'|'-'] LiteralExactNumeric
+ * LiteralApproxNumeric ::= ( Digit "." Digit* [ "E" Exponent ] ) |
+ * ( "." Digit+ [ "E" Exponent ] ) |
+ * ( Digit+ "E" Exponent )
+ * LiteralBool ::= "TRUE" | "FALSE"
*
* Literal ::= LiteralBool | LiteralString | LiteralApproxNumeric | LiteralExactNumeric
*
- * // Currently only simple string comparison expressions + IS NULL or IS NOT NULL
* EqOps ::= "=" | "<>"
*
* ComparisonOps ::= EqOps | ">" | ">=" | "<" | "<="
@@ -63,38 +64,54 @@
*
* AndExpression :: = EqualityExpression ( "AND" EqualityExpression )*
*
- * EqualityExpression ::= Identifier "IS" "NULL" |
- * Identifier "IS "NOT" "NULL" |
- * PrimaryExpression EqOps PrimaryExpression |
- * "NOT" EqualityExpression |
+ * ComparisonExpression ::= PrimaryExpression "IS" "NULL" |
+ * PrimaryExpression "IS" "NOT" "NULL" |
+ * PrimaryExpression ComparisonOpsOps PrimaryExpression |
+ * "NOT" ComparisonExpression |
* "(" OrExpression ")"
*
* PrimaryExpression :: = Identifier |
- * LiteralString
+ * Literal
*
*/
namespace qpid {
namespace broker {
-class Expression;
-
using std::string;
using std::ostream;
+class Expression {
+public:
+ virtual ~Expression() {}
+ virtual void repr(std::ostream&) const = 0;
+ virtual Value eval(const SelectorEnv&) const = 0;
+};
+
+class BoolExpression : public Expression {
+public:
+ virtual ~BoolExpression() {}
+ virtual void repr(std::ostream&) const = 0;
+ virtual BoolOrNone eval_bool(const SelectorEnv&) const = 0;
+
+ Value eval(const SelectorEnv& env) const {
+ return eval_bool(env);
+ }
+};
+
// Operators
-class EqualityOperator {
+class ComparisonOperator {
public:
virtual void repr(ostream&) const = 0;
- virtual bool eval(Expression&, Expression&, const SelectorEnv&) const = 0;
+ virtual BoolOrNone eval(Expression&, Expression&, const SelectorEnv&) const = 0;
};
template <typename T>
class UnaryBooleanOperator {
public:
virtual void repr(ostream&) const = 0;
- virtual bool eval(T&, const SelectorEnv&) const = 0;
+ virtual BoolOrNone eval(T&, const SelectorEnv&) const = 0;
};
////////////////////////////////////////////////////
@@ -107,13 +124,7 @@ ostream& operator<<(ostream& os, const E
return os;
}
-ostream& operator<<(ostream& os, const BoolExpression& e)
-{
- e.repr(os);
- return os;
-}
-
-ostream& operator<<(ostream& os, const EqualityOperator& e)
+ostream& operator<<(ostream& os, const ComparisonOperator& e)
{
e.repr(os);
return os;
@@ -128,13 +139,13 @@ ostream& operator<<(ostream& os, const U
// Boolean Expression types...
-class EqualityExpression : public BoolExpression {
- EqualityOperator* op;
+class ComparisonExpression : public BoolExpression {
+ ComparisonOperator* op;
boost::scoped_ptr<Expression> e1;
boost::scoped_ptr<Expression> e2;
public:
- EqualityExpression(EqualityOperator* o, Expression* e, Expression* e_):
+ ComparisonExpression(ComparisonOperator* o, Expression* e, Expression* e_):
op(o),
e1(e),
e2(e_)
@@ -144,7 +155,7 @@ public:
os << "(" << *e1 << *op << *e2 << ")";
}
- bool eval(const SelectorEnv& env) const {
+ BoolOrNone eval_bool(const SelectorEnv& env) const {
return op->eval(*e1, *e2, env);
}
};
@@ -163,9 +174,19 @@ public:
os << "(" << *e1 << " OR " << *e2 << ")";
}
- // We can use the regular C++ short-circuiting operator||
- bool eval(const SelectorEnv& env) const {
- return e1->eval(env) || e2->eval(env);
+ BoolOrNone eval_bool(const SelectorEnv& env) const {
+ BoolOrNone bn1(e1->eval_bool(env));
+ if (bn1==BN_TRUE) {
+ return BN_TRUE;
+ } else {
+ BoolOrNone bn2(e2->eval_bool(env));
+ if (bn2==BN_TRUE) {
+ return BN_TRUE;
+ } else {
+ if (bn1==BN_FALSE && bn2==BN_FALSE) return BN_FALSE;
+ else return BN_UNKNOWN;
+ }
+ }
}
};
@@ -183,9 +204,19 @@ public:
os << "(" << *e1 << " AND " << *e2 << ")";
}
- // We can use the regular C++ short-circuiting operator&&
- bool eval(const SelectorEnv& env) const {
- return e1->eval(env) && e2->eval(env);
+ BoolOrNone eval_bool(const SelectorEnv& env) const {
+ BoolOrNone bn1(e1->eval_bool(env));
+ if (bn1==BN_FALSE) {
+ return BN_FALSE;
+ } else {
+ BoolOrNone bn2(e2->eval_bool(env));
+ if (bn2==BN_FALSE) {
+ return BN_FALSE;
+ } else {
+ if (bn1==BN_TRUE && bn2==BN_TRUE) return BN_TRUE;
+ else return BN_UNKNOWN;
+ }
+ }
}
};
@@ -204,7 +235,7 @@ public:
os << *op << "(" << *e1 << ")";
}
- virtual bool eval(const SelectorEnv& env) const {
+ virtual BoolOrNone eval_bool(const SelectorEnv& env) const {
return op->eval(*e1, env);
}
};
@@ -212,10 +243,28 @@ public:
// Expression types...
class Literal : public Expression {
- string value;
+ const Value value;
+
+public:
+ template <typename T>
+ Literal(const T& v) :
+ value(v)
+ {}
+
+ void repr(ostream& os) const {
+ os << value;
+ }
+
+ Value eval(const SelectorEnv&) const {
+ return value;
+ }
+};
+
+class StringLiteral : public Expression {
+ const string value;
public:
- Literal(const string& v) :
+ StringLiteral(const string& v) :
value(v)
{}
@@ -223,7 +272,7 @@ public:
os << "'" << value << "'";
}
- string eval(const SelectorEnv&) const {
+ Value eval(const SelectorEnv&) const {
return value;
}
};
@@ -240,60 +289,113 @@ public:
os << "I:" << identifier;
}
- string eval(const SelectorEnv& env) const {
+ Value eval(const SelectorEnv& env) const {
return env.value(identifier);
}
-
- bool present(const SelectorEnv& env) const {
- return env.present(identifier);
- }
};
////////////////////////////////////////////////////
// Some operators...
+typedef bool BoolOp(const Value&, const Value&);
+
+BoolOrNone booleval(BoolOp* op, Expression& e1, Expression& e2, const SelectorEnv& env) {
+ const Value v1(e1.eval(env));
+ if (!unknown(v1)) {
+ const Value v2(e2.eval(env));
+ if (!unknown(v2)) {
+ return BoolOrNone(op(v1, v2));
+ }
+ }
+ return BN_UNKNOWN;
+}
+
// "="
-class Eq : public EqualityOperator {
+class Eq : public ComparisonOperator {
void repr(ostream& os) const {
os << "=";
}
- bool eval(Expression& e1, Expression& e2, const SelectorEnv& env) const {
- return e1.eval(env) == e2.eval(env);
+ BoolOrNone eval(Expression& e1, Expression& e2, const SelectorEnv& env) const {
+ return booleval(&operator==, e1, e2, env);
}
};
// "<>"
-class Neq : public EqualityOperator {
+class Neq : public ComparisonOperator {
void repr(ostream& os) const {
os << "<>";
}
- bool eval(Expression& e1, Expression& e2, const SelectorEnv& env) const {
- return e1.eval(env) != e2.eval(env);
+ BoolOrNone eval(Expression& e1, Expression& e2, const SelectorEnv& env) const {
+ return booleval(&operator!=, e1, e2, env);
+ }
+};
+
+// "<"
+class Ls : public ComparisonOperator {
+ void repr(ostream& os) const {
+ os << "<";
+ }
+
+ BoolOrNone eval(Expression& e1, Expression& e2, const SelectorEnv& env) const {
+ return booleval(&operator<, e1, e2, env);
+ }
+};
+
+// ">"
+class Gr : public ComparisonOperator {
+ void repr(ostream& os) const {
+ os << ">";
+ }
+
+ BoolOrNone eval(Expression& e1, Expression& e2, const SelectorEnv& env) const {
+ return booleval(&operator>, e1, e2, env);
+ }
+};
+
+// "<="
+class Lseq : public ComparisonOperator {
+ void repr(ostream& os) const {
+ os << "<=";
+ }
+
+ BoolOrNone eval(Expression& e1, Expression& e2, const SelectorEnv& env) const {
+ return booleval(&operator<=, e1, e2, env);
+ }
+};
+
+// ">="
+class Greq : public ComparisonOperator {
+ void repr(ostream& os) const {
+ os << ">=";
+ }
+
+ BoolOrNone eval(Expression& e1, Expression& e2, const SelectorEnv& env) const {
+ return booleval(&operator>=, e1, e2, env);
}
};
// "IS NULL"
-class IsNull : public UnaryBooleanOperator<Identifier> {
+class IsNull : public UnaryBooleanOperator<Expression> {
void repr(ostream& os) const {
os << "IsNull";
}
- bool eval(Identifier& i, const SelectorEnv& env) const {
- return !i.present(env);
+ BoolOrNone eval(Expression& e, const SelectorEnv& env) const {
+ return BoolOrNone(unknown(e.eval(env)));
}
};
// "IS NOT NULL"
-class IsNonNull : public UnaryBooleanOperator<Identifier> {
+class IsNonNull : public UnaryBooleanOperator<Expression> {
void repr(ostream& os) const {
os << "IsNonNull";
}
- bool eval(Identifier& i, const SelectorEnv& env) const {
- return i.present(env);
+ BoolOrNone eval(Expression& e, const SelectorEnv& env) const {
+ return BoolOrNone(!unknown(e.eval(env)));
}
};
@@ -303,21 +405,51 @@ class Not : public UnaryBooleanOperator<
os << "NOT";
}
- bool eval(BoolExpression& e, const SelectorEnv& env) const {
- return !e.eval(env);
+ BoolOrNone eval(BoolExpression& e, const SelectorEnv& env) const {
+ BoolOrNone bn = e.eval_bool(env);
+ if (bn==BN_UNKNOWN) return bn;
+ else return BoolOrNone(!bn);
}
};
Eq eqOp;
Neq neqOp;
+Ls lsOp;
+Gr grOp;
+Lseq lseqOp;
+Greq greqOp;
IsNull isNullOp;
IsNonNull isNonNullOp;
Not notOp;
////////////////////////////////////////////////////
+BoolExpression* parseOrExpression(Tokeniser&);
+BoolExpression* parseAndExpression(Tokeniser&);
+BoolExpression* parseComparisonExpression(Tokeniser&);
+Expression* parsePrimaryExpression(Tokeniser&);
+
// Top level parser
-BoolExpression* parseTopBoolExpression(const string& exp)
+class TopBoolExpression : public TopExpression {
+ boost::scoped_ptr<BoolExpression> expression;
+
+ void repr(ostream& os) const {
+ expression->repr(os);
+ }
+
+ bool eval(const SelectorEnv& env) const {
+ BoolOrNone bn = expression->eval_bool(env);
+ if (bn==BN_TRUE) return true;
+ else return false;
+ }
+
+public:
+ TopBoolExpression(BoolExpression* be) :
+ expression(be)
+ {}
+};
+
+TopExpression* TopExpression::parse(const string& exp)
{
string::const_iterator s = exp.begin();
string::const_iterator e = exp.end();
@@ -325,7 +457,7 @@ BoolExpression* parseTopBoolExpression(c
std::auto_ptr<BoolExpression> 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 b.release();
+ return new TopBoolExpression(b.release());
}
BoolExpression* parseOrExpression(Tokeniser& tokeniser)
@@ -344,11 +476,11 @@ BoolExpression* parseOrExpression(Tokeni
BoolExpression* parseAndExpression(Tokeniser& tokeniser)
{
- std::auto_ptr<BoolExpression> e(parseEqualityExpression(tokeniser));
+ std::auto_ptr<BoolExpression> 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(parseEqualityExpression(tokeniser));
+ std::auto_ptr<BoolExpression> e2(parseComparisonExpression(tokeniser));
if (!e2.get()) return 0;
e.reset(new AndExpression(e1.release(), e2.release()));
}
@@ -356,31 +488,16 @@ BoolExpression* parseAndExpression(Token
return e.release();
}
-BoolExpression* parseEqualityExpression(Tokeniser& tokeniser)
+BoolExpression* parseComparisonExpression(Tokeniser& tokeniser)
{
const Token t = tokeniser.nextToken();
- if ( t.type==T_IDENTIFIER ) {
- // Check for "IS NULL" and "IS NOT NULL"
- if ( tokeniser.nextToken().type==T_IS ) {
- // The rest must be T_NULL or T_NOT, T_NULL
- switch (tokeniser.nextToken().type) {
- case T_NULL:
- return new UnaryBooleanExpression<Identifier>(&isNullOp, new Identifier(t.val));
- case T_NOT:
- if ( tokeniser.nextToken().type == T_NULL)
- return new UnaryBooleanExpression<Identifier>(&isNonNullOp, new Identifier(t.val));
- default:
- return 0;
- }
- }
- tokeniser.returnTokens();
- } else if ( t.type==T_LPAREN ) {
+ 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(parseEqualityExpression(tokeniser));
+ std::auto_ptr<BoolExpression> e(parseComparisonExpression(tokeniser));
if (!e.get()) return 0;
return new UnaryBooleanExpression<BoolExpression>(¬Op, e.release());
}
@@ -389,6 +506,21 @@ BoolExpression* parseEqualityExpression(
std::auto_ptr<Expression> e1(parsePrimaryExpression(tokeniser));
if (!e1.get()) return 0;
+ // Check for "IS NULL" and "IS NOT NULL"
+ if ( tokeniser.nextToken().type==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;
+ }
+ }
+ tokeniser.returnTokens();
+
const Token op = tokeniser.nextToken();
if (op.type != T_OPERATOR) {
return 0;
@@ -397,8 +529,12 @@ BoolExpression* parseEqualityExpression(
std::auto_ptr<Expression> e2(parsePrimaryExpression(tokeniser));
if (!e2.get()) return 0;
- if (op.val == "=") return new EqualityExpression(&eqOp, e1.release(), e2.release());
- if (op.val == "<>") return new EqualityExpression(&neqOp, e1.release(), e2.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());
return 0;
}
@@ -410,7 +546,15 @@ Expression* parsePrimaryExpression(Token
case T_IDENTIFIER:
return new Identifier(t.val);
case T_STRING:
- return new Literal(t.val);
+ return new StringLiteral(t.val);
+ case T_FALSE:
+ return new Literal(false);
+ case T_TRUE:
+ return new Literal(true);
+ case T_NUMERIC_EXACT:
+ return new Literal(boost::lexical_cast<int64_t>(t.val));
+ case T_NUMERIC_APPROX:
+ return new Literal(boost::lexical_cast<double>(t.val));
default:
return 0;
}
Modified: qpid/trunk/qpid/cpp/src/qpid/broker/SelectorExpression.h
URL: http://svn.apache.org/viewvc/qpid/trunk/qpid/cpp/src/qpid/broker/SelectorExpression.h?rev=1452525&r1=1452524&r2=1452525&view=diff
==============================================================================
--- qpid/trunk/qpid/cpp/src/qpid/broker/SelectorExpression.h (original)
+++ qpid/trunk/qpid/cpp/src/qpid/broker/SelectorExpression.h Mon Mar 4 21:08:45 2013
@@ -29,29 +29,15 @@ namespace qpid {
namespace broker {
class SelectorEnv;
-class Tokeniser;
-class Expression {
+class TopExpression {
public:
- virtual ~Expression() {}
- virtual void repr(std::ostream&) const = 0;
- virtual std::string eval(const SelectorEnv&) const = 0;
-};
-
-class BoolExpression {
-public:
- virtual ~BoolExpression() {};
+ virtual ~TopExpression() {};
virtual void repr(std::ostream&) const = 0;
virtual bool eval(const SelectorEnv&) const = 0;
-};
-BoolExpression* parseTopBoolExpression(const std::string& exp);
-BoolExpression* parseBoolExpression(Tokeniser&);
-BoolExpression* parseOrExpression(Tokeniser&);
-BoolExpression* parseAndExpression(Tokeniser&);
-BoolExpression* parseNotExpression(Tokeniser&);
-BoolExpression* parseEqualityExpression(Tokeniser&);
-Expression* parsePrimaryExpression(Tokeniser&);
+ static TopExpression* parse(const std::string& exp);
+};
}}
Modified: qpid/trunk/qpid/cpp/src/qpid/broker/SelectorToken.cpp
URL: http://svn.apache.org/viewvc/qpid/trunk/qpid/cpp/src/qpid/broker/SelectorToken.cpp?rev=1452525&r1=1452524&r2=1452525&view=diff
==============================================================================
--- qpid/trunk/qpid/cpp/src/qpid/broker/SelectorToken.cpp (original)
+++ qpid/trunk/qpid/cpp/src/qpid/broker/SelectorToken.cpp Mon Mar 4 21:08:45 2013
@@ -221,10 +221,77 @@ bool tokeniseOperator(std::string::const
return true;
}
-// Can't parse numerics yet
-bool tokeniseNumeric(std::string::const_iterator& /*s*/, std::string::const_iterator& /*e*/, Token& /*tok*/)
+bool tokeniseNumeric(std::string::const_iterator& s, std::string::const_iterator& e, Token& tok)
{
- return false;
+ std::string::const_iterator t = s;
+
+ // Hand constructed state machine recogniser
+ enum {
+ START,
+ REJECT,
+ DIGIT,
+ DECIMAL_START,
+ DECIMAL,
+ EXPONENT_SIGN,
+ EXPONENT_START,
+ EXPONENT,
+ ACCEPT_EXACT,
+ ACCEPT_INEXACT
+ } state = START;
+
+ while (true)
+ switch (state) {
+ case START:
+ if (t==e) {state = REJECT;}
+ else if (std::isdigit(*t)) {++t; state = DIGIT;}
+ else if (*t=='.') {++t; state = DECIMAL_START;}
+ else state = REJECT;
+ break;
+ case DECIMAL_START:
+ if (t==e) {state = REJECT;}
+ else if (std::isdigit(*t)) {++t; state = DECIMAL;}
+ else state = REJECT;
+ break;
+ 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;
+ case EXPONENT_START:
+ if (t==e) {state = REJECT;}
+ else if (std::isdigit(*t)) {++t; state = EXPONENT;}
+ else state = REJECT;
+ break;
+ case DIGIT:
+ if (t==e) {state = ACCEPT_EXACT;}
+ 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;
+ case DECIMAL:
+ if (t==e) {state = ACCEPT_INEXACT;}
+ else if (std::isdigit(*t)) {++t; state = DECIMAL;}
+ else if (*t=='e' || *t=='E') {++t; state = EXPONENT_SIGN;}
+ else state = ACCEPT_INEXACT;
+ break;
+ case EXPONENT:
+ if (t==e) {state = ACCEPT_INEXACT;}
+ else if (std::isdigit(*t)) {++t; state = EXPONENT;}
+ else state = ACCEPT_INEXACT;
+ break;
+ case ACCEPT_EXACT:
+ tok = Token(T_NUMERIC_EXACT, s, t);
+ s = t;
+ return true;
+ case ACCEPT_INEXACT:
+ tok = Token(T_NUMERIC_APPROX, s, t);
+ s = t;
+ return true;
+ case REJECT:
+ return false;
+ };
}
Tokeniser::Tokeniser(const std::string::const_iterator& s, const std::string::const_iterator& e) :
Modified: qpid/trunk/qpid/cpp/src/qpid/broker/SelectorToken.h
URL: http://svn.apache.org/viewvc/qpid/trunk/qpid/cpp/src/qpid/broker/SelectorToken.h?rev=1452525&r1=1452524&r2=1452525&view=diff
==============================================================================
--- qpid/trunk/qpid/cpp/src/qpid/broker/SelectorToken.h (original)
+++ qpid/trunk/qpid/cpp/src/qpid/broker/SelectorToken.h Mon Mar 4 21:08:45 2013
@@ -83,19 +83,12 @@ public:
};
bool tokeniseEos(std::string::const_iterator& s, std::string::const_iterator& e, Token& tok);
-
bool tokeniseIdentifier(std::string::const_iterator& s, std::string::const_iterator& e, Token& tok);
-
bool tokeniseReservedWord(std::string::const_iterator& s, std::string::const_iterator& e, Token& tok);
-
bool tokeniseIdentifierOrReservedWord(std::string::const_iterator& s, std::string::const_iterator& e, Token& tok);
-
bool tokeniseString(std::string::const_iterator& s, std::string::const_iterator& e, Token& tok);
-
bool tokeniseParens(std::string::const_iterator& s, std::string::const_iterator& e, Token& tok);
-
bool tokeniseOperator(std::string::const_iterator& s, std::string::const_iterator& e, Token& tok);
-
bool tokeniseNumeric(std::string::const_iterator& s, std::string::const_iterator& e, Token& tok);
class Tokeniser {
Added: qpid/trunk/qpid/cpp/src/qpid/broker/SelectorValue.cpp
URL: http://svn.apache.org/viewvc/qpid/trunk/qpid/cpp/src/qpid/broker/SelectorValue.cpp?rev=1452525&view=auto
==============================================================================
--- qpid/trunk/qpid/cpp/src/qpid/broker/SelectorValue.cpp (added)
+++ qpid/trunk/qpid/cpp/src/qpid/broker/SelectorValue.cpp Mon Mar 4 21:08:45 2013
@@ -0,0 +1,196 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#include "qpid/broker/SelectorValue.h"
+
+#include <ostream>
+#include <boost/scoped_ptr.hpp>
+
+using std::ostream;
+
+namespace qpid {
+namespace broker {
+
+ostream& operator<<(ostream& os, const Value& v)
+{
+ switch (v.type) {
+ case Value::T_UNKNOWN: os << "UNKNOWN"; break;
+ case Value::T_BOOL: os << "BOOL:" << std::boolalpha << v.b; break;
+ case Value::T_EXACT: os << "EXACT:" << v.i; break;
+ case Value::T_INEXACT: os << "APPROX:" << v.x; break;
+ case Value::T_STRING: os << "STRING:'" << *v.s << "'"; break;
+ };
+ return os;
+}
+
+class NumericPairBase {
+public:
+ virtual Value add() = 0;
+ virtual Value sub() = 0;
+ virtual Value mul() = 0;
+ virtual Value div() = 0;
+
+ virtual bool eq() = 0;
+ virtual bool ne() = 0;
+ virtual bool ls() = 0;
+ virtual bool gr() = 0;
+ virtual bool le() = 0;
+ virtual bool ge() = 0;
+};
+
+template <typename T>
+class NumericPair : public NumericPairBase {
+ const T n1;
+ const T n2;
+
+ Value add() { return n1+n2; }
+ Value sub() { return n1-n2; }
+ Value mul() { return n1*n2; }
+ Value div() { return n1/n2; }
+
+ bool eq() { return n1==n2; }
+ bool ne() { return n1!=n2; }
+ bool ls() { return n1<n2; }
+ bool gr() { return n1>n2; }
+ bool le() { return n1<=n2; }
+ bool ge() { return n1>=n2; }
+
+public:
+ NumericPair(T x, T y) :
+ n1(x),
+ n2(y)
+ {}
+};
+
+NumericPairBase* promoteNumeric(const Value& v1, const Value& v2)
+{
+ if (!numeric(v1) || !numeric(v2)) return 0;
+
+ if (v1.type != v2.type) {
+ switch (v1.type) {
+ case Value::T_INEXACT: return new NumericPair<double>(v1.x, v2.i);
+ case Value::T_EXACT: return new NumericPair<double>(v1.i, v2.x);
+ default:
+ assert(false);
+ }
+ } else {
+ switch (v1.type) {
+ case Value::T_INEXACT: return new NumericPair<double>(v1.x, v2.x);
+ case Value::T_EXACT: return new NumericPair<int64_t>(v1.i, v2.i);
+ default:
+ assert(false);
+ }
+ }
+}
+
+bool operator==(const Value& v1, const Value& v2)
+{
+ boost::scoped_ptr<NumericPairBase> nbp(promoteNumeric(v1, v2));
+ if (nbp) return nbp->eq();
+
+ if (v1.type != v2.type) return false;
+ switch (v1.type) {
+ case Value::T_BOOL: return v1.b == v2.b;
+ case Value::T_STRING: return *v1.s == *v2.s;
+ default: // Cannot ever get here
+ return false;
+ }
+}
+
+bool operator!=(const Value& v1, const Value& v2)
+{
+ boost::scoped_ptr<NumericPairBase> nbp(promoteNumeric(v1, v2));
+ if (nbp) return nbp->ne();
+
+ if (v1.type != v2.type) return false;
+ switch (v1.type) {
+ case Value::T_BOOL: return v1.b != v2.b;
+ case Value::T_STRING: return *v1.s != *v2.s;
+ default: // Cannot ever get here
+ return false;
+ }
+}
+
+bool operator<(const Value& v1, const Value& v2)
+{
+ boost::scoped_ptr<NumericPairBase> nbp(promoteNumeric(v1, v2));
+ if (nbp) return nbp->ls();
+
+ return false;
+}
+
+bool operator>(const Value& v1, const Value& v2)
+{
+ boost::scoped_ptr<NumericPairBase> nbp(promoteNumeric(v1, v2));
+ if (nbp) return nbp->gr();
+
+ return false;
+}
+
+bool operator<=(const Value& v1, const Value& v2)
+{
+ boost::scoped_ptr<NumericPairBase> nbp(promoteNumeric(v1, v2));
+ if (nbp) return nbp->le();
+
+ return false;
+}
+
+bool operator>=(const Value& v1, const Value& v2)
+{
+ boost::scoped_ptr<NumericPairBase> nbp(promoteNumeric(v1, v2));
+ if (nbp) return nbp->ge();
+
+ return false;
+}
+
+Value operator+(const Value& v1, const Value& v2)
+{
+ boost::scoped_ptr<NumericPairBase> nbp(promoteNumeric(v1, v2));
+ if (nbp) return nbp->add();
+
+ return Value();
+}
+
+Value operator-(const Value& v1, const Value& v2)
+{
+ boost::scoped_ptr<NumericPairBase> nbp(promoteNumeric(v1, v2));
+ if (nbp) return nbp->sub();
+
+ return Value();
+}
+
+Value operator*(const Value& v1, const Value& v2)
+{
+ boost::scoped_ptr<NumericPairBase> nbp(promoteNumeric(v1, v2));
+ if (nbp) return nbp->mul();
+
+ return Value();
+}
+
+Value operator/(const Value& v1, const Value& v2)
+{
+ boost::scoped_ptr<NumericPairBase> nbp(promoteNumeric(v1, v2));
+ if (nbp) return nbp->div();
+
+ return Value();
+}
+
+}}
\ No newline at end of file
Added: qpid/trunk/qpid/cpp/src/qpid/broker/SelectorValue.h
URL: http://svn.apache.org/viewvc/qpid/trunk/qpid/cpp/src/qpid/broker/SelectorValue.h?rev=1452525&view=auto
==============================================================================
--- qpid/trunk/qpid/cpp/src/qpid/broker/SelectorValue.h (added)
+++ qpid/trunk/qpid/cpp/src/qpid/broker/SelectorValue.h Mon Mar 4 21:08:45 2013
@@ -0,0 +1,115 @@
+#ifndef QPID_BROKER_SELECTORVALUE_H
+#define QPID_BROKER_SELECTORVALUE_H
+
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#include "qpid/sys/IntegerTypes.h"
+
+#include <iosfwd>
+#include <string>
+
+namespace qpid {
+namespace broker {
+
+enum BoolOrNone {
+ BN_FALSE = false,
+ BN_TRUE = true,
+ BN_UNKNOWN
+};
+
+// The user of the Value class for strings must ensure that
+// the string has a lifetime longer than the string used and
+// is responsible for managing its lifetime.
+class Value {
+public:
+ union {
+ bool b;
+ int64_t i;
+ double x;
+ const std::string* s;
+ };
+ enum {
+ T_UNKNOWN,
+ T_BOOL,
+ T_STRING,
+ T_EXACT,
+ T_INEXACT
+ } type;
+
+ // Default copy contructor
+ // Default assignment operator
+ // Default destructor
+ Value() :
+ type(T_UNKNOWN)
+ {}
+
+ Value(const std::string& s0) :
+ s(&s0),
+ type(T_STRING)
+ {}
+
+ Value(const int64_t i0) :
+ i(i0),
+ type(T_EXACT)
+ {}
+
+ Value(const double x0) :
+ x(x0),
+ type(T_INEXACT)
+ {}
+
+ Value(bool b0) :
+ b(b0),
+ type(T_BOOL)
+ {}
+
+ Value(BoolOrNone bn) :
+ b(bn),
+ type(bn==BN_UNKNOWN ? T_UNKNOWN : T_BOOL)
+ {}
+};
+
+inline bool unknown(const Value& v) {
+ return v.type == Value::T_UNKNOWN;
+}
+
+inline bool numeric(const Value& v) {
+ return v.type == Value::T_EXACT || v.type == Value::T_INEXACT;
+}
+
+std::ostream& operator<<(std::ostream& os, const Value& v);
+
+bool operator==(const Value&, const Value&);
+bool operator!=(const Value&, const Value&);
+bool operator<(const Value&, const Value&);
+bool operator>(const Value&, const Value&);
+bool operator<=(const Value&, const Value&);
+bool 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&, const Value&);
+
+}}
+
+#endif
Modified: qpid/trunk/qpid/cpp/src/tests/Selector.cpp
URL: http://svn.apache.org/viewvc/qpid/trunk/qpid/cpp/src/tests/Selector.cpp?rev=1452525&r1=1452524&r2=1452525&view=diff
==============================================================================
--- qpid/trunk/qpid/cpp/src/tests/Selector.cpp (original)
+++ qpid/trunk/qpid/cpp/src/tests/Selector.cpp Mon Mar 4 21:08:45 2013
@@ -21,14 +21,17 @@
#include "qpid/broker/SelectorToken.h"
#include "qpid/broker/Selector.h"
+#include "qpid/broker/SelectorValue.h"
#include "unit_test.h"
#include <string>
#include <map>
+#include <boost/ptr_container/ptr_vector.hpp>
using std::string;
using std::map;
+using std::vector;
namespace qb = qpid::broker;
@@ -86,6 +89,15 @@ QPID_AUTO_TEST_CASE(tokeniseSuccess)
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");
}
QPID_AUTO_TEST_CASE(tokeniseFailure)
@@ -105,6 +117,13 @@ QPID_AUTO_TEST_CASE(tokeniseFailure)
verifyTokeniserFail(&tokeniseOperator, ")");
verifyTokeniserFail(&tokeniseParens, "=");
verifyTokeniserFail(&tokeniseParens, "what ho!");
+ verifyTokeniserFail(&tokeniseNumeric, "kill");
+ verifyTokeniserFail(&tokeniseNumeric, "e3");
+ verifyTokeniserFail(&tokeniseNumeric, "1.e.5");
+ verifyTokeniserFail(&tokeniseNumeric, ".e5");
+ verifyTokeniserFail(&tokeniseNumeric, "34e");
+ verifyTokeniserFail(&tokeniseNumeric, ".3e+");
+ verifyTokeniserFail(&tokeniseNumeric, ".3e-.");
}
QPID_AUTO_TEST_CASE(tokenString)
@@ -138,11 +157,25 @@ QPID_AUTO_TEST_CASE(tokenString)
BOOST_CHECK_EQUAL(u.nextToken(), Token(qb::T_NULL, "null"));
BOOST_CHECK_EQUAL(u.nextToken(), Token(qb::T_EOS, ""));
BOOST_CHECK_EQUAL(u.nextToken(), Token(qb::T_EOS, ""));
+
+ exp = "(a+6)*7.5/1e6";
+ s = exp.begin();
+ e = exp.end();
+ Tokeniser v(s, e);
+
+ 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_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_NUMERIC_APPROX, "7.5"));
+ BOOST_CHECK_EQUAL(v.nextToken(), Token(qb::T_OPERATOR, "/"));
+ BOOST_CHECK_EQUAL(v.nextToken(), Token(qb::T_NUMERIC_APPROX, "1e6"));
}
QPID_AUTO_TEST_CASE(parseStringFail)
{
- BOOST_CHECK_THROW(qb::Selector e("'Daft' is not null"), std::range_error);
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);
@@ -152,67 +185,132 @@ QPID_AUTO_TEST_CASE(parseStringFail)
}
class TestSelectorEnv : public qpid::broker::SelectorEnv {
- map<string, string> values;
+ map<string, qb::Value> values;
+ boost::ptr_vector<string> strings;
+ static const qb::Value EMPTY;
- bool present(const std::string& v) const {
+ bool present(const string& v) const {
return values.find(v)!=values.end();
}
- std::string value(const std::string& v) const {
- return present(v) ? values.at(v) : "";
+ const qb::Value& value(const string& v) const {
+ const qb::Value& r = present(v) ? values.at(v) : EMPTY;
+ return r;
}
public:
- void set(const string& id, const string& value) {
- values[id] = value;
+ 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)
{
- qb::Selector a("A is not null");
- qb::Selector a1("A is null");
- qb::Selector a2("A = C");
- qb::Selector a3("A <> C");
- qb::Selector c("C is not null");
- qb::Selector c1("C is null");
- qb::Selector d("A='hello kitty'");
- qb::Selector e("A<>'hello kitty'");
- qb::Selector f("A=B");
- qb::Selector g("A<>B");
- qb::Selector h("A='hello kitty' OR B='Bye, bye cruel world'");
- qb::Selector i("B='hello kitty' OR A='Bye, bye cruel world'");
- qb::Selector j("B='hello kitty' AnD A='Bye, bye cruel world'");
- qb::Selector k("B='hello kitty' AnD B='Bye, bye cruel world'");
- qb::Selector a4("A is null or A='Bye, bye cruel world'");
- qb::Selector a5("Z is null OR A is not null and A<>'Bye, bye cruel world'");
- qb::Selector a6("(Z is null OR A is not null) and A<>'Bye, bye cruel world'");
- qb::Selector t("NOT C is not null OR C is null");
- qb::Selector n("Not A='' or B=z");
+ BOOST_CHECK_NO_THROW(qb::Selector e("'Daft' is not null"));
+ BOOST_CHECK_NO_THROW(qb::Selector e("42 is null"));
+ BOOST_CHECK_NO_THROW(qb::Selector e("A is not null"));
+ BOOST_CHECK_NO_THROW(qb::Selector e("A is null"));
+ BOOST_CHECK_NO_THROW(qb::Selector e("A = C"));
+ BOOST_CHECK_NO_THROW(qb::Selector e("A <> C"));
+ BOOST_CHECK_NO_THROW(qb::Selector e("A='hello kitty'"));
+ BOOST_CHECK_NO_THROW(qb::Selector e("A<>'hello kitty'"));
+ BOOST_CHECK_NO_THROW(qb::Selector e("A=B"));
+ BOOST_CHECK_NO_THROW(qb::Selector e("A<>B"));
+ BOOST_CHECK_NO_THROW(qb::Selector e("A='hello kitty' OR B='Bye, bye cruel world'"));
+ BOOST_CHECK_NO_THROW(qb::Selector e("B='hello kitty' AnD A='Bye, bye cruel world'"));
+ BOOST_CHECK_NO_THROW(qb::Selector e("A is null or A='Bye, bye cruel world'"));
+ BOOST_CHECK_NO_THROW(qb::Selector e("Z is null OR A is not null and A<>'Bye, bye cruel world'"));
+ BOOST_CHECK_NO_THROW(qb::Selector e("(Z is null OR A is not null) and A<>'Bye, bye cruel world'"));
+ BOOST_CHECK_NO_THROW(qb::Selector e("NOT C is not null OR C is null"));
+ BOOST_CHECK_NO_THROW(qb::Selector e("Not A='' or B=z"));
+ BOOST_CHECK_NO_THROW(qb::Selector e("Not A=17 or B=5.6"));
+ BOOST_CHECK_NO_THROW(qb::Selector e("A<>17 and B=5.6e17"));
+}
+QPID_AUTO_TEST_CASE(simpleEval)
+{
TestSelectorEnv env;
env.set("A", "Bye, bye cruel world");
env.set("B", "hello kitty");
- BOOST_CHECK(a.eval(env));
- BOOST_CHECK(!a1.eval(env));
- BOOST_CHECK(!a2.eval(env));
- BOOST_CHECK(a3.eval(env));
- BOOST_CHECK(!c.eval(env));
- BOOST_CHECK(c1.eval(env));
- BOOST_CHECK(!d.eval(env));
- BOOST_CHECK(e.eval(env));
- BOOST_CHECK(!f.eval(env));
- BOOST_CHECK(g.eval(env));
- BOOST_CHECK(!h.eval(env));
- BOOST_CHECK(i.eval(env));
- BOOST_CHECK(j.eval(env));
- BOOST_CHECK(!k.eval(env));
- BOOST_CHECK(a4.eval(env));
- BOOST_CHECK(a5.eval(env));
- BOOST_CHECK(!a6.eval(env));
- BOOST_CHECK(t.eval(env));
- BOOST_CHECK(n.eval(env));
+ BOOST_CHECK(qb::Selector("A is not null").eval(env));
+ BOOST_CHECK(!qb::Selector("A is null").eval(env));
+ BOOST_CHECK(!qb::Selector("A = C").eval(env));
+ BOOST_CHECK(!qb::Selector("A <> C").eval(env));
+ BOOST_CHECK(!qb::Selector("C is not null").eval(env));
+ BOOST_CHECK(qb::Selector("C is null").eval(env));
+ BOOST_CHECK(qb::Selector("A='Bye, bye cruel world'").eval(env));
+ BOOST_CHECK(!qb::Selector("A<>'Bye, bye cruel world'").eval(env));
+ BOOST_CHECK(!qb::Selector("A='hello kitty'").eval(env));
+ BOOST_CHECK(qb::Selector("A<>'hello kitty'").eval(env));
+ BOOST_CHECK(!qb::Selector("A=B").eval(env));
+ BOOST_CHECK(qb::Selector("A<>B").eval(env));
+ BOOST_CHECK(!qb::Selector("A='hello kitty' OR B='Bye, bye cruel world'").eval(env));
+ BOOST_CHECK(qb::Selector("B='hello kitty' OR A='Bye, bye cruel world'").eval(env));
+ BOOST_CHECK(qb::Selector("B='hello kitty' AnD A='Bye, bye cruel world'").eval(env));
+ BOOST_CHECK(!qb::Selector("B='hello kitty' AnD B='Bye, bye cruel world'").eval(env));
+ BOOST_CHECK(qb::Selector("A is null or A='Bye, bye cruel world'").eval(env));
+ BOOST_CHECK(qb::Selector("Z is null OR A is not null and A<>'Bye, bye cruel world'").eval(env));
+ BOOST_CHECK(!qb::Selector("(Z is null OR A is not null) and A<>'Bye, bye cruel world'").eval(env));
+ BOOST_CHECK(qb::Selector("NOT C is not null OR C is null").eval(env));
+ BOOST_CHECK(qb::Selector("Not A='' or B=z").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("C=D").eval(env));
+ BOOST_CHECK(qb::Selector("13 is not null").eval(env));
+ BOOST_CHECK(!qb::Selector("'boo!' is null").eval(env));
+}
+
+QPID_AUTO_TEST_CASE(numericEval)
+{
+ TestSelectorEnv env;
+ env.set("A", 42.0);
+ env.set("B", 39l);
+
+ BOOST_CHECK(qb::Selector("A>B").eval(env));
+ BOOST_CHECK(qb::Selector("A=42").eval(env));
+ 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));
+}
+
+QPID_AUTO_TEST_CASE(comparisonEval)
+{
+ TestSelectorEnv env;
+
+ BOOST_CHECK(!qb::Selector("17 > 19.0").eval(env));
+ BOOST_CHECK(!qb::Selector("'hello' > 19.0").eval(env));
+ BOOST_CHECK(!qb::Selector("'hello' < 19.0").eval(env));
+ BOOST_CHECK(!qb::Selector("'hello' = 19.0").eval(env));
+ BOOST_CHECK(!qb::Selector("'hello'>42 and 'hello'<42 and 'hello'=42 and 'hello'<>42").eval(env));
+ BOOST_CHECK(qb::Selector("20 >= 19.0 and 20 > 19").eval(env));
+ BOOST_CHECK(qb::Selector("42 <= 42.0 and 37.0 >= 37").eval(env));
+}
+
+QPID_AUTO_TEST_CASE(NullEval)
+{
+ TestSelectorEnv env;
+
+ BOOST_CHECK(qb::Selector("P > 19.0 or (P is null)").eval(env));
+ BOOST_CHECK(qb::Selector("P is null or P=''").eval(env));
+ BOOST_CHECK(!qb::Selector("P=Q").eval(env));
+ BOOST_CHECK(!qb::Selector("not P=Q").eval(env));
+ BOOST_CHECK(!qb::Selector("not P=Q and not P=Q").eval(env));
+ BOOST_CHECK(!qb::Selector("P=Q or not P=Q").eval(env));
+ BOOST_CHECK(!qb::Selector("P > 19.0 or P <= 19.0").eval(env));
+ BOOST_CHECK(qb::Selector("P > 19.0 or 17 <= 19.0").eval(env));
}
QPID_AUTO_TEST_SUITE_END()
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@qpid.apache.org
For additional commands, e-mail: commits-help@qpid.apache.org