From 95136cbfc73306c4d3c38d1d0b83e6dcd52e21a3 Mon Sep 17 00:00:00 2001 From: ChAoS_UnItY <43753315+ChAoSUnItY@users.noreply.github.com> Date: Tue, 21 Sep 2021 19:02:17 +0800 Subject: [PATCH] all: add unsigned shift operators (#11536) --- doc/docs.md | 5 +- vlib/v/checker/checker.v | 98 ++++++++++++++++++++++++ vlib/v/parser/expr.v | 1 - vlib/v/scanner/scanner.v | 28 +++++++ vlib/v/tests/unsigned_right_shift_test.v | 36 +++++++++ vlib/v/token/token.v | 16 +++- 6 files changed, 177 insertions(+), 7 deletions(-) create mode 100644 vlib/v/tests/unsigned_right_shift_test.v diff --git a/doc/docs.md b/doc/docs.md index 4acab5e0db..74c295b9cf 100644 --- a/doc/docs.md +++ b/doc/docs.md @@ -5557,10 +5557,11 @@ This lists operators for [primitive types](#primitive-types) only. << left shift integer << unsigned integer >> right shift integer >> unsigned integer +>>> unsigned right shift integer >> unsigned integer Precedence Operator - 5 * / % << >> & + 5 * / % << >> >>> & 4 + - | ^ 3 == != < <= > >= 2 && @@ -5570,5 +5571,5 @@ Precedence Operator Assignment Operators += -= *= /= %= &= |= ^= ->>= <<= +>>= <<= >>>= ``` diff --git a/vlib/v/checker/checker.v b/vlib/v/checker/checker.v index 8bf438bc4a..af385b020c 100644 --- a/vlib/v/checker/checker.v +++ b/vlib/v/checker/checker.v @@ -1599,6 +1599,50 @@ pub fn (mut c Checker) infix_expr(mut node ast.InfixExpr) ast.Type { .right_shift { return c.check_shift(left_type, right_type, left_pos, right_pos) } + .unsigned_right_shift { + modified_left_type := if !left_type.is_int() { + c.error('invalid operation: shift on type `${c.table.get_type_symbol(left_type).name}`', + left_pos) + ast.void_type_idx + } else if left_type.is_int_literal() { + // int literal => i64 + ast.u32_type_idx + } else if left_type.is_unsigned() { + left_type + } else { + // signed types' idx adds with 5 will get correct relative unsigned type + // i8 => byte + // i16 => u16 + // int => u32 + // i64 => u64 + // isize => usize + // i128 => u128 NOT IMPLEMENTED YET + left_type.idx() + ast.u32_type_idx - ast.int_type_idx + } + + if modified_left_type == 0 { + return ast.void_type + } + + node = ast.InfixExpr{ + left: ast.CastExpr{ + expr: node.left + typ: modified_left_type + typname: c.table.type_str(modified_left_type) + pos: node.pos + } + left_type: left_type + op: .right_shift + right: node.right + right_type: right_type + is_stmt: false + pos: node.pos + auto_locked: node.auto_locked + or_block: node.or_block + } + + return c.check_shift(left_type, right_type, left_pos, right_pos) + } .key_is, .not_is { right_expr := node.right mut typ := match right_expr { @@ -4275,6 +4319,60 @@ pub fn (mut c Checker) assign_stmt(mut node ast.AssignStmt) { right.position()) } } + .unsigned_right_shift_assign { + if node.left.len != 1 || node.right.len != 1 { + c.error('unsupported operation: unable to lower expression for unsigned shift assignment.', + node.pos) + } + + modified_left_type := if !left_type.is_int() { + c.error('invalid operation: shift on type `${c.table.get_type_symbol(left_type).name}`', + node.pos) + ast.void_type_idx + } else if left_type.is_int_literal() { + // int literal => i64 + ast.u32_type_idx + } else if left_type.is_unsigned() { + left_type + } else { + // signed types' idx adds with 5 will get correct relative unsigned type + // i8 => byte + // i16 => u16 + // int => u32 + // i64 => u64 + // isize => usize + // i128 => u128 NOT IMPLEMENTED YET + left_type.idx() + ast.u32_type_idx - ast.int_type_idx + } + + node = ast.AssignStmt{ + op: .assign + pos: node.pos + comments: node.comments + end_comments: node.end_comments + left: node.left + right: [ + ast.Expr(ast.InfixExpr{ + left: ast.CastExpr{ + expr: node.left[0] + typ: modified_left_type + typname: c.table.type_str(modified_left_type) + pos: node.pos + } + op: .right_shift + right: node.right[0] + left_type: modified_left_type + right_type: right_type + pos: node.pos + }), + ] + left_types: node.left_types + right_types: node.right_types + is_static: node.is_static + is_simple: node.is_simple + has_cross_var: node.has_cross_var + } + } else {} } if node.op in [.plus_assign, .minus_assign, .mod_assign, .mult_assign, .div_assign] diff --git a/vlib/v/parser/expr.v b/vlib/v/parser/expr.v index c6c329e4d3..e96fdaf4c3 100644 --- a/vlib/v/parser/expr.v +++ b/vlib/v/parser/expr.v @@ -480,7 +480,6 @@ fn (mut p Parser) infix_expr(left ast.Expr) ast.Expr { if is_key_in { p.inside_in_array = false } - p.expecting_type = prev_expecting_type if p.pref.is_vet && op in [.key_in, .not_in] && right is ast.ArrayInit && (right as ast.ArrayInit).exprs.len == 1 { diff --git a/vlib/v/scanner/scanner.v b/vlib/v/scanner/scanner.v index 5aa315c1bf..1e0556e83f 100644 --- a/vlib/v/scanner/scanner.v +++ b/vlib/v/scanner/scanner.v @@ -936,11 +936,31 @@ fn (mut s Scanner) text_scan() token.Token { for typ in typs { // TODO: combine two ifs once logic shortcut with `.all()` is fixed if typ.len == 0 { + if s.text[s.pos + 2] == `>` { + if s.pos + 3 < s.text.len && s.text[s.pos + 3] == `=` { + s.pos += 3 + return s.new_token(.unsigned_right_shift_assign, + '', 4) + } + s.pos += 2 + return s.new_token(.unsigned_right_shift, '', + 3) + } s.pos++ return s.new_token(.right_shift, '', 2) } if !(typ[0].is_capital() && typ[1..].bytes().all(it.is_alnum())) && typ !in ast.builtin_type_names { + if s.text[s.pos + 2] == `>` { + if s.pos + 3 < s.text.len && s.text[s.pos + 3] == `=` { + s.pos += 3 + return s.new_token(.unsigned_right_shift_assign, + '', 4) + } + s.pos += 2 + return s.new_token(.unsigned_right_shift, '', + 3) + } s.pos++ return s.new_token(.right_shift, '', 2) } @@ -948,6 +968,14 @@ fn (mut s Scanner) text_scan() token.Token { return s.new_token(.gt, '', 1) } } + if s.text[s.pos + 2] == `>` { + if s.pos + 3 < s.text.len && s.text[s.pos + 3] == `=` { + s.pos += 3 + return s.new_token(.unsigned_right_shift_assign, '', 4) + } + s.pos += 2 + return s.new_token(.unsigned_right_shift, '', 3) + } s.pos++ return s.new_token(.right_shift, '', 2) } diff --git a/vlib/v/tests/unsigned_right_shift_test.v b/vlib/v/tests/unsigned_right_shift_test.v new file mode 100644 index 0000000000..aff0eba90f --- /dev/null +++ b/vlib/v/tests/unsigned_right_shift_test.v @@ -0,0 +1,36 @@ +const answer_u64 = u64(9223372036854775805) + +const ( + answer_u32 = u32(2147483645) + answer_u16 = u16(32765) + answer_u8 = u8(125) +) + +fn test_unsigned_right_shift_expr_isize_usize() { + $if x32 { + assert isize(-5) >>> 1 == answer_u32 + assert usize(-5) >>> 1 == answer_u32 + } + $if x64 { + assert isize(-5) >>> 1 == answer_u64 + assert usize(-5) >>> 1 == answer_u64 + } +} + +fn test_unsigned_right_shift_expr() { + assert i64(-5) >>> 1 == answer_u64 + assert -5 >>> 1 == answer_u32 // because int literal's size defaults to int's size, without an explicit cast + assert int(-5) >>> 1 == answer_u32 + assert i16(-5) >>> 1 == answer_u16 + assert i8(-5) >>> 1 == answer_u8 +} + +fn test_unsigned_right_shift_assignment() { + mut x, mut y, mut z := i64(-5), -5, int(-5) + x >>>= 1 + y >>>= 1 + z >>>= 1 + assert x == answer_u64 + assert y == answer_u32 + assert z == answer_u32 +} diff --git a/vlib/v/token/token.v b/vlib/v/token/token.v index 6936d1ace5..b4af7e9f17 100644 --- a/vlib/v/token/token.v +++ b/vlib/v/token/token.v @@ -48,6 +48,7 @@ pub enum Kind { str_dollar left_shift // << right_shift // >> + unsigned_right_shift // >>> not_in // !in not_is // !is assign // = @@ -62,6 +63,7 @@ pub enum Kind { and_assign // &= right_shift_assign // <<= left_shift_assign // >>= + unsigned_right_shift_assign // >>>= lcbr // { rcbr // } lpar // ( @@ -131,7 +133,9 @@ pub enum Kind { pub const ( assign_tokens = [Kind.assign, .plus_assign, .minus_assign, .mult_assign, .div_assign, .xor_assign, - .mod_assign, .or_assign, .and_assign, .right_shift_assign, .left_shift_assign] + .mod_assign, .or_assign, .and_assign, .right_shift_assign, .left_shift_assign, + .unsigned_right_shift_assign, + ] ) const ( @@ -238,6 +242,7 @@ fn build_token_str() []string { s[Kind.or_assign] = '|=' s[Kind.and_assign] = '&=' s[Kind.right_shift_assign] = '>>=' + s[Kind.unsigned_right_shift_assign] = '>>>=' s[Kind.left_shift_assign] = '<<=' s[Kind.lcbr] = '{' s[Kind.rcbr] = '}' @@ -254,6 +259,7 @@ fn build_token_str() []string { s[Kind.question] = '?' s[Kind.left_shift] = '<<' s[Kind.right_shift] = '>>' + s[Kind.unsigned_right_shift] = '>>>' s[Kind.comment] = 'comment' s[Kind.nl] = 'NLL' s[Kind.dollar] = '$' @@ -372,7 +378,7 @@ pub enum Precedence { eq // == or != // less_greater // > or < sum // + - | ^ - product // * / << >> & + product // * / << >> >>> & // mod // % prefix // -X or !X postfix // ++ or -- @@ -388,12 +394,13 @@ pub fn build_precedences() []Precedence { p[Kind.inc] = .postfix p[Kind.dec] = .postfix p[Kind.question] = .postfix - // `*` | `/` | `%` | `<<` | `>>` | `&` + // `*` | `/` | `%` | `<<` | `>>` | `>>>` | `&` p[Kind.mul] = .product p[Kind.div] = .product p[Kind.mod] = .product p[Kind.left_shift] = .product p[Kind.right_shift] = .product + p[Kind.unsigned_right_shift] = .product p[Kind.amp] = .product p[Kind.arrow] = .product // `+` | `-` | `|` | `^` @@ -419,6 +426,7 @@ pub fn build_precedences() []Precedence { // <<= | *= | ... p[Kind.left_shift_assign] = .assign p[Kind.right_shift_assign] = .assign + p[Kind.unsigned_right_shift_assign] = .assign p[Kind.mult_assign] = .assign p[Kind.xor_assign] = .assign p[Kind.key_in] = .in_as @@ -474,7 +482,7 @@ pub fn (kind Kind) is_prefix() bool { pub fn (kind Kind) is_infix() bool { return kind in [.plus, .minus, .mod, .mul, .div, .eq, .ne, .gt, .lt, .key_in, .key_as, .ge, .le, .logical_or, .xor, .not_in, .key_is, .not_is, .and, .dot, .pipe, .amp, .left_shift, - .right_shift, .arrow] + .right_shift, .unsigned_right_shift, .arrow] } // Pass ast.builtin_type_names