v/vlib/v/parser/if_match.v

284 lines
6.4 KiB
V
Raw Normal View History

// Copyright (c) 2019-2020 Alexander Medvednikov. All rights reserved.
// Use of this source code is governed by an MIT license
// that can be found in the LICENSE file.
module parser
import v.ast
import v.table
import v.token
2020-04-23 01:16:58 +02:00
fn (mut p Parser) if_expr() ast.IfExpr {
was_inside_if_expr := p.inside_if_expr
2020-05-12 00:09:59 +02:00
defer {
p.inside_if_expr = was_inside_if_expr
2020-05-12 00:09:59 +02:00
}
p.inside_if_expr = true
pos := p.tok.position()
mut branches := []ast.IfBranch{}
2020-04-23 01:16:58 +02:00
mut has_else := false
mut comments := []ast.Comment{}
mut prev_guard := false
for p.tok.kind in [.key_if, .key_else] {
p.inside_if = true
start_pos := p.tok.position()
if p.tok.kind == .key_if {
p.next()
} else {
comments << p.eat_comments()
p.check(.key_else)
comments << p.eat_comments()
if p.tok.kind == .key_if {
p.next()
} else {
// else {
has_else = true
p.inside_if = false
end_pos := p.prev_tok.position()
2020-07-08 15:17:28 +02:00
body_pos := p.tok.position()
// only declare `err` if previous branch was an `if` guard
if prev_guard {
p.open_scope()
p.scope.register('errcode', ast.Var{
name: 'errcode'
typ: table.int_type
pos: body_pos
is_used: true
})
p.scope.register('err', ast.Var{
name: 'err'
typ: table.string_type
pos: body_pos
is_used: true
})
}
branches << ast.IfBranch{
stmts: p.parse_block()
pos: start_pos.extend(end_pos)
2020-07-08 15:17:28 +02:00
body_pos: body_pos.extend(p.tok.position())
comments: comments
}
if prev_guard {
p.close_scope()
}
comments = []
break
}
}
2020-04-23 01:16:58 +02:00
mut cond := ast.Expr{}
mut is_guard := false
comments << p.eat_comments()
// `if x := opt() {`
if p.peek_tok.kind == .decl_assign {
p.open_scope()
is_guard = true
var_pos := p.tok.position()
var_name := p.check_name()
comments << p.eat_comments()
p.check(.decl_assign)
comments << p.eat_comments()
expr := p.expr(0)
p.scope.register(var_name, ast.Var{
name: var_name
expr: expr
pos: var_pos
})
cond = ast.IfGuardExpr{
var_name: var_name
expr: expr
}
prev_guard = true
} else {
prev_guard = false
cond = p.expr(0)
}
comments << p.eat_comments()
mut left_as_name := ''
if cond is ast.InfixExpr as infix {
// if sum is T
is_is_cast := infix.op == .key_is
is_ident := infix.left is ast.Ident
left_as_name = if is_is_cast && p.tok.kind == .key_as {
p.next()
p.check_name()
} else if is_ident {
ident := infix.left as ast.Ident
ident.name
} else {
''
}
}
end_pos := p.prev_tok.position()
2020-07-08 15:17:28 +02:00
body_pos := p.tok.position()
p.inside_if = false
stmts := p.parse_block()
if is_guard {
p.close_scope()
}
branches << ast.IfBranch{
cond: cond
stmts: stmts
pos: start_pos.extend(end_pos)
2020-07-08 15:17:28 +02:00
body_pos: body_pos.extend(p.tok.position())
comments: comments
left_as_name: left_as_name
}
comments = p.eat_comments()
if p.tok.kind != .key_else {
break
}
}
return ast.IfExpr{
branches: branches
post_comments: comments
pos: pos
has_else: has_else
}
}
2020-04-23 01:16:58 +02:00
fn (mut p Parser) match_expr() ast.MatchExpr {
match_first_pos := p.tok.position()
p.inside_match = true
p.check(.key_match)
2020-04-30 16:55:33 +02:00
is_mut := p.tok.kind == .key_mut
2020-04-23 01:16:58 +02:00
mut is_sum_type := false
if is_mut {
p.next()
}
cond_pos := p.tok.position()
cond := p.expr(0)
p.inside_match = false
mut var_name := ''
if p.tok.kind == .key_as {
p.next()
var_name = p.check_name()
}
2020-08-10 19:54:38 +02:00
if p.tok.kind == .lcbr {
p.check(.lcbr)
}
mut branches := []ast.MatchBranch{}
for {
branch_first_pos := p.tok.position()
2020-04-25 17:49:16 +02:00
comment := p.check_comment() // comment before {}
mut exprs := []ast.Expr{}
p.open_scope()
// final else
2020-04-23 01:16:58 +02:00
mut is_else := false
if p.tok.kind == .key_else {
is_else = true
p.next()
2020-08-10 19:54:38 +02:00
} else if p.tok.kind == .name && !(p.tok.lit == 'C' &&
p.peek_tok.kind == .dot) && (p.tok.lit in table.builtin_type_names || p.tok.lit[0].is_capital() ||
(p.peek_tok.kind == .dot && p.peek_tok2.lit[0].is_capital())) {
if var_name.len == 0 {
match cond {
ast.Ident {
// shadow match cond variable
var_name = cond.name
}
else {
// ast.SelectorExpr {
// p.error('expecting `as` (eg. `match user.attribute as user_attr`) when matching struct fields')
// }
// p.error('only variables can be used in sum types matches')
}
}
}
// Sum type match
typ := p.parse_type()
exprs << ast.Type{
typ: typ
}
p.scope.register('it', ast.Var{
name: 'it'
2020-04-25 09:08:53 +02:00
typ: typ.to_ptr()
pos: cond_pos
is_used: true
is_mut: is_mut
})
if var_name.len > 0 {
// Register shadow variable or `as` variable with actual type
p.scope.register(var_name, ast.Var{
name: var_name
typ: typ.to_ptr()
pos: cond_pos
is_used: true
is_mut: is_mut
})
}
// TODO
if p.tok.kind == .comma {
p.next()
p.parse_type()
}
is_sum_type = true
} else {
// Expression match
for {
p.inside_match_case = true
expr := p.expr(0)
p.inside_match_case = false
if p.tok.kind == .dotdot {
p.error_with_pos('match only supports inclusive (`...`) ranges, not exclusive (`..`)',
p.tok.position())
} else if p.tok.kind == .ellipsis {
p.next()
expr2 := p.expr(0)
exprs << ast.RangeExpr{
low: expr
high: expr2
has_low: true
has_high: true
}
} else {
exprs << expr
}
if p.tok.kind != .comma {
break
}
p.check(.comma)
}
}
branch_last_pos := p.tok.position()
// p.warn('match block')
2020-05-12 00:26:39 +02:00
p.inside_match_body = true
stmts := p.parse_block()
2020-05-12 00:26:39 +02:00
p.inside_match_body = false
post_comments := p.eat_comments()
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
}
branches << ast.MatchBranch{
exprs: exprs
stmts: stmts
pos: pos
comment: comment
is_else: is_else
post_comments: post_comments
}
p.close_scope()
2020-08-10 19:54:38 +02:00
if p.tok.kind == .rcbr || is_else {
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
}
2020-08-10 19:54:38 +02:00
if p.tok.kind == .rcbr {
p.check(.rcbr)
}
// return ast.StructInit{}
return ast.MatchExpr{
branches: branches
cond: cond
is_sum_type: is_sum_type
pos: pos
is_mut: is_mut
var_name: var_name
}
}