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 | | pub type Expr = AnonFn | ArrayInit | AsCast | Assoc | BoolLiteral | CallExpr | CastExpr | | ||||||
| 	ChanInit | CharLiteral | Comment | ComptimeCall | ConcatExpr | EnumVal | FloatLiteral | | 	ChanInit | CharLiteral | Comment | ComptimeCall | ConcatExpr | EnumVal | FloatLiteral | | ||||||
| 	Ident | IfExpr | IfGuardExpr | IndexExpr | InfixExpr | IntegerLiteral | Likely | LockExpr | | 	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 | | 	SelectorExpr | SizeOf | SqlExpr | StringInterLiteral | StringLiteral | StructInit | Type | | ||||||
| 	TypeOf | UnsafeExpr | 	TypeOf | UnsafeExpr | ||||||
| 
 | 
 | ||||||
|  | @ -536,6 +536,27 @@ pub: | ||||||
| 	post_comments []Comment | 	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: | CompIf.is_opt: | ||||||
| `$if xyz? {}` => this compile time `if` is optional, | `$if xyz? {}` => this compile time `if` is optional, | ||||||
|  | @ -1062,6 +1083,9 @@ pub fn (expr Expr) position() token.Position { | ||||||
| 			return expr.pos | 			return expr.pos | ||||||
| 		} | 		} | ||||||
| 		// ast.ParExpr { }
 | 		// ast.ParExpr { }
 | ||||||
|  | 		SelectExpr { | ||||||
|  | 			return expr.pos | ||||||
|  | 		} | ||||||
| 		SelectorExpr { | 		SelectorExpr { | ||||||
| 			return expr.pos | 			return expr.pos | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
|  | @ -2641,6 +2641,10 @@ pub fn (mut c Checker) expr(node ast.Expr) table.Type { | ||||||
| 			// never happens
 | 			// never happens
 | ||||||
| 			return table.void_type | 			return table.void_type | ||||||
| 		} | 		} | ||||||
|  | 		ast.SelectExpr { | ||||||
|  | 			// TODO: to be implemented
 | ||||||
|  | 			return table.void_type | ||||||
|  | 		} | ||||||
| 		ast.SelectorExpr { | 		ast.SelectorExpr { | ||||||
| 			return c.selector_expr(mut node) | 			return c.selector_expr(mut node) | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
|  | @ -953,6 +953,9 @@ pub fn (mut f Fmt) expr(node ast.Expr) { | ||||||
| 			f.write('..') | 			f.write('..') | ||||||
| 			f.expr(node.high) | 			f.expr(node.high) | ||||||
| 		} | 		} | ||||||
|  | 		ast.SelectExpr { | ||||||
|  | 			// TODO: implement this
 | ||||||
|  | 		} | ||||||
| 		ast.SelectorExpr { | 		ast.SelectorExpr { | ||||||
| 			f.expr(node.expr) | 			f.expr(node.expr) | ||||||
| 			f.write('.') | 			f.write('.') | ||||||
|  |  | ||||||
|  | @ -2137,6 +2137,9 @@ fn (mut g Gen) expr(node ast.Expr) { | ||||||
| 		ast.RangeExpr { | 		ast.RangeExpr { | ||||||
| 			// Only used in IndexExpr
 | 			// Only used in IndexExpr
 | ||||||
| 		} | 		} | ||||||
|  | 		ast.SelectExpr { | ||||||
|  | 			// TODO: to be implemented
 | ||||||
|  | 		} | ||||||
| 		ast.SizeOf { | 		ast.SizeOf { | ||||||
| 			if node.is_type { | 			if node.is_type { | ||||||
| 				node_typ := g.unwrap_generic(node.typ) | 				node_typ := g.unwrap_generic(node.typ) | ||||||
|  |  | ||||||
|  | @ -619,6 +619,9 @@ fn (mut g JsGen) expr(node ast.Expr) { | ||||||
| 		ast.RangeExpr { | 		ast.RangeExpr { | ||||||
| 			// Only used in IndexExpr, requires index type info
 | 			// Only used in IndexExpr, requires index type info
 | ||||||
| 		} | 		} | ||||||
|  | 		ast.SelectExpr { | ||||||
|  | 			// TODO: to be implemented
 | ||||||
|  | 		} | ||||||
| 		ast.SelectorExpr { | 		ast.SelectorExpr { | ||||||
| 			g.gen_selector_expr(node) | 			g.gen_selector_expr(node) | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
|  | @ -288,3 +288,139 @@ fn (mut p Parser) match_expr() ast.MatchExpr { | ||||||
| 		var_name: var_name | 		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() { | 	if p.tok.kind in [.assign, .decl_assign] || p.tok.kind.is_assign() { | ||||||
| 		return p.partial_assign_stmt(left, left_comments) | 		return p.partial_assign_stmt(left, left_comments) | ||||||
| 	} else if is_top_level && tok.kind !in [.key_if, .key_match, .key_lock, .key_rlock] && | 	} else if is_top_level && tok.kind !in | ||||||
| 		left0 !is ast.CallExpr && left0 !is ast.PostfixExpr && !(left0 is ast.InfixExpr && | 		[.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 { | 		(left0 as ast.InfixExpr).op in [.left_shift, .arrow]) && left0 !is ast.ComptimeCall { | ||||||
| 		p.error_with_pos('expression evaluated but not used', left0.position()) | 		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 { | 		.key_match { | ||||||
| 			node = p.match_expr() | 			node = p.match_expr() | ||||||
| 		} | 		} | ||||||
|  | 		.key_select { | ||||||
|  | 			node = p.select_expr() | ||||||
|  | 		} | ||||||
| 		.number { | 		.number { | ||||||
| 			node = p.parse_number_literal() | 			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