parser: assign.v, containers.v, for.v, if.v

pull/4465/head
Alexander Medvednikov 2020-04-17 18:16:55 +02:00
parent b53fb365a6
commit 59baef89a0
5 changed files with 529 additions and 500 deletions

View File

@ -0,0 +1,115 @@
// 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
fn (var p Parser) assign_stmt() ast.Stmt {
is_static := p.tok.kind == .key_static
if is_static {
p.next()
}
idents := p.parse_assign_lhs()
op := p.tok.kind
p.next() // :=, =
pos := p.tok.position()
exprs := p.parse_assign_rhs()
is_decl := op == .decl_assign
for i, ident in idents {
known_var := p.scope.known_var(ident.name)
if !is_decl && !known_var {
p.error('unknown variable `$ident.name`')
}
if is_decl && ident.kind != .blank_ident {
if p.scope.known_var(ident.name) {
p.error('redefinition of `$ident.name`')
}
if idents.len == exprs.len {
p.scope.register(ident.name, ast.Var{
name: ident.name
expr: exprs[i]
is_mut: ident.is_mut || p.inside_for
})
} else {
p.scope.register(ident.name, ast.Var{
name: ident.name
is_mut: ident.is_mut || p.inside_for
})
}
}
}
return ast.AssignStmt{
left: idents
right: exprs
op: op
pos: pos
is_static: is_static
}
}
// TODO: is it possible to merge with AssignStmt?
pub fn (var p Parser) assign_expr(left ast.Expr) ast.AssignExpr {
op := p.tok.kind
p.next()
pos := p.tok.position()
val := p.expr(0)
match left {
ast.IndexExpr {
// it.mark_as_setter()
it.is_setter = true
}
else {}
}
node := ast.AssignExpr{
left: left
val: val
op: op
pos: pos
}
return node
}
fn (var p Parser) parse_assign_lhs() []ast.Ident {
var idents := []ast.Ident
for {
is_mut := p.tok.kind == .key_mut || p.tok.kind == .key_var
if is_mut {
p.next()
}
is_static := p.tok.kind == .key_static
if is_static {
p.check(.key_static)
}
var ident := p.parse_ident(false, false)
ident.is_mut = is_mut
ident.info = ast.IdentVar{
is_mut: is_mut
is_static: is_static
}
idents << ident
if p.tok.kind == .comma {
p.check(.comma)
} else {
break
}
}
return idents
}
// right hand side of `=` or `:=` in `a,b,c := 1,2,3`
fn (var p Parser) parse_assign_rhs() []ast.Expr {
var exprs := []ast.Expr
for {
expr := p.expr(0)
exprs << expr
if p.tok.kind == .comma {
p.check(.comma)
} else {
break
}
}
return exprs
}

View File

@ -2,3 +2,114 @@
// 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
fn (var p Parser) array_init() ast.ArrayInit {
first_pos := p.tok.position()
var last_pos := token.Position{}
p.check(.lsbr)
// p.warn('array_init() exp=$p.expected_type')
var array_type := table.void_type
var elem_type := table.void_type
var exprs := []ast.Expr
var is_fixed := false
if p.tok.kind == .rsbr {
// []typ => `[]` and `typ` must be on the same line
line_nr := p.tok.line_nr
p.check(.rsbr)
// []string
if p.tok.kind in [.name, .amp] && p.tok.line_nr == line_nr {
elem_type = p.parse_type()
// this is set here becasue its a known type, others could be the
// result of expr so we do those in checker
idx := p.table.find_or_register_array(elem_type, 1)
array_type = table.new_type(idx)
}
} else {
// [1,2,3] or [const]byte
for i := 0; p.tok.kind != .rsbr; i++ {
expr := p.expr(0)
exprs << expr
if p.tok.kind == .comma {
p.check(.comma)
}
// p.check_comment()
}
line_nr := p.tok.line_nr
$if tinyc {
// NB: do not remove the next line without testing
// v selfcompilation with tcc first
tcc_stack_bug := 12345
}
last_pos = p.tok.position()
p.check(.rsbr)
// [100]byte
if exprs.len == 1 && p.tok.kind in [.name, .amp] && p.tok.line_nr == line_nr {
elem_type = p.parse_type()
is_fixed = true
}
}
// !
if p.tok.kind == .not {
last_pos = p.tok.position()
p.next()
}
if p.tok.kind == .not {
last_pos = p.tok.position()
p.next()
}
if p.tok.kind == .lcbr && exprs.len == 0 {
// `[]int{ len: 10, cap: 100}` syntax
p.next()
for p.tok.kind != .rcbr {
key := p.check_name()
p.check(.colon)
if !(key in ['len', 'cap', 'init']) {
p.error('wrong field `$key`, expecting `len`, `cap`, or `init`')
}
p.expr(0)
if p.tok.kind != .rcbr {
p.check(.comma)
}
}
p.check(.rcbr)
}
pos := token.Position{
line_nr: first_pos.line_nr
pos: first_pos.pos
len: last_pos.pos - first_pos.pos + last_pos.len
}
return ast.ArrayInit{
is_fixed: is_fixed
mod: p.mod
elem_type: elem_type
typ: array_type
exprs: exprs
pos: pos
}
}
fn (var p Parser) map_init() ast.MapInit {
pos := p.tok.position()
var keys := []ast.Expr
var vals := []ast.Expr
for p.tok.kind != .rcbr && p.tok.kind != .eof {
// p.check(.str)
key := p.expr(0)
keys << key
p.check(.colon)
val := p.expr(0)
vals << val
if p.tok.kind == .comma {
p.next()
}
}
return ast.MapInit{
keys: keys
vals: vals
pos: pos
}
}

