extern const isc::log::MessageID EVAL_DEBUG_PKT = "EVAL_DEBUG_PKT";
extern const isc::log::MessageID EVAL_DEBUG_PKT4 = "EVAL_DEBUG_PKT4";
extern const isc::log::MessageID EVAL_DEBUG_PKT6 = "EVAL_DEBUG_PKT6";
+extern const isc::log::MessageID EVAL_DEBUG_POP_AND_BRANCH_FALSE = "EVAL_DEBUG_POP_AND_BRANCH_FALSE";
+extern const isc::log::MessageID EVAL_DEBUG_POP_OR_BRANCH_FALSE = "EVAL_DEBUG_POP_OR_BRANCH_FALSE";
+extern const isc::log::MessageID EVAL_DEBUG_POP_OR_BRANCH_TRUE = "EVAL_DEBUG_POP_OR_BRANCH_TRUE";
extern const isc::log::MessageID EVAL_DEBUG_RELAY6 = "EVAL_DEBUG_RELAY6";
extern const isc::log::MessageID EVAL_DEBUG_RELAY6_RANGE = "EVAL_DEBUG_RELAY6_RANGE";
extern const isc::log::MessageID EVAL_DEBUG_SPLIT = "EVAL_DEBUG_SPLIT";
"EVAL_DEBUG_PKT", "%1: Pushing PKT meta data %2 with value %3",
"EVAL_DEBUG_PKT4", "%1: Pushing PKT4 field %2 with value %3",
"EVAL_DEBUG_PKT6", "%1: Pushing PKT6 field %2 with value %3",
+ "EVAL_DEBUG_POP_AND_BRANCH_FALSE", "Value is false: branching to %1",
+ "EVAL_DEBUG_POP_OR_BRANCH_FALSE", "Value is false: keeping it and branching to %1",
+ "EVAL_DEBUG_POP_OR_BRANCH_TRUE", "Value is true: keeping it and branching to %1",
"EVAL_DEBUG_RELAY6", "%1: Pushing PKT6 relay field %2 nest %3 with value %4",
"EVAL_DEBUG_RELAY6_RANGE", "%1: Pushing PKT6 relay field %2 nest %3 with value %4",
"EVAL_DEBUG_SPLIT", "%1: Popping field %2, delimiters %3, string %4, pushing result %5",
extern const isc::log::MessageID EVAL_DEBUG_PKT;
extern const isc::log::MessageID EVAL_DEBUG_PKT4;
extern const isc::log::MessageID EVAL_DEBUG_PKT6;
+extern const isc::log::MessageID EVAL_DEBUG_POP_AND_BRANCH_FALSE;
+extern const isc::log::MessageID EVAL_DEBUG_POP_OR_BRANCH_FALSE;
+extern const isc::log::MessageID EVAL_DEBUG_POP_OR_BRANCH_TRUE;
extern const isc::log::MessageID EVAL_DEBUG_RELAY6;
extern const isc::log::MessageID EVAL_DEBUG_RELAY6_RANGE;
extern const isc::log::MessageID EVAL_DEBUG_SPLIT;
the value stack, negated and then pushed onto the value stack.
The string is displayed in text.
+# For use with TokenPopAndBranchFalse
+% EVAL_DEBUG_POP_AND_BRANCH_FALSE Value is false: branching to %1
+Logged at debug log level 55.
+This debug message indicates that a branch on false condition is performed
+to the displayed target.
+
+# For use with TokenPopOrBranchFalse
+% EVAL_DEBUG_POP_OR_BRANCH_FALSE Value is false: keeping it and branching to %1
+Logged at debug log level 55.
+This debug message indicates that a branch on false condition is performed
+to the displayed target.
+
+# For use with TokenPopOrBranchTrue
+% EVAL_DEBUG_POP_OR_BRANCH_TRUE Value is true: keeping it and branching to %1
+Logged at debug log level 55.
+This debug message indicates that a branch on true condition is performed
+to the displayed target.
+
# For use with TokenOption based classes. These include TokenOption,
# TokenRelay4Option and TokenRelay6Option.
EXPECT_EQ(2, values.size());
}
-};
+// Tests the pop or branch when true / left or.
+TEST_F(ExpressionsTest, popOrBranchTrue) {
+ // The left or can be implemented as <left><P|BT-L><right><L>.
+ // Do the complete table.
+ TokenPtr branch;
+ ASSERT_NO_THROW(branch.reset(new TokenPopOrBranchTrue(123)));
+ TokenPtr label;
+ ASSERT_NO_THROW(label.reset(new TokenLabel(123)));
+ TokenPtr left;
+ TokenPtr right;
+ bool result_(false);
+
+ // False or false == false.
+ ASSERT_NO_THROW(left.reset(new TokenString("false")));
+ ASSERT_NO_THROW(right.reset(new TokenString("false")));
+ e_.push_back(left);
+ e_.push_back(branch);
+ e_.push_back(right);
+ e_.push_back(label);
+ ASSERT_NO_THROW(result_ = evaluateBool(e_, *pkt4_));
+ EXPECT_FALSE(result_);
+ e_.clear();
+
+ // False or true == true.
+ ASSERT_NO_THROW(left.reset(new TokenString("false")));
+ ASSERT_NO_THROW(right.reset(new TokenString("true")));
+ e_.push_back(left);
+ e_.push_back(branch);
+ e_.push_back(right);
+ e_.push_back(label);
+ ASSERT_NO_THROW(result_ = evaluateBool(e_, *pkt4_));
+ EXPECT_TRUE(result_);
+ e_.clear();
+
+ // True or any thing == true.
+ ASSERT_NO_THROW(left.reset(new TokenString("true")));
+ ASSERT_NO_THROW(right.reset(new TokenString("any thing")));
+ e_.push_back(left);
+ e_.push_back(branch);
+ e_.push_back(right);
+ e_.push_back(label);
+ EXPECT_NO_THROW(result_ = evaluateBool(e_, *pkt6_));
+ EXPECT_TRUE(result_);
+}
+
+// Tests the pop or branch when false / left and.
+TEST_F(ExpressionsTest, popOrBranchFalse) {
+ // The left and can be implemented as <left><P|BF-L><right><L>.
+ // Do the complete table.
+ TokenPtr branch;
+ ASSERT_NO_THROW(branch.reset(new TokenPopOrBranchFalse(123)));
+ TokenPtr label;
+ ASSERT_NO_THROW(label.reset(new TokenLabel(123)));
+ TokenPtr left;
+ TokenPtr right;
+ bool result_(false);
+
+ // True and true == true.
+ ASSERT_NO_THROW(left.reset(new TokenString("true")));
+ ASSERT_NO_THROW(right.reset(new TokenString("true")));
+ e_.push_back(left);
+ e_.push_back(branch);
+ e_.push_back(right);
+ e_.push_back(label);
+ ASSERT_NO_THROW(result_ = evaluateBool(e_, *pkt4_));
+ EXPECT_TRUE(result_);
+ e_.clear();
+
+ // True and false == false.
+ ASSERT_NO_THROW(left.reset(new TokenString("true")));
+ ASSERT_NO_THROW(right.reset(new TokenString("false")));
+ e_.push_back(left);
+ e_.push_back(branch);
+ e_.push_back(right);
+ e_.push_back(label);
+ ASSERT_NO_THROW(result_ = evaluateBool(e_, *pkt4_));
+ EXPECT_FALSE(result_);
+ e_.clear();
+
+ // False and any thing == false.
+ ASSERT_NO_THROW(left.reset(new TokenString("false")));
+ ASSERT_NO_THROW(right.reset(new TokenString("any thing")));
+ e_.push_back(left);
+ e_.push_back(branch);
+ e_.push_back(right);
+ e_.push_back(label);
+ EXPECT_NO_THROW(result_ = evaluateBool(e_, *pkt6_));
+ EXPECT_FALSE(result_);
+}
+
+// Tests the pop and branch when false / lazy if.
+TEST_F(ExpressionsTest, popAndBranchFalse) {
+ // The lazy can be implemented as:
+ // <test><P&BF-L1><then><B-L2><L1><else><L2>>.
+ // Do the complete table.
+ TokenPtr brancht;
+ ASSERT_NO_THROW(brancht.reset(new TokenPopAndBranchFalse(123)));
+ TokenPtr branchu;
+ ASSERT_NO_THROW(branchu.reset(new TokenBranch(567)));
+ TokenPtr label1;
+ ASSERT_NO_THROW(label1.reset(new TokenLabel(123)));
+ TokenPtr label2;
+ ASSERT_NO_THROW(label2.reset(new TokenLabel(567)));
+ TokenPtr test;
+ TokenPtr foo;
+ ASSERT_NO_THROW(foo.reset(new TokenString("foo")));
+ TokenPtr bar;
+ ASSERT_NO_THROW(bar.reset(new TokenString("bar")));
+ TokenPtr extra;
+ ASSERT_NO_THROW(extra.reset(new TokenString("extra token")));
+ string result_("");
+
+ // if true then foo else bar == foo
+ ASSERT_NO_THROW(test.reset(new TokenString("true")));
+ e_.push_back(test);
+ e_.push_back(brancht);
+ e_.push_back(foo);
+ e_.push_back(branchu);
+ e_.push_back(label1);
+ e_.push_back(bar);
+ e_.push_back(extra);
+ e_.push_back(label2);
+ ASSERT_NO_THROW(result_ = evaluateString(e_, *pkt4_));
+ EXPECT_EQ("foo", result_);
+ e_.clear();
+
+ // if false then foo else bar == bar
+ ASSERT_NO_THROW(test.reset(new TokenString("false")));
+ e_.push_back(test);
+ e_.push_back(brancht);
+ e_.push_back(foo);
+ e_.push_back(extra);
+ e_.push_back(branchu);
+ e_.push_back(label1);
+ e_.push_back(bar);
+ e_.push_back(label2);
+ ASSERT_NO_THROW(result_ = evaluateString(e_, *pkt6_));
+ EXPECT_EQ("bar", result_);
+}
+
+}
ASSERT_TRUE(values_.empty());
}
+// Verify TokenPopOrBranchTrue.
+TEST_F(TokenTest, popOrBranchTrue) {
+ // 0 is not a valid branch.
+ ASSERT_THROW(t_.reset(new TokenPopOrBranchTrue(0)), EvalParseError);
+
+ ASSERT_NO_THROW(t_.reset(new TokenPopOrBranchTrue(123)));
+ EXPECT_EQ(0, t_->getLabel());
+
+ // CASE 1: The stack is empty.
+ EXPECT_THROW(t_->evaluate(*pkt4_, values_), EvalBadStack);
+
+ // CASE 2: The top value is not a boolen.
+ values_.push("foo");
+ EXPECT_THROW(t_->evaluate(*pkt6_, values_), EvalTypeError);
+
+ // CASE 3: The top value is true.
+ clearStack(false);
+ values_.push("true");
+ ASSERT_EQ(1, values_.size());
+ unsigned next(0);
+ ASSERT_NO_THROW(next = t_->evaluate(*pkt4_, values_));
+ EXPECT_EQ(123, next);
+ ASSERT_EQ(1, values_.size());
+ string result = values_.top();
+ EXPECT_EQ("true", result);
+
+ // CASE 4: The top value is false.
+ clearStack(false);
+ values_.push("false");
+ ASSERT_EQ(1, values_.size());
+ testEvaluate(t_, *pkt6_, values_);
+ EXPECT_TRUE(values_.empty());
+}
+
+// Verify TokenPopOrBranchFalse.
+TEST_F(TokenTest, popOrBranchFalse) {
+ // 0 is not a valid branch.
+ ASSERT_THROW(t_.reset(new TokenPopOrBranchFalse(0)), EvalParseError);
+
+ ASSERT_NO_THROW(t_.reset(new TokenPopOrBranchFalse(123)));
+ EXPECT_EQ(0, t_->getLabel());
+
+ // CASE 1: The stack is empty.
+ EXPECT_THROW(t_->evaluate(*pkt4_, values_), EvalBadStack);
+
+ // CASE 2: The top value is not a boolen.
+ values_.push("foo");
+ EXPECT_THROW(t_->evaluate(*pkt6_, values_), EvalTypeError);
+
+ // CASE 3: The top value is true.
+ clearStack(false);
+ values_.push("true");
+ ASSERT_EQ(1, values_.size());
+ testEvaluate(t_, *pkt6_, values_);
+ EXPECT_TRUE(values_.empty());
+
+ // CASE 4: The top value is false.
+ clearStack(false);
+ values_.push("false");
+ ASSERT_EQ(1, values_.size());
+ unsigned next(0);
+ ASSERT_NO_THROW(next = t_->evaluate(*pkt4_, values_));
+ EXPECT_EQ(123, next);
+ ASSERT_EQ(1, values_.size());
+ string result = values_.top();
+ EXPECT_EQ("false", result);
+}
+
+// Verify TokenPopAndBranchFalse.
+TEST_F(TokenTest, popAndBranchFalse) {
+ // 0 is not a valid branch.
+ ASSERT_THROW(t_.reset(new TokenPopAndBranchFalse(0)), EvalParseError);
+
+ ASSERT_NO_THROW(t_.reset(new TokenPopAndBranchFalse(123)));
+ EXPECT_EQ(0, t_->getLabel());
+
+ // CASE 1: The stack is empty.
+ EXPECT_THROW(t_->evaluate(*pkt4_, values_), EvalBadStack);
+
+ // CASE 2: The top value is not a boolen.
+ values_.push("foo");
+ EXPECT_THROW(t_->evaluate(*pkt6_, values_), EvalTypeError);
+
+ // CASE 3: The top value is true.
+ clearStack(false);
+ values_.push("true");
+ ASSERT_EQ(1, values_.size());
+ testEvaluate(t_, *pkt6_, values_);
+ EXPECT_TRUE(values_.empty());
+
+ // CASE 4: The top value is false.
+ clearStack(false);
+ values_.push("false");
+ ASSERT_EQ(1, values_.size());
+ unsigned next(0);
+ ASSERT_NO_THROW(next = t_->evaluate(*pkt4_, values_));
+ EXPECT_EQ(123, next);
+ EXPECT_TRUE(values_.empty());
+}
+
}
.arg(target_);
return (target_);
}
+
+TokenPopOrBranchTrue::TokenPopOrBranchTrue(const unsigned target)
+ : TokenBranch(target) {
+}
+
+unsigned
+TokenPopOrBranchTrue::evaluate(Pkt&, ValueStack& values) {
+ if (values.size() == 0) {
+ isc_throw(EvalBadStack, "Incorrect empty stack.");
+ }
+
+ string op = values.top();
+ bool val = toBool(op);
+
+ if (!val) {
+ values.pop();
+ return (0);
+ }
+
+ LOG_DEBUG(eval_logger, EVAL_DBG_STACK, EVAL_DEBUG_POP_OR_BRANCH_TRUE)
+ .arg(target_);
+ return (target_);
+}
+
+TokenPopOrBranchFalse::TokenPopOrBranchFalse(const unsigned target)
+ : TokenBranch(target) {
+}
+
+unsigned
+TokenPopOrBranchFalse::evaluate(Pkt&, ValueStack& values) {
+ if (values.size() == 0) {
+ isc_throw(EvalBadStack, "Incorrect empty stack.");
+ }
+
+ string op = values.top();
+ bool val = toBool(op);
+
+ if (val) {
+ values.pop();
+ return (0);
+ }
+
+ LOG_DEBUG(eval_logger, EVAL_DBG_STACK, EVAL_DEBUG_POP_OR_BRANCH_FALSE)
+ .arg(target_);
+ return (target_);
+}
+
+TokenPopAndBranchFalse::TokenPopAndBranchFalse(const unsigned target)
+ : TokenBranch(target) {
+}
+
+unsigned
+TokenPopAndBranchFalse::evaluate(Pkt&, ValueStack& values) {
+ if (values.size() == 0) {
+ isc_throw(EvalBadStack, "Incorrect empty stack.");
+ }
+
+ string op = values.top();
+ values.pop();
+ bool val = toBool(op);
+
+ if (val) {
+ return (0);
+ }
+
+ LOG_DEBUG(eval_logger, EVAL_DBG_STACK, EVAL_DEBUG_POP_AND_BRANCH_FALSE)
+ .arg(target_);
+ return (target_);
+}
/// @brief Token that represents logical and operator
///
+/// @note Strict version i.e. evaluating right branch even left is false
+///
/// For example "option[10].exists and option[11].exists"
class TokenAnd : public Token {
public:
/// @brief Token that represents logical or operator
///
+/// @note Strict version i.e. evaluating right branch even left is right
+///
/// For example "option[10].exists or option[11].exists"
class TokenOr : public Token {
public:
unsigned label_;
};
-/// @brief Token unconditional branch.
+/// @brief Token that represents unconditional branch.
///
/// Unconditionally branch to a forward target.
+/// Also the base class of branch tokens.
class TokenBranch : public Token {
public:
/// @brief Constructor
unsigned target_;
};
+/// @brief Token that represents pop or branch if true.
+///
+/// Branch to a forward target when the top value is true else pop it.
+/// Can be used to implement the left "or" boolean operator.
+class TokenPopOrBranchTrue : public TokenBranch {
+public:
+ /// @brief Constructor
+ ///
+ /// @param target the label to branch to
+ /// @throw EvalParseError when target is 0
+ TokenPopOrBranchTrue(const unsigned target);
+
+ /// @brief Looks at the top of stack which must be "false" or "true".
+ /// On "false" pops it and returns 0, on "true" return the branch target.
+ ///
+ /// @param pkt (unused)
+ /// @param values - stack of values (use/pop boolean top of stack)
+ /// @throw EvalBadStack if there are less than 1 value on stack
+ /// @throw EvalTypeError if the top value on the stack is not either
+ /// "true" or "false"
+ virtual unsigned evaluate(Pkt& pkt, ValueStack& values);
+};
+
+/// @brief Token that represents pop or branch if false.
+///
+/// Branch to a forward target when the top value is false else pop it.
+/// Can be used to implement the left "and" boolean operator.
+class TokenPopOrBranchFalse : public TokenBranch {
+public:
+ /// @brief Constructor
+ ///
+ /// @param target the label to branch to
+ /// @throw EvalParseError when target is 0
+ TokenPopOrBranchFalse(const unsigned target);
+
+ /// @brief Looks at the top of stack which must be "false" or "true".
+ /// On "true" pops it and returns 0, on "false" return the branch target.
+ ///
+ /// @param pkt (unused)
+ /// @param values - stack of values (use/pop boolean top of stack)
+ /// @throw EvalBadStack if there are less than 1 value on stack
+ /// @throw EvalTypeError if the top value on the stack is not either
+ /// "true" or "false"
+ virtual unsigned evaluate(Pkt& pkt, ValueStack& values);
+};
+
+/// @brief Token that represents pop and branch if false.
+///
+/// Pop the op value, branch to a forward target when false.
+/// Can be used to implement the lazy "if" boolean operator i.e.
+/// evaluating either "then" or "else" branches (vs. strict the version
+/// which evaluates both).
+class TokenPopAndBranchFalse : public TokenBranch {
+public:
+ /// @brief Constructor
+ ///
+ /// @param target the label to branch to
+ /// @throw EvalParseError when target is 0
+ TokenPopAndBranchFalse(const unsigned target);
+
+ /// @brief Pops the top of stack which must be "false" or "true".
+ /// On "true" returns 0, on "false" return the branch target.
+ ///
+ /// @param pkt (unused)
+ /// @param values - stack of values (pop the boolean top value)
+ /// @throw EvalBadStack if there are less than 1 value on stack
+ /// @throw EvalTypeError if the top value on the stack is not either
+ /// "true" or "false"
+ virtual unsigned evaluate(Pkt& pkt, ValueStack& values);
+};
+
} // end of isc::dhcp namespace
} // end of isc namespace
#endif