parser: implement parsing of `select` block (#6379)
							parent
							
								
									3a795e6d9b
								
							
						
					
					
						commit
						1bc9063573
					
				|  | @ -12,7 +12,7 @@ pub type TypeDecl = AliasTypeDecl | FnTypeDecl | SumTypeDecl | |||
| pub type Expr = AnonFn | ArrayInit | AsCast | Assoc | BoolLiteral | CallExpr | CastExpr | | ||||
| 	ChanInit | CharLiteral | Comment | ComptimeCall | ConcatExpr | EnumVal | FloatLiteral | | ||||
| 	Ident | IfExpr | IfGuardExpr | IndexExpr | InfixExpr | IntegerLiteral | Likely | LockExpr | | ||||
| 	MapInit | MatchExpr | None | OrExpr | ParExpr | PostfixExpr | PrefixExpr | RangeExpr | | ||||
| 	MapInit | MatchExpr | None | OrExpr | ParExpr | PostfixExpr | PrefixExpr | RangeExpr | SelectExpr | | ||||
| 	SelectorExpr | SizeOf | SqlExpr | StringInterLiteral | StringLiteral | StructInit | Type | | ||||
| 	TypeOf | UnsafeExpr | ||||
| 
 | ||||
|  | @ -536,6 +536,27 @@ pub: | |||
| 	post_comments []Comment | ||||
| } | ||||
| 
 | ||||
| pub struct SelectExpr { | ||||
| pub: | ||||
| 	branches      []SelectBranch | ||||
| 	pos           token.Position | ||||
| pub mut: | ||||
| 	is_expr       bool // returns a value
 | ||||
| 	return_type   table.Type | ||||
| 	expected_type table.Type // for debugging only
 | ||||
| } | ||||
| 
 | ||||
| pub struct SelectBranch { | ||||
| pub: | ||||
| 	stmt          Stmt // `a := <-ch` or `ch <- a`
 | ||||
| 	stmts         []Stmt // right side
 | ||||
| 	pos           token.Position | ||||
| 	comment       Comment // comment above `select {`
 | ||||
| 	is_else       bool | ||||
| 	is_timeout    bool | ||||
| 	post_comments []Comment | ||||
| } | ||||
| 
 | ||||
