parser: assign.v, containers.v, for.v, if.v
							parent
							
								
									b53fb365a6
								
							
						
					
					
						commit
						59baef89a0
					
				| 
						 | 
				
			
			@ -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
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -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
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -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
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -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 {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue