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