parser: match expression + match fixes
							parent
							
								
									b6336f730b
								
							
						
					
					
						commit
						7edcbeca1a
					
				|  | @ -64,6 +64,7 @@ mut: | ||||||
| 	builtin_mod    bool | 	builtin_mod    bool | ||||||
| 	vh_lines       []string | 	vh_lines       []string | ||||||
| 	inside_if_expr bool | 	inside_if_expr bool | ||||||
|  | 	inside_unwrapping_match_statement bool | ||||||
| 	is_struct_init bool | 	is_struct_init bool | ||||||
| 	if_expr_cnt    int | 	if_expr_cnt    int | ||||||
| 	for_expr_cnt   int // to detect whether `continue` can be used
 | 	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 { | fn (p mut Parser) statements_no_rcbr() string { | ||||||
| 	p.cur_fn.open_scope() | 	p.cur_fn.open_scope() | ||||||
|  | 
 | ||||||
| 	if !p.inside_if_expr { | 	if !p.inside_if_expr { | ||||||
| 		p.genln('') | 		p.genln('') | ||||||
| 	} | 	} | ||||||
|  | @ -1049,6 +1051,7 @@ fn (p mut Parser) statements_no_rcbr() string { | ||||||
| 	} | 	} | ||||||
| 	//p.fmt_dec()
 | 	//p.fmt_dec()
 | ||||||
| 	// println('close scope line=$p.scanner.line_nr')
 | 	// println('close scope line=$p.scanner.line_nr')
 | ||||||
|  | 
 | ||||||
| 	p.close_scope() | 	p.close_scope() | ||||||
| 	return last_st_typ | 	return last_st_typ | ||||||
| } | } | ||||||
|  | @ -1152,8 +1155,10 @@ fn (p mut Parser) statement(add_semi bool) string { | ||||||
| 		p.if_st(false, 0) | 		p.if_st(false, 0) | ||||||
| 	case Token.key_for: | 	case Token.key_for: | ||||||
| 		p.for_st() | 		p.for_st() | ||||||
| 	case Token.key_switch, Token.key_match: | 	case Token.key_switch: | ||||||
| 		p.switch_statement() | 		p.switch_statement() | ||||||
|  | 	case Token.key_match: | ||||||
|  | 		p.match_statement(false) | ||||||
| 	case Token.key_mut, Token.key_static: | 	case Token.key_mut, Token.key_static: | ||||||
| 		p.var_decl() | 		p.var_decl() | ||||||
| 	case Token.key_return: | 	case Token.key_return: | ||||||
|  | @ -2409,6 +2414,9 @@ fn (p mut Parser) factor() string { | ||||||
| 	case Token.key_if: | 	case Token.key_if: | ||||||
| 		typ = p.if_st(true, 0) | 		typ = p.if_st(true, 0) | ||||||
| 		return typ | 		return typ | ||||||
|  | 	case Token.key_match: | ||||||
|  | 		typ = p.match_statement(true) | ||||||
|  | 		return typ | ||||||
| 	default: | 	default: | ||||||
| 		if p.pref.is_verbose || p.pref.is_debug { | 		if p.pref.is_verbose || p.pref.is_debug { | ||||||
| 			next := p.peek() | 			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
 | 	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() { | fn (p mut Parser) assert_statement() { | ||||||
| 	if p.first_pass() { | 	if p.first_pass() { | ||||||
| 		return | 		return | ||||||
|  |  | ||||||
|  | @ -1,12 +1,88 @@ | ||||||
| fn test_match() { | enum Color{ | ||||||
| 	a := 3  |     red, green, blue | ||||||
| 	mut b := 0  | } | ||||||
| 	match a { | 
 | ||||||
| 	   2 => println('two')  | fn test_match_integers() { | ||||||
| 	   3 => println('three')  | 	// a := 3 
 | ||||||
| 	        b = 3  | 	// mut b := 0 
 | ||||||
| 	   4 => println('four')  | 	// match a {
 | ||||||
| 	else => println('???')  | 	//    2 => println('two') 
 | ||||||
| 	}  | 	//    3 => println('three') 
 | ||||||
| 	assert b == 3  | 	//         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