diff --git a/ChangeLog b/ChangeLog index 4fc769b90e..c81bc84a97 100644 --- a/ChangeLog +++ b/ChangeLog @@ -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 diff --git a/doc/guide/classify.xml b/doc/guide/classify.xml index e261281637..f24c712acc 100644 --- a/doc/guide/classify.xml +++ b/doc/guide/classify.xml @@ -164,6 +164,7 @@ Option Textoption[code].textThe value of the option with code "code" from the packet as text --> Option Hexoption[code].hexThe value of the option with code "code" from the packet as hex +Option Existoption[code].existIf the option with code "code" is present in the packet "true" else "false" DHCPv4 Relay Agent sub-optionrelay[code].hexThe 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. + + "option[code].exist" checks if an option with the given code is present + in the incoming packet. It can be used with empty options. + + "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 Equal 'foo' == 'bar'Compare the two values and return "true" or "false" +Not not ('foo' == 'bar')Logical negation +And ('foo' == 'bar') and ('bar' == 'foo')Logical and +Or ('foo' == 'bar') or ('bar' == 'foo')Logical or Substringsubstring('foobar',0,3)Return the requested substring +
+ Logical operators + 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"). +
+
Substring The substring operator "substring(value, start, length)" accepts both positive and @@ -376,7 +394,7 @@ sub-option with code "code" from the Relay Agent Information option - The following example shows restricting access to a DHCPv6 subnet. This + The following example shows restricting access to a DHCPv6 subnet. This configuration will restrict use of the addresses 2001:db8:1::1 to 2001:db8:1::FFFF to members of the "Client_enterprise" class. diff --git a/src/lib/eval/evaluate.cc b/src/lib/eval/evaluate.cc index aba0e1fb4d..630b227310 100644 --- a/src/lib/eval/evaluate.cc +++ b/src/lib/eval/evaluate.cc @@ -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 diff --git a/src/lib/eval/lexer.cc b/src/lib/eval/lexer.cc index 33e5c55d5e..438b5fdf08 100644 --- a/src/lib/eval/lexer.cc +++ b/src/lib/eval/lexer.cc @@ -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 */ @@ -1027,23 +1026,24 @@ 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)]; 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,15 +1591,15 @@ 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; - return yy_is_jam ? 0 : yy_current_state; + return yy_is_jam ? 0 : yy_current_state; } /* %if-c-only */ @@ -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" @@ -2339,7 +2368,7 @@ EvalContext::scanStringBegin() buffer = yy_scan_bytes(string_.c_str(),string_.size()); if (!buffer) { fatal("cannot scan string"); - // fatal() throws an exception so this can't be reached + // fatal() throws an exception so this can't be reached } } diff --git a/src/lib/eval/lexer.ll b/src/lib/eval/lexer.ll index 6911a3a2bc..34aab5df0c 100644 --- a/src/lib/eval/lexer.ll +++ b/src/lib/eval/lexer.ll @@ -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); @@ -151,7 +155,7 @@ EvalContext::scanStringBegin() buffer = yy_scan_bytes(string_.c_str(), string_.size()); if (!buffer) { fatal("cannot scan string"); - // fatal() throws an exception so this can't be reached + // fatal() throws an exception so this can't be reached } } diff --git a/src/lib/eval/location.hh b/src/lib/eval/location.hh index c01735e7bd..82d4891240 100644 --- a/src/lib/eval/location.hh +++ b/src/lib/eval/location.hh @@ -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++ diff --git a/src/lib/eval/parser.cc b/src/lib/eval/parser.cc index 9289f5364c..ab19e2f68a 100644 --- a/src/lib/eval/parser.cc +++ b/src/lib/eval/parser.cc @@ -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 +#line 719 "parser.cc" // lalr1.cc:859 break; - case 10: -#line 127 "parser.yy" // lalr1.cc:847 + case 15: +#line 156 "parser.yy" // lalr1.cc:859 { yylhs.value.as< uint16_t > () = ctx.convertOptionCode(yystack_[0].value.as< std::string > (), yystack_[0].location); } -#line 684 "parser.cc" // lalr1.cc:847 +#line 727 "parser.cc" // lalr1.cc:859 break; - case 11: -#line 131 "parser.yy" // lalr1.cc:847 + 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 692 "parser.cc" // lalr1.cc:847 +#line 735 "parser.cc" // lalr1.cc:859 break; - case 12: -#line 137 "parser.yy" // lalr1.cc:847 + case 17: +#line 166 "parser.yy" // lalr1.cc:859 { yylhs.value.as< TokenOption::RepresentationType > () = TokenOption::TEXTUAL; } -#line 700 "parser.cc" // lalr1.cc:847 +#line 743 "parser.cc" // lalr1.cc:859 break; - case 13: -#line 141 "parser.yy" // lalr1.cc:847 + case 18: +#line 170 "parser.yy" // lalr1.cc:859 { yylhs.value.as< TokenOption::RepresentationType > () = TokenOption::HEXADECIMAL; } -#line 708 "parser.cc" // lalr1.cc:847 +#line 751 "parser.cc" // lalr1.cc:859 break; - case 14: -#line 147 "parser.yy" // lalr1.cc:847 + 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 717 "parser.cc" // lalr1.cc:847 +#line 760 "parser.cc" // lalr1.cc:859 break; - case 15: -#line 154 "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 726 "parser.cc" // lalr1.cc:847 +#line 769 "parser.cc" // lalr1.cc:859 break; - case 16: -#line 159 "parser.yy" // lalr1.cc:847 + 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, diff --git a/src/lib/eval/parser.h b/src/lib/eval/parser.h index 388cb5fbb4..93bc8cf8ce 100644 --- a/src/lib/eval/parser.h +++ b/src/lib/eval/parser.h @@ -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 #include @@ -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 -# include +# include // std::abort # include # include # include +# include # include "stack.hh" # include "location.hh" #include @@ -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 variant (const T& t) - : yytname_ (typeid (T).name ()) + : yytypeid_ (&typeid (T)) { YYASSERT (sizeof (T) <= S); new (yyas_ ()) 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; } @@ -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); } @@ -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_ (); } @@ -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_ (); } @@ -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 (), other.as ()); } @@ -247,7 +248,7 @@ namespace isc { namespace eval { destroy () { as ().~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(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 inline EvalParser::basic_symbol::~basic_symbol () + { + clear (); + } + + template + inline + void + EvalParser::basic_symbol::clear () { // User destructor. symbol_number_type yytype = this->type_get (); + basic_symbol& 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 + inline + bool + EvalParser::basic_symbol::empty () const + { + return Base::type_get () == empty_symbol; } template @@ -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 (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 diff --git a/src/lib/eval/parser.yy b/src/lib/eval/parser.yy index b127442ae3..c8b0f3700b 100644 --- a/src/lib/eval/parser.yy +++ b/src/lib/eval/parser.yy @@ -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 option_code %type 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 diff --git a/src/lib/eval/position.hh b/src/lib/eval/position.hh index ba6f717461..59f4b295f5 100644 --- a/src/lib/eval/position.hh +++ b/src/lib/eval/position.hh @@ -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++ diff --git a/src/lib/eval/stack.hh b/src/lib/eval/stack.hh index 246a2cdd1a..0ad2aa5370 100644 --- a/src/lib/eval/stack.hh +++ b/src/lib/eval/stack.hh @@ -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++ diff --git a/src/lib/eval/tests/Makefile.am b/src/lib/eval/tests/Makefile.am index 77d0ad2bf1..b284fb01e5 100644 --- a/src/lib/eval/tests/Makefile.am +++ b/src/lib/eval/tests/Makefile.am @@ -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 diff --git a/src/lib/eval/tests/boolean_unittest.cc b/src/lib/eval/tests/boolean_unittest.cc new file mode 100644 index 0000000000..5e478ea0fb --- /dev/null +++ b/src/lib/eval/tests/boolean_unittest.cc @@ -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 +#include +#include +#include +#include + +#include +#include +#include + +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); +} + +}; diff --git a/src/lib/eval/tests/context_unittest.cc b/src/lib/eval/tests/context_unittest.cc index a58bfdb085..2e2446bc2b 100644 --- a/src/lib/eval/tests/context_unittest.cc +++ b/src/lib/eval/tests/context_unittest.cc @@ -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 opt = + boost::dynamic_pointer_cast(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 tnot = + boost::dynamic_pointer_cast(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 tand = + boost::dynamic_pointer_cast(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 tor = + boost::dynamic_pointer_cast(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 tand = + boost::dynamic_pointer_cast(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 tor = + boost::dynamic_pointer_cast(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 tnot = + boost::dynamic_pointer_cast(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 tand = + boost::dynamic_pointer_cast(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'", ":1.6-10: syntax error, unexpected constant string, " "expecting =="); + checkError("'foo' (", + ":1.7: syntax error, unexpected (, expecting =="); checkError("== 'ab'", ":1.1-2: syntax error, unexpected =="); checkError("'foo' ==", ":1.9: syntax error, unexpected end of file"); + checkError("('foo' == 'bar'", + ":1.16: syntax error, unexpected end of file, " + "expecting and or or or )"); + checkError("('foo' == 'bar') ''", + ":1.18-19: syntax error, unexpected constant string, " + "expecting end of file"); + checkError("not", + ":1.4: syntax error, unexpected end of file"); + checkError("not 'foo'", + ":1.10: syntax error, unexpected end of file, " + "expecting =="); + checkError("not()", + ":1.5: syntax error, unexpected )"); + checkError("(not('foo' 'bar')", + ":1.12-16: syntax error, unexpected constant string, " + "expecting =="); + checkError("and", + ":1.1-3: syntax error, unexpected and"); + checkError("'foo' and", + ":1.7-9: syntax error, unexpected and, expecting =="); + checkError("'foo' == 'bar' and", + ":1.19: syntax error, unexpected end of file"); + checkError("'foo' == 'bar' and ''", + ":1.22: syntax error, unexpected end of file, " + "expecting =="); + checkError("or", + ":1.1-2: syntax error, unexpected or"); + checkError("'foo' or", + ":1.7-8: syntax error, unexpected or, expecting =="); + checkError("'foo' == 'bar' or", + ":1.18: syntax error, unexpected end of file"); + checkError("'foo' == 'bar' or ''", + ":1.21: syntax error, unexpected end of file, " + "expecting =="); checkError("option 'ab'", ":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", ":1.12: Invalid character: b"); checkError("option[boot-size].bin", ":1.19: Invalid character: b"); + checkError("option[10].exists == 'foo'", + ":1.19-20: syntax error, unexpected ==, " + "expecting end of file"); checkError("substring('foobar') == 'f'", ":1.19: syntax error, " "unexpected ), expecting \",\""); @@ -402,7 +557,7 @@ TEST_F(EvalContextTest, parseErrors) { ":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'", ":1.9: syntax error, unexpected end of file, " @@ -413,6 +568,16 @@ TEST_F(EvalContextTest, typeErrors) { checkError("substring('foobar',0x32,1) == 'foo'", ":1.20-23: syntax error, unexpected constant " "hexstring, expecting integer"); + checkError("('foo' == 'bar') == 'false'", + ":1.18-19: syntax error, unexpected ==, " + "expecting end of file"); + checkError("not 'true'", + ":1.11: syntax error, unexpected end of file, " + "expecting =="); + checkError("'true' and 'false'", + ":1.8-10: syntax error, unexpected and, expecting =="); + checkError("'true' or 'false'", + ":1.8-9: syntax error, unexpected or, expecting =="); } }; diff --git a/src/lib/eval/tests/evaluate_unittest.cc b/src/lib/eval/tests/evaluate_unittest.cc index 9c25f229f8..e08204a58b 100644 --- a/src/lib/eval/tests/evaluate_unittest.cc +++ b/src/lib/eval/tests/evaluate_unittest.cc @@ -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; diff --git a/src/lib/eval/tests/token_unittest.cc b/src/lib/eval/tests/token_unittest.cc index d475fe6d5f..de10b7a3fe 100644 --- a/src/lib/eval/tests/token_unittest.cc +++ b/src/lib/eval/tests/token_unittest.cc @@ -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 diff --git a/src/lib/eval/token.cc b/src/lib/eval/token.cc index 78e080d24c..72e73e91fb 100644 --- a/src/lib/eval/token.cc +++ b/src/lib/eval/token.cc @@ -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 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"); + } +} diff --git a/src/lib/eval/token.h b/src/lib/eval/token.h index 6b512175a2..7dd7a022eb 100644 --- a/src/lib/eval/token.h +++ b/src/lib/eval/token.h @@ -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