all: add unsigned shift operators (#11536)

pull/11568/head
ChAoS_UnItY 2021-09-21 19:02:17 +08:00 committed by GitHub
parent e4bd2306da
commit 95136cbfc7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 177 additions and 7 deletions

View File

@ -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
+= -= *= /= %=
&= |= ^=
>>= <<=
>>= <<= >>>=
```

View File

@ -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]

View File

@ -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 {

View File

@ -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)
}

View File

@ -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
}

View File

@ -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