128
vlib/v/parser/for.v 100644
View File

@ -0,0 +1,128 @@
// 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
fn (var p Parser) for_stmt() ast.Stmt {
p.check(.key_for)
pos := p.tok.position()
p.open_scope()
p.inside_for = true
// defer { p.close_scope() }
// Infinite loop
if p.tok.kind == .lcbr {
p.inside_for = false
stmts := p.parse_block()
p.close_scope()
return ast.ForStmt{
stmts: stmts
pos: pos
is_inf: true
}
} else if p.tok.kind in [.key_mut, .key_var] {
p.error('`mut` is not needed in for loops')
} else if p.peek_tok.kind in [.decl_assign, .assign, .semicolon] || p.tok.kind == .semicolon {
// `for i := 0; i < 10; i++ {`
var init := ast.Stmt{}
var cond := p.new_true_expr()
// mut inc := ast.Stmt{}
var inc := ast.Expr{}
var has_init := false
var has_cond := false
var has_inc := false
if p.peek_tok.kind in [.assign, .decl_assign] {
init = p.assign_stmt()
has_init = true
} else if p.tok.kind != .semicolon {
}
// allow `for ;; i++ {`
// Allow `for i = 0; i < ...`
p.check(.semicolon)
if p.tok.kind != .semicolon {
var typ := table.void_type
cond = p.expr(0)
has_cond = true
}
p.check(.semicolon)
if p.tok.kind != .lcbr {
// inc = p.stmt()
inc = p.expr(0)
has_inc = true
}
p.inside_for = false
stmts := p.parse_block()
p.close_scope()
return ast.ForCStmt{
stmts: stmts
has_init: has_init
has_cond: has_cond
has_inc: has_inc
init: init
cond: cond
inc: inc
pos: pos
}
} else if p.peek_tok.kind in [.key_in, .comma] {
// `for i in vals`, `for i in start .. end`
var key_var_name := ''
var val_var_name := p.check_name()
if p.tok.kind == .comma {
p.check(.comma)
key_var_name = val_var_name
val_var_name = p.check_name()
p.scope.register(key_var_name, ast.Var{
name: key_var_name
typ: table.int_type
})
}
p.check(.key_in)
// arr_expr
cond := p.expr(0)
// 0 .. 10
// start := p.tok.lit.int()
// TODO use RangeExpr
var high_expr := ast.Expr{}
var is_range := false
if p.tok.kind == .dotdot {
is_range = true
p.check(.dotdot)
high_expr = p.expr(0)
p.scope.register(val_var_name, ast.Var{
name: val_var_name
typ: table.int_type
})
} else {
// this type will be set in checker
p.scope.register(val_var_name, ast.Var{
name: val_var_name
})
}
p.inside_for = false
stmts := p.parse_block()
// println('nr stmts=$stmts.len')
p.close_scope()
return ast.ForInStmt{
stmts: stmts
cond: cond
key_var: key_var_name
val_var: val_var_name
high: high_expr
is_range: is_range
pos: pos
}
}
// `for cond {`
cond := p.expr(0)
p.inside_for = false
stmts := p.parse_block()
p.close_scope()
return ast.ForStmt{
cond: cond
stmts: stmts
pos: pos
}
}

