all: add unsigned shift operators (#11536)
							parent
							
								
									e4bd2306da
								
							
						
					
					
						commit
						95136cbfc7
					
				|  | @ -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 | ||||
| +=   -=   *=   /=   %= | ||||
| &=   |=   ^= | ||||
| >>=  <<= | ||||
| >>=  <<=  >>>= | ||||
| ``` | ||||
|  |  | |||
|  | @ -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] | ||||
|  |  | |||
|  | @ -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 { | ||||
|  |  | |||
|  | @ -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) | ||||
| 				} | ||||
|  |  | |||
|  | @ -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 | ||||
| } | ||||
|  | @ -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
 | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue