From eec10b436b928bd5eb3139c2f98ec9a6a818f30e Mon Sep 17 00:00:00 2001 From: Francis Dupont Date: Fri, 19 Feb 2016 17:21:36 +0100 Subject: [PATCH] [master] Merged trac4233 (new concat string function to classification expression) --- ChangeLog | 7 +- doc/guide/classify.xml | 9 + src/lib/eval/lexer.cc | 261 +++++++++++++------------ src/lib/eval/lexer.ll | 3 +- src/lib/eval/location.hh | 2 +- src/lib/eval/parser.cc | 199 +++++++++---------- src/lib/eval/parser.h | 101 +++++----- src/lib/eval/parser.yy | 9 +- src/lib/eval/position.hh | 2 +- src/lib/eval/stack.hh | 2 +- src/lib/eval/tests/context_unittest.cc | 89 ++++++--- src/lib/eval/tests/token_unittest.cc | 240 ++++++++++++----------- src/lib/eval/token.cc | 41 ++-- src/lib/eval/token.h | 67 ++++--- 14 files changed, 577 insertions(+), 455 deletions(-) diff --git a/ChangeLog b/ChangeLog index c81bc84a97..023dc8521e 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,8 +1,13 @@ +1080. [func] fdupont + Added a concat function in classification which concatenates two + strings. + (Trac #4233, git ...) + 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) + (Trac #4231, git 8e01dbe2fe2d8c97f89c20f5bb1d03748a2432e0) 1078. [func] tomek Client classification in DHCPv4 has been enhanced. It is now diff --git a/doc/guide/classify.xml b/doc/guide/classify.xml index f24c712acc..57419dea2f 100644 --- a/doc/guide/classify.xml +++ b/doc/guide/classify.xml @@ -224,6 +224,7 @@ sub-option with code "code" from the Relay Agent Information option And ('foo' == 'bar') and ('bar' == 'foo')Logical and Or ('foo' == 'bar') or ('bar' == 'foo')Logical or Substringsubstring('foobar',0,3)Return the requested substring +Concatconcat('foo','bar')Return the concatenation of the strings @@ -261,6 +262,14 @@ sub-option with code "code" from the Relay Agent Information option substring('foobar', 10, 2) == '' +
+ Concat + The concat function "concat(string1, string2)" returns the + concatenation of its two arguments. For instance: + + concat('foo', 'bar') == 'foobar' + +
diff --git a/src/lib/eval/lexer.cc b/src/lib/eval/lexer.cc index 438b5fdf08..c95a7c06d6 100644 --- a/src/lib/eval/lexer.cc +++ b/src/lib/eval/lexer.cc @@ -460,8 +460,8 @@ static void yy_fatal_error (yyconst char msg[] ); (yy_c_buf_p) = yy_cp; /* %% [4.0] data tables for the DFA and the user's section 1 definitions go here */ -#define YY_NUM_RULES 25 -#define YY_END_OF_BUFFER 26 +#define YY_NUM_RULES 26 +#define YY_END_OF_BUFFER 27 /* This struct is not used in this scanner, but its presence is necessary. */ struct yy_trans_info @@ -469,33 +469,35 @@ struct yy_trans_info flex_int32_t yy_verify; flex_int32_t yy_nxt; }; -static yyconst flex_int16_t yy_acclist[119] = +static yyconst flex_int16_t yy_acclist[128] = { 0, - 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, + 27, 25, 26, 1, 25, 26, 2, 26, 25, 26, + 20, 25, 26, 21, 25, 26, 24, 25, 26, 25, + 26, 19, 25, 26, 5, 25, 26, 5, 25, 26, + 25, 26, 25, 26,16390, 22, 25, 26, 23, 25, + 26, 25, 26,16390, 25, 26,16390, 25, 26,16390, + 25, 26,16390, 25, 26,16390, 25, 26,16390, 25, + 26,16390, 25, 26,16390, 25, 26,16390, 1, 2, + 3, 5, 7,16390, 8198,16390,16390,16390,16390,16390, + 16390,16390, 18,16390,16390,16390,16390, 4, 14,16390, + 17,16390,16390,16390, 10,16390, 16,16390,16390,16390, - 16390, 9,16390,16390,16390,16390,16390, 11,16390, 8, - 16390, 13,16390,16390,16390,16390, 12,16390 + 16390,16390,16390,16390,16390,16390,16390, 9,16390,16390, + 16390,16390,16390,16390, 15,16390, 11,16390, 8,16390, + 12,16390,16390,16390,16390, 13,16390 } ; -static yyconst flex_int16_t yy_accept[76] = +static yyconst flex_int16_t yy_accept[82] = { 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, 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 + 48, 51, 54, 57, 60, 63, 66, 69, 70, 71, + 71, 72, 73, 73, 74, 74, 74, 75, 76, 77, + 78, 79, 80, 81, 82, 83, 85, 86, 87, 88, + 89, 91, 93, 94, 95, 97, 99, 100, 101, 102, + 103, 104, 105, 106, 107, 108, 110, 111, 112, 113, + 114, 115, 117, 119, 121, 123, 124, 125, 126, 128, + 128 } ; static yyconst flex_int32_t yy_ec[256] = @@ -509,11 +511,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, 22, + 17, 1, 18, 1, 19, 1, 20, 21, 22, 23, - 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, + 24, 14, 25, 26, 27, 15, 15, 28, 15, 29, + 30, 31, 15, 32, 33, 34, 35, 15, 15, 36, + 37, 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, @@ -530,114 +532,118 @@ static yyconst flex_int32_t yy_ec[256] = 1, 1, 1, 1, 1 } ; -static yyconst flex_int32_t yy_meta[37] = +static yyconst flex_int32_t yy_meta[38] = { 0, 1, 2, 3, 1, 1, 1, 1, 2, 1, 4, 4, 4, 1, 4, 2, 2, 1, 2, 2, 4, - 4, 4, 4, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2 + 4, 4, 4, 4, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2 } ; -static yyconst flex_int16_t yy_base[78] = +static yyconst flex_int16_t yy_base[84] = { 0, - 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 + 0, 0, 202, 219, 167, 155, 138, 219, 219, 219, + 28, 219, 31, 34, 94, 46, 219, 219, 66, 22, + 27, 29, 31, 44, 42, 51, 53, 103, 80, 74, + 219, 77, 0, 219, 88, 90, 68, 219, 70, 81, + 72, 84, 86, 92, 95, 74, 88, 104, 99, 0, + 102, 108, 112, 110, 116, 118, 121, 131, 124, 127, + 134, 136, 138, 143, 145, 147, 149, 153, 156, 175, + 160, 162, 165, 167, 169, 172, 177, 182, 180, 219, + 211, 214, 58 } ; -static yyconst flex_int16_t yy_def[78] = +static yyconst flex_int16_t yy_def[84] = { 0, - 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, + 80, 1, 80, 80, 80, 80, 81, 80, 80, 80, + 80, 80, 80, 80, 80, 82, 80, 80, 82, 19, + 19, 19, 19, 19, 19, 19, 19, 80, 80, 81, + 80, 80, 83, 80, 80, 19, 19, 80, 19, 19, + 19, 19, 19, 19, 19, 19, 19, 19, 19, 83, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, - 19, 19, 19, 0, 74, 74, 74 + 19, 19, 19, 19, 19, 19, 19, 19, 19, 0, + 80, 80, 80 } ; -static yyconst flex_int16_t yy_nxt[242] = +static yyconst flex_int16_t yy_nxt[257] = { 0, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 14, 15, 16, 16, 16, 17, 18, 4, 19, - 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, + 16, 20, 16, 21, 16, 22, 16, 16, 23, 24, + 16, 25, 26, 27, 16, 16, 16, 32, 32, 32, + 32, 32, 32, 32, 32, 32, 33, 35, 35, 37, + 37, 41, 43, 36, 37, 37, 37, 37, 37, 37, + 44, 50, 42, 38, 36, 47, 33, 35, 35, 37, + 37, 37, 37, 36, 45, 46, 49, 31, 37, 37, + 37, 37, 29, 38, 36, 48, 32, 32, 32, 35, + 35, 80, 80, 39, 40, 37, 37, 51, 37, 37, - 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, + 53, 37, 37, 52, 28, 38, 34, 80, 37, 37, + 54, 37, 37, 37, 37, 58, 37, 37, 37, 37, + 37, 55, 37, 37, 59, 56, 37, 37, 57, 37, + 37, 37, 37, 61, 60, 37, 37, 37, 37, 37, + 37, 31, 62, 37, 37, 37, 37, 63, 37, 37, + 64, 37, 37, 67, 37, 37, 65, 29, 37, 37, + 66, 37, 37, 37, 37, 37, 37, 69, 28, 68, + 37, 37, 37, 37, 37, 37, 37, 37, 71, 70, + 37, 37, 72, 37, 74, 73, 75, 37, 37, 37, + 37, 76, 37, 37, 37, 37, 37, 37, 77, 37, - 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 + 37, 80, 37, 37, 37, 78, 79, 37, 37, 37, + 37, 30, 30, 80, 30, 37, 37, 37, 3, 80, + 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, + 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, + 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, + 80, 80, 80, 80, 80, 80 } ; -static yyconst flex_int16_t yy_chk[242] = +static yyconst flex_int16_t yy_chk[257] = { 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, 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, + 1, 1, 1, 1, 1, 1, 1, 11, 11, 11, + 13, 13, 13, 14, 14, 14, 13, 16, 16, 20, + 20, 20, 22, 16, 21, 21, 22, 22, 23, 23, + 23, 83, 21, 16, 16, 25, 13, 19, 19, 25, + 25, 24, 24, 19, 24, 24, 27, 30, 26, 26, + 27, 27, 29, 19, 19, 26, 32, 32, 32, 35, + 35, 36, 36, 19, 19, 37, 37, 39, 39, 41, - 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, + 41, 46, 46, 40, 28, 35, 15, 36, 40, 40, + 42, 42, 42, 43, 43, 47, 47, 36, 36, 44, + 44, 43, 45, 45, 48, 44, 49, 49, 45, 51, + 51, 48, 48, 53, 49, 52, 52, 54, 54, 53, + 53, 7, 54, 55, 55, 56, 56, 57, 57, 57, + 58, 59, 59, 61, 60, 60, 59, 6, 58, 58, + 60, 61, 61, 62, 62, 63, 63, 63, 5, 62, + 64, 64, 65, 65, 66, 66, 67, 67, 65, 64, + 68, 68, 67, 69, 69, 68, 70, 71, 71, 72, + 72, 71, 73, 73, 74, 74, 75, 75, 76, 76, - 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 + 76, 3, 70, 70, 77, 77, 78, 79, 79, 78, + 78, 81, 81, 0, 81, 82, 82, 82, 80, 80, + 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, + 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, + 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, + 80, 80, 80, 80, 80, 80 } ; /* Table of booleans, true if rule could match eol. */ -static yyconst flex_int32_t yy_rule_can_match_eol[26] = +static yyconst flex_int32_t yy_rule_can_match_eol[27] = { 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, 0, 0, 0, 0, 0, }; extern int yy_flex_debug; int yy_flex_debug = 1; -static yyconst flex_int16_t yy_rule_linenum[25] = +static yyconst flex_int16_t yy_rule_linenum[26] = { 0, 78, 82, 88, 98, 104, 118, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, - 139, 140, 141, 143 + 139, 140, 141, 142, 144 } ; static yy_state_type *yy_state_buf=0, *yy_state_ptr=0; @@ -714,7 +720,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 718 "lexer.cc" +#line 724 "lexer.cc" #define INITIAL 0 @@ -962,7 +968,7 @@ YY_DECL loc.step(); -#line 966 "lexer.cc" +#line 972 "lexer.cc" if ( !(yy_init) ) { @@ -1030,14 +1036,14 @@ 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 >= 75 ) + if ( yy_current_state >= 81 ) 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 != 74 ); + while ( yy_current_state != 80 ); yy_find_action: /* %% [10.0] code to find the action number goes here */ @@ -1101,13 +1107,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 < 25 ) + else if ( yy_act < 26 ) fprintf( stderr, "--accepting rule at line %ld (\"%s\")\n", (long)yy_rule_linenum[yy_act], yytext ); - else if ( yy_act == 25 ) + else if ( yy_act == 26 ) fprintf( stderr, "--accepting default rule (\"%s\")\n", yytext ); - else if ( yy_act == 26 ) + else if ( yy_act == 27 ) fprintf( stderr, "--(end of buffer or a NUL)\n" ); else fprintf( stderr, "--EOF (start condition %d)\n", YY_START ); @@ -1212,12 +1218,12 @@ return isc::eval::EvalParser::make_EXISTS(loc); case 12: YY_RULE_SETUP #line 130 "lexer.ll" -return isc::eval::EvalParser::make_SUBSTRING(loc); +return isc::eval::EvalParser::make_RELAY4(loc); YY_BREAK case 13: YY_RULE_SETUP #line 131 "lexer.ll" -return isc::eval::EvalParser::make_RELAY4(loc); +return isc::eval::EvalParser::make_SUBSTRING(loc); YY_BREAK case 14: YY_RULE_SETUP @@ -1227,63 +1233,68 @@ return isc::eval::EvalParser::make_ALL(loc); case 15: YY_RULE_SETUP #line 133 "lexer.ll" -return isc::eval::EvalParser::make_NOT(loc); +return isc::eval::EvalParser::make_CONCAT(loc); YY_BREAK case 16: YY_RULE_SETUP #line 134 "lexer.ll" -return isc::eval::EvalParser::make_AND(loc); +return isc::eval::EvalParser::make_NOT(loc); YY_BREAK case 17: YY_RULE_SETUP #line 135 "lexer.ll" -return isc::eval::EvalParser::make_OR(loc); +return isc::eval::EvalParser::make_AND(loc); YY_BREAK case 18: YY_RULE_SETUP #line 136 "lexer.ll" -return isc::eval::EvalParser::make_DOT(loc); +return isc::eval::EvalParser::make_OR(loc); YY_BREAK case 19: YY_RULE_SETUP #line 137 "lexer.ll" -return isc::eval::EvalParser::make_LPAREN(loc); +return isc::eval::EvalParser::make_DOT(loc); YY_BREAK case 20: YY_RULE_SETUP #line 138 "lexer.ll" -return isc::eval::EvalParser::make_RPAREN(loc); +return isc::eval::EvalParser::make_LPAREN(loc); YY_BREAK case 21: YY_RULE_SETUP #line 139 "lexer.ll" -return isc::eval::EvalParser::make_LBRACKET(loc); +return isc::eval::EvalParser::make_RPAREN(loc); YY_BREAK case 22: YY_RULE_SETUP #line 140 "lexer.ll" -return isc::eval::EvalParser::make_RBRACKET(loc); +return isc::eval::EvalParser::make_LBRACKET(loc); YY_BREAK case 23: YY_RULE_SETUP #line 141 "lexer.ll" -return isc::eval::EvalParser::make_COMA(loc); +return isc::eval::EvalParser::make_RBRACKET(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); +#line 142 "lexer.ll" +return isc::eval::EvalParser::make_COMA(loc); YY_BREAK case 25: YY_RULE_SETUP +#line 144 "lexer.ll" +driver.error (loc, "Invalid character: " + std::string(yytext)); + YY_BREAK +case YY_STATE_EOF(INITIAL): #line 145 "lexer.ll" +return isc::eval::EvalParser::make_END(loc); + YY_BREAK +case 26: +YY_RULE_SETUP +#line 146 "lexer.ll" ECHO; YY_BREAK -#line 1287 "lexer.cc" +#line 1298 "lexer.cc" case YY_END_OF_BUFFER: { @@ -1563,7 +1574,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 >= 75 ) + if ( yy_current_state >= 81 ) yy_c = yy_meta[(unsigned int) yy_c]; } yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c]; @@ -1591,11 +1602,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 >= 75 ) + if ( yy_current_state >= 81 ) 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 == 74); + yy_is_jam = (yy_current_state == 80); if ( ! yy_is_jam ) *(yy_state_ptr)++ = yy_current_state; @@ -2353,7 +2364,7 @@ void yyfree (void * ptr ) /* %ok-for-header */ -#line 145 "lexer.ll" +#line 146 "lexer.ll" diff --git a/src/lib/eval/lexer.ll b/src/lib/eval/lexer.ll index 34aab5df0c..e0dace0741 100644 --- a/src/lib/eval/lexer.ll +++ b/src/lib/eval/lexer.ll @@ -127,9 +127,10 @@ blank [ \t] "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); +"substring" return isc::eval::EvalParser::make_SUBSTRING(loc); "all" return isc::eval::EvalParser::make_ALL(loc); +"concat" return isc::eval::EvalParser::make_CONCAT(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); diff --git a/src/lib/eval/location.hh b/src/lib/eval/location.hh index 82d4891240..8c57aa1b64 100644 --- a/src/lib/eval/location.hh +++ b/src/lib/eval/location.hh @@ -1,4 +1,4 @@ -// Generated 20160219 +// Generated at 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 ab19e2f68a..34467ec460 100644 --- a/src/lib/eval/parser.cc +++ b/src/lib/eval/parser.cc @@ -255,11 +255,10 @@ namespace isc { namespace eval { value.move< TokenOption::RepresentationType > (that.value); break; - case 20: // "constant string" - case 21: // "integer" - case 22: // "constant hexstring" - case 23: // "option name" - case 24: // TOKEN + case 21: // "constant string" + case 22: // "integer" + case 23: // "constant hexstring" + case 24: // "option name" value.move< std::string > (that.value); break; @@ -286,11 +285,10 @@ namespace isc { namespace eval { value.copy< TokenOption::RepresentationType > (that.value); break; - case 20: // "constant string" - case 21: // "integer" - case 22: // "constant hexstring" - case 23: // "option name" - case 24: // TOKEN + case 21: // "constant string" + case 22: // "integer" + case 23: // "constant hexstring" + case 24: // "option name" value.copy< std::string > (that.value); break; @@ -334,53 +332,46 @@ namespace isc { namespace eval { << yysym.location << ": "; switch (yytype) { - case 20: // "constant string" + case 21: // "constant string" #line 71 "parser.yy" // lalr1.cc:636 { yyoutput << yysym.value.template as< std::string > (); } -#line 342 "parser.cc" // lalr1.cc:636 +#line 340 "parser.cc" // lalr1.cc:636 break; - case 21: // "integer" + case 22: // "integer" #line 71 "parser.yy" // lalr1.cc:636 { yyoutput << yysym.value.template as< std::string > (); } -#line 349 "parser.cc" // lalr1.cc:636 +#line 347 "parser.cc" // lalr1.cc:636 break; - case 22: // "constant hexstring" + case 23: // "constant hexstring" #line 71 "parser.yy" // lalr1.cc:636 { yyoutput << yysym.value.template as< std::string > (); } -#line 356 "parser.cc" // lalr1.cc:636 +#line 354 "parser.cc" // lalr1.cc:636 break; - case 23: // "option name" + case 24: // "option name" #line 71 "parser.yy" // lalr1.cc:636 { yyoutput << yysym.value.template as< std::string > (); } -#line 363 "parser.cc" // lalr1.cc:636 - break; - - case 24: // TOKEN - -#line 71 "parser.yy" // lalr1.cc:636 - { yyoutput << yysym.value.template as< std::string > (); } -#line 370 "parser.cc" // lalr1.cc:636 +#line 361 "parser.cc" // lalr1.cc:636 break; case 29: // option_code #line 71 "parser.yy" // lalr1.cc:636 { yyoutput << yysym.value.template as< uint16_t > (); } -#line 377 "parser.cc" // lalr1.cc:636 +#line 368 "parser.cc" // lalr1.cc:636 break; case 30: // option_repr_type #line 71 "parser.yy" // lalr1.cc:636 { yyoutput << yysym.value.template as< TokenOption::RepresentationType > (); } -#line 384 "parser.cc" // lalr1.cc:636 +#line 375 "parser.cc" // lalr1.cc:636 break; @@ -584,11 +575,10 @@ namespace isc { namespace eval { yylhs.value.build< TokenOption::RepresentationType > (); break; - case 20: // "constant string" - case 21: // "integer" - case 22: // "constant hexstring" - case 23: // "option name" - case 24: // TOKEN + case 21: // "constant string" + case 22: // "integer" + case 23: // "constant hexstring" + case 24: // "option name" yylhs.value.build< std::string > (); break; @@ -619,7 +609,7 @@ namespace isc { namespace eval { TokenPtr neg(new TokenNot()); ctx.expression.push_back(neg); } -#line 623 "parser.cc" // lalr1.cc:859 +#line 613 "parser.cc" // lalr1.cc:859 break; case 5: @@ -628,7 +618,7 @@ namespace isc { namespace eval { TokenPtr neg(new TokenAnd()); ctx.expression.push_back(neg); } -#line 632 "parser.cc" // lalr1.cc:859 +#line 622 "parser.cc" // lalr1.cc:859 break; case 6: @@ -637,7 +627,7 @@ namespace isc { namespace eval { TokenPtr neg(new TokenOr()); ctx.expression.push_back(neg); } -#line 641 "parser.cc" // lalr1.cc:859 +#line 631 "parser.cc" // lalr1.cc:859 break; case 7: @@ -646,7 +636,7 @@ namespace isc { namespace eval { TokenPtr eq(new TokenEqual()); ctx.expression.push_back(eq); } -#line 650 "parser.cc" // lalr1.cc:859 +#line 640 "parser.cc" // lalr1.cc:859 break; case 8: @@ -655,7 +645,7 @@ namespace isc { namespace eval { TokenPtr opt(new TokenOption(yystack_[3].value.as< uint16_t > (), TokenOption::EXISTS)); ctx.expression.push_back(opt); } -#line 659 "parser.cc" // lalr1.cc:859 +#line 649 "parser.cc" // lalr1.cc:859 break; case 9: @@ -664,7 +654,7 @@ namespace isc { namespace eval { TokenPtr str(new TokenString(yystack_[0].value.as< std::string > ())); ctx.expression.push_back(str); } -#line 668 "parser.cc" // lalr1.cc:859 +#line 658 "parser.cc" // lalr1.cc:859 break; case 10: @@ -673,7 +663,7 @@ namespace isc { namespace eval { TokenPtr hex(new TokenHexString(yystack_[0].value.as< std::string > ())); ctx.expression.push_back(hex); } -#line 677 "parser.cc" // lalr1.cc:859 +#line 667 "parser.cc" // lalr1.cc:859 break; case 11: @@ -682,7 +672,7 @@ namespace isc { namespace eval { TokenPtr opt(new TokenOption(yystack_[3].value.as< uint16_t > (), yystack_[0].value.as< TokenOption::RepresentationType > ())); ctx.expression.push_back(opt); } -#line 686 "parser.cc" // lalr1.cc:859 +#line 676 "parser.cc" // lalr1.cc:859 break; case 12: @@ -706,7 +696,7 @@ namespace isc { namespace eval { error(yystack_[5].location, "relay4 can only be used in DHCPv4."); } } -#line 710 "parser.cc" // lalr1.cc:859 +#line 700 "parser.cc" // lalr1.cc:859 break; case 13: @@ -715,70 +705,79 @@ namespace isc { namespace eval { TokenPtr sub(new TokenSubstring()); ctx.expression.push_back(sub); } -#line 719 "parser.cc" // lalr1.cc:859 +#line 709 "parser.cc" // lalr1.cc:859 + break; + + case 14: +#line 152 "parser.yy" // lalr1.cc:859 + { + TokenPtr conc(new TokenConcat()); + ctx.expression.push_back(conc); + } +#line 718 "parser.cc" // lalr1.cc:859 break; case 15: -#line 156 "parser.yy" // lalr1.cc:859 +#line 159 "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 +#line 726 "parser.cc" // lalr1.cc:859 break; case 16: -#line 160 "parser.yy" // lalr1.cc:859 +#line 163 "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 +#line 734 "parser.cc" // lalr1.cc:859 break; case 17: -#line 166 "parser.yy" // lalr1.cc:859 +#line 169 "parser.yy" // lalr1.cc:859 { yylhs.value.as< TokenOption::RepresentationType > () = TokenOption::TEXTUAL; } -#line 743 "parser.cc" // lalr1.cc:859 +#line 742 "parser.cc" // lalr1.cc:859 break; case 18: -#line 170 "parser.yy" // lalr1.cc:859 +#line 173 "parser.yy" // lalr1.cc:859 { yylhs.value.as< TokenOption::RepresentationType > () = TokenOption::HEXADECIMAL; } -#line 751 "parser.cc" // lalr1.cc:859 +#line 750 "parser.cc" // lalr1.cc:859 break; case 19: -#line 176 "parser.yy" // lalr1.cc:859 +#line 179 "parser.yy" // lalr1.cc:859 { TokenPtr str(new TokenString(yystack_[0].value.as< std::string > ())); ctx.expression.push_back(str); } -#line 760 "parser.cc" // lalr1.cc:859 +#line 759 "parser.cc" // lalr1.cc:859 break; case 20: -#line 183 "parser.yy" // lalr1.cc:859 +#line 186 "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 +#line 768 "parser.cc" // lalr1.cc:859 break; case 21: -#line 188 "parser.yy" // lalr1.cc:859 +#line 191 "parser.yy" // lalr1.cc:859 { TokenPtr str(new TokenString("all")); ctx.expression.push_back(str); } -#line 778 "parser.cc" // lalr1.cc:859 +#line 777 "parser.cc" // lalr1.cc:859 break; -#line 782 "parser.cc" // lalr1.cc:859 +#line 781 "parser.cc" // lalr1.cc:859 default: break; } @@ -1033,75 +1032,77 @@ namespace isc { namespace eval { } - const signed char EvalParser::yypact_ninf_ = -11; + const signed char EvalParser::yypact_ninf_ = -14; const signed char EvalParser::yytable_ninf_ = -1; const signed char EvalParser::yypact_[] = { - -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 + -2, -12, -4, 3, -2, 14, -2, -14, -14, 39, + 33, 40, 16, 6, 6, -14, 16, 17, -14, -2, + -2, 6, -14, -14, 25, 27, 28, 31, 29, -14, + -14, 42, -14, 36, 16, 26, 6, 37, 24, 34, + -14, 41, 35, 18, -14, -14, -14, -14, 43, 0, + -14, -14, 18, -14, -14, 38, -14 }; const unsigned char EvalParser::yydefact_[] = { - 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 + 0, 0, 0, 0, 0, 0, 0, 9, 10, 0, + 2, 0, 0, 0, 0, 4, 0, 0, 1, 0, + 0, 0, 15, 16, 0, 0, 0, 0, 0, 3, + 5, 6, 7, 0, 0, 0, 0, 0, 0, 0, + 19, 0, 0, 0, 17, 18, 8, 11, 0, 0, + 14, 12, 0, 21, 20, 0, 13 }; const signed char EvalParser::yypgoto_[] = { - -11, -11, 8, 15, -10, 18, -11, -11 + -14, -14, 12, -13, -10, 19, -14, -14 }; const signed char EvalParser::yydefgoto_[] = { - -1, 9, 10, 11, 23, 43, 38, 50 + -1, 9, 10, 11, 24, 47, 41, 55 }; const unsigned char EvalParser::yytable_[] = { - 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 + 26, 27, 1, 2, 3, 4, 28, 12, 32, 5, + 25, 2, 3, 13, 53, 6, 15, 5, 17, 7, + 14, 8, 54, 42, 39, 19, 20, 7, 44, 8, + 45, 30, 31, 16, 44, 29, 45, 46, 22, 18, + 23, 19, 20, 21, 35, 33, 34, 36, 40, 37, + 19, 38, 43, 50, 48, 0, 56, 49, 52, 0, + 0, 0, 51 }; const signed char EvalParser::yycheck_[] = { - 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 + 13, 14, 4, 5, 6, 7, 16, 19, 21, 11, + 4, 5, 6, 17, 14, 17, 4, 11, 6, 21, + 17, 23, 22, 36, 34, 8, 9, 21, 10, 23, + 12, 19, 20, 19, 10, 18, 12, 13, 22, 0, + 24, 8, 9, 3, 16, 20, 19, 16, 22, 20, + 8, 15, 15, 18, 20, -1, 18, 16, 15, -1, + -1, -1, 43 }; const unsigned char EvalParser::yystos_[] = { - 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 + 0, 4, 5, 6, 7, 11, 17, 21, 23, 26, + 27, 28, 19, 17, 17, 27, 19, 27, 0, 8, + 9, 3, 22, 24, 29, 4, 28, 28, 29, 18, + 27, 27, 28, 20, 19, 16, 16, 20, 15, 29, + 22, 31, 28, 15, 10, 12, 13, 30, 20, 16, + 18, 30, 15, 14, 22, 32, 18 }; const unsigned char @@ -1116,7 +1117,7 @@ namespace isc { namespace eval { EvalParser::yyr2_[] = { 0, 2, 1, 3, 2, 3, 3, 3, 6, 1, - 1, 6, 6, 8, 1, 1, 1, 1, 1, 1, + 1, 6, 6, 8, 6, 1, 1, 1, 1, 1, 1, 1 }; @@ -1128,10 +1129,10 @@ namespace isc { namespace eval { const EvalParser::yytname_[] = { "\"end of file\"", "error", "$undefined", "\"==\"", "\"option\"", - "\"substring\"", "\"not\"", "\"and\"", "\"or\"", "\"text\"", - "\"relay4\"", "\"hex\"", "\"exists\"", "\"all\"", "\".\"", "\",\"", - "\"(\"", "\")\"", "\"[\"", "\"]\"", "\"constant string\"", "\"integer\"", - "\"constant hexstring\"", "\"option name\"", "TOKEN", "$accept", + "\"substring\"", "\"concat\"", "\"not\"", "\"and\"", "\"or\"", + "\"text\"", "\"relay4\"", "\"hex\"", "\"exists\"", "\"all\"", "\".\"", + "\",\"", "\"(\"", "\")\"", "\"[\"", "\"]\"", "\"constant string\"", + "\"integer\"", "\"constant hexstring\"", "\"option name\"", "$accept", "expression", "bool_expr", "string_expr", "option_code", "option_repr_type", "start_expr", "length_expr", YY_NULLPTR }; @@ -1141,8 +1142,8 @@ namespace isc { namespace eval { EvalParser::yyrline_[] = { 0, 80, 80, 83, 84, 89, 94, 99, 104, 111, - 116, 121, 126, 146, 151, 155, 159, 165, 169, 175, - 182, 187 + 116, 121, 126, 146, 151, 158, 162, 168, 172, 178, + 185, 190 }; // Print the state stack on the debug stream. @@ -1177,8 +1178,8 @@ namespace isc { namespace eval { #line 13 "parser.yy" // lalr1.cc:1167 } } // isc::eval -#line 1181 "parser.cc" // lalr1.cc:1167 -#line 194 "parser.yy" // lalr1.cc:1168 +#line 1182 "parser.cc" // lalr1.cc:1167 +#line 197 "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 93bc8cf8ce..d77fd9966f 100644 --- a/src/lib/eval/parser.h +++ b/src/lib/eval/parser.h @@ -302,7 +302,6 @@ namespace isc { namespace eval { // "integer" // "constant hexstring" // "option name" - // TOKEN char dummy2[sizeof(std::string)]; // option_code @@ -333,25 +332,25 @@ namespace isc { namespace eval { TOKEN_EQUAL = 258, TOKEN_OPTION = 259, TOKEN_SUBSTRING = 260, - 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 + TOKEN_CONCAT = 261, + TOKEN_NOT = 262, + TOKEN_AND = 263, + TOKEN_OR = 264, + TOKEN_TEXT = 265, + TOKEN_RELAY4 = 266, + TOKEN_HEX = 267, + TOKEN_EXISTS = 268, + TOKEN_ALL = 269, + TOKEN_DOT = 270, + TOKEN_COMA = 271, + TOKEN_LPAREN = 272, + TOKEN_RPAREN = 273, + TOKEN_LBRACKET = 274, + TOKEN_RBRACKET = 275, + TOKEN_STRING = 276, + TOKEN_INTEGER = 277, + TOKEN_HEXSTRING = 278, + TOKEN_OPTION_NAME = 279 }; }; @@ -478,6 +477,10 @@ namespace isc { namespace eval { symbol_type make_SUBSTRING (const location_type& l); + static inline + symbol_type + make_CONCAT (const location_type& l); + static inline symbol_type make_NOT (const location_type& l); @@ -550,10 +553,6 @@ namespace isc { namespace eval { symbol_type make_OPTION_NAME (const std::string& v, const location_type& l); - static inline - symbol_type - make_TOKEN (const std::string& v, const location_type& l); - /// Build a parser object. EvalParser (EvalContext& ctx_yyarg); @@ -759,9 +758,9 @@ namespace isc { namespace eval { enum { yyeof_ = 0, - yylast_ = 57, ///< Last index in yytable_. + yylast_ = 62, ///< Last index in yytable_. yynnts_ = 8, ///< Number of nonterminal symbols. - yyfinal_ = 17, ///< Termination state number. + yyfinal_ = 18, ///< Termination state number. yyterror_ = 1, yyerrcode_ = 256, yyntokens_ = 25 ///< Number of tokens. @@ -847,11 +846,10 @@ namespace isc { namespace eval { value.copy< TokenOption::RepresentationType > (other.value); break; - case 20: // "constant string" - case 21: // "integer" - case 22: // "constant hexstring" - case 23: // "option name" - case 24: // TOKEN + case 21: // "constant string" + case 22: // "integer" + case 23: // "constant hexstring" + case 24: // "option name" value.copy< std::string > (other.value); break; @@ -880,11 +878,10 @@ namespace isc { namespace eval { value.copy< TokenOption::RepresentationType > (v); break; - case 20: // "constant string" - case 21: // "integer" - case 22: // "constant hexstring" - case 23: // "option name" - case 24: // TOKEN + case 21: // "constant string" + case 22: // "integer" + case 23: // "constant hexstring" + case 24: // "option name" value.copy< std::string > (v); break; @@ -958,11 +955,10 @@ namespace isc { namespace eval { value.template destroy< TokenOption::RepresentationType > (); break; - case 20: // "constant string" - case 21: // "integer" - case 22: // "constant hexstring" - case 23: // "option name" - case 24: // TOKEN + case 21: // "constant string" + case 22: // "integer" + case 23: // "constant hexstring" + case 24: // "option name" value.template destroy< std::string > (); break; @@ -997,11 +993,10 @@ namespace isc { namespace eval { value.move< TokenOption::RepresentationType > (s.value); break; - case 20: // "constant string" - case 21: // "integer" - case 22: // "constant hexstring" - case 23: // "option name" - case 24: // TOKEN + case 21: // "constant string" + case 22: // "integer" + case 23: // "constant hexstring" + case 24: // "option name" value.move< std::string > (s.value); break; @@ -1095,6 +1090,12 @@ namespace isc { namespace eval { return symbol_type (token::TOKEN_SUBSTRING, l); } + EvalParser::symbol_type + EvalParser::make_CONCAT (const location_type& l) + { + return symbol_type (token::TOKEN_CONCAT, l); + } + EvalParser::symbol_type EvalParser::make_NOT (const location_type& l) { @@ -1203,16 +1204,10 @@ namespace isc { namespace eval { return symbol_type (token::TOKEN_OPTION_NAME, v, l); } - EvalParser::symbol_type - EvalParser::make_TOKEN (const std::string& v, const location_type& l) - { - return symbol_type (token::TOKEN_TOKEN, v, l); - } - #line 13 "parser.yy" // lalr1.cc:392 } } // isc::eval -#line 1216 "parser.h" // lalr1.cc:392 +#line 1211 "parser.h" // lalr1.cc:392 diff --git a/src/lib/eval/parser.yy b/src/lib/eval/parser.yy index c8b0f3700b..11a1b319e1 100644 --- a/src/lib/eval/parser.yy +++ b/src/lib/eval/parser.yy @@ -39,6 +39,7 @@ using namespace isc::eval; EQUAL "==" OPTION "option" SUBSTRING "substring" + CONCAT "concat" NOT "not" AND "and" OR "or" @@ -59,7 +60,6 @@ using namespace isc::eval; %token INTEGER "integer" %token HEXSTRING "constant hexstring" %token OPTION_NAME "option name" -%token TOKEN %type option_code %type option_repr_type @@ -148,8 +148,11 @@ string_expr : STRING TokenPtr sub(new TokenSubstring()); ctx.expression.push_back(sub); } - | TOKEN - // Temporary unused token to avoid explicit but long errors + | CONCAT "(" string_expr "," string_expr ")" + { + TokenPtr conc(new TokenConcat()); + ctx.expression.push_back(conc); + } ; option_code : INTEGER diff --git a/src/lib/eval/position.hh b/src/lib/eval/position.hh index 59f4b295f5..cb76426e7b 100644 --- a/src/lib/eval/position.hh +++ b/src/lib/eval/position.hh @@ -1,4 +1,4 @@ -// Generated 20160219 +// Generated at 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 0ad2aa5370..2b3656900f 100644 --- a/src/lib/eval/stack.hh +++ b/src/lib/eval/stack.hh @@ -1,4 +1,4 @@ -// Generated 20160219 +// Generated at 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/context_unittest.cc b/src/lib/eval/tests/context_unittest.cc index 2e2446bc2b..1bebf29c22 100644 --- a/src/lib/eval/tests/context_unittest.cc +++ b/src/lib/eval/tests/context_unittest.cc @@ -81,14 +81,6 @@ public: EXPECT_EQ(expected_code, opt->getCode()); } - /// @brief checks if the given token is a substring operator - void checkTokenSubstring(const TokenPtr& token) { - ASSERT_TRUE(token); - boost::shared_ptr sub = - boost::dynamic_pointer_cast(token); - EXPECT_TRUE(sub); - } - /// @brief check if the given token is relay4 with the expected code void checkTokenRelay4(const TokenPtr& token, uint16_t code) { ASSERT_TRUE(token); @@ -101,6 +93,22 @@ public: } } + /// @brief checks if the given token is a substring operator + void checkTokenSubstring(const TokenPtr& token) { + ASSERT_TRUE(token); + boost::shared_ptr sub = + boost::dynamic_pointer_cast(token); + EXPECT_TRUE(sub); + } + + /// @brief checks if the given token is a concat operator + void checkTokenConcat(const TokenPtr& token) { + ASSERT_TRUE(token); + boost::shared_ptr conc = + boost::dynamic_pointer_cast(token); + EXPECT_TRUE(conc); + } + /// @brief checks if the given expression raises the expected message /// when it is parsed. void checkError(const string& expr, const string& msg) { @@ -275,6 +283,33 @@ TEST_F(EvalContextTest, optionHex) { checkTokenOption(eval.expression.at(0), 123); } +// This test checks that the relay[code].hex can be used in expressions. +TEST_F(EvalContextTest, relay4Option) { + + EvalContext eval(Option::V4); + EXPECT_NO_THROW(parsed_ = + eval.parseString("relay4[13].hex == 'thirteen'")); + EXPECT_TRUE(parsed_); + ASSERT_EQ(3, eval.expression.size()); + + TokenPtr tmp1 = eval.expression.at(0); + TokenPtr tmp2 = eval.expression.at(1); + TokenPtr tmp3 = eval.expression.at(2); + + checkTokenRelay4(tmp1, 13); + checkTokenString(tmp2, "thirteen"); + checkTokenEq(tmp3); +} + +// Verify that relay4[13] is not usable in v6 +// There will be a separate relay accessor for v6. +TEST_F(EvalContextTest, relay4Error) { + universe_ = Option::V6; + + checkError("relay4[13].hex == 'thirteen'", + ":1.1-6: relay4 can only be used in DHCPv4."); +} + // Test parsing of logical operators TEST_F(EvalContextTest, logicalOps) { // option.exists @@ -402,31 +437,23 @@ TEST_F(EvalContextTest, substring) { checkTokenSubstring(tmp4); } -// This test checks that the relay[code].hex can be used in expressions. -TEST_F(EvalContextTest, relay4Option) { - +// Test the parsing of a concat expression +TEST_F(EvalContextTest, concat) { EvalContext eval(Option::V4); + EXPECT_NO_THROW(parsed_ = - eval.parseString("relay4[13].hex == 'thirteen'")); + eval.parseString("concat('foo','bar') == 'foobar'")); EXPECT_TRUE(parsed_); - ASSERT_EQ(3, eval.expression.size()); + + ASSERT_EQ(5, eval.expression.size()); TokenPtr tmp1 = eval.expression.at(0); TokenPtr tmp2 = eval.expression.at(1); TokenPtr tmp3 = eval.expression.at(2); - checkTokenRelay4(tmp1, 13); - checkTokenString(tmp2, "thirteen"); - checkTokenEq(tmp3); -} - -// Verify that relay4[13] is not usable in v6 -// There will be a separate relay accessor for v6. -TEST_F(EvalContextTest, relay4Error) { - universe_ = Option::V6; - - checkError("relay4[13].hex == 'thirteen'", - ":1.1-6: relay4 can only be used in DHCPv4."); + checkTokenString(tmp1, "foo"); + checkTokenString(tmp2, "bar"); + checkTokenConcat(tmp3); } // Test some scanner error cases @@ -439,6 +466,7 @@ TEST_F(EvalContextTest, scanErrors) { checkError("subtring", ":1.1: Invalid character: s"); checkError("foo", ":1.1: Invalid character: f"); checkError(" bar", ":1.2: Invalid character: b"); + checkError("relay[12].hex == 'foo'", ":1.1: Invalid character: r"); } // Tests some scanner/parser error cases @@ -546,8 +574,7 @@ TEST_F(EvalContextTest, parseErrors) { ":1.19-20: syntax error, unexpected ==, " "expecting end of file"); checkError("substring('foobar') == 'f'", - ":1.19: syntax error, " - "unexpected ), expecting \",\""); + ":1.19: syntax error, unexpected ), expecting \",\""); checkError("substring('foobar',3) == 'bar'", ":1.21: syntax error, unexpected ), expecting \",\""); checkError("substring('foobar','3',3) == 'bar'", @@ -555,6 +582,10 @@ TEST_F(EvalContextTest, parseErrors) { "expecting integer"); checkError("substring('foobar',1,a) == 'foo'", ":1.22: Invalid character: a"); + checkError("concat('foobar') == 'f'", + ":1.16: syntax error, unexpected ), expecting \",\""); + checkError("concat('foo','bar','') == 'foobar'", + ":1.19: syntax error, unexpected \",\", expecting )"); } // Tests some type error cases @@ -568,6 +599,10 @@ TEST_F(EvalContextTest, typeErrors) { checkError("substring('foobar',0x32,1) == 'foo'", ":1.20-23: syntax error, unexpected constant " "hexstring, expecting integer"); + checkError("concat('foo',3) == 'foo3'", + ":1.14: syntax error, unexpected integer"); + checkError("concat(3,'foo') == '3foo'", + ":1.8: syntax error, unexpected integer"); checkError("('foo' == 'bar') == 'false'", ":1.18-19: syntax error, unexpected ==, " "expecting end of file"); diff --git a/src/lib/eval/tests/token_unittest.cc b/src/lib/eval/tests/token_unittest.cc index de10b7a3fe..087dd63365 100644 --- a/src/lib/eval/tests/token_unittest.cc +++ b/src/lib/eval/tests/token_unittest.cc @@ -341,7 +341,7 @@ TEST_F(TokenTest, optionHexString4) { } // This test checks if a token representing an option value is able to check -// the existence ofthe option from an IPv4 packet. +// the existence of the option from an IPv4 packet. TEST_F(TokenTest, optionExistsString4) { TokenPtr found; TokenPtr not_found; @@ -420,7 +420,7 @@ TEST_F(TokenTest, optionHexString6) { } // This test checks if a token representing an option value is able to check -// the existence ofthe option from an IPv6 packet. +// the existence of the option from an IPv6 packet. TEST_F(TokenTest, optionExistsString6) { TokenPtr found; TokenPtr not_found; @@ -441,6 +441,109 @@ TEST_F(TokenTest, optionExistsString6) { EXPECT_EQ("true", values_.top()); } +// This test checks that the existing relay option can be found. +TEST_F(TokenTest, relayOption) { + + // Insert relay option with sub-options 1 and 13 + insertRelay4Option(); + + // Creating the token should be safe. + ASSERT_NO_THROW(t_.reset(new TokenRelay4Option(13, TokenOption::TEXTUAL))); + + // We should be able to evaluate it. + EXPECT_NO_THROW(t_->evaluate(*pkt4_, values_)); + + // we should have one value on the stack + ASSERT_EQ(1, values_.size()); + + // The option should be found and relay[13] should evaluate to the + // content of that sub-option, i.e. "thirteen" + EXPECT_EQ("thirteen", values_.top()); +} + +// This test checks that the code properly handles cases when +// there is a RAI option, but there's no requested sub-option. +TEST_F(TokenTest, relayOptionNoSuboption) { + + // Insert relay option with sub-options 1 and 13 + insertRelay4Option(); + + // Creating the token should be safe. + ASSERT_NO_THROW(t_.reset(new TokenRelay4Option(15, TokenOption::TEXTUAL))); + + // We should be able to evaluate it. + EXPECT_NO_THROW(t_->evaluate(*pkt4_, values_)); + + // we should have one value on the stack + ASSERT_EQ(1, values_.size()); + + // The option should NOT be found (there is no sub-option 15), + // so the expression should evaluate to "" + EXPECT_EQ("", values_.top()); +} + +// This test checks that the code properly handles cases when +// there's no RAI option at all. +TEST_F(TokenTest, relayOptionNoRai) { + + // We didn't call insertRelay4Option(), so there's no RAI option. + + // Creating the token should be safe. + ASSERT_NO_THROW(t_.reset(new TokenRelay4Option(13, TokenOption::TEXTUAL))); + + // We should be able to evaluate it. + EXPECT_NO_THROW(t_->evaluate(*pkt4_, values_)); + + // we should have one value on the stack + ASSERT_EQ(1, values_.size()); + + // The option should NOT be found (there is no sub-option 13), + // so the expression should evaluate to "" + EXPECT_EQ("", values_.top()); +} + +// This test checks that only the RAI is searched for the requested +// sub-option. +TEST_F(TokenTest, relayRAIOnly) { + + // Insert relay option with sub-options 1 and 13 + insertRelay4Option(); + + // Add options 13 and 70 to the packet. + OptionPtr opt13(new OptionString(Option::V4, 13, "THIRTEEN")); + OptionPtr opt70(new OptionString(Option::V4, 70, "SEVENTY")); + pkt4_->addOption(opt13); + pkt4_->addOption(opt70); + + // The situation is as follows: + // Packet: + // - option 13 (containing "THIRTEEN") + // - option 82 (rai) + // - option 1 (containing "one") + // - option 13 (containing "thirteen") + + // Let's try to get option 13. It should get the one from RAI + ASSERT_NO_THROW(t_.reset(new TokenRelay4Option(13, TokenOption::TEXTUAL))); + EXPECT_NO_THROW(t_->evaluate(*pkt4_, values_)); + ASSERT_EQ(1, values_.size()); + EXPECT_EQ("thirteen", values_.top()); + + // Try to get option 1. It should get the one from RAI + clearStack(); + ASSERT_NO_THROW(t_.reset(new TokenRelay4Option(1, TokenOption::TEXTUAL))); + EXPECT_NO_THROW(t_->evaluate(*pkt4_, values_)); + ASSERT_EQ(1, values_.size()); + EXPECT_EQ("one", values_.top()); + + // Try to get option 70. It should fail, as there's no such + // sub option in RAI. + clearStack(); + ASSERT_NO_THROW(t_.reset(new TokenRelay4Option(70, TokenOption::TEXTUAL))); + EXPECT_NO_THROW(t_->evaluate(*pkt4_, values_)); + ASSERT_EQ(1, values_.size()); + EXPECT_EQ("", 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) { @@ -490,7 +593,7 @@ TEST_F(TokenTest, optionEqualTrue) { // This test checks if a token representing a not is able to // negate a boolean value (with incorrectly built stack). -TEST_F(TokenTest, optionNotInvalid) { +TEST_F(TokenTest, operatorNotInvalid) { ASSERT_NO_THROW(t_.reset(new TokenNot())); @@ -504,7 +607,7 @@ TEST_F(TokenTest, optionNotInvalid) { // This test checks if a token representing a not operator is able to // negate a boolean value. -TEST_F(TokenTest, optionNot) { +TEST_F(TokenTest, operatorNot) { ASSERT_NO_THROW(t_.reset(new TokenNot())); @@ -523,7 +626,7 @@ TEST_F(TokenTest, optionNot) { // This test checks if a token representing an and is able to // conjugate two values (with incorrectly built stack). -TEST_F(TokenTest, optionAndInvalid) { +TEST_F(TokenTest, operatorAndInvalid) { ASSERT_NO_THROW(t_.reset(new TokenAnd())); @@ -547,7 +650,7 @@ TEST_F(TokenTest, optionAndInvalid) { // This test checks if a token representing an and operator is able to // conjugate false with another logical -TEST_F(TokenTest, optionAndFalse) { +TEST_F(TokenTest, operatorAndFalse) { ASSERT_NO_THROW(t_.reset(new TokenAnd())); @@ -574,7 +677,7 @@ TEST_F(TokenTest, optionAndFalse) { // This test checks if a token representing an and is able to // conjugate two true values. -TEST_F(TokenTest, optionAndTrue) { +TEST_F(TokenTest, operatorAndTrue) { ASSERT_NO_THROW(t_.reset(new TokenAnd())); @@ -589,7 +692,7 @@ TEST_F(TokenTest, optionAndTrue) { // This test checks if a token representing an or is able to // combinate two values (with incorrectly built stack). -TEST_F(TokenTest, optionOrInvalid) { +TEST_F(TokenTest, operatorOrInvalid) { ASSERT_NO_THROW(t_.reset(new TokenOr())); @@ -613,7 +716,7 @@ TEST_F(TokenTest, optionOrInvalid) { // This test checks if a token representing an or is able to // conjugate two false values. -TEST_F(TokenTest, optionOrFalse) { +TEST_F(TokenTest, operatorOrFalse) { ASSERT_NO_THROW(t_.reset(new TokenOr())); @@ -628,7 +731,7 @@ TEST_F(TokenTest, optionOrFalse) { // This test checks if a token representing an == operator is able to // conjugate true with another logical -TEST_F(TokenTest, optionOrTrue) { +TEST_F(TokenTest, operatorOrTrue) { ASSERT_NO_THROW(t_.reset(new TokenOr())); @@ -655,7 +758,7 @@ TEST_F(TokenTest, optionOrTrue) { }; -// This test checks if an a token representing a substring request +// This test checks if a token representing a substring request // throws an exception if there aren't enough values on the stack. // The stack from the top is: length, start, string. // The actual packet is not used. @@ -663,7 +766,7 @@ TEST_F(TokenTest, substringNotEnoughValues) { ASSERT_NO_THROW(t_.reset(new TokenSubstring())); // Subsring requires three values on the stack, try - // with 0, 1 and 2 all should thorw an exception + // with 0, 1 and 2 all should throw an exception EXPECT_THROW(t_->evaluate(*pkt4_, values_), EvalBadStack); values_.push(""); @@ -790,7 +893,7 @@ TEST_F(TokenTest, substringEquals) { // The substring values // Subsring requires three values on the stack, try - // with 0, 1 and 2 all should thorw an exception + // with 0, 1 and 2 all should throw an exception values_.push("foobar"); values_.push("1"); values_.push("4"); @@ -813,7 +916,7 @@ TEST_F(TokenTest, substringEquals) { // The substring values // Subsring requires three values on the stack, try - // with 0, 1 and 2 all should thorw an exception + // with 0, 1 and 2 all should throw an exception values_.push("foobar"); values_.push("1"); values_.push("4"); @@ -829,105 +932,24 @@ TEST_F(TokenTest, substringEquals) { } -// This test checks that the existing relay option can be found. -TEST_F(TokenTest, relayOption) { +// This test checks if a token representing a concat request +// throws an exception if there aren't enough values on the stack. +// The actual packet is not used. +TEST_F(TokenTest, concat) { + ASSERT_NO_THROW(t_.reset(new TokenConcat())); - // Insert relay option with sub-options 1 and 13 - insertRelay4Option(); + // Concat requires two values on the stack, try + // with 0 and 1 both should throw an exception + EXPECT_THROW(t_->evaluate(*pkt4_, values_), EvalBadStack); - // Creating the token should be safe. - ASSERT_NO_THROW(t_.reset(new TokenRelay4Option(13, TokenOption::TEXTUAL))); + values_.push("foo"); + EXPECT_THROW(t_->evaluate(*pkt4_, values_), EvalBadStack); - // We should be able to evaluate it. + // Two should work + values_.push("bar"); EXPECT_NO_THROW(t_->evaluate(*pkt4_, values_)); - // we should have one value on the stack + // Check the result ASSERT_EQ(1, values_.size()); - - // The option should be found and relay[13] should evaluate to the - // content of that sub-option, i.e. "thirteen" - EXPECT_EQ("thirteen", values_.top()); -} - -// This test checks that the code properly handles cases when -// there is a RAI option, but there's no requested sub-option. -TEST_F(TokenTest, relayOptionNoSuboption) { - - // Insert relay option with sub-options 1 and 13 - insertRelay4Option(); - - // Creating the token should be safe. - ASSERT_NO_THROW(t_.reset(new TokenRelay4Option(15, TokenOption::TEXTUAL))); - - // We should be able to evaluate it. - EXPECT_NO_THROW(t_->evaluate(*pkt4_, values_)); - - // we should have one value on the stack - ASSERT_EQ(1, values_.size()); - - // The option should NOT be found (there is no sub-option 15), - // so the expression should evaluate to "" - EXPECT_EQ("", values_.top()); -} - -// This test checks that the code properly handles cases when -// there's no RAI option at all. -TEST_F(TokenTest, relayOptionNoRai) { - - // We didn't call insertRelay4Option(), so there's no RAI option. - - // Creating the token should be safe. - ASSERT_NO_THROW(t_.reset(new TokenRelay4Option(13, TokenOption::TEXTUAL))); - - // We should be able to evaluate it. - EXPECT_NO_THROW(t_->evaluate(*pkt4_, values_)); - - // we should have one value on the stack - ASSERT_EQ(1, values_.size()); - - // The option should NOT be found (there is no sub-option 13), - // so the expression should evaluate to "" - EXPECT_EQ("", values_.top()); -} - -// This test checks that only the RAI is searched for the requested -// sub-option. -TEST_F(TokenTest, relayRAIOnly) { - - // Insert relay option with sub-options 1 and 13 - insertRelay4Option(); - - // Add options 13 and 70 to the packet. - OptionPtr opt13(new OptionString(Option::V4, 13, "THIRTEEN")); - OptionPtr opt70(new OptionString(Option::V4, 70, "SEVENTY")); - pkt4_->addOption(opt13); - pkt4_->addOption(opt70); - - // The situation is as follows: - // Packet: - // - option 13 (containing "THIRTEEN") - // - option 82 (rai) - // - option 1 (containing "one") - // - option 13 (containing "thirteen") - - // Let's try to get option 13. It should get the one from RAI - ASSERT_NO_THROW(t_.reset(new TokenRelay4Option(13, TokenOption::TEXTUAL))); - EXPECT_NO_THROW(t_->evaluate(*pkt4_, values_)); - ASSERT_EQ(1, values_.size()); - EXPECT_EQ("thirteen", values_.top()); - - // Try to get option 1. It should get the one from RAI - clearStack(); - ASSERT_NO_THROW(t_.reset(new TokenRelay4Option(1, TokenOption::TEXTUAL))); - EXPECT_NO_THROW(t_->evaluate(*pkt4_, values_)); - ASSERT_EQ(1, values_.size()); - EXPECT_EQ("one", values_.top()); - - // Try to get option 70. It should fail, as there's no such - // sub option in RAI. - clearStack(); - ASSERT_NO_THROW(t_.reset(new TokenRelay4Option(70, TokenOption::TEXTUAL))); - EXPECT_NO_THROW(t_->evaluate(*pkt4_, values_)); - ASSERT_EQ(1, values_.size()); - EXPECT_EQ("", values_.top()); + EXPECT_EQ("foobar", values_.top()); } diff --git a/src/lib/eval/token.cc b/src/lib/eval/token.cc index 72e73e91fb..dd23dbaa12 100644 --- a/src/lib/eval/token.cc +++ b/src/lib/eval/token.cc @@ -84,6 +84,23 @@ TokenOption::evaluate(const Pkt& pkt, ValueStack& values) { values.push(opt_str); } +TokenRelay4Option::TokenRelay4Option(const uint16_t option_code, + const RepresentationType& rep_type) + :TokenOption(option_code, rep_type) { +} + +OptionPtr TokenRelay4Option::getOption(const Pkt& pkt) { + + // Check if there is Relay Agent Option. + OptionPtr rai = pkt.getOption(DHO_DHCP_AGENT_OPTIONS); + if (!rai) { + return (OptionPtr()); + } + + // If there is, try to return its suboption + return (rai->getOption(option_code_)); +} + void TokenEqual::evaluate(const Pkt& /*pkt*/, ValueStack& values) { @@ -178,21 +195,21 @@ TokenSubstring::evaluate(const Pkt& /*pkt*/, ValueStack& values) { values.push(string_str.substr(start_pos, length)); } -TokenRelay4Option::TokenRelay4Option(const uint16_t option_code, - const RepresentationType& rep_type) - :TokenOption(option_code, rep_type) { -} +void +TokenConcat::evaluate(const Pkt& /*pkt*/, ValueStack& values) { -OptionPtr TokenRelay4Option::getOption(const Pkt& pkt) { - - // Check if there is Relay Agent Option. - OptionPtr rai = pkt.getOption(DHO_DHCP_AGENT_OPTIONS); - if (!rai) { - return (OptionPtr()); + if (values.size() < 2) { + isc_throw(EvalBadStack, "Incorrect stack order. Expected at least " + "2 values for concat, got " << values.size()); } - // If there is, try to return its suboption - return (rai->getOption(option_code_)); + string op1 = values.top(); + values.pop(); + string op2 = values.top(); + values.pop(); // Dammit, std::stack interface is awkward. + + // The top of the stack was evaluated last so this is the right order + values.push(op2 + op1); } void diff --git a/src/lib/eval/token.h b/src/lib/eval/token.h index 7dd7a022eb..f38c7bfa07 100644 --- a/src/lib/eval/token.h +++ b/src/lib/eval/token.h @@ -219,6 +219,34 @@ protected: RepresentationType representation_type_; ///< Representation type. }; +/// @brief Represents a sub-option inserted by the DHCPv4 relay. +/// +/// DHCPv4 relays insert sub-options in option 82. This token attempts to extract +/// such sub-options. Note in DHCPv6 it is radically different (possibly +/// many encapsulation levels), thus there are separate classes for v4 and v6. +/// +/// This token can represent the following expressions: +/// relay[13].text - Textual representation of sub-option 13 in RAI (option 82) +/// relay[13].hex - Binary representation of sub-option 13 in RAI (option 82) +/// relay[vendor-class].text - Text representation of sub-option X in RAI (option 82) +/// relay[vendor-class].hex - Binary representation of sub-option X in RAI (option 82) +class TokenRelay4Option : public TokenOption { +public: + + /// @brief Constructor for extracting sub-option from RAI (option 82) + /// + /// @param option_code code of the requested sub-option + /// @param rep_type code representation (currently .hex and .text are supported) + TokenRelay4Option(const uint16_t option_code, + const RepresentationType& rep_type); + +protected: + /// @brief Attempts to obtain specified sub-option of option 82 from the packet + /// @param pkt DHCPv4 packet (that hopefully contains option 82) + /// @return found sub-option from option 82 + virtual OptionPtr getOption(const Pkt& pkt); +}; + /// @brief Token that represents equality operator (compares two other tokens) /// /// For example in the expression option[vendor-class].text == "MSFT" @@ -300,32 +328,27 @@ public: void evaluate(const Pkt& pkt, ValueStack& values); }; -/// @brief Represents a sub-option inserted by the DHCPv4 relay. +/// @brief Token that represents concat operator (concatenates two other tokens) /// -/// DHCPv4 relays insert sub-options in option 82. This token attempts to extract -/// such sub-options. Note in DHCPv6 it is radically different (possibly -/// many encapsulation levels), thus there are separate classes for v4 and v6. -/// -/// This token can represent the following expressions: -/// relay[13].text - Textual representation of sub-option 13 in RAI (option 82) -/// relay[13].hex - Binary representation of sub-option 13 in RAI (option 82) -/// relay[vendor-class].text - Text representation of sub-option X in RAI (option 82) -/// relay[vendor-class].hex - Binary representation of sub-option X in RAI (option 82) -class TokenRelay4Option : public TokenOption { +/// For example in the sub-expression "concat('foo','bar')" the result +/// of the evaluation is "foobar" +class TokenConcat : public Token { public: + /// @brief Constructor (does nothing) + TokenConcat() {} - /// @brief Constructor for extracting sub-option from RAI (option 82) + /// @brief Concatenate two values. /// - /// @param option_code code of the requested sub-option - /// @param rep_type code representation (currently .hex and .text are supported) - TokenRelay4Option(const uint16_t option_code, - const RepresentationType& rep_type); - -protected: - /// @brief Attempts to obtain specified sub-option of option 82 from the packet - /// @param pkt DHCPv4 packet (that hopefully contains option 82) - /// @return found sub-option from option 82 - virtual OptionPtr getOption(const Pkt& pkt); + /// Evaluation does not use packet information, but rather consumes the last + /// two parameters. It does a simple string concatenation. It requires + /// at least two parameters to be present on stack. + /// + /// @throw EvalBadStack if there are less than 2 values on stack + /// + /// @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 negation operator