| /* | ||||
| CompIf.is_opt: | ||||
| `$if xyz? {}` => this compile time `if` is optional, | ||||
|  | @ -1062,6 +1083,9 @@ pub fn (expr Expr) position() token.Position { | |||
| 			return expr.pos | ||||
| 		} | ||||
| 		// ast.ParExpr { }
 | ||||
| 		SelectExpr { | ||||
| 			return expr.pos | ||||
| 		} | ||||
| 		SelectorExpr { | ||||
| 			return expr.pos | ||||
| 		} | ||||
|  |  | |||
|  | @ -2641,6 +2641,10 @@ pub fn (mut c Checker) expr(node ast.Expr) table.Type { | |||
| 			// never happens
 | ||||
| 			return table.void_type | ||||
| 		} | ||||
| 		ast.SelectExpr { | ||||
| 			// TODO: to be implemented
 | ||||
| 			return table.void_type | ||||
| 		} | ||||
| 		ast.SelectorExpr { | ||||
| 			return c.selector_expr(mut node) | ||||
| 		} | ||||
|  |  | |||
|  | @ -953,6 +953,9 @@ pub fn (mut f Fmt) expr(node ast.Expr) { | |||
| 			f.write('..') | ||||
| 			f.expr(node.high) | ||||
| 		} | ||||
| 		ast.SelectExpr { | ||||
| 			// TODO: implement this
 | ||||
| 		} | ||||
| 		ast.SelectorExpr { | ||||
| 			f.expr(node.expr) | ||||
| 			f.write('.') | ||||
|  |  | |||
|  | @ -2137,6 +2137,9 @@ fn (mut g Gen) expr(node ast.Expr) { | |||
| 		ast.RangeExpr { | ||||
| 			// Only used in IndexExpr
 | ||||
| 		} | ||||
| 		ast.SelectExpr { | ||||
| 			// TODO: to be implemented
 | ||||
| 		} | ||||
| 		ast.SizeOf { | ||||
| 			if node.is_type { | ||||
| 				node_typ := g.unwrap_generic(node.typ) | ||||
|  |  | |||
|  | @ -619,6 +619,9 @@ fn (mut g JsGen) expr(node ast.Expr) { | |||
| 		ast.RangeExpr { | ||||
| 			// Only used in IndexExpr, requires index type info
 | ||||
| 		} | ||||
| 		ast.SelectExpr { | ||||
| 			// TODO: to be implemented
 | ||||
| 		} | ||||
| 		ast.SelectorExpr { | ||||
| 			g.gen_selector_expr(node) | ||||
| 		} | ||||
|  |  | |||
|  | @ -288,3 +288,139 @@ fn (mut p Parser) match_expr() ast.MatchExpr { | |||
| 		var_name: var_name | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| fn (mut p Parser) select_expr() ast.SelectExpr { | ||||
| 	match_first_pos := p.tok.position() | ||||
| 	p.check(.key_select) | ||||
| 	no_lcbr := p.tok.kind != .lcbr | ||||
| 	if !no_lcbr { | ||||
| 		p.check(.lcbr) | ||||
| 	} | ||||
| 	mut branches := []ast.SelectBranch{} | ||||
| 	mut has_else := false | ||||
| 	mut has_timeout := false | ||||
| 	for { | ||||
| 		branch_first_pos := p.tok.position() | ||||
| 		comment := p.check_comment() // comment before {}
 | ||||
| 		p.open_scope() | ||||
| 		// final else
 | ||||
| 		mut is_else := false | ||||
| 		mut is_timeout := false | ||||
| 		mut stmt := ast.Stmt{} | ||||
| 		if p.tok.kind == .key_else { | ||||
| 			if has_timeout { | ||||
| 				p.error_with_pos('timeout `> t` and `else` are mutually exclusive `select` keys', p.tok.position()) | ||||
| 			} | ||||
| 			if has_else { | ||||
| 				p.error_with_pos('at most one `else` branch allowed in `select` block', p.tok.position()) | ||||
| 			} | ||||
| 			is_else = true | ||||
| 			has_else = true | ||||
| 			p.next() | ||||
| 		} else if p.tok.kind == .gt { | ||||
| 			if has_else { | ||||
| 				p.error_with_pos('`else` and timeout `> t` are mutually exclusive `select` keys', p.tok.position()) | ||||
| 			} | ||||
| 			if has_timeout { | ||||
| 				p.error_with_pos('at most one timeout `> t` branch allowed in `select` block', p.tok.position()) | ||||
| 			} | ||||
| 			is_timeout = true | ||||
| 			has_timeout = true | ||||
| 			p.next() | ||||
| 			p.inside_match = true | ||||
| 			expr := p.expr(0) | ||||
| 			p.inside_match = false | ||||
| 			stmt = ast.ExprStmt{ | ||||
| 				expr: expr | ||||
| 				pos: expr.position() | ||||
| 				comments: [comment] | ||||
| 				is_expr: true | ||||
| 			} | ||||
| 		} else { | ||||
| 			p.inside_match = true | ||||
| 			exprs, comments := p.expr_list() | ||||
| 			if exprs.len != 1 { | ||||
| 				p.error('only one expression allowed as `select` key') | ||||
| 			} | ||||
| 			if p.tok.kind in [.assign, .decl_assign] { | ||||
| 				stmt = p.partial_assign_stmt(exprs, comments) | ||||
| 			} else { | ||||
| 				stmt = ast.ExprStmt{ | ||||
| 					expr: exprs[0] | ||||
| 					pos: exprs[0].position() | ||||
| 					comments: [comment] | ||||
| 					is_expr: true | ||||
| 				} | ||||
| 			} | ||||
| 			p.inside_match = false | ||||
| 			match stmt { | ||||
| 				ast.ExprStmt { | ||||
| 					if !stmt.is_expr { | ||||
| 						p.error_with_pos('select: invalid expression', stmt.pos) | ||||
| 					} else { | ||||
| 						match stmt.expr as expr { | ||||
| 							ast.InfixExpr { | ||||
| 								if expr.op != .arrow { | ||||
| 									p.error_with_pos('select key: `<-` operator expected', expr.pos) | ||||
| 								} | ||||
| 							} | ||||
| 							else { | ||||
| 								p.error_with_pos('select key: send expression (`ch <- x`) expected', stmt.pos) | ||||
| 							} | ||||
| 						} | ||||
| 					} | ||||
| 				} | ||||
| 				ast.AssignStmt { | ||||
| 					match stmt.right[0] as expr { | ||||
| 						ast.PrefixExpr { | ||||
| 							if expr.op != .arrow { | ||||
| 								p.error_with_pos('select key: `<-` operator expected', expr.pos) | ||||
| 							} | ||||
| 						} else { | ||||
| 							p.error_with_pos('select key: receive expression expected', stmt.right[0].position()) | ||||
| 						} | ||||
| 					} | ||||
| 				} | ||||
| 				else { | ||||
| 					p.error_with_pos('select: transmission statement expected', stmt.position()) | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 		branch_last_pos := p.tok.position() | ||||
| 		p.inside_match_body = true | ||||
| 		stmts := p.parse_block_no_scope(false) | ||||
| 		p.close_scope() | ||||
| 		p.inside_match_body = false | ||||
| 		pos := token.Position{ | ||||
| 			line_nr: branch_first_pos.line_nr | ||||
| 			pos: branch_first_pos.pos | ||||
| 			len: branch_last_pos.pos - branch_first_pos.pos + branch_last_pos.len | ||||
| 		} | ||||
| 		post_comments := p.eat_comments() | ||||
| 		branches << ast.SelectBranch{ | ||||
| 			stmt: stmt | ||||
| 			stmts: stmts | ||||
| 			pos: pos | ||||
| 			comment: comment | ||||
| 			is_else: is_else | ||||
| 			is_timeout: is_timeout | ||||
| 			post_comments: post_comments | ||||
| 		} | ||||
| 		if p.tok.kind == .rcbr || ((is_else || is_timeout) && no_lcbr) { | ||||
| 			break | ||||
| 		} | ||||
| 	} | ||||
| 	match_last_pos := p.tok.position() | ||||
| 	pos := token.Position{ | ||||
| 		line_nr: match_first_pos.line_nr | ||||
| 		pos: match_first_pos.pos | ||||
| 		len: match_last_pos.pos - match_first_pos.pos + match_last_pos.len | ||||
| 	} | ||||
| 	if p.tok.kind == .rcbr { | ||||
| 		p.check(.rcbr) | ||||
| 	} | ||||
| 	return ast.SelectExpr{ | ||||
| 		branches: branches | ||||
| 		pos: pos | ||||
| 	} | ||||
| } | ||||
|  |  | |||
|  | @ -859,8 +859,9 @@ fn (mut p Parser) parse_multi_expr(is_top_level bool) ast.Stmt { | |||
| 	} | ||||
| 	if p.tok.kind in [.assign, .decl_assign] || p.tok.kind.is_assign() { | ||||
| 		return p.partial_assign_stmt(left, left_comments) | ||||
| 	} else if is_top_level && tok.kind !in [.key_if, .key_match, .key_lock, .key_rlock] && | ||||
| 		left0 !is ast.CallExpr && left0 !is ast.PostfixExpr && !(left0 is ast.InfixExpr && | ||||
| 	} else if is_top_level && tok.kind !in | ||||
| 		[.key_if, .key_match, .key_lock, .key_rlock, .key_select] && left0 !is ast.CallExpr && | ||||
| 		left0 !is ast.PostfixExpr && !(left0 is ast.InfixExpr && | ||||
| 		(left0 as ast.InfixExpr).op in [.left_shift, .arrow]) && left0 !is ast.ComptimeCall { | ||||
| 		p.error_with_pos('expression evaluated but not used', left0.position()) | ||||
| 	} | ||||
|  |  | |||
|  | @ -74,6 +74,9 @@ pub fn (mut p Parser) expr(precedence int) ast.Expr { | |||
| 		.key_match { | ||||
| 			node = p.match_expr() | ||||
| 		} | ||||
| 		.key_select { | ||||
| 			node = p.select_expr() | ||||
| 		} | ||||
| 		.number { | ||||
| 			node = p.parse_number_literal() | ||||
| 		} | ||||
|  |  | |||
|  | @ -0,0 +1,7 @@ | |||
| vlib/v/parser/tests/select_bad_key_1.vv:39:8: error: select key: receive expression expected | ||||
|    37 | fn f3_bad(ch1 chan St) { | ||||
|    38 |     select { | ||||
|    39 |         b := 17 { | ||||
|       |              ~~ | ||||
|    40 |             println(b) | ||||
|    41 |         } | ||||
|  | @ -0,0 +1,47 @@ | |||
| import time | ||||
| 
 | ||||
| struct St { | ||||
| 	a int | ||||
| } | ||||
| 
 | ||||
| fn f1_good(ch1 chan St, ch2 chan int, ch3 chan int) { | ||||
| 	mut a := 5 | ||||
| 	select { | ||||
| 		a = <- ch3 { | ||||
| 			println(a) | ||||
| 		} | ||||
| 		b := <- ch1 { | ||||
| 			println(b.a) | ||||
| 		} | ||||
| 		ch1 <- a { | ||||
| 			a++ | ||||
| 		} | ||||
| 		> 50 * time.millisecond { | ||||
| 			println('timeout') | ||||
| 		} | ||||
| 	} | ||||
| 	println('done') | ||||
| } | ||||
| 
 | ||||
| fn f2_good(ch1 chan St) { | ||||
| 	select { | ||||
| 		b := <- ch1 { | ||||
| 			println(b) | ||||
| 		} | ||||
| 		else { | ||||
| 			println('no channel ready') | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| fn f3_bad(ch1 chan St) { | ||||
| 	select { | ||||
| 		b := 17 { | ||||
| 			println(b) | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| fn main() {} | ||||
| 
 | ||||
| 			 | ||||
|  | @ -0,0 +1,7 @@ | |||
| vlib/v/parser/tests/select_bad_key_2.vv:7:5: error: select key: `<-` operator expected | ||||
|     5 |             println(b) | ||||
|     6 |         } | ||||
|     7 |         a + 7 { | ||||
|       |           ^ | ||||
|     8 |             println("shouldn't get here") | ||||
|     9 |         } | ||||
|  | @ -0,0 +1,13 @@ | |||
| fn f3_bad(ch1 chan int) { | ||||
| 	a := 5 | ||||
| 	select { | ||||
| 		b := <-ch1 { | ||||
| 			println(b) | ||||
| 		} | ||||
| 		a + 7 { | ||||
| 			println("shouldn't get here") | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| fn main() {} | ||||
|  | @ -0,0 +1,7 @@ | |||
| vlib/v/parser/tests/select_bad_key_3.vv:7:3: error: select key: send expression (`ch <- x`) expected | ||||
|     5 |             println(b) | ||||
|     6 |         } | ||||
|     7 |         println(7) { | ||||
|       |         ~~~~~~~~~~ | ||||
|     8 |             println("shouldn't get here") | ||||
|     9 |         } | ||||
|  | @ -0,0 +1,13 @@ | |||
| fn f3_bad(ch1 chan int) { | ||||
| 	a := 5 | ||||
| 	select { | ||||
| 		b := <-ch1 { | ||||
| 			println(b) | ||||
| 		} | ||||
| 		println(7) { | ||||
| 			println("shouldn't get here") | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| fn main() {} | ||||
|  | @ -0,0 +1,7 @@ | |||
| vlib/v/parser/tests/select_bad_key_4.vv:7:8: error: select key: `<-` operator expected | ||||
|     5 |             println(b) | ||||
|     6 |         } | ||||
|     7 |         c := -a { | ||||
|       |              ^ | ||||
|     8 |             println("shouldn't get here") | ||||
|     9 |         } | ||||
|  | @ -0,0 +1,13 @@ | |||
| fn f3_bad(ch1 chan int) { | ||||
| 	mut a := 5 | ||||
| 	select { | ||||
| 		a = <-ch1 { | ||||
| 			println(b) | ||||
| 		} | ||||
| 		c := -a { | ||||
| 			println("shouldn't get here") | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| fn main() {} | ||||
|  | @ -0,0 +1,7 @@ | |||
| vlib/v/parser/tests/select_else_1.vv:12:3: error: `else` and timeout `> t` are mutually exclusive `select` keys | ||||
|    10 |             println("shouldn't get here") | ||||
|    11 |         } | ||||
|    12 |         > 30 * time.millisecond { | ||||
|       |         ^ | ||||
|    13 |             println('bad') | ||||
|    14 |         } | ||||
|  | @ -0,0 +1,18 @@ | |||
| import time | ||||
| 
 | ||||
| fn f3_bad(ch1 chan int) { | ||||
| 	a := 5 | ||||
| 	select { | ||||
| 		b := <-ch1 { | ||||
| 			println(b) | ||||
| 		} | ||||
| 		else { | ||||
| 			println("shouldn't get here") | ||||
| 		} | ||||
| 		> 30 * time.millisecond { | ||||
| 			println('bad') | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| fn main() {} | ||||
|  | @ -0,0 +1,7 @@ | |||
| vlib/v/parser/tests/select_else_2.vv:12:3: error: timeout `> t` and `else` are mutually exclusive `select` keys | ||||
|    10 |             println('bad') | ||||
|    11 |         } | ||||
|    12 |         else { | ||||
|       |         ~~~~ | ||||
|    13 |             println("shouldn't get here") | ||||
|    14 |         } | ||||
|  | @ -0,0 +1,18 @@ | |||
| import time | ||||
| 
 | ||||
| fn f3_bad(ch1 chan int) { | ||||
| 	a := 5 | ||||
| 	select { | ||||
| 		b := <-ch1 { | ||||
| 			println(b) | ||||
| 		} | ||||
| 		> 30 * time.millisecond { | ||||
| 			println('bad') | ||||
| 		} | ||||
| 		else { | ||||
| 			println("shouldn't get here") | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| fn main() {} | ||||
		Loading…
	
		Reference in New Issue