2
0
mirror of https://gitlab.isc.org/isc-projects/kea synced 2025-08-29 04:57:52 +00:00

[master] Finished merge of trac4231 (new boolean operators)

This commit is contained in:
Francis Dupont 2016-02-19 16:53:05 +01:00
commit 8e01dbe2fe
18 changed files with 1289 additions and 412 deletions

View File

@ -1,3 +1,9 @@
1079. [func] fdupont
Added Not, And and Or logical operators, parentheses around
logical expressions and option[code].exist logical predicate
(to check the presence of an empty option).
(Trac #4231, git xxx)
1078. [func] tomek
Client classification in DHCPv4 has been enhanced. It is now
possible to access relay sub-options using the expression

View File

@ -164,6 +164,7 @@
<row><entry>Option Text</entry><entry>option[code].text</entry><entry>The value of the option with code "code" from the packet as text</entry></row>
-->
<row><entry>Option Hex</entry><entry>option[code].hex</entry><entry>The value of the option with code "code" from the packet as hex</entry></row>
<row><entry>Option Exist</entry><entry>option[code].exist</entry><entry>If the option with code "code" is present in the packet "true" else "false"</entry></row>
<row><entry>DHCPv4 Relay Agent
sub-option</entry><entry>relay[code].hex</entry><entry>The value of
sub-option with code "code" from the Relay Agent Information option
@ -188,6 +189,11 @@ sub-option with code "code" from the Relay Agent Information option
the option payload without the type code or length fields.
</para>
<para>
"option[code].exist" checks if an option with the given code is present
in the incoming packet. It can be used with empty options.
</para>
<para>
"relay[code].hex" attempts to extract the value of the sub-option
"code" from the option inserted as the Relay Agent Information
@ -214,12 +220,24 @@ sub-option with code "code" from the Relay Agent Information option
</thead>
<tbody>
<row><entry>Equal</entry> <entry>'foo' == 'bar'</entry><entry>Compare the two values and return "true" or "false"</entry></row>
<row><entry>Not</entry> <entry>not ('foo' == 'bar')</entry><entry>Logical negation</entry></row>
<row><entry>And</entry> <entry>('foo' == 'bar') and ('bar' == 'foo')</entry><entry>Logical and</entry></row>
<row><entry>Or</entry> <entry>('foo' == 'bar') or ('bar' == 'foo')</entry><entry>Logical or</entry></row>
<row><entry>Substring</entry><entry>substring('foobar',0,3)</entry><entry>Return the requested substring</entry></row>
</tbody>
</tgroup>
</table>
</para>
<section>
<title>Logical operators</title>
The Not, And and Or logical operators are the common operators. Not
has the highest precedence, Or the lowest. And and Or are (left)
associative, parentheses around a logical expression can be used
to enforce a specific grouping, for instance in "A and (B or C)"
(without parentheses "A and B or C" means "(A and B) or C").
</section>
<section>
<title>Substring</title>
The substring operator "substring(value, start, length)" accepts both positive and

View File

@ -1,4 +1,4 @@
// Copyright (C) 2015 Internet Systems Consortium, Inc. ("ISC")
// Copyright (C) 2015-2016 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
@ -19,14 +19,7 @@ bool evaluate(const Expression& expr, const Pkt& pkt) {
isc_throw(EvalBadStack, "Incorrect stack order. Expected exactly "
"1 value at the end of evaluatuion, got " << values.size());
}
if (values.top() == "false") {
return (false);
} else if (values.top() == "true") {
return (true);
} else {
isc_throw(EvalTypeError, "Incorrect evaluation type. Expected "
"\"false\" or \"true\", got \"" << values.top() << "\"");
}
return (Token::toBool(values.top()));
}
}; // end of isc::dhcp namespace

View File

