parser: match expression + match fixes
							parent
							
								
									b6336f730b
								
							
						
					
					
						commit
						7edcbeca1a
					
				| 
						 | 
				
			
			@ -64,6 +64,7 @@ mut:
 | 
			
		|||
	builtin_mod    bool
 | 
			
		||||
	vh_lines       []string
 | 
			
		||||
	inside_if_expr bool
 | 
			
		||||
	inside_unwrapping_match_statement bool
 | 
			
		||||
	is_struct_init bool
 | 
			
		||||
	if_expr_cnt    int
 | 
			
		||||
	for_expr_cnt   int // to detect whether `continue` can be used
 | 
			
		||||
| 
						 | 
				
			
			@ -1019,6 +1020,7 @@ fn (p mut Parser) statements() string {
 | 
			
		|||
 | 
			
		||||
fn (p mut Parser) statements_no_rcbr() string {
 | 
			
		||||
	p.cur_fn.open_scope()
 | 
			
		||||
 | 
			
		||||
	if !p.inside_if_expr {
 | 
			
		||||
		p.genln('')
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			@ -1049,6 +1051,7 @@ fn (p mut Parser) statements_no_rcbr() string {
 | 
			
		|||
	}
 | 
			
		||||
	//p.fmt_dec()
 | 
			
		||||
	// println('close scope line=$p.scanner.line_nr')
 | 
			
		||||
 | 
			
		||||
	p.close_scope()
 | 
			
		||||
	return last_st_typ
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -1152,8 +1155,10 @@ fn (p mut Parser) statement(add_semi bool) string {
 | 
			
		|||
		p.if_st(false, 0)
 | 
			
		||||
	case Token.key_for:
 | 
			
		||||
		p.for_st()
 | 
			
		||||
	case Token.key_switch, Token.key_match:
 | 
			
		||||
	case Token.key_switch:
 | 
			
		||||
		p.switch_statement()
 | 
			
		||||
	case Token.key_match:
 | 
			
		||||
		p.match_statement(false)
 | 
			
		||||
	case Token.key_mut, Token.key_static:
 | 
			
		||||
		p.var_decl()
 | 
			
		||||
	case Token.key_return:
 | 
			
		||||
| 
						 | 
				
			
			@ -2409,6 +2414,9 @@ fn (p mut Parser) factor() string {
 | 
			
		|||
	case Token.key_if:
 | 
			
		||||
		typ = p.if_st(true, 0)
 | 
			
		||||
		return typ
 | 
			
		||||
	case Token.key_match:
 | 
			
		||||
		typ = p.match_statement(true)
 | 
			
		||||
		return typ
 | 
			
		||||
	default:
 | 
			
		||||
		if p.pref.is_verbose || p.pref.is_debug {
 | 
			
		||||
			next := p.peek()
 | 
			
		||||
| 
						 | 
				
			
			@ -3350,6 +3358,186 @@ fn (p mut Parser) switch_statement() {
 | 
			
		|||
	p.returns = false // only get here when no default, so return is not guaranteed
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Returns typ if used as expession 
 | 
			
		||||
fn (p mut Parser) match_statement(is_expr bool) string {
 | 
			
		||||
	p.check(.key_match)
 | 
			
		||||
	p.cgen.start_tmp()
 | 
			
		||||
	typ := p.bool_expression()
 | 
			
		||||
	expr := p.cgen.end_tmp()
 | 
			
		||||
 | 
			
		||||
	// is it safe to use p.cgen.insert_before ???
 | 
			
		||||
	tmp_var := p.get_tmp()
 | 
			
		||||
	p.cgen.insert_before('$typ $tmp_var = $expr;')
 | 
			
		||||
 | 
			
		||||
	p.check(.lcbr)
 | 
			
		||||
	mut i := 0
 | 
			
		||||
	mut all_cases_return := true
 | 
			
		||||
 | 
			
		||||
	// stores typ of resulting variable 
 | 
			
		||||
	mut res_typ := ''
 | 
			
		||||
 | 
			
		||||
	defer {
 | 
			
		||||
		p.check(.rcbr)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for p.tok != .rcbr {
 | 
			
		||||
		if p.tok == .key_else { 
 | 
			
		||||
			p.check(.key_else) 
 | 
			
		||||
			p.check(.arrow)
 | 
			
		||||
 | 
			
		||||
			// unwrap match if there is only else
 | 
			
		||||
			if i == 0 {
 | 
			
		||||
				if is_expr {
 | 
			
		||||
					// statements are dissallowed (if match is expression) so user cant declare variables there and so on
 | 
			
		||||
 | 
			
		||||
					// allow braces is else
 | 
			
		||||
					got_brace := p.tok == .lcbr
 | 
			
		||||
					if got_brace {
 | 
			
		||||
						p.check(.lcbr)
 | 
			
		||||
					}
 | 
			
		||||
 | 
			
		||||
					p.gen('( ')
 | 
			
		||||
 | 
			
		||||
					res_typ = p.bool_expression()
 | 
			
		||||
 | 
			
		||||
					p.gen(' )')
 | 
			
		||||
 | 
			
		||||
					// allow braces in else
 | 
			
		||||
					if got_brace {
 | 
			
		||||
						p.check(.rcbr)
 | 
			
		||||
					}
 | 
			
		||||
 | 
			
		||||
					return res_typ
 | 
			
		||||
				} else {
 | 
			
		||||
					p.match_parse_statement_branch()
 | 
			
		||||
					p.returns = all_cases_return && p.returns
 | 
			
		||||
					return ''
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			if is_expr {
 | 
			
		||||
				// statements are dissallowed (if match is expression) so user cant declare variables there and so on
 | 
			
		||||
				p.gen(':(')
 | 
			
		||||
 | 
			
		||||
				// allow braces is else
 | 
			
		||||
				got_brace := p.tok == .lcbr
 | 
			
		||||
				if got_brace {
 | 
			
		||||
					p.check(.lcbr)
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
				p.check_types(p.bool_expression(), res_typ)
 | 
			
		||||
 | 
			
		||||
				// allow braces in else
 | 
			
		||||
				if got_brace {
 | 
			
		||||
					p.check(.rcbr)
 | 
			
		||||
				}
 | 
			
		||||
				
 | 
			
		||||
				p.gen(strings.repeat(`)`, i+1))
 | 
			
		||||
 | 
			
		||||
				return res_typ
 | 
			
		||||
			} else {
 | 
			
		||||
				p.genln('else // default:')
 | 
			
		||||
				p.match_parse_statement_branch()
 | 
			
		||||
				p.returns = all_cases_return && p.returns
 | 
			
		||||
				return ''
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if i > 0 {
 | 
			
		||||
			if is_expr {
 | 
			
		||||
				p.gen(': (')
 | 
			
		||||
			} else {
 | 
			
		||||
				p.gen('else ')
 | 
			
		||||
			}
 | 
			
		||||
		} else if is_expr {
 | 
			
		||||
			p.gen('(')
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if is_expr {
 | 
			
		||||
			p.gen('(')
 | 
			
		||||
		} else {
 | 
			
		||||
			p.gen('if (')
 | 
			
		||||
		}
 | 
			
		||||
		
 | 
			
		||||
		// Multiple checks separated by comma
 | 
			
		||||
		mut got_comma := false
 | 
			
		||||
 | 
			
		||||
		for {
 | 
			
		||||
			if got_comma {
 | 
			
		||||
				p.gen(') || (')
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			if typ == 'string' { 
 | 
			
		||||
				// TODO: use tmp variable
 | 
			
		||||
				// p.gen('string_eq($tmp_var, ')
 | 
			
		||||
				p.gen('string_eq($tmp_var, ')
 | 
			
		||||
			}
 | 
			
		||||
			else {
 | 
			
		||||
				// TODO: use tmp variable
 | 
			
		||||
				// p.gen('($tmp_var == ')
 | 
			
		||||
				p.gen('($tmp_var == ')
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			p.expected_type = typ
 | 
			
		||||
			p.check_types(p.bool_expression(), typ)
 | 
			
		||||
			p.expected_type = ''
 | 
			
		||||
 | 
			
		||||
			if p.tok != .comma {
 | 
			
		||||
				break
 | 
			
		||||
			}
 | 
			
		||||
			p.check(.comma)
 | 
			
		||||
			got_comma = true
 | 
			
		||||
		}
 | 
			
		||||
		p.gen(') )')
 | 
			
		||||
		
 | 
			
		||||
		p.check(.arrow)
 | 
			
		||||
		
 | 
			
		||||
		// statements are dissallowed (if match is expression) so user cant declare variables there and so on	
 | 
			
		||||
		if is_expr {
 | 
			
		||||
			p.gen('? (')
 | 
			
		||||
 | 
			
		||||
			// braces are required for now
 | 
			
		||||
			p.check(.lcbr)
 | 
			
		||||
			
 | 
			
		||||
			if i == 0 {
 | 
			
		||||
				// on the first iteration we set value of res_typ
 | 
			
		||||
				res_typ = p.bool_expression()
 | 
			
		||||
			} else {
 | 
			
		||||
				// later on we check that the value is of res_typ type
 | 
			
		||||
				p.check_types(p.bool_expression(), res_typ)
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			// braces are required for now
 | 
			
		||||
			p.check(.rcbr)
 | 
			
		||||
 | 
			
		||||
			p.gen(')')
 | 
			
		||||
		}
 | 
			
		||||
		else {
 | 
			
		||||
			p.match_parse_statement_branch()
 | 
			
		||||
			// p.gen(')')
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		all_cases_return = all_cases_return && p.returns
 | 
			
		||||
		i++
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if is_expr {
 | 
			
		||||
		// we get here if no else found, ternary requires "else" branch
 | 
			
		||||
		p.error('Match expession requires "else"')
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	p.returns = false // only get here when no default, so return is not guaranteed
 | 
			
		||||
 | 
			
		||||
	return ''
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn (p mut Parser) match_parse_statement_branch(){
 | 
			
		||||
	p.check(.lcbr)
 | 
			
		||||
	
 | 
			
		||||
	p.genln('{ ')
 | 
			
		||||
	p.statements()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn (p mut Parser) assert_statement() {
 | 
			
		||||
	if p.first_pass() {
 | 
			
		||||
		return
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,12 +1,88 @@
 | 
			
		|||
fn test_match() {
 | 
			
		||||
	a := 3 
 | 
			
		||||
	mut b := 0 
 | 
			
		||||
	match a {
 | 
			
		||||
	   2 => println('two') 
 | 
			
		||||
	   3 => println('three') 
 | 
			
		||||
	        b = 3 
 | 
			
		||||
	   4 => println('four') 
 | 
			
		||||
	else => println('???') 
 | 
			
		||||
	} 
 | 
			
		||||
	assert b == 3 
 | 
			
		||||
enum Color{
 | 
			
		||||
    red, green, blue
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn test_match_integers() {
 | 
			
		||||
	// a := 3 
 | 
			
		||||
	// mut b := 0 
 | 
			
		||||
	// match a {
 | 
			
		||||
	//    2 => println('two') 
 | 
			
		||||
	//    3 => println('three') 
 | 
			
		||||
	//         b = 3 
 | 
			
		||||
	//    4 => println('four') 
 | 
			
		||||
	// else => println('???') 
 | 
			
		||||
	// } 
 | 
			
		||||
	// assert b == 3 
 | 
			
		||||
 | 
			
		||||
    assert match 2 {
 | 
			
		||||
        1 => {2}
 | 
			
		||||
        2 => {3}
 | 
			
		||||
        else => {5}
 | 
			
		||||
    } == 3
 | 
			
		||||
    
 | 
			
		||||
    assert match 0 {
 | 
			
		||||
        1 => {2}
 | 
			
		||||
        2 => {3}
 | 
			
		||||
        else => 5
 | 
			
		||||
    } == 5
 | 
			
		||||
 | 
			
		||||
    assert match 1 {
 | 
			
		||||
        else => {5}
 | 
			
		||||
    } == 5
 | 
			
		||||
    
 | 
			
		||||
    mut a := 0
 | 
			
		||||
    match 2 {
 | 
			
		||||
        0 => {a = 1}
 | 
			
		||||
        1 => {a = 2}
 | 
			
		||||
        else => {
 | 
			
		||||
            a = 3
 | 
			
		||||
            println('a is $a')
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    assert a == 3
 | 
			
		||||
    
 | 
			
		||||
    a = 0
 | 
			
		||||
    match 1 {
 | 
			
		||||
        0 => {a = 1}
 | 
			
		||||
        1 => {
 | 
			
		||||
            a = 2
 | 
			
		||||
            a = a + 2
 | 
			
		||||
            a = a + 2
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    assert a == 6
 | 
			
		||||
 | 
			
		||||
    a = 0
 | 
			
		||||
    match 1 {
 | 
			
		||||
        else => {
 | 
			
		||||
            a = -2
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    assert a == -2
 | 
			
		||||
} 
 | 
			
		||||
 | 
			
		||||
fn test_match_enums(){
 | 
			
		||||
    mut b := Color.red
 | 
			
		||||
    match b{
 | 
			
		||||
        .red => {
 | 
			
		||||
            b = .green
 | 
			
		||||
        }
 | 
			
		||||
        .green => {b = .blue}
 | 
			
		||||
        else => {
 | 
			
		||||
            println('b is ${b.str()}')
 | 
			
		||||
            b = .red
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    assert b == .green
 | 
			
		||||
 | 
			
		||||
    match b{
 | 
			
		||||
        .red => {
 | 
			
		||||
            b = .green
 | 
			
		||||
        }
 | 
			
		||||
        else => {
 | 
			
		||||
            println('b is ${b.str()}')
 | 
			
		||||
            b = .blue
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    assert b == .blue
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue