1
0
mirror of https://github.com/vlang/v.git synced 2023-08-10 21:13:21 +03:00

parser: implement parsing of select block (#6379)

This commit is contained in:
Uwe Krüger 2020-09-16 15:34:57 +02:00 committed by GitHub
parent 3a795e6d9b
commit 1bc9063573
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
20 changed files with 344 additions and 3 deletions

View File

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

View File

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

View File

@ -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('.')

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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