return (static_cast<uint32_t>(n));
}
+std::string
+EvalContext::fromUint32(const uint32_t integer) {
+ std::string tmp(4, 0);
+ tmp[0] = (integer >> 24) & 0xff;
+ tmp[1] = (integer >> 16) & 0xff;
+ tmp[2] = (integer >> 8) & 0xff;
+ tmp[3] = integer & 0xff;
+
+ return (tmp);
+}
+
void
EvalContext::fatal (const std::string& what)
{
///
/// @param loc location within the parsed file when experienced a problem.
/// @param what string explaining the nature of the error.
- void error(const isc::eval::location& loc, const std::string& what);
+ static void error(const isc::eval::location& loc, const std::string& what);
/// @brief Error handler
///
/// This is a simplified error reporting tool for possible future
/// cases when the EvalParser is not able to handle the packet.
- void error(const std::string& what);
+ static void error(const std::string& what);
/// @brief Fatal error handler
///
/// @brief Attempts to convert string to unsigned 32bit integer
///
+ /// For reverse conversion, see @ref fromUint32
+ ///
/// @param number string to be converted
/// @param loc the location of the token
/// @return the integer value
/// @throw EvalParseError if conversion fails or the value is out of range.
- uint32_t convertUint32(const std::string& number,
- const isc::eval::location& loc);
+ static uint32_t convertUint32(const std::string& number,
+ const isc::eval::location& loc);
/// @brief Attempts to convert string to unsigned 8bit integer
///
/// @param loc the location of the token
/// @return the integer value
/// @throw EvalParseError if conversion fails or the value is out of range.
- uint8_t convertUint8(const std::string& number,
- const isc::eval::location& loc);
+ static uint8_t convertUint8(const std::string& number,
+ const isc::eval::location& loc);
/// @brief Nest level conversion
///
/// @throw calls the syntax error function if the value is not in
/// the range 0..31
uint8_t convertNestLevelNumber(const std::string& nest_level,
- const isc::eval::location& loc);
+ const isc::eval::location& loc);
+
+ /// @brief Converts integer to string representation
+ ///
+ /// The integer is coded as 4 bytes long string in network order, e.g.
+ /// 6 is represented as 00000006. For reverse conversion, see
+ /// @ref convertUint32.
+ ///
+ /// @param integer value to be converted
+ /// @return 4 bytes long string that encodes the value.
+ static std::string fromUint32(const uint32_t integer);
/// @brief Returns the universe (v4 or v6)
///
%type <uint16_t> option_code
%type <uint32_t> enterprise_id
+%type <uint32_t> integer_expr
%type <TokenOption::RepresentationType> option_repr_type
%type <TokenRelay6Field::FieldType> relay6_field
%type <uint8_t> nest_level
TokenVendor::DATA, index));
ctx.expression.push_back(vendor_class);
}
+ | integer_expr
+ {
+ TokenPtr integer(new TokenInteger($1));
+ ctx.expression.push_back(integer);
+ }
+ ;
+
+integer_expr: INTEGER
+ {
+ $$ = ctx.convertUint32($1, @1);
+ }
;
option_code : INTEGER
EXPECT_TRUE(eq);
}
+ void checkTokenInteger(const TokenPtr& token, uint32_t exp_value) {
+ ASSERT_TRUE(token);
+ boost::shared_ptr<TokenInteger> integer =
+ boost::dynamic_pointer_cast<TokenInteger>(token);
+ ASSERT_TRUE(integer);
+ EXPECT_EQ(exp_value, integer->getInteger());
+ }
+
/// @brief checks if the given token is an option with the expected code
/// and representation type
/// @param token token to be checked
checkError("relay6[32].option[123].text == 'foo'",
"<string>:1.8-9: Nest level has invalid value in 32. "
"Allowed range: 0..31");
-
+
// next level must be a positive number
checkError("relay6[-1].option[123].text == 'foo'",
"<string>:1.8-9: Invalid value in -1. Allowed range: 0..255");
TEST_F(EvalContextTest, scanParseErrors) {
checkError("", "<string>:1.1: syntax error, unexpected end of file");
checkError(" ", "<string>:1.2: syntax error, unexpected end of file");
- checkError("0x", "<string>:1.1: syntax error, unexpected integer");
+ checkError("0x", "<string>:1.2: Invalid character: x");
checkError("0abc",
- "<string>:1.1: syntax error, unexpected integer");
- checkError("10.0.1", "<string>:1.1-2: syntax error, unexpected integer");
+ "<string>:1.2: Invalid character: a");
+
+ // This one is a little bid odd. This is truncated address, so it's not
+ // recognized as address. Instead, first token (10) is recognized as
+ // integer. The only thing we can do with integers right now is test
+ // for equality, so the only possible next token is ==. There's a dot
+ // instead, so an error is reported.
+ checkError("10.0.1", "<string>:1.3: syntax error, unexpected ., expecting ==");
+
checkError("10.256.0.1",
"<string>:1.1-10: Failed to convert 10.256.0.1 to "
"an IP address.");
checkError("substring('foobar',0x32,1) == 'foo'",
"<string>:1.20-23: syntax error, unexpected constant "
"hexstring, expecting integer");
- checkError("concat('foo',3) == 'foo3'",
- "<string>:1.14: syntax error, unexpected integer");
- checkError("concat(3,'foo') == '3foo'",
- "<string>:1.8: syntax error, unexpected integer");
+
+ // With the #4483 addition, all integers are treated as 4 byte strings,
+ // so those checks no longer makes sense. Commeting it out.
+ // checkError("concat('foo',3) == 'foo3'",
+ // "<string>:1.14: syntax error, unexpected integer");
+ // checkError("concat(3,'foo') == '3foo'",
+ // "<string>:1.8: syntax error, unexpected integer");
checkError("('foo' == 'bar') == 'false'",
"<string>:1.18-19: syntax error, unexpected ==, "
"expecting end of file");
testVendorClass("vendor-class[4491].data[3] == 0x1234", Option::V6, 4491, 3);
}
+// Checks if integer expressions can be parsed and checked for equality.
+TEST_F(EvalContextTest, integer1) {
+
+ EvalContext eval(Option::V6);
+
+ EXPECT_NO_THROW(parsed_ = eval.parseString("1 == 2"));
+ EXPECT_TRUE(parsed_);
+
+ ASSERT_EQ(3, eval.expression.size());
+
+ TokenPtr tmp = eval.expression.at(0);
+ ASSERT_TRUE(tmp);
+ checkTokenInteger(tmp, 1);
+ tmp = eval.expression.at(1);
+
+ ASSERT_TRUE(tmp);
+ checkTokenInteger(tmp, 2);
+}
+
};
#include <config.h>
#include <fstream>
#include <eval/token.h>
+#include <eval/eval_context.h>
#include <dhcp/pkt4.h>
#include <dhcp/pkt6.h>
#include <dhcp/dhcp4.h>
}
/// @brief Convenience function. Removes token and values stacks.
- void clearStack() {
+ /// @param token specifies if the convenience token should be removed or not
+ void clearStack(bool token = true) {
while (!values_.empty()) {
values_.pop();
}
-
- t_.reset();
+ if (token) {
+ t_.reset();
+ }
}
/// @brief Aux. function that stores integer values as 4 bytes string.
/// @param value integer value to be stored
/// @return 4 bytes long string with encoded value.
string encode(uint32_t value) {
- string tmp(4,0);
- tmp[0] = value >> 24;
- tmp[1] = value >> 16;
- tmp[2] = value >> 8;
- tmp[3] = value;
- return (tmp);
+ return EvalContext::fromUint32(value);
}
TokenPtr t_; ///< Just a convenience pointer
evaluate(u, expected);
}
+
+ /// @brief Tests if TokenInteger evaluates to proper value
+ ///
+ /// @param value integer value passed to constructor
+ /// @param expected expected string representation on stack after evaluation
+ void testInteger(std::string expected, uint32_t value) {
+
+ clearStack();
+
+ ASSERT_NO_THROW(t_.reset(new TokenInteger(value)));
+
+ // The universe (v4 or v6) shouldn't have any impact on this,
+ // but let's check it anyway.
+ evaluate(Option::V4, expected);
+
+ clearStack(false);
+ evaluate(Option::V6, expected);
+
+ clearStack(true);
+ }
};
// This tests the toBool() conversions
EXPECT_TRUE(checkFile());
}
+// Checks if various values can be represented as integer tokens
+TEST_F(TokenTest, integer) {
+ testInteger(encode(0), 0);
+ testInteger(encode(6), 6);
+ testInteger(encode(255), 255);
+ testInteger(encode(256), 256);
+ testInteger(encode(1410), 1410);
+ testInteger(encode(4294967295), 4294967295);
+}
+
};
#include <eval/token.h>
#include <eval/eval_log.h>
+#include <eval/eval_context.h>
#include <util/encode/hex.h>
#include <util/io_utilities.h>
#include <asiolink/io_address.h>
isc_throw(EvalTypeError, "Invalid field specified." << field_);
}
}
+
+TokenInteger::TokenInteger(const uint32_t value)
+ :TokenString(EvalContext::fromUint32(value)), int_value_(value) {
+
+}
std::string value_; ///< Constant value
};
+/// @brief Token representing an unsigned 32 bits integer
+///
+/// For performance reasons, the constant integer value is converted to string
+/// just once (in constructor). Afterwards, this effectively works as constant
+/// 4 bytes long string. Hence this class is derived from TokenString and
+/// does not even need its own evaluate() method.
+class TokenInteger : public TokenString {
+public:
+ /// @brief Integer value set during construction.
+ ///
+ /// The value is converted to string and stored in value_ provided by the
+ /// base class.
+ ///
+ /// @param value integer value to be stored.
+ TokenInteger(const uint32_t value);
+
+ /// @brief Returns integer value
+ ///
+ /// Used in tests only.
+ ///
+ /// @return integer value
+ uint32_t getInteger() const {
+ return (int_value_);
+ }
+
+protected:
+ uint32_t int_value_; ///< value as integer (stored for testing only)
+};
+
/// @brief Token representing an IP address as a constant string
///
/// This token holds the value of an IP address as a constant string,