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