@ -18,7 +18,7 @@
#define FLEX_SCANNER
#define YY_FLEX_MAJOR_VERSION 2
#define YY_FLEX_MINOR_VERSION 5
#define YY_FLEX_SUBMINOR_VERSION 39
#define YY_FLEX_SUBMINOR_VERSION 35
#if YY_FLEX_SUBMINOR_VERSION > 0
#define FLEX_BETA
#endif
@ -72,6 +72,7 @@ typedef int16_t flex_int16_t;
typedef uint16_t flex_uint16_t;
typedef int32_t flex_int32_t;
typedef uint32_t flex_uint32_t;
typedef uint64_t flex_uint64_t;
#else
typedef signed char flex_int8_t;
typedef short int flex_int16_t;
@ -79,6 +80,7 @@ typedef int flex_int32_t;
typedef unsigned char flex_uint8_t;
typedef unsigned short int flex_uint16_t;
typedef unsigned int flex_uint32_t;
#endif /* ! C99 */
/* Limits of integral types. */
#ifndef INT8_MIN
@ -109,8 +111,6 @@ typedef unsigned int flex_uint32_t;
#define UINT32_MAX (4294967295U)
#endif
#endif /* ! C99 */
#endif /* ! FLEXINT_H */
/* %endif */
@ -185,15 +185,7 @@ typedef unsigned int flex_uint32_t;
/* Size of default input buffer. */
#ifndef YY_BUF_SIZE
#ifdef __ia64__
/* On IA-64, the buffer size is 16k, not 8k.
* Moreover, YY_BUF_SIZE is 2*YY_READ_BUF_SIZE in the general case.
* Ditto for the __ia64__ case accordingly.
*/
#define YY_BUF_SIZE 32768
#else
#define YY_BUF_SIZE 16384
#endif /* __ia64__ */
#endif
/* The state buf must be large enough to hold one state per character in the main buffer.
@ -233,18 +225,11 @@ extern FILE *yyin, *yyout;
*/
#define YY_LESS_LINENO(n) \
do { \
int yyl;\
yy_size_t yyl;\
for ( yyl = n; yyl < yyleng; ++yyl )\
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) \
@ -435,7 +420,7 @@ void yyfree (void * );
/* %% [1.0] yytext/yyin/yyout/yy_state_type/yylineno etc. def's & init go here */
/* Begin user sect3 */
#define yywrap() 1
#define yywrap(n) 1
#define YY_SKIP_YYWRAP
#define FLEX_DEBUG
@ -453,8 +438,6 @@ 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 );
@ -470,15 +453,15 @@ static void yy_fatal_error (yyconst char msg[] );
#define YY_DO_BEFORE_ACTION \
(yytext_ptr) = yy_bp; \
/* %% [2.0] code to fiddle yytext and yyleng for yymore() goes here \ */\
yyleng = (size_t) (yy_cp - yy_bp); \
yyleng = (yy_size_t) (yy_cp - yy_bp); \
(yy_hold_char) = *yy_cp; \
*yy_cp = '\0'; \
/* %% [3.0] code to copy yytext_ptr to yytext[] goes here, if %array \ */\
(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 21
#define YY_END_OF_BUFFER 22
#define YY_NUM_RULES 25
#define YY_END_OF_BUFFER 26
/* This struct is not used in this scanner,
but its presence is necessary. */
struct yy_trans_info
@ -486,30 +469,33 @@ struct yy_trans_info
flex_int32_t yy_verify;
flex_int32_t yy_nxt;
};
static yyconst flex_int16_t yy_acclist[99] =
static yyconst flex_int16_t yy_acclist[119] =
{ 0,
22, 20, 21, 1, 20, 21, 2, 21, 20, 21,
15, 20, 21, 16, 20, 21, 19, 20, 21, 20,
21, 14, 20, 21, 5, 20, 21, 5, 20, 21,
20, 21, 20, 21,16390, 17, 20, 21, 18, 20,
21, 20, 21,16390, 20, 21,16390, 20, 21,16390,
20, 21,16390, 20, 21,16390, 20, 21,16390, 1,
2, 3, 5, 7,16390, 8198,16390,16390,16390,16390,
16390,16390, 4, 13,16390, 10,16390,16390,16390,16390,
16390,16390,16390,16390, 9,16390,16390,16390,16390, 8,
16390, 12,16390,16390,16390,16390, 11,16390
26, 24, 25, 1, 24, 25, 2, 25, 24, 25,
19, 24, 25, 20, 24, 25, 23, 24, 25, 24,
25, 18, 24, 25, 5, 24, 25, 5, 24, 25,
24, 25, 24, 25,16390, 21, 24, 25, 22, 24,
25, 24, 25,16390, 24, 25,16390, 24, 25,16390,
24, 25,16390, 24, 25,16390, 24, 25,16390, 24,
25,16390, 24, 25,16390, 1, 2, 3, 5, 7,
16390, 8198,16390,16390,16390,16390,16390,16390, 17,16390,
16390,16390,16390, 4, 14,16390, 16,16390,16390, 10,
16390, 15,16390,16390,16390,16390,16390,16390,16390,16390,
16390, 9,16390,16390,16390,16390,16390, 11,16390, 8,
16390, 13,16390,16390,16390,16390, 12,16390
} ;
static yyconst flex_int16_t yy_accept[64] =
static yyconst flex_int16_t yy_accept[76] =
{ 0,
1, 1, 1, 2, 4, 7, 9, 11, 14, 17,
20, 22, 25, 28, 31, 33, 36, 39, 42, 45,
48, 51, 54, 57, 60, 61, 62, 62, 63, 64,
64, 65, 65, 65, 66, 67, 68, 69, 70, 71,
72, 73, 74, 76, 78, 79, 80, 81, 82, 83,
84, 85, 87, 88, 89, 90, 92, 94, 95, 96,
97, 99, 99
48, 51, 54, 57, 60, 63, 66, 67, 68, 68,
69, 70, 70, 71, 71, 71, 72, 73, 74, 75,
76, 77, 78, 79, 81, 82, 83, 84, 85, 87,
89, 90, 92, 94, 95, 96, 97, 98, 99, 100,
101, 102, 104, 105, 106, 107, 108, 110, 112, 114,
115, 116, 117, 119, 119
} ;
static yyconst flex_int32_t yy_ec[256] =
@ -523,11 +509,11 @@ static yyconst flex_int32_t yy_ec[256] =
13, 1, 1, 1, 14, 14, 14, 14, 14, 14,
15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
15, 15, 15, 15, 15, 15, 15, 16, 15, 15,
17, 1, 18, 1, 19, 1, 20, 21, 14, 14,
17, 1, 18, 1, 19, 1, 20, 21, 14, 22,
22, 14, 23, 24, 25, 15, 15, 26, 15, 27,
28, 29, 15, 30, 31, 32, 33, 15, 15, 34,
35, 15, 1, 1, 1, 1, 1, 1, 1, 1,
23, 14, 24, 25, 26, 15, 15, 27, 15, 28,
29, 30, 15, 31, 32, 33, 34, 15, 15, 35,
36, 15, 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,
@ -544,95 +530,114 @@ static yyconst flex_int32_t yy_ec[256] =
1, 1, 1, 1, 1
} ;
static yyconst flex_int32_t yy_meta[36] =
static yyconst flex_int32_t yy_meta[37] =
{ 0,
1, 2, 3, 1, 1, 1, 1, 2, 1, 4,
4, 4, 1, 4, 2, 2, 1, 2, 2, 4,
4, 4, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2
4, 4, 4, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2
} ;
static yyconst flex_int16_t yy_base[66] =
static yyconst flex_int16_t yy_base[78] =
{ 0,
0, 0, 135, 136, 132, 130, 128, 136, 136, 136,
26, 136, 29, 32, 118, 46, 136, 136, 64, 24,
26, 25, 27, 35, 128, 126, 124, 136, 58, 0,
136, 56, 73, 101, 136, 100, 45, 30, 99, 52,
51, 0, 98, 97, 55, 66, 58, 61, 68, 62,
69, 96, 76, 86, 74, 95, 90, 80, 81, 87,
85, 136, 113, 116, 105
0, 0, 113, 205, 108, 101, 98, 205, 205, 205,
27, 205, 30, 33, 87, 45, 205, 205, 64, 22,
28, 31, 43, 52, 34, 58, 82, 66, 50, 205,
66, 0, 205, 85, 87, 66, 205, 68, 79, 71,
81, 84, 91, 93, 95, 104, 99, 0, 101, 108,
110, 112, 116, 119, 121, 123, 125, 129, 132, 136,
138, 140, 142, 148, 161, 150, 152, 155, 157, 164,
159, 169, 167, 205, 197, 200, 48
} ;
static yyconst flex_int16_t yy_def[66] =
static yyconst flex_int16_t yy_def[78] =
{ 0,
62, 1, 62, 62, 62, 62, 63, 62, 62, 62,
62, 62, 62, 62, 62, 64, 62, 62, 64, 19,
19, 19, 19, 19, 62, 62, 63, 62, 62, 65,
62, 62, 19, 19, 62, 19, 19, 19, 19, 19,
19, 65, 19, 19, 19, 19, 19, 19, 19, 19,
74, 1, 74, 74, 74, 74, 75, 74, 74, 74,
74, 74, 74, 74, 74, 76, 74, 74, 76, 19,
19, 19, 19, 19, 19, 19, 74, 74, 75, 74,
74, 77, 74, 74, 19, 19, 74, 19, 19, 19,
19, 19, 19, 19, 19, 19, 19, 77, 19, 19,
19, 19, 19, 19, 19, 19, 19, 19, 19, 19,
19, 0, 62, 62, 62
19, 19, 19, 19, 19, 19, 19, 19, 19, 19,
19, 19, 19, 0, 74, 74, 74
} ;
static yyconst flex_int16_t yy_nxt[172] =
static yyconst flex_int16_t yy_nxt[242] =
{ 0,
4, 5, 6, 7, 8, 9, 10, 11, 12, 13,
14, 14, 15, 16, 16, 16, 17, 18, 4, 19,
16, 16, 16, 20, 16, 16, 16, 21, 16, 22,
23, 24, 16, 16, 16, 29, 29, 29, 29, 29,
29, 29, 29, 29, 30, 37, 39, 32, 32, 34,
34, 34, 34, 33, 38, 34, 41, 32, 32, 40,
34, 45, 30, 35, 33, 32, 32, 29, 29, 29,
34, 33, 47, 35, 62, 62, 34, 34, 44, 49,
34, 35, 33, 34, 48, 50, 34, 34, 51, 36,
62, 34, 52, 34, 34, 53, 54, 57, 34, 34,
16, 16, 20, 16, 21, 16, 16, 22, 23, 16,
24, 25, 26, 16, 16, 16, 31, 31, 31, 31,
31, 31, 31, 31, 31, 32, 34, 34, 36, 36,
41, 48, 35, 30, 36, 36, 40, 36, 36, 42,
36, 36, 37, 35, 32, 34, 34, 46, 28, 36,
36, 35, 43, 44, 45, 31, 31, 31, 36, 36,
47, 37, 35, 27, 36, 36, 34, 34, 74, 74,
38, 39, 36, 36, 49, 36, 51, 36, 36, 33,
55, 34, 56, 58, 59, 34, 34, 60, 42, 61,
34, 34, 34, 27, 27, 34, 27, 34, 34, 34,
34, 34, 34, 34, 46, 43, 34, 28, 26, 25,
31, 28, 26, 25, 62, 3, 62, 62, 62, 62,
62, 62, 62, 62, 62, 62, 62, 62, 62, 62,
62, 62, 62, 62, 62, 62, 62, 62, 62, 62,
62, 62, 62, 62, 62, 62, 62, 62, 62, 62,
62
50, 30, 37, 28, 74, 36, 36, 36, 36, 27,
36, 36, 74, 36, 36, 52, 53, 36, 36, 36,
36, 55, 36, 54, 56, 36, 36, 36, 36, 74,
36, 36, 74, 57, 36, 36, 36, 36, 36, 36,
60, 58, 36, 36, 59, 36, 36, 36, 36, 36,
36, 36, 36, 74, 61, 36, 36, 62, 36, 36,
64, 63, 36, 36, 36, 36, 36, 36, 36, 36,
66, 65, 69, 67, 36, 68, 36, 36, 36, 36,
70, 36, 36, 36, 36, 36, 72, 36, 36, 71,
36, 36, 73, 36, 36, 36, 36, 29, 29, 74,
29, 36, 36, 36, 3, 74, 74, 74, 74, 74,
74, 74, 74, 74, 74, 74, 74, 74, 74, 74,
74, 74, 74, 74, 74, 74, 74, 74, 74, 74,
74, 74, 74, 74, 74, 74, 74, 74, 74, 74,
74
} ;
static yyconst flex_int16_t yy_chk[172] =
static yyconst flex_int16_t yy_chk[242] =
{ 0,
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, 11, 11, 11, 13, 13,
13, 14, 14, 14, 13, 20, 22, 16, 16, 20,
22, 21, 23, 16, 21, 38, 24, 32, 32, 23,
24, 38, 13, 16, 16, 19, 19, 29, 29, 29,
37, 19, 40, 32, 33, 33, 41, 40, 37, 45,
45, 19, 19, 47, 41, 46, 48, 50, 47, 19,
33, 46, 48, 49, 51, 49, 50, 54, 33, 55,
1, 1, 1, 1, 1, 1, 11, 11, 11, 13,
13, 13, 14, 14, 14, 13, 16, 16, 20, 20,
21, 77, 16, 29, 21, 21, 20, 22, 22, 22,
25, 25, 16, 16, 13, 19, 19, 25, 28, 23,
23, 19, 23, 23, 24, 31, 31, 31, 24, 24,
26, 19, 19, 27, 26, 26, 34, 34, 35, 35,
19, 19, 36, 36, 38, 38, 40, 40, 40, 15,
51, 53, 53, 55, 58, 58, 59, 59, 65, 60,
61, 54, 60, 63, 63, 57, 63, 64, 64, 64,
56, 52, 44, 43, 39, 36, 34, 27, 26, 25,
15, 7, 6, 5, 3, 62, 62, 62, 62, 62,
62, 62, 62, 62, 62, 62, 62, 62, 62, 62,
62, 62, 62, 62, 62, 62, 62, 62, 62, 62,
62, 62, 62, 62, 62, 62, 62, 62, 62, 62,
62
39, 7, 34, 6, 35, 39, 39, 41, 41, 5,
42, 42, 3, 35, 35, 41, 42, 43, 43, 44,
44, 45, 45, 43, 46, 47, 47, 49, 49, 0,
46, 46, 0, 47, 50, 50, 51, 51, 52, 52,
55, 51, 53, 53, 54, 54, 54, 55, 55, 56,
56, 57, 57, 0, 56, 58, 58, 57, 59, 59,
59, 58, 60, 60, 61, 61, 62, 62, 63, 63,
61, 60, 65, 63, 64, 64, 66, 66, 67, 67,
66, 68, 68, 69, 69, 71, 71, 65, 65, 70,
70, 70, 72, 73, 73, 72, 72, 75, 75, 0,
75, 76, 76, 76, 74, 74, 74, 74, 74, 74,
74, 74, 74, 74, 74, 74, 74, 74, 74, 74,
74, 74, 74, 74, 74, 74, 74, 74, 74, 74,
74, 74, 74, 74, 74, 74, 74, 74, 74, 74,
74
} ;
/* Table of booleans, true if rule could match eol. */
static yyconst flex_int32_t yy_rule_can_match_eol[22] =
static yyconst flex_int32_t yy_rule_can_match_eol[26] =
{ 0,
0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, };
0, 0, 0, 0, 0, 0, };
extern int yy_flex_debug;
int yy_flex_debug = 1;
static yyconst flex_int16_t yy_rule_linenum[21] =
static yyconst flex_int16_t yy_rule_linenum[25] =
{ 0,
78, 82, 88, 98, 104, 118, 125, 126, 127, 128,
129, 130, 131, 132, 133, 134, 135, 136, 137, 139
129, 130, 131, 132, 133, 134, 135, 136, 137, 138,
139, 140, 141, 143
} ;
static yy_state_type *yy_state_buf=0, *yy_state_ptr=0;
@ -709,7 +714,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 713 "lexer.cc"
#line 718 "lexer.cc"
#define INITIAL 0
@ -818,12 +823,7 @@ static int input (void );
/* Amount of stuff to slurp up with each read. */
#ifndef YY_READ_BUF_SIZE
#ifdef __ia64__
/* On IA-64, the buffer size is 16k, not 8k */
#define YY_READ_BUF_SIZE 16384
#else
#define YY_READ_BUF_SIZE 8192
#endif /* __ia64__ */
#endif
/* Copy whatever the last rule matched to the standard output. */
@ -832,7 +832,7 @@ static int input (void );
/* This used to be an fputs(), but since the string might contain NUL's,
* we now use fwrite().
*/
#define ECHO do { if (fwrite( yytext, yyleng, 1, yyout )) {} } while (0)
#define ECHO fwrite( yytext, yyleng, 1, yyout )
/* %endif */
/* %if-c++-only C++ definition */
/* %endif */
@ -847,7 +847,7 @@ static int input (void );
if ( YY_CURRENT_BUFFER_LVALUE->yy_is_interactive ) \
{ \
int c = '*'; \
size_t n; \
yy_size_t n; \
for ( n = 0; n < max_size && \
(c = getc( yyin )) != EOF && c != '\n'; ++n ) \
buf[n] = (char) c; \
@ -953,6 +953,17 @@ YY_DECL
register char *yy_cp, *yy_bp;
register int yy_act;
/* %% [7.0] user's declarations go here */
#line 71 "lexer.ll"
// Code run each time yylex is called.
loc.step();
#line 966 "lexer.cc"
if ( !(yy_init) )
{
(yy_init) = 1;
@ -993,18 +1004,6 @@ YY_DECL
yy_load_buffer_state( );
}
{
/* %% [7.0] user's declarations go here */
#line 71 "lexer.ll"
// Code run each time yylex is called.
loc.step();
#line 1007 "lexer.cc"
while ( 1 ) /* loops until end-of-file is reached */
{
/* %% [8.0] yymore()-related code goes here */
@ -1031,19 +1030,20 @@ 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 >= 63 )
if ( yy_current_state >= 75 )
yy_c = yy_meta[(unsigned int) yy_c];
}
yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c];
*(yy_state_ptr)++ = yy_current_state;
++yy_cp;
}
while ( yy_current_state != 62 );
while ( yy_current_state != 74 );
yy_find_action:
/* %% [10.0] code to find the action number goes here */
yy_current_state = *--(yy_state_ptr);
(yy_lp) = yy_accept[yy_current_state];
goto find_rule; /* Shut up GCC warning -Wall */
find_rule: /* we branch to this label when backing up */
for ( ; ; ) /* until we find what rule we matched */
{
@ -1101,13 +1101,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 < 21 )
else if ( yy_act < 25 )
fprintf( stderr, "--accepting rule at line %ld (\"%s\")\n",
(long)yy_rule_linenum[yy_act], yytext );
else if ( yy_act == 21 )
else if ( yy_act == 25 )
fprintf( stderr, "--accepting default rule (\"%s\")\n",
yytext );
else if ( yy_act == 22 )
else if ( yy_act == 26 )
fprintf( stderr, "--(end of buffer or a NUL)\n" );
else
fprintf( stderr, "--EOF (start condition %d)\n", YY_START );
@ -1207,63 +1207,83 @@ return isc::eval::EvalParser::make_HEX(loc);
case 11:
YY_RULE_SETUP
#line 129 "lexer.ll"
return isc::eval::EvalParser::make_SUBSTRING(loc);
return isc::eval::EvalParser::make_EXISTS(loc);
YY_BREAK
case 12:
YY_RULE_SETUP
#line 130 "lexer.ll"
return isc::eval::EvalParser::make_RELAY4(loc);
return isc::eval::EvalParser::make_SUBSTRING(loc);
YY_BREAK
case 13:
YY_RULE_SETUP
#line 131 "lexer.ll"
return isc::eval::EvalParser::make_ALL(loc);
return isc::eval::EvalParser::make_RELAY4(loc);
YY_BREAK
case 14:
YY_RULE_SETUP
#line 132 "lexer.ll"
return isc::eval::EvalParser::make_DOT(loc);
return isc::eval::EvalParser::make_ALL(loc);
YY_BREAK
case 15:
YY_RULE_SETUP
#line 133 "lexer.ll"
return isc::eval::EvalParser::make_LPAREN(loc);
return isc::eval::EvalParser::make_NOT(loc);
YY_BREAK
case 16:
YY_RULE_SETUP
#line 134 "lexer.ll"
return isc::eval::EvalParser::make_RPAREN(loc);
return isc::eval::EvalParser::make_AND(loc);
YY_BREAK
case 17:
YY_RULE_SETUP
#line 135 "lexer.ll"
return isc::eval::EvalParser::make_LBRACKET(loc);
return isc::eval::EvalParser::make_OR(loc);
YY_BREAK
case 18:
YY_RULE_SETUP
#line 136 "lexer.ll"
return isc::eval::EvalParser::make_RBRACKET(loc);
return isc::eval::EvalParser::make_DOT(loc);
YY_BREAK
case 19:
YY_RULE_SETUP
#line 137 "lexer.ll"
return isc::eval::EvalParser::make_COMA(loc);
return isc::eval::EvalParser::make_LPAREN(loc);
YY_BREAK
case 20:
YY_RULE_SETUP
#line 139 "lexer.ll"
driver.error (loc, "Invalid character: " + std::string(yytext));
YY_BREAK
case YY_STATE_EOF(INITIAL):
#line 140 "lexer.ll"
return isc::eval::EvalParser::make_END(loc);
#line 138 "lexer.ll"
return isc::eval::EvalParser::make_RPAREN(loc);
YY_BREAK
case 21:
YY_RULE_SETUP
#line 139 "lexer.ll"
return isc::eval::EvalParser::make_LBRACKET(loc);
YY_BREAK
case 22:
YY_RULE_SETUP
#line 140 "lexer.ll"
return isc::eval::EvalParser::make_RBRACKET(loc);
YY_BREAK
case 23:
YY_RULE_SETUP
#line 141 "lexer.ll"
return isc::eval::EvalParser::make_COMA(loc);
YY_BREAK
case 24:
YY_RULE_SETUP
#line 143 "lexer.ll"
driver.error (loc, "Invalid character: " + std::string(yytext));
YY_BREAK
case YY_STATE_EOF(INITIAL):
#line 144 "lexer.ll"
return isc::eval::EvalParser::make_END(loc);
YY_BREAK
case 25:
YY_RULE_SETUP
#line 145 "lexer.ll"
ECHO;
YY_BREAK
#line 1267 "lexer.cc"
#line 1287 "lexer.cc"
case YY_END_OF_BUFFER:
{
@ -1393,7 +1413,6 @@ ECHO;
"fatal flex scanner internal error--no action found" );
} /* end of action switch */
} /* end of scanning one token */
} /* end of user's declarations */
} /* end of yylex */
/* %ok-for-header */
@ -1544,7 +1563,7 @@ static int yy_get_next_buffer (void)
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 >= 63 )
if ( yy_current_state >= 75 )
yy_c = yy_meta[(unsigned int) yy_c];
}
yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c];
@ -1572,11 +1591,11 @@ static int yy_get_next_buffer (void)
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 >= 63 )
if ( yy_current_state >= 75 )
yy_c = yy_meta[(unsigned int) yy_c];
}
yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c];
yy_is_jam = (yy_current_state == 62);
yy_is_jam = (yy_current_state == 74);
if ( ! yy_is_jam )
*(yy_state_ptr)++ = yy_current_state;
@ -1639,7 +1658,7 @@ static int yy_get_next_buffer (void)
case EOB_ACT_END_OF_FILE:
{
if ( yywrap( ) )
return EOF;
return 0;
if ( ! (yy_did_buffer_switch_on_eof) )
YY_NEW_FILE;
@ -1803,6 +1822,17 @@ static void yy_load_buffer_state (void)
yyfree((void *) b );
}
/* %if-c-only */
#ifndef __cplusplus
extern int isatty (int );
#endif /* __cplusplus */
/* %endif */
/* %if-c++-only */
/* %endif */
/* Initializes or reinitializes a buffer.
* This function is sometimes called more than once on the same buffer,
* such as during a yyrestart() or at EOF.
@ -2043,8 +2073,8 @@ YY_BUFFER_STATE yy_scan_string (yyconst char * yystr )
/* %if-c-only */
/** Setup the input buffer state to scan the given bytes. The next call to yylex() will
* scan from a @e copy of @a bytes.
* @param yybytes the byte buffer to scan
* @param _yybytes_len the number of bytes in the buffer pointed to by @a bytes.
* @param bytes the byte buffer to scan
* @param len the number of bytes in the buffer pointed to by @a bytes.
*
* @return the newly allocated buffer state object.
*/
@ -2052,8 +2082,7 @@ YY_BUFFER_STATE yy_scan_bytes (yyconst char * yybytes, yy_size_t _yybytes_len
{
YY_BUFFER_STATE b;
char *buf;
yy_size_t n;
yy_size_t i;
yy_size_t n, i;
/* Get memory for full buffer, including space for trailing EOB's. */
n = _yybytes_len + 2;
@ -2324,7 +2353,7 @@ void yyfree (void * ptr )
/* %ok-for-header */
#line 140 "lexer.ll"
#line 145 "lexer.ll"

View File

@ -126,9 +126,13 @@ blank [ \t]
"option" return isc::eval::EvalParser::make_OPTION(loc);
"text" return isc::eval::EvalParser::make_TEXT(loc);
"hex" return isc::eval::EvalParser::make_HEX(loc);
"exists" return isc::eval::EvalParser::make_EXISTS(loc);
"substring" return isc::eval::EvalParser::make_SUBSTRING(loc);
"relay4" return isc::eval::EvalParser::make_RELAY4(loc);
"all" return isc::eval::EvalParser::make_ALL(loc);
"not" return isc::eval::EvalParser::make_NOT(loc);
"and" return isc::eval::EvalParser::make_AND(loc);
"or" return isc::eval::EvalParser::make_OR(loc);
"." return isc::eval::EvalParser::make_DOT(loc);
"(" return isc::eval::EvalParser::make_LPAREN(loc);
")" return isc::eval::EvalParser::make_RPAREN(loc);

View File

@ -1,4 +1,5 @@
// A Bison parser, made by GNU Bison 3.0.2.
// Generated 20160219
// A Bison parser, made by GNU Bison 3.0.4.
// Locations for Bison parsers in C++

View File

@ -1,8 +1,8 @@
// A Bison parser, made by GNU Bison 3.0.2.
// A Bison parser, made by GNU Bison 3.0.4.
// Skeleton implementation for Bison LALR(1) parsers in C++
// Copyright (C) 2002-2013 Free Software Foundation, Inc.
// Copyright (C) 2002-2015 Free Software Foundation, Inc.
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
@ -33,7 +33,7 @@
// First part of user declarations.
#line 37 "parser.cc" // lalr1.cc:399
#line 37 "parser.cc" // lalr1.cc:404
# ifndef YY_NULLPTR
# if defined __cplusplus && 201103L <= __cplusplus
@ -47,13 +47,13 @@
// User implementation prologue.
#line 51 "parser.cc" // lalr1.cc:407
#line 51 "parser.cc" // lalr1.cc:412
// Unqualified %code blocks.
#line 32 "parser.yy" // lalr1.cc:408
#line 32 "parser.yy" // lalr1.cc:413
# include "eval_context.h"
#line 57 "parser.cc" // lalr1.cc:408
#line 57 "parser.cc" // lalr1.cc:413
#ifndef YY_
@ -130,16 +130,16 @@
#endif // !YYDEBUG
#define yyerrok (yyerrstatus_ = 0)
#define yyclearin (yyempty = true)
#define yyclearin (yyla.clear ())
#define YYACCEPT goto yyacceptlab
#define YYABORT goto yyabortlab
#define YYERROR goto yyerrorlab
#define YYRECOVERING() (!!yyerrstatus_)
#line 13 "parser.yy" // lalr1.cc:474
#line 13 "parser.yy" // lalr1.cc:479
namespace isc { namespace eval {
#line 143 "parser.cc" // lalr1.cc:474
#line 143 "parser.cc" // lalr1.cc:479
/* Return YYSTR after stripping away unnecessary quotes and
backslashes, so that it's suitable for yyerror. The heuristic is
@ -202,7 +202,7 @@ namespace isc { namespace eval {
// by_state.
inline
EvalParser::by_state::by_state ()
: state (empty)
: state (empty_state)
{}
inline
@ -210,12 +210,19 @@ namespace isc { namespace eval {
: state (other.state)
{}
inline
void
EvalParser::by_state::clear ()
{
state = empty_state;
}
inline
void
EvalParser::by_state::move (by_state& that)
{
state = that.state;
that.state = empty;
that.clear ();
}
inline
@ -227,7 +234,10 @@ namespace isc { namespace eval {
EvalParser::symbol_number_type
EvalParser::by_state::type_get () const
{
return state == empty ? 0 : yystos_[state];
if (state == empty_state)
return empty_symbol;
else
return yystos_[state];
}
inline
@ -241,19 +251,19 @@ namespace isc { namespace eval {
{
switch (that.type_get ())
{
case 26: // option_repr_type
case 30: // option_repr_type
value.move< TokenOption::RepresentationType > (that.value);
break;
case 16: // "constant string"
case 17: // "integer"
case 18: // "constant hexstring"
case 19: // "option name"
case 20: // TOKEN
case 20: // "constant string"
case 21: // "integer"
case 22: // "constant hexstring"
case 23: // "option name"
case 24: // TOKEN
value.move< std::string > (that.value);
break;
case 25: // option_code
case 29: // option_code
value.move< uint16_t > (that.value);
break;
@ -262,7 +272,7 @@ namespace isc { namespace eval {
}
// that is emptied.
that.type = empty;
that.type = empty_symbol;
}
inline
@ -272,19 +282,19 @@ namespace isc { namespace eval {
state = that.state;
switch (that.type_get ())
{
case 26: // option_repr_type
case 30: // option_repr_type
value.copy< TokenOption::RepresentationType > (that.value);
break;
case 16: // "constant string"
case 17: // "integer"
case 18: // "constant hexstring"
case 19: // "option name"
case 20: // TOKEN
case 20: // "constant string"
case 21: // "integer"
case 22: // "constant hexstring"
case 23: // "option name"
case 24: // TOKEN
value.copy< std::string > (that.value);
break;
case 25: // option_code
case 29: // option_code
value.copy< uint16_t > (that.value);
break;
@ -315,58 +325,62 @@ namespace isc { namespace eval {
std::ostream& yyoutput = yyo;
YYUSE (yyoutput);
symbol_number_type yytype = yysym.type_get ();
// Avoid a (spurious) G++ 4.8 warning about "array subscript is
// below array bounds".
if (yysym.empty ())
std::abort ();
yyo << (yytype < yyntokens_ ? "token" : "nterm")
<< ' ' << yytname_[yytype] << " ("
<< yysym.location << ": ";
switch (yytype)
{
case 16: // "constant string"
case 20: // "constant string"
#line 63 "parser.yy" // lalr1.cc:617
#line 71 "parser.yy" // lalr1.cc:636
{ yyoutput << yysym.value.template as< std::string > (); }
#line 328 "parser.cc" // lalr1.cc:617
#line 342 "parser.cc" // lalr1.cc:636
break;
case 17: // "integer"
case 21: // "integer"
#line 63 "parser.yy" // lalr1.cc:617
#line 71 "parser.yy" // lalr1.cc:636
{ yyoutput << yysym.value.template as< std::string > (); }
#line 335 "parser.cc" // lalr1.cc:617
#line 349 "parser.cc" // lalr1.cc:636
break;
case 18: // "constant hexstring"
case 22: // "constant hexstring"
#line 63 "parser.yy" // lalr1.cc:617
#line 71 "parser.yy" // lalr1.cc:636
{ yyoutput << yysym.value.template as< std::string > (); }
#line 342 "parser.cc" // lalr1.cc:617
#line 356 "parser.cc" // lalr1.cc:636
break;
case 19: // "option name"
case 23: // "option name"
#line 63 "parser.yy" // lalr1.cc:617
#line 71 "parser.yy" // lalr1.cc:636
{ yyoutput << yysym.value.template as< std::string > (); }
#line 349 "parser.cc" // lalr1.cc:617
#line 363 "parser.cc" // lalr1.cc:636
break;
case 20: // TOKEN
case 24: // TOKEN
#line 63 "parser.yy" // lalr1.cc:617
#line 71 "parser.yy" // lalr1.cc:636
{ yyoutput << yysym.value.template as< std::string > (); }
#line 356 "parser.cc" // lalr1.cc:617
#line 370 "parser.cc" // lalr1.cc:636
break;
case 25: // option_code
case 29: // option_code
#line 63 "parser.yy" // lalr1.cc:617
#line 71 "parser.yy" // lalr1.cc:636
{ yyoutput << yysym.value.template as< uint16_t > (); }
#line 363 "parser.cc" // lalr1.cc:617
#line 377 "parser.cc" // lalr1.cc:636
break;
case 26: // option_repr_type
case 30: // option_repr_type
#line 63 "parser.yy" // lalr1.cc:617
#line 71 "parser.yy" // lalr1.cc:636
{ yyoutput << yysym.value.template as< TokenOption::RepresentationType > (); }
#line 370 "parser.cc" // lalr1.cc:617
#line 384 "parser.cc" // lalr1.cc:636
break;
@ -453,9 +467,6 @@ namespace isc { namespace eval {
int
EvalParser::parse ()
{
/// Whether yyla contains a lookahead.
bool yyempty = true;
// State.
int yyn;
/// Length of the RHS of the rule being reduced.
@ -507,7 +518,7 @@ namespace isc { namespace eval {
goto yydefault;
// Read a lookahead token.
if (yyempty)
if (yyla.empty ())
{
YYCDEBUG << "Reading a token: ";
try
@ -520,7 +531,6 @@ namespace isc { namespace eval {
error (yyexc);
goto yyerrlab1;
}
yyempty = false;
}
YY_SYMBOL_PRINT ("Next token is", yyla);
@ -540,9 +550,6 @@ namespace isc { namespace eval {
goto yyreduce;
}
// Discard the token being shifted.
yyempty = true;
// Count tokens shifted since error; after three, turn off error status.
if (yyerrstatus_)
--yyerrstatus_;
@ -573,19 +580,19 @@ namespace isc { namespace eval {
when using variants. */
switch (yyr1_[yyn])
{
case 26: // option_repr_type
case 30: // option_repr_type
yylhs.value.build< TokenOption::RepresentationType > ();
break;
case 16: // "constant string"
case 17: // "integer"
case 18: // "constant hexstring"
case 19: // "option name"
case 20: // TOKEN
case 20: // "constant string"
case 21: // "integer"
case 22: // "constant hexstring"
case 23: // "option name"
case 24: // TOKEN
yylhs.value.build< std::string > ();
break;
case 25: // option_code
case 29: // option_code
yylhs.value.build< uint16_t > ();
break;
@ -606,44 +613,80 @@ namespace isc { namespace eval {
{
switch (yyn)
{
case 3:
#line 76 "parser.yy" // lalr1.cc:847
case 4:
#line 85 "parser.yy" // lalr1.cc:859
{
TokenPtr neg(new TokenNot());
ctx.expression.push_back(neg);
}
#line 623 "parser.cc" // lalr1.cc:859
break;
case 5:
#line 90 "parser.yy" // lalr1.cc:859
{
TokenPtr neg(new TokenAnd());
ctx.expression.push_back(neg);
}
#line 632 "parser.cc" // lalr1.cc:859
break;
case 6:
#line 95 "parser.yy" // lalr1.cc:859
{
TokenPtr neg(new TokenOr());
ctx.expression.push_back(neg);
}
#line 641 "parser.cc" // lalr1.cc:859
break;
case 7:
#line 100 "parser.yy" // lalr1.cc:859
{
TokenPtr eq(new TokenEqual());
ctx.expression.push_back(eq);
}
#line 616 "parser.cc" // lalr1.cc:847
#line 650 "parser.cc" // lalr1.cc:859
break;
case 4:
#line 83 "parser.yy" // lalr1.cc:847
case 8:
#line 105 "parser.yy" // lalr1.cc:859
{
TokenPtr opt(new TokenOption(yystack_[3].value.as< uint16_t > (), TokenOption::EXISTS));
ctx.expression.push_back(opt);
}
#line 659 "parser.cc" // lalr1.cc:859
break;
case 9:
#line 112 "parser.yy" // lalr1.cc:859
{
TokenPtr str(new TokenString(yystack_[0].value.as< std::string > ()));
ctx.expression.push_back(str);
}
#line 625 "parser.cc" // lalr1.cc:847
#line 668 "parser.cc" // lalr1.cc:859
break;
case 5:
#line 88 "parser.yy" // lalr1.cc:847
case 10:
#line 117 "parser.yy" // lalr1.cc:859
{
TokenPtr hex(new TokenHexString(yystack_[0].value.as< std::string > ()));
ctx.expression.push_back(hex);
}
#line 634 "parser.cc" // lalr1.cc:847
#line 677 "parser.cc" // lalr1.cc:859
break;
case 6:
#line 93 "parser.yy" // lalr1.cc:847
case 11:
#line 122 "parser.yy" // lalr1.cc:859
{
TokenPtr opt(new TokenOption(yystack_[3].value.as< uint16_t > (), yystack_[0].value.as< TokenOption::RepresentationType > ()));
ctx.expression.push_back(opt);
}
#line 643 "parser.cc" // lalr1.cc:847
#line 686 "parser.cc" // lalr1.cc:859
break;
case 7:
#line 98 "parser.yy" // lalr1.cc:847
case 12:
#line 127 "parser.yy" // lalr1.cc:859
{
switch (ctx.getUniverse()) {
case Option::V4:
@ -663,79 +706,79 @@ namespace isc { namespace eval {
error(yystack_[5].location, "relay4 can only be used in DHCPv4.");
}
}
#line 667 "parser.cc" // lalr1.cc:847
#line 710 "parser.cc" // lalr1.cc:859
break;
case 8:
#line 118 "parser.yy" // lalr1.cc:847
case 13:
#line 147 "parser.yy" // lalr1.cc:859
{
TokenPtr sub(new TokenSubstring());
ctx.expression.push_back(sub);
}
#line 676 "parser.cc" // lalr1.cc:847
break;
case 10:
#line 127 "parser.yy" // lalr1.cc:847
{
yylhs.value.as< uint16_t > () = ctx.convertOptionCode(yystack_[0].value.as< std::string > (), yystack_[0].location);
}
#line 684 "parser.cc" // lalr1.cc:847
break;
case 11:
#line 131 "parser.yy" // lalr1.cc:847
{
yylhs.value.as< uint16_t > () = ctx.convertOptionName(yystack_[0].value.as< std::string > (), yystack_[0].location);
}
#line 692 "parser.cc" // lalr1.cc:847
break;
case 12:
#line 137 "parser.yy" // lalr1.cc:847
{
yylhs.value.as< TokenOption::RepresentationType > () = TokenOption::TEXTUAL;
}
#line 700 "parser.cc" // lalr1.cc:847
break;
case 13:
#line 141 "parser.yy" // lalr1.cc:847
{
yylhs.value.as< TokenOption::RepresentationType > () = TokenOption::HEXADECIMAL;
}
#line 708 "parser.cc" // lalr1.cc:847
break;
case 14:
#line 147 "parser.yy" // lalr1.cc:847
{
TokenPtr str(new TokenString(yystack_[0].value.as< std::string > ()));
ctx.expression.push_back(str);
}
#line 717 "parser.cc" // lalr1.cc:847
#line 719 "parser.cc" // lalr1.cc:859
break;
case 15:
#line 154 "parser.yy" // lalr1.cc:847
#line 156 "parser.yy" // lalr1.cc:859
{
yylhs.value.as< uint16_t > () = ctx.convertOptionCode(yystack_[0].value.as< std::string > (), yystack_[0].location);
}
#line 727 "parser.cc" // lalr1.cc:859
break;
case 16:
#line 160 "parser.yy" // lalr1.cc:859
{
yylhs.value.as< uint16_t > () = ctx.convertOptionName(yystack_[0].value.as< std::string > (), yystack_[0].location);
}
#line 735 "parser.cc" // lalr1.cc:859
break;
case 17:
#line 166 "parser.yy" // lalr1.cc:859
{
yylhs.value.as< TokenOption::RepresentationType > () = TokenOption::TEXTUAL;
}
#line 743 "parser.cc" // lalr1.cc:859
break;
case 18:
#line 170 "parser.yy" // lalr1.cc:859
{
yylhs.value.as< TokenOption::RepresentationType > () = TokenOption::HEXADECIMAL;
}
#line 751 "parser.cc" // lalr1.cc:859
break;
case 19:
#line 176 "parser.yy" // lalr1.cc:859
{
TokenPtr str(new TokenString(yystack_[0].value.as< std::string > ()));
ctx.expression.push_back(str);
}
#line 726 "parser.cc" // lalr1.cc:847
#line 760 "parser.cc" // lalr1.cc:859
break;
case 16:
#line 159 "parser.yy" // lalr1.cc:847
case 20:
#line 183 "parser.yy" // lalr1.cc:859
{
TokenPtr str(new TokenString(yystack_[0].value.as< std::string > ()));
ctx.expression.push_back(str);
}
#line 769 "parser.cc" // lalr1.cc:859
break;
case 21:
#line 188 "parser.yy" // lalr1.cc:859
{
TokenPtr str(new TokenString("all"));
ctx.expression.push_back(str);
}
#line 735 "parser.cc" // lalr1.cc:847
#line 778 "parser.cc" // lalr1.cc:859
break;
#line 739 "parser.cc" // lalr1.cc:847
#line 782 "parser.cc" // lalr1.cc:859
default:
break;
}
@ -763,8 +806,7 @@ namespace isc { namespace eval {
if (!yyerrstatus_)
{
++yynerrs_;
error (yyla.location, yysyntax_error_ (yystack_[0].state,
yyempty ? yyempty_ : yyla.type_get ()));
error (yyla.location, yysyntax_error_ (yystack_[0].state, yyla));
}
@ -777,10 +819,10 @@ namespace isc { namespace eval {
// Return failure if at end of input.
if (yyla.type_get () == yyeof_)
YYABORT;
else if (!yyempty)
else if (!yyla.empty ())
{
yy_destroy_ ("Error: discarding", yyla);
yyempty = true;
yyla.clear ();
}
}
@ -856,7 +898,7 @@ namespace isc { namespace eval {
goto yyreturn;
yyreturn:
if (!yyempty)
if (!yyla.empty ())
yy_destroy_ ("Cleanup: discarding lookahead", yyla);
/* Do not reclaim the symbols of the rule whose action triggered
@ -876,7 +918,7 @@ namespace isc { namespace eval {
<< std::endl;
// Do not try to display the values of the reclaimed symbols,
// as their printer might throw an exception.
if (!yyempty)
if (!yyla.empty ())
yy_destroy_ (YY_NULLPTR, yyla);
while (1 < yystack_.size ())
@ -896,9 +938,8 @@ namespace isc { namespace eval {
// Generate an error message.
std::string
EvalParser::yysyntax_error_ (state_type yystate, symbol_number_type yytoken) const
EvalParser::yysyntax_error_ (state_type yystate, const symbol_type& yyla) const
{
std::string yyres;
// Number of reported tokens (one for the "unexpected", one per
// "expected").
size_t yycount = 0;
@ -912,7 +953,7 @@ namespace isc { namespace eval {
the only way this function was invoked is if the default action
is an error action. In that case, don't check for expected
tokens because there are none.
- The only way there can be no lookahead present (in yytoken) is
- The only way there can be no lookahead present (in yyla) is
if this state is a consistent state with a default action.
Thus, detecting the absence of a lookahead is sufficient to
determine that there is no unexpected or expected token to
@ -932,8 +973,9 @@ namespace isc { namespace eval {
token that will not be accepted due to an error action in a
later state.
*/
if (yytoken != yyempty_)
if (!yyla.empty ())
{
int yytoken = yyla.type_get ();
yyarg[yycount++] = yytname_[yytoken];
int yyn = yypact_[yystate];
if (!yy_pact_value_is_default_ (yyn))
@ -976,6 +1018,7 @@ namespace isc { namespace eval {
#undef YYCASE_
}
std::string yyres;
// Argument number.
size_t yyi = 0;
for (char const* yyp = yyformat; *yyp; ++yyp)
@ -990,79 +1033,91 @@ namespace isc { namespace eval {
}
const signed char EvalParser::yypact_ninf_ = -14;
const signed char EvalParser::yypact_ninf_ = -11;
const signed char EvalParser::yytable_ninf_ = -1;
const signed char
EvalParser::yypact_[] =
{
-4, -3, 3, -1, -14, -14, -14, 17, -14, 15,
-13, -4, -13, -14, -4, -14, -14, 4, 9, 6,
-14, 12, 7, 13, 1, -14, 14, 1, -14, -14,
-14, -7, -14, -14, -14, 16, -14
-4, -8, 9, -4, 12, -4, -11, -11, -11, 29,
7, 37, 16, -1, -11, 16, 0, -11, -4, -4,
-1, -11, -11, 23, 25, 26, 28, -11, -11, 38,
-11, 30, 16, 31, 32, 22, 34, -11, 33, 27,
-11, -11, -11, -11, 35, 11, -11, 27, -11, -11,
39, -11
};
const unsigned char
EvalParser::yydefact_[] =
{
0, 0, 0, 0, 4, 5, 9, 0, 2, 0,
0, 0, 0, 1, 0, 10, 11, 0, 0, 0,
3, 0, 0, 0, 0, 14, 0, 0, 12, 13,
6, 0, 7, 16, 15, 0, 8
0, 0, 0, 0, 0, 0, 9, 10, 14, 0,
2, 0, 0, 0, 4, 0, 0, 1, 0, 0,
0, 15, 16, 0, 0, 0, 0, 3, 5, 6,
7, 0, 0, 0, 0, 0, 0, 19, 0, 0,
17, 18, 8, 11, 0, 0, 12, 0, 21, 20,
0, 13
};
const signed char
EvalParser::yypgoto_[] =
{
-14, -14, -14, -6, 18, 0, -14, -14
-11, -11, 8, 15, -10, 18, -11, -11
};
const signed char
EvalParser::yydefgoto_[] =
{
-1, 7, 8, 9, 17, 30, 26, 35
-1, 9, 10, 11, 23, 43, 38, 50
};
const unsigned char
EvalParser::yytable_[] =
{
1, 2, 33, 3, 15, 18, 16, 28, 20, 29,
34, 10, 4, 12, 5, 11, 6, 13, 14, 21,
22, 23, 24, 27, 25, 31, 0, 32, 0, 36,
19
1, 2, 3, 24, 2, 26, 4, 18, 19, 4,
12, 14, 5, 16, 18, 19, 6, 27, 7, 6,
8, 7, 36, 8, 48, 13, 28, 29, 25, 17,
15, 40, 49, 41, 42, 30, 40, 21, 41, 22,
20, 33, 31, 32, 35, 18, 39, 34, 45, 47,
0, 0, 37, 44, 0, 0, 51, 46
};
const signed char
EvalParser::yycheck_[] =
{
4, 5, 9, 7, 17, 11, 19, 6, 14, 8,
17, 14, 16, 14, 18, 12, 20, 0, 3, 15,
11, 15, 10, 10, 17, 11, -1, 27, -1, 13,
12
4, 5, 6, 4, 5, 15, 10, 7, 8, 10,
18, 3, 16, 5, 7, 8, 20, 17, 22, 20,
24, 22, 32, 24, 13, 16, 18, 19, 13, 0,
18, 9, 21, 11, 12, 20, 9, 21, 11, 23,
3, 15, 19, 18, 14, 7, 14, 19, 15, 14,
-1, -1, 21, 19, -1, -1, 17, 39
};
const unsigned char
EvalParser::yystos_[] =
{
0, 4, 5, 7, 16, 18, 20, 22, 23, 24,
14, 12, 14, 0, 3, 17, 19, 25, 24, 25,
24, 15, 11, 15, 10, 17, 27, 10, 6, 8,
26, 11, 26, 9, 17, 28, 13
0, 4, 5, 6, 10, 16, 20, 22, 24, 26,
27, 28, 18, 16, 27, 18, 27, 0, 7, 8,
3, 21, 23, 29, 4, 28, 29, 17, 27, 27,
28, 19, 18, 15, 19, 14, 29, 21, 31, 14,
9, 11, 12, 30, 19, 15, 30, 14, 13, 21,
32, 17
};
const unsigned char
EvalParser::yyr1_[] =
{
0, 21, 22, 23, 24, 24, 24, 24, 24, 24,
25, 25, 26, 26, 27, 28, 28
0, 25, 26, 27, 27, 27, 27, 27, 27, 28,
28, 28, 28, 28, 28, 29, 29, 30, 30, 31,
32, 32
};
const unsigned char
EvalParser::yyr2_[] =
{
0, 2, 1, 3, 1, 1, 6, 6, 8, 1,
1, 1, 1, 1, 1, 1, 1
0, 2, 1, 3, 2, 3, 3, 3, 6, 1,
1, 6, 6, 8, 1, 1, 1, 1, 1, 1,
1, 1
};
@ -1073,10 +1128,11 @@ namespace isc { namespace eval {
const EvalParser::yytname_[] =
{
"\"end of file\"", "error", "$undefined", "\"==\"", "\"option\"",
"\"substring\"", "\"text\"", "\"relay4\"", "\"hex\"", "\"all\"", "\".\"",
"\",\"", "\"(\"", "\")\"", "\"[\"", "\"]\"", "\"constant string\"",
"\"integer\"", "\"constant hexstring\"", "\"option name\"", "TOKEN",
"$accept", "expression", "bool_expr", "string_expr", "option_code",
"\"substring\"", "\"not\"", "\"and\"", "\"or\"", "\"text\"",
"\"relay4\"", "\"hex\"", "\"exists\"", "\"all\"", "\".\"", "\",\"",
"\"(\"", "\")\"", "\"[\"", "\"]\"", "\"constant string\"", "\"integer\"",
"\"constant hexstring\"", "\"option name\"", "TOKEN", "$accept",
"expression", "bool_expr", "string_expr", "option_code",
"option_repr_type", "start_expr", "length_expr", YY_NULLPTR
};
@ -1084,8 +1140,9 @@ namespace isc { namespace eval {
const unsigned char
EvalParser::yyrline_[] =
{
0, 72, 72, 75, 82, 87, 92, 97, 117, 122,
126, 130, 136, 140, 146, 153, 158
0, 80, 80, 83, 84, 89, 94, 99, 104, 111,
116, 121, 126, 146, 151, 155, 159, 165, 169, 175,
182, 187
};
// Print the state stack on the debug stream.
@ -1118,10 +1175,10 @@ namespace isc { namespace eval {
#endif // YYDEBUG
#line 13 "parser.yy" // lalr1.cc:1155
#line 13 "parser.yy" // lalr1.cc:1167
} } // isc::eval
#line 1124 "parser.cc" // lalr1.cc:1155
#line 165 "parser.yy" // lalr1.cc:1156
#line 1181 "parser.cc" // lalr1.cc:1167
#line 194 "parser.yy" // lalr1.cc:1168
void
isc::eval::EvalParser::error(const location_type& loc,

View File

@ -1,8 +1,8 @@
// A Bison parser, made by GNU Bison 3.0.2.
// A Bison parser, made by GNU Bison 3.0.4.
// Skeleton interface for Bison LALR(1) parsers in C++
// Copyright (C) 2002-2013 Free Software Foundation, Inc.
// Copyright (C) 2002-2015 Free Software Foundation, Inc.
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
@ -40,7 +40,7 @@
#ifndef YY_YY_PARSER_H_INCLUDED
# define YY_YY_PARSER_H_INCLUDED
// // "%code requires" blocks.
#line 16 "parser.yy" // lalr1.cc:372
#line 16 "parser.yy" // lalr1.cc:392
#include <string>
#include <eval/token.h>
@ -51,13 +51,14 @@
using namespace isc::dhcp;
using namespace isc::eval;
#line 55 "parser.h" // lalr1.cc:372
#line 55 "parser.h" // lalr1.cc:392
# include <cassert>
# include <vector>
# include <cstdlib> // std::abort
# include <iostream>
# include <stdexcept>
# include <string>
# include <vector>
# include "stack.hh"
# include "location.hh"
#include <typeinfo>
@ -125,9 +126,9 @@ using namespace isc::eval;
# define YYDEBUG 1
#endif
#line 13 "parser.yy" // lalr1.cc:372
#line 13 "parser.yy" // lalr1.cc:392
namespace isc { namespace eval {
#line 131 "parser.h" // lalr1.cc:372
#line 132 "parser.h" // lalr1.cc:392
@ -144,13 +145,13 @@ namespace isc { namespace eval {
/// Empty construction.
variant ()
: yytname_ (YY_NULLPTR)
: yytypeid_ (YY_NULLPTR)
{}
/// Construct and fill.
template <typename T>
variant (const T& t)
: yytname_ (typeid (T).name ())
: yytypeid_ (&typeid (T))
{
YYASSERT (sizeof (T) <= S);
new (yyas_<T> ()) T (t);
@ -159,7 +160,7 @@ namespace isc { namespace eval {
/// Destruction, allowed only if empty.
~variant ()
{
YYASSERT (!yytname_);
YYASSERT (!yytypeid_);
}
/// Instantiate an empty \a T in here.
@ -167,9 +168,9 @@ namespace isc { namespace eval {
T&
build ()
{
YYASSERT (!yytname_);
YYASSERT (!yytypeid_);
YYASSERT (sizeof (T) <= S);
yytname_ = typeid (T).name ();
yytypeid_ = & typeid (T);
return *new (yyas_<T> ()) T;
}
@ -178,9 +179,9 @@ namespace isc { namespace eval {
T&
build (const T& t)
{
YYASSERT (!yytname_);
YYASSERT (!yytypeid_);
YYASSERT (sizeof (T) <= S);
yytname_ = typeid (T).name ();
yytypeid_ = & typeid (T);
return *new (yyas_<T> ()) T (t);
}
@ -189,7 +190,7 @@ namespace isc { namespace eval {
T&
as ()
{
YYASSERT (yytname_ == typeid (T).name ());
YYASSERT (*yytypeid_ == typeid (T));
YYASSERT (sizeof (T) <= S);
return *yyas_<T> ();
}
@ -199,7 +200,7 @@ namespace isc { namespace eval {
const T&
as () const
{
YYASSERT (yytname_ == typeid (T).name ());
YYASSERT (*yytypeid_ == typeid (T));
YYASSERT (sizeof (T) <= S);
return *yyas_<T> ();
}
@ -216,8 +217,8 @@ namespace isc { namespace eval {
void
swap (self_type& other)
{
YYASSERT (yytname_);
YYASSERT (yytname_ == other.yytname_);
YYASSERT (yytypeid_);
YYASSERT (*yytypeid_ == *other.yytypeid_);
std::swap (as<T> (), other.as<T> ());
}
@ -247,7 +248,7 @@ namespace isc { namespace eval {
destroy ()
{
as<T> ().~T ();
yytname_ = YY_NULLPTR;
yytypeid_ = YY_NULLPTR;
}
private:
@ -282,7 +283,7 @@ namespace isc { namespace eval {
} yybuffer_;
/// Whether the content is built: if defined, the name of the stored type.
const char *yytname_;
const std::type_info *yytypeid_;
};
@ -332,30 +333,37 @@ namespace isc { namespace eval {
TOKEN_EQUAL = 258,
TOKEN_OPTION = 259,
TOKEN_SUBSTRING = 260,
TOKEN_TEXT = 261,
TOKEN_RELAY4 = 262,
TOKEN_HEX = 263,
TOKEN_ALL = 264,
TOKEN_DOT = 265,
TOKEN_COMA = 266,
TOKEN_LPAREN = 267,
TOKEN_RPAREN = 268,
TOKEN_LBRACKET = 269,
TOKEN_RBRACKET = 270,
TOKEN_STRING = 271,
TOKEN_INTEGER = 272,
TOKEN_HEXSTRING = 273,
TOKEN_OPTION_NAME = 274,
TOKEN_TOKEN = 275
TOKEN_NOT = 261,
TOKEN_AND = 262,
TOKEN_OR = 263,
TOKEN_TEXT = 264,
TOKEN_RELAY4 = 265,
TOKEN_HEX = 266,
TOKEN_EXISTS = 267,
TOKEN_ALL = 268,
TOKEN_DOT = 269,
TOKEN_COMA = 270,
TOKEN_LPAREN = 271,
TOKEN_RPAREN = 272,
TOKEN_LBRACKET = 273,
TOKEN_RBRACKET = 274,
TOKEN_STRING = 275,
TOKEN_INTEGER = 276,
TOKEN_HEXSTRING = 277,
TOKEN_OPTION_NAME = 278,
TOKEN_TOKEN = 279
};
};
/// (External) token type, as returned by yylex.
typedef token::yytokentype token_type;
/// Internal symbol number.
/// Symbol type: an internal symbol number.
typedef int symbol_number_type;
/// The symbol type number to denote an empty symbol.
enum { empty_symbol = -2 };
/// Internal symbol number for tokens (subsumed by symbol_number_type).
typedef unsigned char token_number_type;
@ -393,8 +401,15 @@ namespace isc { namespace eval {
const semantic_type& v,
const location_type& l);
/// Destroy the symbol.
~basic_symbol ();
/// Destroy contents, and record that is empty.
void clear ();
/// Whether empty.
bool empty () const;
/// Destructive move, \a s is emptied into this.
void move (basic_symbol& s);
@ -424,21 +439,23 @@ namespace isc { namespace eval {
/// Constructor from (external) token numbers.
by_type (kind_type t);
/// Record that this symbol is empty.
void clear ();
/// Steal the symbol type from \a that.
void move (by_type& that);
/// The (internal) type number (corresponding to \a type).
/// -1 when this symbol is empty.
/// \a empty when empty.
symbol_number_type type_get () const;
/// The token.
token_type token () const;
enum { empty = 0 };
/// The symbol type.
/// -1 when this symbol is empty.
token_number_type type;
/// \a empty_symbol when empty.
/// An int, not token_number_type, to be able to store empty_symbol.
int type;
};
/// "External" symbols: returned by the scanner.
@ -461,6 +478,18 @@ namespace isc { namespace eval {
symbol_type
make_SUBSTRING (const location_type& l);
static inline
symbol_type
make_NOT (const location_type& l);
static inline
symbol_type
make_AND (const location_type& l);
static inline
symbol_type
make_OR (const location_type& l);
static inline
symbol_type
make_TEXT (const location_type& l);
@ -473,6 +502,10 @@ namespace isc { namespace eval {
symbol_type
make_HEX (const location_type& l);
static inline
symbol_type
make_EXISTS (const location_type& l);
static inline
symbol_type
make_ALL (const location_type& l);
@ -562,9 +595,9 @@ namespace isc { namespace eval {
/// Generate an error message.
/// \param yystate the state where the error occurred.
/// \param yytoken the lookahead token type, or yyempty_.
/// \param yyla the lookahead token.
virtual std::string yysyntax_error_ (state_type yystate,
symbol_number_type yytoken) const;
const symbol_type& yyla) const;
/// Compute post-reduction state.
/// \param yystate the current state
@ -667,16 +700,21 @@ namespace isc { namespace eval {
/// Copy constructor.
by_state (const by_state& other);
/// Record that this symbol is empty.
void clear ();
/// Steal the symbol type from \a that.
void move (by_state& that);
/// The (internal) type number (corresponding to \a state).
/// "empty" when empty.
/// \a empty_symbol when empty.
symbol_number_type type_get () const;
enum { empty = 0 };
/// The state number used to denote an empty symbol.
enum { empty_state = -1 };
/// The state.
/// \a empty when empty.
state_type state;
};
@ -717,17 +755,16 @@ namespace isc { namespace eval {
/// Pop \a n symbols the three stacks.
void yypop_ (unsigned int n = 1);
// Constants.
/// Constants.
enum
{
yyeof_ = 0,
yylast_ = 30, ///< Last index in yytable_.
yylast_ = 57, ///< Last index in yytable_.
yynnts_ = 8, ///< Number of nonterminal symbols.
yyempty_ = -2,
yyfinal_ = 13, ///< Termination state number.
yyfinal_ = 17, ///< Termination state number.
yyterror_ = 1,
yyerrcode_ = 256,
yyntokens_ = 21 ///< Number of tokens.
yyntokens_ = 25 ///< Number of tokens.
};
@ -771,9 +808,9 @@ namespace isc { namespace eval {
2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 1, 2, 3, 4,
5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
15, 16, 17, 18, 19, 20
15, 16, 17, 18, 19, 20, 21, 22, 23, 24
};
const unsigned int user_token_number_max_ = 275;
const unsigned int user_token_number_max_ = 279;
const token_number_type undef_token_ = 2;
if (static_cast<int>(t) <= yyeof_)
@ -806,19 +843,19 @@ namespace isc { namespace eval {
{
switch (other.type_get ())
{
case 26: // option_repr_type
case 30: // option_repr_type
value.copy< TokenOption::RepresentationType > (other.value);
break;
case 16: // "constant string"
case 17: // "integer"
case 18: // "constant hexstring"
case 19: // "option name"
case 20: // TOKEN
case 20: // "constant string"
case 21: // "integer"
case 22: // "constant hexstring"
case 23: // "option name"
case 24: // TOKEN
value.copy< std::string > (other.value);
break;
case 25: // option_code
case 29: // option_code
value.copy< uint16_t > (other.value);
break;
@ -839,19 +876,19 @@ namespace isc { namespace eval {
(void) v;
switch (this->type_get ())
{
case 26: // option_repr_type
case 30: // option_repr_type
value.copy< TokenOption::RepresentationType > (v);
break;
case 16: // "constant string"
case 17: // "integer"
case 18: // "constant hexstring"
case 19: // "option name"
case 20: // TOKEN
case 20: // "constant string"
case 21: // "integer"
case 22: // "constant hexstring"
case 23: // "option name"
case 24: // TOKEN
value.copy< std::string > (v);
break;
case 25: // option_code
case 29: // option_code
value.copy< uint16_t > (v);
break;
@ -895,9 +932,19 @@ namespace isc { namespace eval {
template <typename Base>
inline
EvalParser::basic_symbol<Base>::~basic_symbol ()
{
clear ();
}
template <typename Base>
inline
void
EvalParser::basic_symbol<Base>::clear ()
{
// User destructor.
symbol_number_type yytype = this->type_get ();
basic_symbol<Base>& yysym = *this;
(void) yysym;
switch (yytype)
{
default:
@ -907,19 +954,19 @@ namespace isc { namespace eval {
// Type destructor.
switch (yytype)
{
case 26: // option_repr_type
case 30: // option_repr_type
value.template destroy< TokenOption::RepresentationType > ();
break;
case 16: // "constant string"
case 17: // "integer"
case 18: // "constant hexstring"
case 19: // "option name"
case 20: // TOKEN
case 20: // "constant string"
case 21: // "integer"
case 22: // "constant hexstring"
case 23: // "option name"
case 24: // TOKEN
value.template destroy< std::string > ();
break;
case 25: // option_code
case 29: // option_code
value.template destroy< uint16_t > ();
break;
@ -927,6 +974,15 @@ namespace isc { namespace eval {
break;
}
Base::clear ();
}
template <typename Base>
inline
bool
EvalParser::basic_symbol<Base>::empty () const
{
return Base::type_get () == empty_symbol;
}
template <typename Base>
@ -937,19 +993,19 @@ namespace isc { namespace eval {
super_type::move(s);
switch (this->type_get ())
{
case 26: // option_repr_type
case 30: // option_repr_type
value.move< TokenOption::RepresentationType > (s.value);
break;
case 16: // "constant string"
case 17: // "integer"
case 18: // "constant hexstring"
case 19: // "option name"
case 20: // TOKEN
case 20: // "constant string"
case 21: // "integer"
case 22: // "constant hexstring"
case 23: // "option name"
case 24: // TOKEN
value.move< std::string > (s.value);
break;
case 25: // option_code
case 29: // option_code
value.move< uint16_t > (s.value);
break;
@ -963,7 +1019,7 @@ namespace isc { namespace eval {
// by_type.
inline
EvalParser::by_type::by_type ()
: type (empty)
: type (empty_symbol)
{}
inline
@ -976,12 +1032,19 @@ namespace isc { namespace eval {
: type (yytranslate_ (t))
{}
inline
void
EvalParser::by_type::clear ()
{
type = empty_symbol;
}
inline
void
EvalParser::by_type::move (by_type& that)
{
type = that.type;
that.type = empty;
that.clear ();
}
inline
@ -1003,7 +1066,7 @@ namespace isc { namespace eval {
{
0, 256, 257, 258, 259, 260, 261, 262, 263, 264,
265, 266, 267, 268, 269, 270, 271, 272, 273, 274,
275
275, 276, 277, 278, 279
};
return static_cast<token_type> (yytoken_number_[type]);
}
@ -1032,6 +1095,24 @@ namespace isc { namespace eval {
return symbol_type (token::TOKEN_SUBSTRING, l);
}
EvalParser::symbol_type
EvalParser::make_NOT (const location_type& l)
{
return symbol_type (token::TOKEN_NOT, l);
}
EvalParser::symbol_type
EvalParser::make_AND (const location_type& l)
{
return symbol_type (token::TOKEN_AND, l);
}
EvalParser::symbol_type
EvalParser::make_OR (const location_type& l)
{
return symbol_type (token::TOKEN_OR, l);
}
EvalParser::symbol_type
EvalParser::make_TEXT (const location_type& l)
{
@ -1050,6 +1131,12 @@ namespace isc { namespace eval {
return symbol_type (token::TOKEN_HEX, l);
}
EvalParser::symbol_type
EvalParser::make_EXISTS (const location_type& l)
{
return symbol_type (token::TOKEN_EXISTS, l);
}
EvalParser::symbol_type
EvalParser::make_ALL (const location_type& l)
{
@ -1123,9 +1210,9 @@ namespace isc { namespace eval {
}
#line 13 "parser.yy" // lalr1.cc:372
#line 13 "parser.yy" // lalr1.cc:392
} } // isc::eval
#line 1129 "parser.h" // lalr1.cc:372
#line 1216 "parser.h" // lalr1.cc:392

View File

@ -39,9 +39,13 @@ using namespace isc::eval;
EQUAL "=="
OPTION "option"
SUBSTRING "substring"
NOT "not"
AND "and"
OR "or"
TEXT "text"
RELAY4 "relay4"
HEX "hex"
EXISTS "exists"
ALL "all"
DOT "."
COMA ","
@ -60,6 +64,10 @@ using namespace isc::eval;
%type <uint16_t> option_code
%type <TokenOption::RepresentationType> option_repr_type
%left OR
%left AND
%precedence NOT
%printer { yyoutput << $$; } <*>;
%%
@ -72,11 +80,32 @@ using namespace isc::eval;
expression : bool_expr
;
bool_expr : string_expr EQUAL string_expr
bool_expr : "(" bool_expr ")"
| NOT bool_expr
{
TokenPtr neg(new TokenNot());
ctx.expression.push_back(neg);
}
| bool_expr AND bool_expr
{
TokenPtr neg(new TokenAnd());
ctx.expression.push_back(neg);
}
| bool_expr OR bool_expr
{
TokenPtr neg(new TokenOr());
ctx.expression.push_back(neg);
}
| string_expr EQUAL string_expr
{
TokenPtr eq(new TokenEqual());
ctx.expression.push_back(eq);
}
| OPTION "[" option_code "]" "." EXISTS
{
TokenPtr opt(new TokenOption($3, TokenOption::EXISTS));
ctx.expression.push_back(opt);
}
;
string_expr : STRING
@ -120,7 +149,7 @@ string_expr : STRING
ctx.expression.push_back(sub);
}
| TOKEN
// Temporary unused token to avoid explict but long errors
// Temporary unused token to avoid explicit but long errors
;
option_code : INTEGER

View File

@ -1,4 +1,5 @@
// A Bison parser, made by GNU Bison 3.0.2.
// Generated 20160219
// A Bison parser, made by GNU Bison 3.0.4.
// Positions for Bison parsers in C++

View File

@ -1,4 +1,5 @@
// A Bison parser, made by GNU Bison 3.0.2.
// Generated 20160219
// A Bison parser, made by GNU Bison 3.0.4.
// Stack handling for Bison parsers in C++

View File

@ -26,7 +26,8 @@ if HAVE_GTEST
TESTS += libeval_unittests
libeval_unittests_SOURCES = context_unittest.cc
libeval_unittests_SOURCES = boolean_unittest.cc
libeval_unittests_SOURCES += context_unittest.cc
libeval_unittests_SOURCES += evaluate_unittest.cc
libeval_unittests_SOURCES += token_unittest.cc
libeval_unittests_SOURCES += run_unittests.cc

View File

@ -0,0 +1,72 @@
// Copyright (C) 2016 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.
#include <config.h>
#include <eval/eval_context.h>
#include <eval/evaluate.h>
#include <eval/token.h>
#include <dhcp/pkt4.h>
#include <boost/shared_ptr.hpp>
#include <boost/scoped_ptr.hpp>
#include <gtest/gtest.h>
using namespace std;
using namespace isc::dhcp;
namespace {
/// @brief Test fixture for testing booleans.
class BooleanTest : public ::testing::Test {
public:
void check(const string& expr, bool expected) {
EvalContext eval(Option::V4);
ASSERT_TRUE(eval.parseString(expr));
Pkt4Ptr pkt4(new Pkt4(DHCPDISCOVER, 12345));
if (expected) {
EXPECT_TRUE(evaluate(eval.expression, *pkt4));
} else {
EXPECT_FALSE(evaluate(eval.expression, *pkt4));
}
}
};
// A group of tests
TEST_F(BooleanTest, tests) {
// true and (false or false)
check("('a' == 'a') and (('a' == 'b') or ('b' == 'a'))", false);
// (true and false) or false
check("(('a' == 'a') and ('a' == 'b')) or ('b' == 'a')", false);
// not true
check("not ('a' == 'a')", false);
// not false
check("not ('a' == 'b')", true);
// true and true and true and false
check("('a' == 'a') and ('b' == 'b') and ('c' == 'c') and ('a' == 'c')",
false);
// false or false or false or true
check("('a' == 'b') or ('a' == 'c') or ('b' == 'c') or ('b' == 'b')",
true);
// true or false or false or false
check("('a' == 'a') or ('a' == 'b') or ('a' == 'c') or ('b' == 'c')",
true);
// not (true or false)
check("not (('a' == 'a') or ('a' == 'b'))", false);
// not (true and false)
check("not (('a' == 'a') and ('a' == 'b'))", true);
// (not true) and false
check("(not ('a' == 'a')) and ('a' == 'b')",false);
}
};

View File

@ -232,6 +232,16 @@ TEST_F(EvalContextTest, optionWithName) {
checkTokenOption(eval.expression.at(0), 12);
}
// Test parsing of an option existence
TEST_F(EvalContextTest, optionExists) {
EvalContext eval(Option::V4);
EXPECT_NO_THROW(parsed_ = eval.parseString("option[100].exists"));
EXPECT_TRUE(parsed_);
ASSERT_EQ(1, eval.expression.size());
checkTokenOption(eval.expression.at(0), 100);
}
// Test checking that whitespace can surround option name.
TEST_F(EvalContextTest, optionWithNameAndWhitespace) {
EvalContext eval(Option::V4);
@ -265,6 +275,112 @@ TEST_F(EvalContextTest, optionHex) {
checkTokenOption(eval.expression.at(0), 123);
}
// Test parsing of logical operators
TEST_F(EvalContextTest, logicalOps) {
// option.exists
EvalContext eval0(Option::V4);
EXPECT_NO_THROW(parsed_ = eval0.parseString("option[123].exists"));
EXPECT_TRUE(parsed_);
ASSERT_EQ(1, eval0.expression.size());
TokenPtr token = eval0.expression.at(0);
ASSERT_TRUE(token);
boost::shared_ptr<TokenOption> opt =
boost::dynamic_pointer_cast<TokenOption>(token);
EXPECT_TRUE(opt);
// not
EvalContext evaln(Option::V4);
EXPECT_NO_THROW(parsed_ = evaln.parseString("not option[123].exists"));
EXPECT_TRUE(parsed_);
ASSERT_EQ(2, evaln.expression.size());
token = evaln.expression.at(1);
ASSERT_TRUE(token);
boost::shared_ptr<TokenNot> tnot =
boost::dynamic_pointer_cast<TokenNot>(token);
EXPECT_TRUE(tnot);
// 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);
// or
EvalContext evalo(Option::V4);
EXPECT_NO_THROW(parsed_ =
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);
}
// Test parsing of logical operators with precedence
TEST_F(EvalContextTest, logicalPrecedence) {
// 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);
// and precedence > or precedence
EvalContext evaloa(Option::V4);
EXPECT_NO_THROW(parsed_ =
evaloa.parseString("option[123].exists or option[123].exists "
"and option[123].exists"));
EXPECT_TRUE(parsed_);
ASSERT_EQ(5, evaloa.expression.size());
token = evaloa.expression.at(4);
ASSERT_TRUE(token);
boost::shared_ptr<TokenOr> tor =
boost::dynamic_pointer_cast<TokenOr>(token);
EXPECT_TRUE(tor);
}
// 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);
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<TokenNot> tnot =
boost::dynamic_pointer_cast<TokenNot>(token);
EXPECT_TRUE(tnot);
// and precedence > or precedence
EvalContext evaloa(Option::V4);
EXPECT_NO_THROW(parsed_ =
evaloa.parseString("(option[123].exists or option[123].exists) "
"and option[123].exists"));
EXPECT_TRUE(parsed_);
ASSERT_EQ(5, evaloa.expression.size());
token = evaloa.expression.at(4);
ASSERT_TRUE(token);
boost::shared_ptr<TokenAnd> tand =
boost::dynamic_pointer_cast<TokenAnd>(token);
EXPECT_TRUE(tand);
}
// Test the parsing of a substring expression
TEST_F(EvalContextTest, substring) {
EvalContext eval(Option::V4);
@ -369,9 +485,45 @@ TEST_F(EvalContextTest, parseErrors) {
checkError("'foo''bar'",
"<string>:1.6-10: syntax error, unexpected constant string, "
"expecting ==");
checkError("'foo' (",
"<string>:1.7: syntax error, unexpected (, expecting ==");
checkError("== 'ab'", "<string>:1.1-2: syntax error, unexpected ==");
checkError("'foo' ==",
"<string>:1.9: syntax error, unexpected end of file");
checkError("('foo' == 'bar'",
"<string>:1.16: syntax error, unexpected end of file, "
"expecting and or or or )");
checkError("('foo' == 'bar') ''",
"<string>:1.18-19: syntax error, unexpected constant string, "
"expecting end of file");
checkError("not",
"<string>:1.4: syntax error, unexpected end of file");
checkError("not 'foo'",
"<string>:1.10: syntax error, unexpected end of file, "
"expecting ==");
checkError("not()",
"<string>:1.5: syntax error, unexpected )");
checkError("(not('foo' 'bar')",
"<string>:1.12-16: syntax error, unexpected constant string, "
"expecting ==");
checkError("and",
"<string>:1.1-3: syntax error, unexpected and");
checkError("'foo' and",
"<string>:1.7-9: syntax error, unexpected and, expecting ==");
checkError("'foo' == 'bar' and",
"<string>:1.19: syntax error, unexpected end of file");
checkError("'foo' == 'bar' and ''",
"<string>:1.22: syntax error, unexpected end of file, "
"expecting ==");
checkError("or",
"<string>:1.1-2: syntax error, unexpected or");
checkError("'foo' or",
"<string>:1.7-8: syntax error, unexpected or, expecting ==");
checkError("'foo' == 'bar' or",
"<string>:1.18: syntax error, unexpected end of file");
checkError("'foo' == 'bar' or ''",
"<string>:1.21: syntax error, unexpected end of file, "
"expecting ==");
checkError("option 'ab'",
"<string>:1.8-11: syntax error, unexpected "
"constant string, expecting [");
@ -390,6 +542,9 @@ TEST_F(EvalContextTest, parseErrors) {
"expecting integer or option name");
checkError("option[10].bin", "<string>:1.12: Invalid character: b");
checkError("option[boot-size].bin", "<string>:1.19: Invalid character: b");
checkError("option[10].exists == 'foo'",
"<string>:1.19-20: syntax error, unexpected ==, "
"expecting end of file");
checkError("substring('foobar') == 'f'",
"<string>:1.19: syntax error, "
"unexpected ), expecting \",\"");
@ -402,7 +557,7 @@ TEST_F(EvalContextTest, parseErrors) {
"<string>:1.22: Invalid character: a");
}
// Tests some type error cases (caught only by the strongly typed parser)
// Tests some type error cases
TEST_F(EvalContextTest, typeErrors) {
checkError("'foobar'",
"<string>:1.9: syntax error, unexpected end of file, "
@ -413,6 +568,16 @@ TEST_F(EvalContextTest, typeErrors) {
checkError("substring('foobar',0x32,1) == 'foo'",
"<string>:1.20-23: syntax error, unexpected constant "
"hexstring, expecting integer");
checkError("('foo' == 'bar') == 'false'",
"<string>:1.18-19: syntax error, unexpected ==, "
"expecting end of file");
checkError("not 'true'",
"<string>:1.11: syntax error, unexpected end of file, "
"expecting ==");
checkError("'true' and 'false'",
"<string>:1.8-10: syntax error, unexpected and, expecting ==");
checkError("'true' or 'false'",
"<string>:1.8-9: syntax error, unexpected or, expecting ==");
}
};

View File

@ -1,4 +1,4 @@
// Copyright (C) 2015 Internet Systems Consortium, Inc. ("ISC")
// Copyright (C) 2015-2016 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
@ -182,6 +182,32 @@ TEST_F(EvaluateTest, compare6) {
EXPECT_FALSE(result_);
}
// A test using option existence
TEST_F(EvaluateTest, exists) {
TokenPtr toption;
ASSERT_NO_THROW(toption.reset(new TokenOption(100, TokenOption::EXISTS)));
e_.push_back(toption);
ASSERT_NO_THROW(result_ = evaluate(e_, *pkt4_));
EXPECT_TRUE(result_);
ASSERT_NO_THROW(result_ = evaluate(e_, *pkt6_));
EXPECT_TRUE(result_);
}
// A test using option non-existence
TEST_F(EvaluateTest, dontExists) {
TokenPtr toption;
ASSERT_NO_THROW(toption.reset(new TokenOption(101, TokenOption::EXISTS)));
e_.push_back(toption);
ASSERT_NO_THROW(result_ = evaluate(e_, *pkt4_));
EXPECT_FALSE(result_);
ASSERT_NO_THROW(result_ = evaluate(e_, *pkt6_));
EXPECT_FALSE(result_);
}
// A test using packets.
TEST_F(EvaluateTest, packet) {
TokenPtr toption;

View File

@ -125,6 +125,24 @@ public:
/// @todo: Add more option types here
};
// This tests the toBool() conversions
TEST_F(TokenTest, toBool) {
ASSERT_NO_THROW(Token::toBool("true"));
EXPECT_TRUE(Token::toBool("true"));
ASSERT_NO_THROW(Token::toBool("false"));
EXPECT_FALSE(Token::toBool("false"));
// Token::toBool() is case-sensitive
EXPECT_THROW(Token::toBool("True"), EvalTypeError);
EXPECT_THROW(Token::toBool("TRUE"), EvalTypeError);
// Proposed aliases
EXPECT_THROW(Token::toBool("1"), EvalTypeError);
EXPECT_THROW(Token::toBool("0"), EvalTypeError);
EXPECT_THROW(Token::toBool(""), EvalTypeError);
}
// This simple test checks that a TokenString, representing a constant string,
// can be used in Pkt4 evaluation. (The actual packet is not used)
TEST_F(TokenTest, string4) {
@ -322,6 +340,28 @@ TEST_F(TokenTest, optionHexString4) {
EXPECT_EQ("hundred4", values_.top());
}
// This test checks if a token representing an option value is able to check
// the existence ofthe option from an IPv4 packet.
TEST_F(TokenTest, optionExistsString4) {
TokenPtr found;
TokenPtr not_found;
// The packets we use have option 100 with a string in them.
ASSERT_NO_THROW(found.reset(new TokenOption(100, TokenOption::EXISTS)));
ASSERT_NO_THROW(not_found.reset(new TokenOption(101, TokenOption::EXISTS)));
ASSERT_NO_THROW(found->evaluate(*pkt4_, values_));
ASSERT_NO_THROW(not_found->evaluate(*pkt4_, values_));
// There should be 2 values evaluated.
ASSERT_EQ(2, values_.size());
// This is a stack, so the pop order is inversed.
EXPECT_EQ("false", values_.top());
values_.pop();
EXPECT_EQ("true", values_.top());
}
// This test checks if a token representing an option value is able to extract
// the option from an IPv6 packet and properly store the option's value.
TEST_F(TokenTest, optionString6) {
@ -379,6 +419,28 @@ TEST_F(TokenTest, optionHexString6) {
EXPECT_EQ("hundred6", values_.top());
}
// This test checks if a token representing an option value is able to check
// the existence ofthe option from an IPv6 packet.
TEST_F(TokenTest, optionExistsString6) {
TokenPtr found;
TokenPtr not_found;
// The packets we use have option 100 with a string in them.
ASSERT_NO_THROW(found.reset(new TokenOption(100, TokenOption::EXISTS)));
ASSERT_NO_THROW(not_found.reset(new TokenOption(101, TokenOption::EXISTS)));
ASSERT_NO_THROW(found->evaluate(*pkt6_, values_));
ASSERT_NO_THROW(not_found->evaluate(*pkt6_, values_));
// There should be 2 values evaluated.
ASSERT_EQ(2, values_.size());
// This is a stack, so the pop order is inversed.
EXPECT_EQ("false", values_.top());
values_.pop();
EXPECT_EQ("true", values_.top());
}
// This test checks if a token representing an == operator is able to
// compare two values (with incorrectly built stack).
TEST_F(TokenTest, optionEqualInvalid) {
@ -426,6 +488,171 @@ TEST_F(TokenTest, optionEqualTrue) {
EXPECT_EQ("true", values_.top());
}
// This test checks if a token representing a not is able to
// negate a boolean value (with incorrectly built stack).
TEST_F(TokenTest, optionNotInvalid) {
ASSERT_NO_THROW(t_.reset(new TokenNot()));
// CASE 1: The stack is empty.
EXPECT_THROW(t_->evaluate(*pkt4_, values_), EvalBadStack);
// CASE 2: The top value is not a boolean
values_.push("foo");
EXPECT_THROW(t_->evaluate(*pkt4_, values_), EvalTypeError);
}
// This test checks if a token representing a not operator is able to
// negate a boolean value.
TEST_F(TokenTest, optionNot) {
ASSERT_NO_THROW(t_.reset(new TokenNot()));
values_.push("true");
EXPECT_NO_THROW(t_->evaluate(*pkt4_, values_));
// After evaluation there should be the negation of the value.
ASSERT_EQ(1, values_.size());
EXPECT_EQ("false", values_.top());
// Double negation is identity.
EXPECT_NO_THROW(t_->evaluate(*pkt4_, values_));
ASSERT_EQ(1, values_.size());
EXPECT_EQ("true", values_.top());
}
// This test checks if a token representing an and is able to
// conjugate two values (with incorrectly built stack).
TEST_F(TokenTest, optionAndInvalid) {
ASSERT_NO_THROW(t_.reset(new TokenAnd()));
// CASE 1: There's not enough values on the stack. and is an operator that
// takes two parameters. There are 0 on the stack.
EXPECT_THROW(t_->evaluate(*pkt4_, values_), EvalBadStack);
// CASE 2: One value is still not enough.
values_.push("foo");
EXPECT_THROW(t_->evaluate(*pkt4_, values_), EvalBadStack);
// CASE 3: The two values must be logical
values_.push("true");
EXPECT_THROW(t_->evaluate(*pkt4_, values_), EvalTypeError);
// Swap the 2 values
values_.push("true");
values_.push("foo");
EXPECT_THROW(t_->evaluate(*pkt4_, values_), EvalTypeError);
}
// This test checks if a token representing an and operator is able to
// conjugate false with another logical
TEST_F(TokenTest, optionAndFalse) {
ASSERT_NO_THROW(t_.reset(new TokenAnd()));
values_.push("true");
values_.push("false");
EXPECT_NO_THROW(t_->evaluate(*pkt4_, values_));
// After evaluation there should be a single "false" value
ASSERT_EQ(1, values_.size());
EXPECT_EQ("false", values_.top());
// After true and false, checks false and true
values_.push("true");
EXPECT_NO_THROW(t_->evaluate(*pkt4_, values_));
ASSERT_EQ(1, values_.size());
EXPECT_EQ("false", values_.top());
// And false and false
values_.push("false");
EXPECT_NO_THROW(t_->evaluate(*pkt4_, values_));
ASSERT_EQ(1, values_.size());
EXPECT_EQ("false", values_.top());
}
// This test checks if a token representing an and is able to
// conjugate two true values.
TEST_F(TokenTest, optionAndTrue) {
ASSERT_NO_THROW(t_.reset(new TokenAnd()));
values_.push("true");
values_.push("true");
EXPECT_NO_THROW(t_->evaluate(*pkt4_, values_));
// After evaluation there should be a single "true" value
ASSERT_EQ(1, values_.size());
EXPECT_EQ("true", values_.top());
}
// This test checks if a token representing an or is able to
// combinate two values (with incorrectly built stack).
TEST_F(TokenTest, optionOrInvalid) {
ASSERT_NO_THROW(t_.reset(new TokenOr()));
// CASE 1: There's not enough values on the stack. or is an operator that
// takes two parameters. There are 0 on the stack.
EXPECT_THROW(t_->evaluate(*pkt4_, values_), EvalBadStack);
// CASE 2: One value is still not enough.
values_.push("foo");
EXPECT_THROW(t_->evaluate(*pkt4_, values_), EvalBadStack);
// CASE 3: The two values must be logical
values_.push("true");
EXPECT_THROW(t_->evaluate(*pkt4_, values_), EvalTypeError);
// Swap the 2 values
values_.push("true");
values_.push("foo");
EXPECT_THROW(t_->evaluate(*pkt4_, values_), EvalTypeError);
}
// This test checks if a token representing an or is able to
// conjugate two false values.
TEST_F(TokenTest, optionOrFalse) {
ASSERT_NO_THROW(t_.reset(new TokenOr()));
values_.push("false");
values_.push("false");
EXPECT_NO_THROW(t_->evaluate(*pkt4_, values_));
// After evaluation there should be a single "false" value
ASSERT_EQ(1, values_.size());
EXPECT_EQ("false", values_.top());
}
// This test checks if a token representing an == operator is able to
// conjugate true with another logical
TEST_F(TokenTest, optionOrTrue) {
ASSERT_NO_THROW(t_.reset(new TokenOr()));
values_.push("false");
values_.push("true");
EXPECT_NO_THROW(t_->evaluate(*pkt4_, values_));
// After evaluation there should be a single "true" value
ASSERT_EQ(1, values_.size());
EXPECT_EQ("true", values_.top());
// After false or true, checks true or false
values_.push("false");
EXPECT_NO_THROW(t_->evaluate(*pkt4_, values_));
ASSERT_EQ(1, values_.size());
EXPECT_EQ("true", values_.top());
// And true or true
values_.push("true");
EXPECT_NO_THROW(t_->evaluate(*pkt4_, values_));
ASSERT_EQ(1, values_.size());
EXPECT_EQ("true", values_.top());
}
};
// This test checks if an a token representing a substring request

View File

@ -66,13 +66,17 @@ TokenOption::evaluate(const Pkt& pkt, ValueStack& values) {
if (opt) {
if (representation_type_ == TEXTUAL) {
opt_str = opt->toString();
} else {
} else if (representation_type_ == HEXADECIMAL) {
std::vector<uint8_t> binary = opt->toBinary();
opt_str.resize(binary.size());
if (!binary.empty()) {
memmove(&opt_str[0], &binary[0], binary.size());
}
} else {
opt_str = "true";
}
} else if (representation_type_ == EXISTS) {
opt_str = "false";
}
// Push value of the option or empty string if there was no such option
@ -190,3 +194,65 @@ OptionPtr TokenRelay4Option::getOption(const Pkt& pkt) {
// If there is, try to return its suboption
return (rai->getOption(option_code_));
}
void
TokenNot::evaluate(const Pkt& /*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) {
values.push("true");
} else {
values.push("false");
}
}
void
TokenAnd::evaluate(const Pkt& /*pkt*/, ValueStack& values) {
if (values.size() < 2) {
isc_throw(EvalBadStack, "Incorrect stack order. Expected at least "
"2 values for and operator, got " << values.size());
}
string op1 = values.top();
values.pop();
bool val1 = toBool(op1);
string op2 = values.top();
values.pop(); // Dammit, std::stack interface is awkward.
bool val2 = toBool(op2);
if (val1 && val2) {
values.push("true");
} else {
values.push("false");
}
}
void
TokenOr::evaluate(const Pkt& /*pkt*/, ValueStack& values) {
if (values.size() < 2) {
isc_throw(EvalBadStack, "Incorrect stack order. Expected at least "
"2 values for or operator, got " << values.size());
}
string op1 = values.top();
values.pop();
bool val1 = toBool(op1);
string op2 = values.top();
values.pop(); // Dammit, std::stack interface is awkward.
bool val2 = toBool(op2);
if (val1 || val2) {
values.push("true");
} else {
values.push("false");
}
}

View File

@ -79,6 +79,24 @@ public:
/// @brief Virtual destructor
virtual ~Token() {}
/// @brief Coverts a (string) value to a boolean
///
/// Only "true" and "false" are expected.
///
/// @param the (string) value
/// @return the boolean represented by the value
/// @throw EvalTypeError when the value is not either "true" or "false".
static inline bool toBool(std::string value) {
if (value == "true") {
return (true);
} else if (value == "false") {
return (false);
} else {
isc_throw(EvalTypeError, "Incorrect boolean. Expected exactly "
"\"false\" or \"true\", got \"" << value << "\"");
}
}
};
/// @brief Token representing a constant string
@ -136,20 +154,22 @@ protected:
/// option[vendor-class].text
///
/// During the evaluation it tries to extract the value of the specified
/// option. If the option is not found, an empty string ("") is returned.
/// option. If the option is not found, an empty string ("") is returned
/// (or "false" when the representation is EXISTS).
class TokenOption : public Token {
public:
/// @brief Token representation type.
///
/// There are many possible ways in which option can be presented.
/// Currently the textual and hexadecimal representations are
/// Currently the textual, hexadecimal and exists representations are
/// supported. The type of representation is specified in the
/// constructor and it affects the value generated by the
/// @c TokenOption::evaluate function.
enum RepresentationType {
TEXTUAL,
HEXADECIMAL
HEXADECIMAL,
EXISTS
};
/// @brief Constructor that takes an option code as a parameter
@ -308,6 +328,80 @@ protected:
virtual OptionPtr getOption(const Pkt& pkt);
};
/// @brief Token that represents logical negation operator
///
/// For example in the expression "not(option[vendor-class].text == 'MSF')"
/// this token represents the leading "not"
class TokenNot : public Token {
public:
/// @brief Constructor (does nothing)
TokenNot() {}
/// @brief Logical negation.
///
/// Evaluation does not use packet information, but rather consumes the last
/// result. It does a simple string comparison and sets the value to
/// either "true" or "false". It requires at least one value to be
/// present on stack and to be either "true" or "false".
///
/// @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"
///
/// @param pkt (unused)
/// @param values - stack of values (logical top value negated)
void evaluate(const Pkt& pkt, ValueStack& values);
};
/// @brief Token that represents logical and operator
///
/// For example "option[10].exists and option[11].exists"
class TokenAnd : public Token {
public:
/// @brief Constructor (does nothing)
TokenAnd() {}
/// @brief Logical and.
///
/// Evaluation does not use packet information, but rather consumes the last
/// two parameters. It returns "true" if and only if both are "true".
/// It requires at least two logical (i.e., "true" or "false') values
/// present on stack.
///
/// @throw EvalBadStack if there are less than 2 values on stack
/// @throw EvalTypeError if one of the 2 values on stack is not
/// "true" or "false"
///
/// @param pkt (unused)
/// @param values - stack of values (2 arguments will be popped, 1 result
/// will be pushed)
void evaluate(const Pkt& pkt, ValueStack& values);
};
/// @brief Token that represents logical or operator
///
/// For example "option[10].exists or option[11].exists"
class TokenOr : public Token {
public:
/// @brief Constructor (does nothing)
TokenOr() {}
/// @brief Logical or.
///
/// Evaluation does not use packet information, but rather consumes the last
/// two parameters. It returns "false" if and only if both are "false".
/// It requires at least two logical (i.e., "true" or "false') values
/// present on stack.
///
/// @throw EvalBadStack if there are less than 2 values on stack
/// @throw EvalTypeError if one of the 2 values on stack is not
/// "true" or "false"
///
/// @param pkt (unused)
/// @param values - stack of values (2 arguments will be popped, 1 result
/// will be pushed)
void evaluate(const Pkt& pkt, ValueStack& values);
};
}; // end of isc::dhcp namespace
}; // end of isc namespace