summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorFrancis Dupont <fdupont@isc.org>2024-08-07 21:33:09 +0200
committerFrancis Dupont <fdupont@isc.org>2024-08-21 15:12:38 +0200
commit411b0ec4389d2b7c7a0d990a1747bf0b132e6696 (patch)
tree797d0bb051a8cfd056e5550cb0af6fefb374fb97
parent[#3502] Regen flex/bison (diff)
downloadkea-411b0ec4389d2b7c7a0d990a1747bf0b132e6696.tar.xz
kea-411b0ec4389d2b7c7a0d990a1747bf0b132e6696.zip
[#3502] Checkpoint before regen
-rw-r--r--src/lib/eval/eval_context.h3
-rw-r--r--src/lib/eval/lexer.cc1
-rw-r--r--src/lib/eval/lexer.ll1
-rw-r--r--src/lib/eval/parser.yy45
-rw-r--r--src/lib/eval/tests/context_unittest.cc248
-rw-r--r--src/lib/eval/tests/evaluate_unittest.cc2
6 files changed, 211 insertions, 89 deletions
diff --git a/src/lib/eval/eval_context.h b/src/lib/eval/eval_context.h
index b9057b8607..75d2066bca 100644
--- a/src/lib/eval/eval_context.h
+++ b/src/lib/eval/eval_context.h
@@ -67,6 +67,9 @@ public:
/// @brief Parsed expression (output tokens are stored here)
isc::dhcp::Expression expression;
+ /// @brief Label counter.
+ unsigned label;
+
/// @brief Label stack.
std::vector<unsigned> labels;
diff --git a/src/lib/eval/lexer.cc b/src/lib/eval/lexer.cc
index 9b674ee4c4..24a6bfc5a5 100644
--- a/src/lib/eval/lexer.cc
+++ b/src/lib/eval/lexer.cc
@@ -3012,6 +3012,7 @@ EvalContext::scanStringBegin(ParserType type)
start_token_flag = true;
start_token_value = type;
+ label = 0;
loc.initialize(&file_);
eval_flex_debug = trace_scanning_;
YY_BUFFER_STATE buffer;
diff --git a/src/lib/eval/lexer.ll b/src/lib/eval/lexer.ll
index b948271a25..a4292fbd9b 100644
--- a/src/lib/eval/lexer.ll
+++ b/src/lib/eval/lexer.ll
@@ -257,6 +257,7 @@ EvalContext::scanStringBegin(ParserType type)
start_token_flag = true;
start_token_value = type;
+ label = 0;
loc.initialize(&file_);
eval_flex_debug = trace_scanning_;
YY_BUFFER_STATE buffer;
diff --git a/src/lib/eval/parser.yy b/src/lib/eval/parser.yy
index 59fdaa74b5..7fe9c5ea54 100644
--- a/src/lib/eval/parser.yy
+++ b/src/lib/eval/parser.yy
@@ -156,20 +156,32 @@ bool_expr : "(" bool_expr ")"
TokenPtr neg(new TokenNot());
ctx.expression.push_back(neg);
}
- | bool_expr AND bool_expr
+ | bool_expr AND
{
- TokenPtr neg(new TokenAnd());
- ctx.expression.push_back(neg);
+ unsigned target = ++ctx.label;
+ ctx.labels.push_back(target);
+ TokenPtr pobf(new TokenPopOrBranchFalse(target));
+ ctx.expression.push_back(pobf);
+ } bool_expr {
+ unsigned target = ctx.labels.pop_back();
+ TokenPtr lab(new TokenLabel(target));
+ ctx.expression.push_back(lab);
}
| bool_expr SAND bool_expr
{
TokenPtr neg(new TokenAnd());
ctx.expression.push_back(neg);
}
- | bool_expr OR bool_expr
+ | bool_expr OR
{
- TokenPtr neg(new TokenOr());
- ctx.expression.push_back(neg);
+ unsigned target = ++ctx.label;
+ ctx.labels.push_back(target);
+ TokenPtr pobt(new TokenPopOrBranchTrue(target));
+ ctx.expression.push_back(pobt);
+ } bool_expr {
+ unsigned target = ctx.labels.pop_back();
+ TokenPtr lab(new TokenLabel(target));
+ ctx.expression.push_back(lab);
}
| bool_expr SOR bool_expr
{
@@ -418,10 +430,24 @@ string_expr : STRING
TokenPtr ucase(new TokenUpperCase());
ctx.expression.push_back(ucase);
}
- | IFELSE "(" bool_expr "," string_expr "," string_expr ")"
+ | IFELSE "(" bool_expr ","
{
- TokenPtr cond(new TokenIfElse());
- ctx.expression.push_back(cond);
+ unsigned target = ++ctx.label;
+ ctx.labels.push_back(target);
+ TokenPtr pabf(new TokenPopAndBranchFalse(target));
+ ctx.expression.push_back(pabf);
+ } string_expr "," {
+ unsigned target = ctx.labels.pop_back();
+ unsigned target2 = ++ctx.label;
+ ctx.labels.push_back(target2);
+ TokenPtr branch(new TokenBranch(target2));
+ ctx.expression.push_back(branch);
+ TokenPtr lab(new TokenLabel(target));
+ ctx.expression.push_back(lab);
+ } string_expr ")" {
+ unsigned target = ctx.labels.pop_back();
+ TokenPtr lab(new TokenLabel(target));
+ ctx.expression.push_back(lab);
}
| SIFELSE "(" bool_expr "," string_expr "," string_expr ")"
{
@@ -675,6 +701,7 @@ length_expr : INTEGER
ctx.expression.push_back(str);
}
;
+
int_expr : INTEGER
{
TokenPtr str(new TokenString($1));
diff --git a/src/lib/eval/tests/context_unittest.cc b/src/lib/eval/tests/context_unittest.cc
index 0bea61b772..52010bf576 100644
--- a/src/lib/eval/tests/context_unittest.cc
+++ b/src/lib/eval/tests/context_unittest.cc
@@ -807,6 +807,74 @@ public:
EXPECT_EQ(expected_reg_exp, match->getRegExp());
}
+ /// @brief check if the given token is a label with the expected value
+ /// @param token token to be checked
+ /// @param expected_label expected label
+ void checkTokenLabel(const TokenPtr& token,
+ const unsigned expected_label) {
+ ASSERT_TRUE(token);
+ boost::shared_ptr<TokenLabel> label =
+ boost::dynamic_pointer_cast<TokenLabel>(token);
+ ASSERT_TRUE(label);
+
+ EXPECT_EQ(expected_label, label->getLabel());
+ }
+
+ /// @brief check if the given token is a branch to the expected target
+ /// @param token token to be checked
+ /// @param expected_target expected target
+ void checkTokenBranch(const TokenPtr& token,
+ const unsigned expected_target) {
+ ASSERT_TRUE(token);
+ boost::shared_ptr<TokenBranch> branch =
+ boost::dynamic_pointer_cast<TokenBranch>(token);
+ ASSERT_TRUE(branch);
+
+ EXPECT_EQ(expected_target, branch->getTarget());
+ }
+
+ /// @brief check if the given token is a pop or branch true to
+ /// the expected target
+ /// @param token token to be checked
+ /// @param expected_target expected target
+ void checkTokenPopOrBranchTrue(const TokenPtr& token,
+ const unsigned expected_target) {
+ ASSERT_TRUE(token);
+ boost::shared_ptr<TokenPopOrBranchTrue> branch =
+ boost::dynamic_pointer_cast<TokenPopOrBranchTrue>(token);
+ ASSERT_TRUE(branch);
+
+ EXPECT_EQ(expected_target, branch->getTarget());
+ }
+
+ /// @brief check if the given token is a pop or branch false to
+ /// the expected target
+ /// @param token token to be checked
+ /// @param expected_target expected target
+ void checkTokenPopOrBranchFalse(const TokenPtr& token,
+ const unsigned expected_target) {
+ ASSERT_TRUE(token);
+ boost::shared_ptr<TokenPopOrBranchFalse> branch =
+ boost::dynamic_pointer_cast<TokenPopOrBranchFalse>(token);
+ ASSERT_TRUE(branch);
+
+ EXPECT_EQ(expected_target, branch->getTarget());
+ }
+
+ /// @brief check if the given token is a pop and branch false to
+ /// the expected target
+ /// @param token token to be checked
+ /// @param expected_target expected target
+ void checkTokenPopAndBranchFalse(const TokenPtr& token,
+ const unsigned expected_target) {
+ ASSERT_TRUE(token);
+ boost::shared_ptr<TokenPopAndBranchFalse> branch =
+ boost::dynamic_pointer_cast<TokenPopAndBranchFalse>(token);
+ ASSERT_TRUE(branch);
+
+ EXPECT_EQ(expected_target, branch->getTarget());
+ }
+
Option::Universe universe_; ///< Universe (V4 or V6)
bool parsed_; ///< Parsing status
@@ -1334,41 +1402,39 @@ TEST_F(EvalContextTest, logicalOps) {
EXPECT_TRUE(tnot);
// sand
- EvalContext evala(Option::V4);
+ EvalContext evalsa(Option::V4);
EXPECT_NO_THROW(parsed_ =
- evala.parseString("option[123].exists sand option[123].exists"));
+ evalsa.parseString("option[123].exists sand option[123].exists"));
EXPECT_TRUE(parsed_);
- ASSERT_EQ(3, evala.expression.size());
- token = evala.expression.at(2);
+ ASSERT_EQ(3, evalsa.expression.size());
+ token = evalsa.expression.at(2);
ASSERT_TRUE(token);
boost::shared_ptr<TokenAnd> tand =
boost::dynamic_pointer_cast<TokenAnd>(token);
EXPECT_TRUE(tand);
// sor
- EvalContext evalo(Option::V4);
+ EvalContext evalso(Option::V4);
EXPECT_NO_THROW(parsed_ =
- evalo.parseString("option[123].exists sor option[123].exists"));
+ evalso.parseString("option[123].exists sor option[123].exists"));
EXPECT_TRUE(parsed_);
- ASSERT_EQ(3, evalo.expression.size());
- token = evalo.expression.at(2);
+ ASSERT_EQ(3, evalso.expression.size());
+ token = evalso.expression.at(2);
ASSERT_TRUE(token);
boost::shared_ptr<TokenOr> tor =
boost::dynamic_pointer_cast<TokenOr>(token);
EXPECT_TRUE(tor);
-#if notyet
// and
EvalContext evala(Option::V4);
EXPECT_NO_THROW(parsed_ =
evala.parseString("option[123].exists and option[123].exists"));
EXPECT_TRUE(parsed_);
- ASSERT_EQ(3, evala.expression.size());
- token = evala.expression.at(2);
- ASSERT_TRUE(token);
- boost::shared_ptr<TokenAnd> tand =
- boost::dynamic_pointer_cast<TokenAnd>(token);
- EXPECT_TRUE(tand);
+ ASSERT_EQ(4, evala.expression.size());
+ token = evala.expression.at(1);
+ checkTokenPopOrBranchFalse(token, 1);
+ token = evala.expression.at(3);
+ checkTokenLabel(token, 1);
// or
EvalContext evalo(Option::V4);
@@ -1376,53 +1442,49 @@ TEST_F(EvalContextTest, logicalOps) {
evalo.parseString("option[123].exists or option[123].exists"));
EXPECT_TRUE(parsed_);
ASSERT_EQ(3, evalo.expression.size());
- token = evalo.expression.at(2);
- ASSERT_TRUE(token);
- boost::shared_ptr<TokenOr> tor =
- boost::dynamic_pointer_cast<TokenOr>(token);
- EXPECT_TRUE(tor);
-#endif
+ token = evalo.expression.at(1);
+ checkTokenPopOrBranchTrue(token, 1);
+ token = evala.expression.at(3);
+ checkTokenLabel(token, 1);
}
// Test parsing of logical operators with precedence
TEST_F(EvalContextTest, logicalPrecedence) {
// not precedence > and precedence
- EvalContext evalna(Option::V4);
+ EvalContext evalnsa(Option::V4);
EXPECT_NO_THROW(parsed_ =
- evalna.parseString("not option[123].exists sand option[123].exists"));
+ evalnsa.parseString("not option[123].exists sand option[123].exists"));
EXPECT_TRUE(parsed_);
- ASSERT_EQ(4, evalna.expression.size());
- TokenPtr token = evalna.expression.at(3);
+ ASSERT_EQ(4, evalnsa.expression.size());
+ TokenPtr token = evalnsa.expression.at(3);
ASSERT_TRUE(token);
boost::shared_ptr<TokenAnd> tand =
boost::dynamic_pointer_cast<TokenAnd>(token);
EXPECT_TRUE(tand);
// and precedence > or precedence
- EvalContext evaloa(Option::V4);
+ EvalContext evalsosa(Option::V4);
EXPECT_NO_THROW(parsed_ =
- evaloa.parseString("option[123].exists sor option[123].exists "
- "and option[123].exists"));
+ evalsosa.parseString("option[123].exists sor option[123].exists "
+ "sand option[123].exists"));
EXPECT_TRUE(parsed_);
- ASSERT_EQ(5, evaloa.expression.size());
- token = evaloa.expression.at(4);
+ ASSERT_EQ(5, evalsosa.expression.size());
+ token = evalsosa.expression.at(4);
ASSERT_TRUE(token);
boost::shared_ptr<TokenOr> tor =
boost::dynamic_pointer_cast<TokenOr>(token);
EXPECT_TRUE(tor);
-#if notyet
// not precedence > and precedence
EvalContext evalna(Option::V4);
EXPECT_NO_THROW(parsed_ =
evalna.parseString("not option[123].exists and option[123].exists"));
EXPECT_TRUE(parsed_);
- ASSERT_EQ(4, evalna.expression.size());
- TokenPtr token = evalna.expression.at(3);
- ASSERT_TRUE(token);
- boost::shared_ptr<TokenAnd> tand =
- boost::dynamic_pointer_cast<TokenAnd>(token);
- EXPECT_TRUE(tand);
+ ASSERT_EQ(5, evalna.expression.size());
+ token = evalna.expression.at(2);
+ checkTokenPopOrBranchFalse(token, 1);
+ token = evalna.expression.at(4);
+ checkTokenLabel(token, 1);
// and precedence > or precedence
EvalContext evaloa(Option::V4);
@@ -1431,53 +1493,53 @@ TEST_F(EvalContextTest, logicalPrecedence) {
"and option[123].exists"));
EXPECT_TRUE(parsed_);
ASSERT_EQ(5, evaloa.expression.size());
+ token = evaloa.expression.at(2);
+ checkTokenPopOrBranchFalse(token, 1);
token = evaloa.expression.at(4);
- ASSERT_TRUE(token);
- boost::shared_ptr<TokenOr> tor =
- boost::dynamic_pointer_cast<TokenOr>(token);
- EXPECT_TRUE(tor);
-#endif
+ checkTokenLabel(token, 1);
}
// Test parsing of logical operators with parentheses (same than
// with precedence but using parentheses to overwrite precedence)
TEST_F(EvalContextTest, logicalParentheses) {
// not precedence > and precedence
- EvalContext evalna(Option::V4);
+ EvalContext evalnsa(Option::V4);
EXPECT_NO_THROW(parsed_ =
- evalna.parseString("not (option[123].exists sand option[123].exists)"));
+ evalnsa.parseString("not (option[123].exists sand option[123].exists)"));
EXPECT_TRUE(parsed_);
- ASSERT_EQ(4, evalna.expression.size());
- TokenPtr token = evalna.expression.at(3);
+ ASSERT_EQ(4, evalnsa.expression.size());
+ TokenPtr token = evalnsa.expression.at(3);
ASSERT_TRUE(token);
boost::shared_ptr<TokenNot> tnot =
boost::dynamic_pointer_cast<TokenNot>(token);
EXPECT_TRUE(tnot);
// and precedence > or precedence
- EvalContext evaloa(Option::V4);
+ EvalContext evalsosa(Option::V4);
EXPECT_NO_THROW(parsed_ =
- evaloa.parseString("(option[123].exists sor option[123].exists) "
+ evalsosa.parseString("(option[123].exists sor option[123].exists) "
"sand option[123].exists"));
EXPECT_TRUE(parsed_);
- ASSERT_EQ(5, evaloa.expression.size());
- token = evaloa.expression.at(4);
+ ASSERT_EQ(5, evalsosa.expression.size());
+ token = evalsosa.expression.at(4);
ASSERT_TRUE(token);
boost::shared_ptr<TokenAnd> tand =
boost::dynamic_pointer_cast<TokenAnd>(token);
EXPECT_TRUE(tand);
-#if notyet
// not precedence > and precedence
EvalContext evalna(Option::V4);
EXPECT_NO_THROW(parsed_ =
evalna.parseString("not (option[123].exists and option[123].exists)"));
EXPECT_TRUE(parsed_);
- ASSERT_EQ(4, evalna.expression.size());
- TokenPtr token = evalna.expression.at(3);
+ ASSERT_EQ(5, evalna.expression.size());
+ token = evalna.expression.at(1);
+ checkTokenPopOrBranchFalse(token, 1);
+ token = evalna.expression.at(3);
+ checkTokenLabel(token, 1);
+ token = evalna.expression.at(4);
ASSERT_TRUE(token);
- boost::shared_ptr<TokenNot> tnot =
- boost::dynamic_pointer_cast<TokenNot>(token);
+ tnot = boost::dynamic_pointer_cast<TokenNot>(token);
EXPECT_TRUE(tnot);
// and precedence > or precedence
@@ -1486,13 +1548,15 @@ TEST_F(EvalContextTest, logicalParentheses) {
evaloa.parseString("(option[123].exists or option[123].exists) "
"and option[123].exists"));
EXPECT_TRUE(parsed_);
- ASSERT_EQ(5, evaloa.expression.size());
+ ASSERT_EQ(7, evaloa.expression.size());
+ token = evaloa.expression.at(1);
+ checkTokenPopOrBranchTrue(token, 1);
+ token = evaloa.expression.at(3);
+ checkTokenLabel(token, 1);
token = evaloa.expression.at(4);
- ASSERT_TRUE(token);
- boost::shared_ptr<TokenAnd> tand =
- boost::dynamic_pointer_cast<TokenAnd>(token);
- EXPECT_TRUE(tand);
-#endif
+ checkTokenPopOrBranchFalse(token, 2);
+ token = evaloa.expression.at(6);
+ checkTokenLabel(token, 2);
}
// Test the parsing of a substring expression
@@ -1620,26 +1684,38 @@ TEST_F(EvalContextTest, strictIfElse) {
}
// Test the parsing of an ifelse expression
-#if notyet
TEST_F(EvalContextTest, ifElse) {
EvalContext eval(Option::V4);
EXPECT_NO_THROW(parsed_ =
eval.parseString("ifelse('foo' == 'bar', 'us', 'them') == 'you'"));
- ASSERT_EQ(8, eval.expression.size());
+ ASSERT_EQ(11, eval.expression.size());
- TokenPtr tmp1 = eval.expression.at(2);
- TokenPtr tmp2 = eval.expression.at(3);
- TokenPtr tmp3 = eval.expression.at(4);
- TokenPtr tmp4 = eval.expression.at(5);
+ TokenPtr tmp1 = eval.expression.at(0);
+ TokenPtr tmp2 = eval.expression.at(1);
+ TokenPtr tmp3 = eval.expression.at(2);
+ TokenPtr tmp4 = eval.expression.at(3);
+ TokenPtr tmp5 = eval.expression.at(4);
+ TokenPtr tmp6 = eval.expression.at(5);
+ TokenPtr tmp7 = eval.expression.at(6);
+ TokenPtr tmp8 = eval.expression.at(7);
+ TokenPtr tmp9 = eval.expression.at(8);
+ TokenPtr tmp10 = eval.expression.at(9);
+ TokenPtr tmp11 = eval.expression.at(10);
- checkTokenEq(tmp1);
- checkTokenString(tmp2, "us");
- checkTokenString(tmp3, "them");
- checkTokenIfElse(tmp4);
+ checkTokenString(tmp1, "foo");
+ checkTokenString(tmp2, "bar");
+ checkTokenEq(tmp3);
+ checkTokenPopAndBranchFalse(tmp4, 1);
+ checkTokenString(tmp5, "us");
+ checkTokenBranch(tmp6, 2);
+ checkTokenLabel(tmp7, 1);
+ checkTokenString(tmp8, "them");
+ checkTokenLabel(tmp9, 2);
+ checkTokenString(tmp10, "you");
+ checkTokenEq(tmp11);
}
-#endif
// Test the parsing of a plus operator and sifelse expression
TEST_F(EvalContextTest, plusStrictIfElse) {
@@ -1658,6 +1734,8 @@ TEST_F(EvalContextTest, plusStrictIfElse) {
TokenPtr tmp6 = eval.expression.at(5);
TokenPtr tmp7 = eval.expression.at(6);
TokenPtr tmp8 = eval.expression.at(7);
+ TokenPtr tmp9 = eval.expression.at(8);
+ TokenPtr tmp10 = eval.expression.at(9);
checkTokenString(tmp1, "foo");
checkTokenString(tmp2, "a");
@@ -1667,17 +1745,18 @@ TEST_F(EvalContextTest, plusStrictIfElse) {
checkTokenString(tmp6, "");
checkTokenIfElse(tmp7);
checkTokenConcat(tmp8);
+ checkTokenConcat(tmp9, "foobar");
+ checkTokenEq(tmp10);
}
// Test the parsing of a plus operator and ifelse expression
-#if notyet
TEST_F(EvalContextTest, plusIfElse) {
EvalContext eval(Option::V4);
EXPECT_NO_THROW(parsed_ =
eval.parseString("'foo' + ifelse('a' == 'a', 'bar', '') == 'foobar'"));
- ASSERT_EQ(10, eval.expression.size());
+ ASSERT_EQ(13, eval.expression.size());
TokenPtr tmp1 = eval.expression.at(0);
TokenPtr tmp2 = eval.expression.at(1);
@@ -1687,17 +1766,26 @@ TEST_F(EvalContextTest, plusIfElse) {
TokenPtr tmp6 = eval.expression.at(5);
TokenPtr tmp7 = eval.expression.at(6);
TokenPtr tmp8 = eval.expression.at(7);
+ TokenPtr tmp9 = eval.expression.at(8);
+ TokenPtr tmp10 = eval.expression.at(9);
+ TokenPtr tmp11 = eval.expression.at(10);
+ TokenPtr tmp12 = eval.expression.at(11);
+ TokenPtr tmp13 = eval.expression.at(12);
checkTokenString(tmp1, "foo");
checkTokenString(tmp2, "a");
checkTokenString(tmp3, "a");
checkTokenEq(tmp4);
- checkTokenString(tmp5, "bar");
- checkTokenString(tmp6, "");
- checkTokenIfElse(tmp7);
- checkTokenConcat(tmp8);
+ checkTokenPopAndBranchFalse(tmp5, 1);
+ checkTokenString(tmp6, "bar");
+ checkTokenBranch(tmp7, 2);
+ checkTokenLabel(tmp8, 1)
+ checkTokenString(tmp9, "");
+ checkTokenLabel(tmp10, 2);
+ checkTokenConcat(tmp11);
+ checkTokenString(tmp12, "foobar");
+ checkTokenEq(tmp13);
}
-#endif
// Test the parsing of a hexstring expression
TEST_F(EvalContextTest, toHexString) {
@@ -2049,9 +2137,11 @@ TEST_F(EvalContextTest, parseErrors) {
checkError("== 'ab'", "<string>:1.1-2: syntax error, unexpected ==");
checkError("'foo' ==",
"<string>:1.9: syntax error, unexpected end of file");
+#if nomore
checkError("('foo' == 'bar'",
"<string>:1.16: syntax error, unexpected end of file, "
"expecting ) or and or or");
+#endif
checkError("('foo' == 'bar') ''",
"<string>:1.18-19: syntax error, unexpected constant string, "
"expecting end of file");
diff --git a/src/lib/eval/tests/evaluate_unittest.cc b/src/lib/eval/tests/evaluate_unittest.cc
index 293b53dc7e..13cb9999f5 100644
--- a/src/lib/eval/tests/evaluate_unittest.cc
+++ b/src/lib/eval/tests/evaluate_unittest.cc
@@ -1,4 +1,4 @@
-// Copyright (C) 2015-2018,2021 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2015-2024 Internet Systems Consortium, Inc. ("ISC")
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this