Commit ac00ace4 authored by Francis Dupont's avatar Francis Dupont
Browse files

[4088] Merged trac4088fd private branch

parents 11bc4b3c 81ab4ec7
......@@ -17,7 +17,7 @@ libkea_eval_la_SOURCES += token.cc token.h
libkea_eval_la_SOURCES += parser.cc parser.h
libkea_eval_la_SOURCES += lexer.cc
libkea_eval_la_SOURCES += eval_context.cc
libkea_eval_la_SOURCES += eval_context.cc eval_context.h eval_context_decl.h
nodist_libkea_eval_la_SOURCES = eval_messages.h eval_messages.cc
......
......@@ -22,9 +22,8 @@
classification, but in the future may be also used for other applications.
The external interface to the library is the @ref isc::eval::EvalContext
class. Once instantiated, it offers two major methods:
@ref isc::eval::EvalContext::parseFile which parses the content of a file
and @ref isc::eval::EvalContext::parseString, which parses the specified
class. Once instantiated, it offers a major method:
@ref isc::eval::EvalContext::parseString, which parses the specified
string. Once the expression is parsed, it is converted to a collection of
tokens that are stored in Reverse Polish Notation in
EvalContext::expression.
......@@ -66,27 +65,35 @@
@code
01. %start expression;
02.
03. expression:
04. token EQUAL token
05. | token;
03. expression : token EQUAL token
04. | token
05. ;
06.
07. token:
08. STRING {
09. TokenPtr str(new TokenString($1));
10. ctx.expression.push_back(str);
11.}
12.| OPTION {
13. TokenPtr opt(new TokenOption($1));
14. ctx.expression.push_back(opt);
15.};
07. token : STRING
08. {
09. TokenPtr str(new TokenString($1));
10. ctx.expression.push_back(str);
11. }
12. | HEXSTRING
13. {
14. TokenPtr hex(new TokenHexString($1));
15. ctx.expression.push_back(hex);
16. }
17. | OPTION '[' INTEGER ']'
18. {
19. TokenPtr opt(new TokenOption($3));
20. ctx.expression.push_back(opt);
21. }
22. ;
@endcode
This code determines that the grammar starts from expression (line 1).
The actual definition of expression (lines 3-5) may either be a
single token or an expression "token == token" (EQUAL has been defined as
"==" elsewhere). Token is further
defined in lines 7-15: it may either be a string (lines 8-11) or option
(lines 12-15). When the actual case is determined, the respective C++ action
defined in lines 7-22: it may either be a string (lines 7-11),
a hex string (lines 12-16) or option (lines 17-21).
When the actual case is determined, the respective C++ action
is executed. For example, if the token is a string, the TokenString class is
instantiated with the appropriate value and put onto the expression vector.
......
......@@ -26,46 +26,27 @@ EvalContext::~EvalContext()
{
}
int
EvalContext::parseFile(const std::string &filename)
bool
EvalContext::parseString(const std::string& str)
{
file = filename;
scanBegin();
file_ = "<string>";
string_ = str;
scanStringBegin();
isc::eval::EvalParser parser(*this);
parser.set_debug_level(trace_parsing_);
int res = parser.parse();
scanEnd();
return res;
}
int
EvalContext::parseString(const std::string& str)
{
/// @todo: Is there any way for the parser to read from a stream,
/// rather than a file? It would be better to use stringstream,
/// but it seems that the lexer operates on FILE* interface.
// Put the content into a file and then open that file for reading.
remove("/tmp/eval");
std::fstream f("/tmp/eval", std::ios::out);
if (!f.good()) {
isc_throw(isc::Unexpected, "Can't write /tmp/eval file");
}
f << str;
f.close();
return (parseFile("/tmp/eval"));
scanStringEnd();
return (res == 0);
}
void
EvalContext::error(const isc::eval::location& l, const std::string& m)
EvalContext::error(const isc::eval::location& loc, const std::string& what)
{
isc_throw(EvalError, l << ": " << m);
isc_throw(EvalParseError, loc << ": " << what);
}
void
EvalContext::error (const std::string& m)
EvalContext::error (const std::string& what)
{
isc_throw(EvalError, m);
isc_throw(EvalParseError, what);
}
......@@ -17,6 +17,7 @@
#include <string>
#include <map>
#include <eval/parser.h>
#include <eval/eval_context_decl.h>
#include <exceptions/exceptions.h>
// Tell Flex the lexer's prototype ...
......@@ -25,10 +26,13 @@
// ... and declare it for the parser's sake.
YY_DECL;
namespace isc {
namespace eval {
/// @brief Evaluation error exception raised when trying to parse an axceptions.
class EvalError : public isc::Exception {
class EvalParseError : public isc::Exception {
public:
EvalError(const char* file, size_t line, const char* what) :
EvalParseError(const char* file, size_t line, const char* what) :
isc::Exception(file, line, what) { };
};
......@@ -46,32 +50,30 @@ public:
/// @brief Parsed expression (output tokens are stored here)
isc::dhcp::Expression expression;
/// @brief Method called before scanning starts.
void scanBegin();
/// @brief Method called before scanning starts on a string.
void scanStringBegin();
/// @brief Method called after the last tokens are scanned.
void scanEnd();
/// @brief Method called after the last tokens are scanned from a string.
void scanStringEnd();
/// @brief Runs the parser on specified file.
///
/// @param filename
/// Return 0 on success.
int parseFile(const std::string& filename);
/// @brief Run the parser on the string specified.
///
/// @param str string to be written
int parseString(const std::string& str);
/// @return true on success.
bool parseString(const std::string& str);
/// @brief The name of the file being parsed.
/// Used later to pass the file name to the location tracker.
std::string file;
std::string file_;
/// @brief The string being parsed.
std::string string_;
/// @brief Error handler
///
/// @param l location within the parsed file when experienced a problem.
/// @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& l, const std::string& what);
void error(const isc::eval::location& loc, const std::string& what);
/// @brief Error handler
///
......@@ -87,4 +89,8 @@ public:
bool trace_parsing_;
};
#endif // ! EVALCONTEXT_H
}; // end of isc::eval namespace
}; // end of isc namespace
#endif
// Copyright (C) 2015 Internet Systems Consortium, Inc. ("ISC")
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.
#ifndef EVAL_CONTEXT_DECL_H
#define EVAL_CONTEXT_DECL_H
/// @file eval_context_decl.h Forward declaration of the EvalContext class
namespace isc {
namespace eval {
class EvalContext;
}; // end of isc::eval namespace
}; // end of isc namespace
#endif
......@@ -18,8 +18,3 @@ $NAMESPACE isc::dhcp
This debug message indicates that the expression has been evaluated
to said value. This message is mostly useful during debugging of the
client classification expressions.
% EVAL_SUBSTRING_BAD_PARAM_CONVERSION starting %1, length %2
This debug message indicates that the parameter for the starting postion
or length of the substring couldn't be converted to an integer. In this
case the substring routine returns an empty string.
......@@ -18,7 +18,7 @@
#define FLEX_SCANNER
#define YY_FLEX_MAJOR_VERSION 2
#define YY_FLEX_MINOR_VERSION 5
#define YY_FLEX_SUBMINOR_VERSION 37
#define YY_FLEX_SUBMINOR_VERSION 39
#if YY_FLEX_SUBMINOR_VERSION > 0
#define FLEX_BETA
#endif
......@@ -230,6 +230,13 @@ extern FILE *yyin, *yyout;
if ( yytext[yyl] == '\n' )\
--yylineno;\
}while(0)
#define YY_LINENO_REWIND_TO(dst) \
do {\
const char *p;\
for ( p = yy_cp-1; p >= (dst); --p)\
if ( *p == '\n' )\
--yylineno;\
}while(0)
/* Return all but the first "n" matched characters back to the input stream. */
#define yyless(n) \
......@@ -438,6 +445,8 @@ int yylineno = 1;
extern char *yytext;
#define yytext_ptr yytext
/* %% [1.5] DFA */
/* %if-c-only Standard (non-C++) definition */
static yy_state_type yy_get_previous_state (void );
......@@ -460,8 +469,8 @@ static void yy_fatal_error (yyconst char msg[] );
(yy_c_buf_p) = yy_cp;
/* %% [4.0] data tables for the DFA and the user's section 1 definitions go here */
#define YY_NUM_RULES 11
#define YY_END_OF_BUFFER 12
#define YY_NUM_RULES 16
#define YY_END_OF_BUFFER 17
/* This struct is not used in this scanner,
but its presence is necessary. */
struct yy_trans_info
......@@ -469,12 +478,13 @@ struct yy_trans_info
flex_int32_t yy_verify;
flex_int32_t yy_nxt;
};
static yyconst flex_int16_t yy_accept[36] =
static yyconst flex_int16_t yy_accept[44] =
{ 0,
0, 0, 12, 10, 1, 2, 10, 7, 8, 9,
10, 10, 10, 1, 2, 3, 0, 5, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 4, 6, 0
0, 0, 17, 15, 1, 2, 15, 10, 11, 14,
15, 5, 5, 15, 12, 13, 15, 15, 15, 1,
2, 0, 3, 5, 0, 6, 0, 0, 0, 4,
9, 0, 0, 0, 0, 0, 0, 7, 0, 0,
0, 8, 0
} ;
static yyconst flex_int32_t yy_ec[256] =
......@@ -483,16 +493,16 @@ static yyconst flex_int32_t yy_ec[256] =
1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 2, 1, 1, 1, 1, 1, 1, 4, 5,
6, 1, 1, 7, 1, 1, 1, 8, 8, 8,
8, 8, 8, 8, 8, 8, 8, 1, 1, 1,
9, 1, 1, 1, 10, 10, 10, 10, 10, 10,
10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
11, 1, 12, 1, 10, 1, 10, 13, 10, 10,
10, 10, 14, 10, 15, 10, 10, 10, 10, 16,
17, 18, 10, 19, 20, 21, 22, 10, 10, 10,
10, 10, 1, 1, 1, 1, 1, 1, 1, 1,
6, 1, 1, 7, 8, 1, 1, 9, 10, 10,
10, 10, 10, 10, 10, 10, 10, 1, 1, 1,
11, 1, 1, 1, 12, 12, 12, 12, 12, 12,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 13, 1, 1,
14, 1, 15, 1, 1, 1, 16, 17, 12, 12,
12, 12, 18, 1, 19, 1, 1, 20, 1, 21,
22, 23, 1, 24, 25, 26, 27, 1, 1, 13,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
......@@ -509,57 +519,61 @@ static yyconst flex_int32_t yy_ec[256] =
1, 1, 1, 1, 1
} ;
static yyconst flex_int32_t yy_meta[23] =
static yyconst flex_int32_t yy_meta[28] =
{ 0,
1, 1, 1, 2, 1, 1, 1, 2, 1, 2,
1, 1, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2
1, 1, 2, 1, 1, 1, 1, 1, 3, 3,
1, 3, 1, 1, 1, 3, 3, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1
} ;
static yyconst flex_int16_t yy_base[37] =
static yyconst flex_int16_t yy_base[46] =
{ 0,
0, 0, 48, 49, 45, 43, 41, 49, 49, 49,
35, 25, 20, 39, 37, 49, 35, 49, 17, 24,
21, 15, 17, 12, 16, 12, 19, 14, 20, 10,
15, 11, 49, 49, 49, 22
0, 0, 62, 63, 59, 57, 55, 63, 63, 63,
19, 21, 23, 47, 63, 63, 37, 33, 28, 52,
50, 48, 63, 26, 0, 63, 31, 24, 32, 0,
63, 29, 22, 24, 19, 23, 19, 63, 23, 20,
22, 63, 63, 36, 35
} ;
static yyconst flex_int16_t yy_def[37] =
static yyconst flex_int16_t yy_def[46] =
{ 0,
35, 1, 35, 35, 35, 35, 36, 35, 35, 35,
35, 35, 35, 35, 35, 35, 36, 35, 35, 35,
35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
35, 35, 35, 35, 0, 35
43, 1, 43, 43, 43, 43, 44, 43, 43, 43,
43, 43, 43, 43, 43, 43, 43, 43, 43, 43,
43, 44, 43, 43, 45, 43, 43, 43, 43, 45,
43, 43, 43, 43, 43, 43, 43, 43, 43, 43,
43, 43, 0, 43, 43
} ;
static yyconst flex_int16_t yy_nxt[72] =
static yyconst flex_int16_t yy_nxt[91] =
{ 0,
4, 5, 6, 7, 8, 9, 10, 4, 11, 4,
4, 4, 4, 4, 4, 4, 12, 4, 4, 13,
4, 4, 31, 17, 34, 32, 33, 31, 30, 29,
28, 27, 26, 25, 24, 23, 22, 21, 16, 15,
14, 20, 19, 18, 16, 15, 14, 35, 3, 35,
35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
35
4, 5, 6, 7, 8, 9, 10, 11, 12, 13,
14, 4, 4, 15, 16, 17, 4, 4, 4, 4,
4, 18, 4, 4, 19, 4, 4, 24, 24, 24,
24, 24, 24, 25, 24, 24, 22, 30, 22, 42,
41, 40, 39, 38, 37, 36, 35, 34, 33, 32,
31, 23, 21, 20, 29, 28, 27, 26, 23, 21,
20, 43, 3, 43, 43, 43, 43, 43, 43, 43,
43, 43, 43, 43, 43, 43, 43, 43, 43, 43,
43, 43, 43, 43, 43, 43, 43, 43, 43, 43
} ;
static yyconst flex_int16_t yy_chk[72] =
static yyconst flex_int16_t yy_chk[91] =
{ 0,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 31, 36, 32, 30, 31, 29, 28, 27,
26, 25, 24, 23, 22, 21, 20, 19, 17, 15,
14, 13, 12, 11, 7, 6, 5, 3, 35, 35,
35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
35
1, 1, 1, 1, 1, 1, 1, 11, 11, 12,
12, 13, 13, 12, 24, 24, 44, 45, 44, 41,
40, 39, 37, 36, 35, 34, 33, 32, 29, 28,
27, 22, 21, 20, 19, 18, 17, 14, 7, 6,
5, 3, 43, 43, 43, 43, 43, 43, 43, 43,
43, 43, 43, 43, 43, 43, 43, 43, 43, 43,
43, 43, 43, 43, 43, 43, 43, 43, 43, 43
} ;
/* Table of booleans, true if rule could match eol. */
static yyconst flex_int32_t yy_rule_can_match_eol[12] =
static yyconst flex_int32_t yy_rule_can_match_eol[17] =
{ 0,
0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, };
0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, };
static yy_state_type yy_last_accepting_state;
static char *yy_last_accepting_cpos;
......@@ -567,9 +581,10 @@ static char *yy_last_accepting_cpos;
extern int yy_flex_debug;
int yy_flex_debug = 1;
static yyconst flex_int16_t yy_rule_linenum[11] =
static yyconst flex_int16_t yy_rule_linenum[16] =
{ 0,
83, 87, 93, 103, 154, 155, 156, 157, 158, 160
83, 87, 93, 103, 109, 123, 124, 125, 126, 127,
128, 129, 130, 131, 133
} ;
/* The intent behind this definition is that it'll catch
......@@ -636,7 +651,7 @@ static isc::eval::location loc;
// by moving it ahead by yyleng bytes. yyleng specifies the length of the
// currently matched token.
#define YY_USER_ACTION loc.columns(yyleng);
#line 640 "lexer.cc"
#line 655 "lexer.cc"
#define INITIAL 0
......@@ -875,17 +890,6 @@ YY_DECL
register char *yy_cp, *yy_bp;
register int yy_act;
/* %% [7.0] user's declarations go here */
#line 76 "lexer.ll"
// Code run each time yylex is called.
loc.step();
#line 888 "lexer.cc"
if ( !(yy_init) )
{
(yy_init) = 1;
......@@ -920,6 +924,18 @@ YY_DECL
yy_load_buffer_state( );
}
{
/* %% [7.0] user's declarations go here */
#line 76 "lexer.ll"
// Code run each time yylex is called.
loc.step();
#line 938 "lexer.cc"
while ( 1 ) /* loops until end-of-file is reached */
{
/* %% [8.0] yymore()-related code goes here */
......@@ -938,7 +954,7 @@ YY_DECL
yy_match:
do
{
register YY_CHAR yy_c = yy_ec[YY_SC_TO_UI(*yy_cp)];
register YY_CHAR yy_c = yy_ec[YY_SC_TO_UI(*yy_cp)] ;
if ( yy_accept[yy_current_state] )
{
(yy_last_accepting_state) = yy_current_state;
......@@ -947,13 +963,13 @@ yy_match:
while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state )
{
yy_current_state = (int) yy_def[yy_current_state];
if ( yy_current_state >= 36 )
if ( yy_current_state >= 44 )
yy_c = yy_meta[(unsigned int) yy_c];
}
yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c];
++yy_cp;
}
while ( yy_current_state != 35 );
while ( yy_current_state != 43 );
yy_cp = (yy_last_accepting_cpos);
yy_current_state = (yy_last_accepting_state);
......@@ -967,7 +983,7 @@ yy_find_action:
if ( yy_act != YY_END_OF_BUFFER && yy_rule_can_match_eol[yy_act] )
{
int yyl;
yy_size_t yyl;
for ( yyl = 0; yyl < yyleng; ++yyl )
if ( yytext[yyl] == '\n' )
......@@ -982,13 +998,13 @@ do_action: /* This label is used only to access EOF actions. */
{
if ( yy_act == 0 )
fprintf( stderr, "--scanner backing up\n" );
else if ( yy_act < 11 )
else if ( yy_act < 16 )
fprintf( stderr, "--accepting rule at line %ld (\"%s\")\n",
(long)yy_rule_linenum[yy_act], yytext );
else if ( yy_act == 11 )
else if ( yy_act == 16 )
fprintf( stderr, "--accepting default rule (\"%s\")\n",
yytext );
else if ( yy_act == 12 )
else if ( yy_act == 17 )
fprintf( stderr, "--(end of buffer or a NUL)\n" );
else
fprintf( stderr, "--EOF (start condition %d)\n", YY_START );
......@@ -1039,96 +1055,88 @@ case 4:
YY_RULE_SETUP
#line 103 "lexer.ll"
{
// option[123] token found. Let's see if the numeric value can be
// converted to integer and if it has a reasonable value.
// yytext contains the whole expression (.e.g. option[123]). We need
// to trim it down to just the code, which will be transformed to
// integer.
// A hex string has been matched. It contains the '0x' or '0X' header
// followed by at least one hexadecimal digit.
return isc::eval::EvalParser::make_HEXSTRING(yytext, loc);
}
YY_BREAK
case 5:
YY_RULE_SETUP
#line 109 "lexer.ll"
{
// An integer was found.
std::string tmp(yytext);
// Sanity check if the token is at least 9 (strlen("option[X]")) long.
// This should never happen as it would indicate bison bug.
if (tmp.length() < 9) {
driver.error(loc, "The string matched (" + tmp + ") is too short,"
" expected at least 9 (option[X]) characters");
}
size_t pos = tmp.find("[");
if (pos == std::string::npos) {
driver.error(loc, "The string matched (" + tmp + ") is invalid,"
" as it does not contain opening bracket.");
}
// Let's get rid of all the text before [, including [.
tmp = tmp.substr(pos + 1);
// And finally remove the trailing ].
pos = tmp.find("]");
if (pos == std::string::npos) {
driver.error(loc, "The string matched (" + tmp + ") is invalid,"
" as it does not contain closing bracket.");
}
tmp = tmp.substr(0, pos);
uint16_t n = 0;
try {
n = boost::lexical_cast<int>(tmp);
static_cast<void>(boost::lexical_cast<int>(tmp));
} catch (const boost::bad_lexical_cast &) {
driver.error(loc, "Failed to convert specified option code to "
"number ('" + tmp + "' in expression " + std::string(yytext));
}
// 65535 is the maximum value of the option code in DHCPv6. We want the
// code to be the same for v4 and v6, so let's ignore for a moment that
// max. option code in DHCPv4 is 255.
/// @todo: Maybe add a flag somewhere in EvalContext to indicate if we're
/// running in v4 (allowed max 255) or v6 (allowed max 65535).
if (n<0 || n>65535) {
driver.error(loc, "Option code has invalid value in " +
std::string(yytext) + ". Allowed range: 0..65535");
driver.error(loc, "Failed to convert " + tmp + " to an integer.");
}
return isc::eval::EvalParser::make_OPTION(n, loc);
// The parser needs the string form as double conversion is no lossless
return isc::eval::EvalParser::make_INTEGER(tmp, loc);
}
YY_BREAK
case 5: