diff --git a/src/Expr.cc b/src/Expr.cc index 19b29fb521..7ba9559509 100644 --- a/src/Expr.cc +++ b/src/Expr.cc @@ -51,6 +51,8 @@ const char* expr_name(ExprTag t) "&", "|", "^", + "<<", + ">>", "&&", "||", "<", @@ -957,6 +959,12 @@ ValPtr BinaryExpr::Fold(Val* v1, Val* v2) const case EXPR_XOR: DO_UINT_FOLD(^); break; + case EXPR_LSHIFT: + DO_INT_FOLD(<<); + break; + case EXPR_RSHIFT: + DO_INT_FOLD(>>); + break; case EXPR_AND_AND: DO_INT_FOLD(&&); @@ -2156,7 +2164,22 @@ BitExpr::BitExpr(ExprTag arg_tag, ExprPtr arg_op1, ExprPtr arg_op2) if ( IsVector(bt2) ) bt2 = t2->AsVectorType()->Yield()->Tag(); - if ( (bt1 == TYPE_COUNT) && (bt2 == TYPE_COUNT) ) + if ( tag == EXPR_LSHIFT || tag == EXPR_RSHIFT ) + { + if ( IsIntegral(bt1) && bt2 == TYPE_COUNT ) + { + if ( is_vector(op1) || is_vector(op2) ) + SetType(make_intrusive(base_type(bt1))); + else + SetType(base_type(bt1)); + } + else if ( IsIntegral(bt1) && bt2 == TYPE_INT ) + ExprError("requires \"count\" right operand"); + else + ExprError("requires integral operands"); + } + + else if ( (bt1 == TYPE_COUNT) && (bt2 == TYPE_COUNT) ) { if ( is_vector(op1) || is_vector(op2) ) SetType(make_intrusive(base_type(TYPE_COUNT))); diff --git a/src/Expr.h b/src/Expr.h index 4e32097dfc..9cffbabd3f 100644 --- a/src/Expr.h +++ b/src/Expr.h @@ -53,6 +53,8 @@ enum ExprTag : int EXPR_AND, EXPR_OR, EXPR_XOR, + EXPR_LSHIFT, + EXPR_RSHIFT, EXPR_AND_AND, EXPR_OR_OR, EXPR_LT, diff --git a/src/parse.y b/src/parse.y index 252b7390e2..d1ee9972c8 100644 --- a/src/parse.y +++ b/src/parse.y @@ -5,7 +5,7 @@ // Switching parser table type fixes ambiguity problems. %define lr.type ielr -%expect 195 +%expect 199 %token TOK_ADD TOK_ADD_TO TOK_ADDR TOK_ANY %token TOK_ATENDIF TOK_ATELSE TOK_ATIF TOK_ATIFDEF TOK_ATIFNDEF @@ -46,6 +46,7 @@ %left '|' %left '^' %left '&' +%left TOK_LSHIFT TOK_RSHIFT %left '+' '-' %left '*' '/' '%' %left TOK_INCR TOK_DECR @@ -554,6 +555,18 @@ expr: $$ = new BitExpr(EXPR_XOR, {AdoptRef{}, $1}, {AdoptRef{}, $3}); } + | expr TOK_LSHIFT expr + { + set_location(@1, @3); + $$ = new BitExpr(EXPR_LSHIFT, {AdoptRef{}, $1}, {AdoptRef{}, $3}); + } + + | expr TOK_RSHIFT expr + { + set_location(@1, @3); + $$ = new BitExpr(EXPR_RSHIFT, {AdoptRef{}, $1}, {AdoptRef{}, $3}); + } + | expr TOK_AND_AND expr { set_location(@1, @3); diff --git a/src/scan.l b/src/scan.l index 0f0efe4f2d..17c07c9749 100644 --- a/src/scan.l +++ b/src/scan.l @@ -248,6 +248,9 @@ ESCSEQ (\\([^\r\n]|[0-7]+|x[[:xdigit:]]+)) "&&" return TOK_AND_AND; "||" return TOK_OR_OR; +"<<" return TOK_LSHIFT; +">>" return TOK_RSHIFT; + add return TOK_ADD; addr return TOK_ADDR; any return TOK_ANY; diff --git a/src/script_opt/CPP/Exprs.cc b/src/script_opt/CPP/Exprs.cc index 6c0912d2db..f8336b9365 100644 --- a/src/script_opt/CPP/Exprs.cc +++ b/src/script_opt/CPP/Exprs.cc @@ -89,6 +89,10 @@ string CPPCompile::GenExpr(const Expr* e, GenType gt, bool top_level) return GenBinary(e, gt, "|", "or"); case EXPR_XOR: return GenBinary(e, gt, "^", "xor"); + case EXPR_LSHIFT: + return GenBinary(e, gt, "<<", "lshift"); + case EXPR_RSHIFT: + return GenBinary(e, gt, ">>", "rshift"); case EXPR_AND_AND: return GenBinary(e, gt, "&&", "andand"); case EXPR_OR_OR: diff --git a/src/script_opt/Expr.cc b/src/script_opt/Expr.cc index 36a7a2267e..d893431e66 100644 --- a/src/script_opt/Expr.cc +++ b/src/script_opt/Expr.cc @@ -257,6 +257,8 @@ bool Expr::IsFieldAssignable(const Expr* e) const case EXPR_AND: case EXPR_OR: case EXPR_XOR: + case EXPR_LSHIFT: + case EXPR_RSHIFT: case EXPR_FIELD: case EXPR_HAS_FIELD: case EXPR_IN: @@ -1293,7 +1295,7 @@ bool BitExpr::WillTransform(Reducer* c) const ExprPtr BitExpr::Reduce(Reducer* c, StmtPtr& red_stmt) { - if ( GetType()->Tag() != TYPE_COUNT ) + if ( ! IsIntegral(GetType()->Tag()) ) return BinaryExpr::Reduce(c, red_stmt); auto zero1 = op1->IsZero(); @@ -1311,7 +1313,7 @@ ExprPtr BitExpr::Reduce(Reducer* c, StmtPtr& red_stmt) if ( Tag() == EXPR_AND ) return zero_op->ReduceToSingleton(c, red_stmt); else - // OR or XOR + // OR or XOR or LSHIFT or RSHIFT return non_zero_op->ReduceToSingleton(c, red_stmt); } diff --git a/testing/btest/Baseline/language.count/out b/testing/btest/Baseline/language.count/out index fb316ea55c..0d1b3cab6a 100644 --- a/testing/btest/Baseline/language.count/out +++ b/testing/btest/Baseline/language.count/out @@ -20,6 +20,8 @@ bitwise and (PASS) bitwise or (PASS) bitwise or (PASS) bitwise xor (PASS) +bitwise lshift (PASS) +bitwise rshift (PASS) bitwise complement (PASS) bitwise complement (PASS) max count value = 18446744073709551615 (PASS) diff --git a/testing/btest/Baseline/language.int/out b/testing/btest/Baseline/language.int/out index d7924ec012..b2f4f2c04a 100644 --- a/testing/btest/Baseline/language.int/out +++ b/testing/btest/Baseline/language.int/out @@ -18,6 +18,7 @@ modulus operator (PASS) division operator (PASS) assignment operator (PASS) assignment operator (PASS) +bitwise rshift (PASS) max int value = 9223372036854775807 (PASS) min int value = -9223372036854775808 (PASS) max int value = 9223372036854775807 (PASS) diff --git a/testing/btest/language/count.zeek b/testing/btest/language/count.zeek index ca683598ac..7ba4c7982f 100644 --- a/testing/btest/language/count.zeek +++ b/testing/btest/language/count.zeek @@ -49,6 +49,8 @@ event zeek_init() test_case( "bitwise or", c2 | 0x4 == c2 ); test_case( "bitwise or", c4 | 0x4 == c4 ); test_case( "bitwise xor", c4 ^ 0x4 == 251 ); + test_case( "bitwise lshift", c4 << 0x4 == 4080 ); + test_case( "bitwise rshift", c4 >> 0x4 == 15 ); test_case( "bitwise complement", ~c6 == 0 ); test_case( "bitwise complement", ~~c4 == c4 ); diff --git a/testing/btest/language/int.zeek b/testing/btest/language/int.zeek index c9344dd007..1f010e8048 100644 --- a/testing/btest/language/int.zeek +++ b/testing/btest/language/int.zeek @@ -54,6 +54,7 @@ event zeek_init() test_case( "assignment operator", i2 == 7 ); i2 -= 2; test_case( "assignment operator", i2 == 5 ); + test_case( "bitwise rshift", i10 >> 1 == -6 ); # Max/min value tests