175
vlib/v/parser/if.v 100644
View File

@ -0,0 +1,175 @@
// 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
fn (var p Parser) if_expr() ast.IfExpr {
pos := p.tok.position()
var branches := []ast.IfBranch
var has_else := false
for p.tok.kind in [.key_if, .key_else] {
p.inside_if = true
branch_pos := p.tok.position()
var comment := ast.Comment{}
if p.tok.kind == .key_if {
p.check(.key_if)
} else {
// if p.tok.kind == .comment {
// p.error('place comments inside {}')
// }
// comment = p.check_comment()
p.check(.key_else)
if p.tok.kind == .key_if {
p.check(.key_if)
} else {
has_else = true
p.inside_if = false
branches << ast.IfBranch{
stmts: p.parse_block()
pos: branch_pos
comment: comment
}
break
}
}
var cond := ast.Expr{}
var is_or := false
// `if x := opt() {`
if p.peek_tok.kind == .decl_assign {
is_or = true
p.open_scope()
var_name := p.check_name()
p.check(.decl_assign)
expr := p.expr(0)
p.scope.register(var_name, ast.Var{
name: var_name
expr: expr
})
cond = ast.IfGuardExpr{
var_name: var_name
expr: expr
}
} else {
cond = p.expr(0)
}
p.inside_if = false
stmts := p.parse_block()
if is_or {
p.close_scope()
}
branches << ast.IfBranch{
cond: cond
stmts: stmts
pos: branch_pos
comment: ast.Comment{}
}
if p.tok.kind != .key_else {
break
}
}
return ast.IfExpr{
branches: branches
pos: pos
has_else: has_else
}
}
fn (var p Parser) match_expr() ast.MatchExpr {
match_first_pos := p.tok.position()
p.inside_match = true
p.check(.key_match)
is_mut := p.tok.kind in [.key_mut, .key_var]
var is_sum_type := false
if is_mut {
p.next()
}
cond := p.expr(0)
p.inside_match = false
p.check(.lcbr)
var branches := []ast.MatchBranch
for {
branch_first_pos := p.tok.position()
comment := p.check_comment() // comment before {}
var exprs := []ast.Expr
p.open_scope()
// final else
var is_else := false
if p.tok.kind == .key_else {
is_else = true
p.next()
} else if p.tok.kind == .name && (p.tok.lit in table.builtin_type_names || (p.tok.lit[0].is_capital() &&
!p.tok.lit.is_upper()) || p.peek_tok.kind == .dot) {
// Sum type match
// if sym.kind == .sum_type {
// p.warn('is sum')
// TODO `exprs << ast.Type{...}
typ := p.parse_type()
x := ast.Type{
typ: typ
}
var expr := ast.Expr{}
expr = x
exprs << expr
p.scope.register('it', ast.Var{
name: 'it'
typ: table.type_to_ptr(typ)
})
// 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
exprs << expr
if p.tok.kind != .comma {
break
}
p.check(.comma)
}
}
branch_last_pos := p.tok.position()
// p.warn('match block')
stmts := p.parse_block()
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
}
p.close_scope()
if p.tok.kind == .rcbr {
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
}
p.check(.rcbr)
return ast.MatchExpr{
branches: branches
cond: cond
is_sum_type: is_sum_type
pos: pos
is_mut: is_mut
}
}

View File

@ -455,28 +455,6 @@ pub fn (var p Parser) stmt() ast.Stmt {
}
}
// TODO: is it possible to merge with AssignStmt?
pub fn (var p Parser) assign_expr(left ast.Expr) ast.AssignExpr {
op := p.tok.kind
p.next()
pos := p.tok.position()
val := p.expr(0)
match left {
ast.IndexExpr {
// it.mark_as_setter()
it.is_setter = true
}
else {}
}
node := ast.AssignExpr{
left: left
val: val
op: op
pos: pos
}
return node
}
fn (var p Parser) attribute() ast.Attr {
p.check(.lsbr)
if p.tok.kind == .key_if {
@ -826,197 +804,6 @@ fn (var p Parser) enum_val() ast.EnumVal {
}
}
fn (var p Parser) for_stmt() ast.Stmt {
p.check(.key_for)
pos := p.tok.position()
p.open_scope()
p.inside_for = true
// defer { p.close_scope() }
// Infinite loop
if p.tok.kind == .lcbr {
p.inside_for = false
stmts := p.parse_block()
p.close_scope()
return ast.ForStmt{
stmts: stmts
pos: pos
is_inf: true
}
} else if p.tok.kind in [.key_mut, .key_var] {
p.error('`mut` is not needed in for loops')
} else if p.peek_tok.kind in [.decl_assign, .assign, .semicolon] || p.tok.kind == .semicolon {
// `for i := 0; i < 10; i++ {`
var init := ast.Stmt{}
var cond := p.new_true_expr()
// mut inc := ast.Stmt{}
var inc := ast.Expr{}
var has_init := false
var has_cond := false
var has_inc := false
if p.peek_tok.kind in [.assign, .decl_assign] {
init = p.assign_stmt()
has_init = true
} else if p.tok.kind != .semicolon {
}
// allow `for ;; i++ {`
// Allow `for i = 0; i < ...`
p.check(.semicolon)
if p.tok.kind != .semicolon {
var typ := table.void_type
cond = p.expr(0)
has_cond = true
}
p.check(.semicolon)
if p.tok.kind != .lcbr {
// inc = p.stmt()
inc = p.expr(0)
has_inc = true
}
p.inside_for = false
stmts := p.parse_block()
p.close_scope()
return ast.ForCStmt{
stmts: stmts
has_init: has_init
has_cond: has_cond
has_inc: has_inc
init: init
cond: cond
inc: inc
pos: pos
}
} else if p.peek_tok.kind in [.key_in, .comma] {
// `for i in vals`, `for i in start .. end`
var key_var_name := ''
var val_var_name := p.check_name()
if p.tok.kind == .comma {
p.check(.comma)
key_var_name = val_var_name
val_var_name = p.check_name()
p.scope.register(key_var_name, ast.Var{
name: key_var_name
typ: table.int_type
})
}
p.check(.key_in)
// arr_expr
cond := p.expr(0)
// 0 .. 10
// start := p.tok.lit.int()
// TODO use RangeExpr
var high_expr := ast.Expr{}
var is_range := false
if p.tok.kind == .dotdot {
is_range = true
p.check(.dotdot)
high_expr = p.expr(0)
p.scope.register(val_var_name, ast.Var{
name: val_var_name
typ: table.int_type
})
} else {
// this type will be set in checker
p.scope.register(val_var_name, ast.Var{
name: val_var_name
})
}
p.inside_for = false
stmts := p.parse_block()
// println('nr stmts=$stmts.len')
p.close_scope()
return ast.ForInStmt{
stmts: stmts
cond: cond
key_var: key_var_name
val_var: val_var_name
high: high_expr
is_range: is_range
pos: pos
}
}
// `for cond {`
cond := p.expr(0)
p.inside_for = false
stmts := p.parse_block()
p.close_scope()
return ast.ForStmt{
cond: cond
stmts: stmts
pos: pos
}
}
fn (var p Parser) if_expr() ast.IfExpr {
pos := p.tok.position()
var branches := []ast.IfBranch
var has_else := false
for p.tok.kind in [.key_if, .key_else] {
p.inside_if = true
branch_pos := p.tok.position()
var comment := ast.Comment{}
if p.tok.kind == .key_if {
p.check(.key_if)
} else {
// if p.tok.kind == .comment {
// p.error('place comments inside {}')
// }
// comment = p.check_comment()
p.check(.key_else)
if p.tok.kind == .key_if {
p.check(.key_if)
} else {
has_else = true
p.inside_if = false
branches << ast.IfBranch{
stmts: p.parse_block()
pos: branch_pos
comment: comment
}
break
}
}
var cond := ast.Expr{}
var is_or := false
// `if x := opt() {`
if p.peek_tok.kind == .decl_assign {
is_or = true
p.open_scope()
var_name := p.check_name()
p.check(.decl_assign)
expr := p.expr(0)
p.scope.register(var_name, ast.Var{
name: var_name
expr: expr
})
cond = ast.IfGuardExpr{
var_name: var_name
expr: expr
}
} else {
cond = p.expr(0)
}
p.inside_if = false
stmts := p.parse_block()
if is_or {
p.close_scope()
}
branches << ast.IfBranch{
cond: cond
stmts: stmts
pos: branch_pos
comment: ast.Comment{}
}
if p.tok.kind != .key_else {
break
}
}
return ast.IfExpr{
branches: branches
pos: pos
has_else: has_else
}
}
fn (var p Parser) string_expr() ast.Expr {
is_raw := p.tok.kind == .name && p.tok.lit == 'r'
is_cstr := p.tok.kind == .name && p.tok.lit == 'c'
@ -1078,113 +865,6 @@ fn (var p Parser) string_expr() ast.Expr {
return node
}
fn (var p Parser) array_init() ast.ArrayInit {
first_pos := p.tok.position()
var last_pos := token.Position{}
p.check(.lsbr)
// p.warn('array_init() exp=$p.expected_type')
var array_type := table.void_type
var elem_type := table.void_type
var exprs := []ast.Expr
var is_fixed := false
if p.tok.kind == .rsbr {
// []typ => `[]` and `typ` must be on the same line
line_nr := p.tok.line_nr
p.check(.rsbr)
// []string
if p.tok.kind in [.name, .amp] && p.tok.line_nr == line_nr {
elem_type = p.parse_type()
// this is set here becasue its a known type, others could be the
// result of expr so we do those in checker
idx := p.table.find_or_register_array(elem_type, 1)
array_type = table.new_type(idx)
}
} else {
// [1,2,3] or [const]byte
for i := 0; p.tok.kind != .rsbr; i++ {
expr := p.expr(0)
exprs << expr
if p.tok.kind == .comma {
p.check(.comma)
}
// p.check_comment()
}
line_nr := p.tok.line_nr
$if tinyc {
// NB: do not remove the next line without testing
// v selfcompilation with tcc first
tcc_stack_bug := 12345
}
last_pos = p.tok.position()
p.check(.rsbr)
// [100]byte
if exprs.len == 1 && p.tok.kind in [.name, .amp] && p.tok.line_nr == line_nr {
elem_type = p.parse_type()
is_fixed = true
}
}
// !
if p.tok.kind == .not {
last_pos = p.tok.position()
p.next()
}
if p.tok.kind == .not {
last_pos = p.tok.position()
p.next()
}
if p.tok.kind == .lcbr && exprs.len == 0 {
// `[]int{ len: 10, cap: 100}` syntax
p.next()
for p.tok.kind != .rcbr {
key := p.check_name()
p.check(.colon)
if !(key in ['len', 'cap', 'init']) {
p.error('wrong field `$key`, expecting `len`, `cap`, or `init`')
}
p.expr(0)
if p.tok.kind != .rcbr {
p.check(.comma)
}
}
p.check(.rcbr)
}
pos := token.Position{
line_nr: first_pos.line_nr
pos: first_pos.pos
len: last_pos.pos - first_pos.pos + last_pos.len
}
return ast.ArrayInit{
is_fixed: is_fixed
mod: p.mod
elem_type: elem_type
typ: array_type
exprs: exprs
pos: pos
}
}
fn (var p Parser) map_init() ast.MapInit {
pos := p.tok.position()
var keys := []ast.Expr
var vals := []ast.Expr
for p.tok.kind != .rcbr && p.tok.kind != .eof {
// p.check(.str)
key := p.expr(0)
keys << key
p.check(.colon)
val := p.expr(0)
vals << val
if p.tok.kind == .comma {
p.next()
}
}
return ast.MapInit{
keys: keys
vals: vals
pos: pos
}
}
fn (var p Parser) parse_number_literal() ast.Expr {
lit := p.tok.lit
pos := p.tok.position()
@ -1329,91 +1009,6 @@ fn (var p Parser) return_stmt() ast.Return {
}
// left hand side of `=` or `:=` in `a,b,c := 1,2,3`
fn (var p Parser) parse_assign_lhs() []ast.Ident {
var idents := []ast.Ident
for {
is_mut := p.tok.kind == .key_mut || p.tok.kind == .key_var
if is_mut {
p.next()
}
is_static := p.tok.kind == .key_static
if is_static {
p.check(.key_static)
}
var ident := p.parse_ident(false, false)
ident.is_mut = is_mut
ident.info = ast.IdentVar{
is_mut: is_mut
is_static: is_static
}
idents << ident
if p.tok.kind == .comma {
p.check(.comma)
} else {
break
}
}
return idents
}
// right hand side of `=` or `:=` in `a,b,c := 1,2,3`
fn (var p Parser) parse_assign_rhs() []ast.Expr {
var exprs := []ast.Expr
for {
expr := p.expr(0)
exprs << expr
if p.tok.kind == .comma {
p.check(.comma)
} else {
break
}
}
return exprs
}
fn (var p Parser) assign_stmt() ast.Stmt {
is_static := p.tok.kind == .key_static
if is_static {
p.next()
}
idents := p.parse_assign_lhs()
op := p.tok.kind
p.next() // :=, =
pos := p.tok.position()
exprs := p.parse_assign_rhs()
is_decl := op == .decl_assign
for i, ident in idents {
known_var := p.scope.known_var(ident.name)
if !is_decl && !known_var {
p.error('unknown variable `$ident.name`')
}
if is_decl && ident.kind != .blank_ident {
if p.scope.known_var(ident.name) {
p.error('redefinition of `$ident.name`')
}
if idents.len == exprs.len {
p.scope.register(ident.name, ast.Var{
name: ident.name
expr: exprs[i]
is_mut: ident.is_mut || p.inside_for
})
} else {
p.scope.register(ident.name, ast.Var{
name: ident.name
is_mut: ident.is_mut || p.inside_for
})
}
}
}
return ast.AssignStmt{
left: idents
right: exprs
op: op
pos: pos
is_static: is_static
}
}
fn (var p Parser) global_decl() ast.GlobalDecl {
if !p.pref.translated && !p.pref.is_live && !p.builtin_mod && !p.pref.building_v && p.mod !=
'ui' && p.mod != 'gg2' && p.mod != 'uiold' && !os.getwd().contains('/volt') && !p.pref.enable_globals {
@ -1454,101 +1049,6 @@ fn (var p Parser) global_decl() ast.GlobalDecl {
return glob
}
fn (var p Parser) match_expr() ast.MatchExpr {
match_first_pos := p.tok.position()
p.inside_match = true
p.check(.key_match)
is_mut := p.tok.kind in [.key_mut, .key_var]
var is_sum_type := false
if is_mut {
p.next()
}
cond := p.expr(0)
p.inside_match = false
p.check(.lcbr)
var branches := []ast.MatchBranch
for {
branch_first_pos := p.tok.position()
comment := p.check_comment() // comment before {}
var exprs := []ast.Expr
p.open_scope()
// final else
var is_else := false
if p.tok.kind == .key_else {
is_else = true
p.next()
} else if p.tok.kind == .name && (p.tok.lit in table.builtin_type_names || (p.tok.lit[0].is_capital() &&
!p.tok.lit.is_upper()) || p.peek_tok.kind == .dot) {
// Sum type match
// if sym.kind == .sum_type {
// p.warn('is sum')
// TODO `exprs << ast.Type{...}
typ := p.parse_type()
x := ast.Type{
typ: typ
}
var expr := ast.Expr{}
expr = x
exprs << expr
p.scope.register('it', ast.Var{
name: 'it'
typ: table.type_to_ptr(typ)
})
// 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
exprs << expr
if p.tok.kind != .comma {
break
}
p.check(.comma)
}
}
branch_last_pos := p.tok.position()
// p.warn('match block')
stmts := p.parse_block()
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
}
p.close_scope()
if p.tok.kind == .rcbr {
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
}
p.check(.rcbr)
return ast.MatchExpr{
branches: branches
cond: cond
is_sum_type: is_sum_type
pos: pos
is_mut: is_mut
}
}
fn (var p Parser) enum_decl() ast.EnumDecl {
is_pub := p.tok.kind == .key_pub
if is_pub {