2022-02-13 05:52:49 +01:00
// Copyright (c) 2019-2022 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 c
import v . ast
fn ( mut g Gen ) need_tmp_var_in_match ( node ast . MatchExpr ) bool {
if node . is_expr && node . return_type != ast . void_type && node . return_type != 0 {
cond_sym := g . table . final_sym ( node . cond_type )
sym := g . table . sym ( node . return_type )
if g . table . type_kind ( node . return_type ) == . sum_type {
return true
}
if node . return_type . has_flag ( . optional ) {
return true
}
if sym . kind == . multi_return {
return false
}
if cond_sym . kind == . enum_ && node . branches . len > 5 {
return true
}
for branch in node . branches {
if branch . stmts . len > 1 {
return true
}
if branch . stmts . len == 1 {
if branch . stmts [ 0 ] is ast . ExprStmt {
stmt := branch . stmts [ 0 ] as ast . ExprStmt
if stmt . expr in [ ast . CallExpr , ast . IfExpr , ast . MatchExpr ]
|| ( stmt . expr is ast . IndexExpr
&& ( stmt . expr as ast . IndexExpr ) . or_expr . kind != . absent ) {
return true
}
}
}
}
}
return false
}
fn ( mut g Gen ) match_expr ( node ast . MatchExpr ) {
if node . cond_type == 0 {
g . writeln ( ' / / m a t c h 0 ' )
return
}
need_tmp_var := g . need_tmp_var_in_match ( node )
is_expr := ( node . is_expr && node . return_type != ast . void_type ) || g . inside_ternary > 0
mut cond_var := ' '
mut tmp_var := ' '
mut cur_line := ' '
if is_expr && ! need_tmp_var {
g . inside_ternary ++
}
if is_expr && node . return_type . has_flag ( . optional ) {
old := g . inside_match_optional
defer {
g . inside_match_optional = old
}
g . inside_match_optional = true
}
2022-04-25 07:11:44 +02:00
if node . cond in [ ast . Ident , ast . SelectorExpr , ast . IntegerLiteral , ast . StringLiteral , ast . FloatLiteral ] {
2022-02-13 05:52:49 +01:00
cond_var = g . expr_string ( node . cond )
} else {
line := if is_expr {
g . empty_line = true
g . go_before_stmt ( 0 )
} else {
' '
}
cond_var = g . new_tmp_var ( )
g . write ( ' $ { g . typ ( node . cond_type ) } $ cond_var = ' )
g . expr ( node . cond )
g . writeln ( ' ; ' )
g . set_current_pos_as_last_stmt_pos ( )
g . write ( line )
}
if need_tmp_var {
g . empty_line = true
cur_line = g . go_before_stmt ( 0 ) . trim_left ( ' \t ' )
tmp_var = g . new_tmp_var ( )
g . writeln ( ' $ { g . typ ( node . return_type ) } $ tmp_var = $ { g . type_default ( node . return_type ) } ; ' )
}
if is_expr && ! need_tmp_var {
// brackets needed otherwise '?' will apply to everything on the left
g . write ( ' ( ' )
}
typ := g . table . final_sym ( node . cond_type )
if node . is_sum_type {
g . match_expr_sumtype ( node , is_expr , cond_var , tmp_var )
2022-05-20 17:30:16 +02:00
} else if typ . kind == . enum_ && g . loop_depth == 0 && node . branches . len > 5
&& unsafe { g . fn_decl != 0 } { // do not optimize while in top-level
2022-02-13 05:52:49 +01:00
g . match_expr_switch ( node , is_expr , cond_var , tmp_var , typ )
} else {
g . match_expr_classic ( node , is_expr , cond_var , tmp_var )
}
g . set_current_pos_as_last_stmt_pos ( )
g . write ( cur_line )
if need_tmp_var {
g . write ( ' $ tmp_var ' )
}
if is_expr && ! need_tmp_var {
g . write ( ' ) ' )
g . decrement_inside_ternary ( )
}
}
fn ( mut g Gen ) match_expr_sumtype ( node ast . MatchExpr , is_expr bool , cond_var string , tmp_var string ) {
for j , branch in node . branches {
mut sumtype_index := 0
// iterates through all types in sumtype branches
for {
g . aggregate_type_idx = sumtype_index
is_last := j == node . branches . len - 1
sym := g . table . sym ( node . cond_type )
if branch . is_else || ( node . is_expr && is_last && tmp_var . len == 0 ) {
if is_expr && tmp_var . len == 0 {
// TODO too many branches. maybe separate ?: matches
g . write ( ' : ' )
} else {
g . writeln ( ' ' )
g . write_v_source_line_info ( branch . pos )
g . writeln ( ' e l s e { ' )
}
} else {
if j > 0 || sumtype_index > 0 {
if is_expr && tmp_var . len == 0 {
g . write ( ' : ' )
} else {
g . write_v_source_line_info ( branch . pos )
g . write ( ' e l s e ' )
}
}
if is_expr && tmp_var . len == 0 {
g . write ( ' ( ' )
} else {
if j == 0 && sumtype_index == 0 {
g . empty_line = true
}
g . write_v_source_line_info ( branch . pos )
g . write ( ' i f ( ' )
}
g . write ( cond_var )
dot_or_ptr := if node . cond_type . is_ptr ( ) { ' - > ' } else { ' . ' }
if sym . kind == . sum_type {
g . write ( ' $ { dot_or_ptr } _ t y p = = ' )
g . expr ( branch . exprs [ sumtype_index ] )
} else if sym . kind == . interface_ {
if branch . exprs [ sumtype_index ] is ast . TypeNode {
typ := branch . exprs [ sumtype_index ] as ast . TypeNode
branch_sym := g . table . sym ( g . unwrap_generic ( typ . typ ) )
g . write ( ' $ { dot_or_ptr } _ t y p = = _ $ { sym . cname } _ $ { branch_sym . cname } _ i n d e x ' )
2022-04-12 13:19:10 +02:00
} else if branch . exprs [ sumtype_index ] is ast . None
&& sym . idx == ast . error_type_idx {
2022-02-13 05:52:49 +01:00
g . write ( ' $ { dot_or_ptr } _ t y p = = _ I E r r o r _ N o n e _ _ _ i n d e x ' )
}
}
if is_expr && tmp_var . len == 0 {
2022-05-13 05:56:21 +02:00
g . write ( ' ) ? ' )
2022-02-13 05:52:49 +01:00
} else {
g . writeln ( ' ) { ' )
}
}
if is_expr && tmp_var . len > 0 && g . table . sym ( node . return_type ) . kind == . sum_type {
g . expected_cast_type = node . return_type
}
g . stmts_with_tmp_var ( branch . stmts , tmp_var )
g . expected_cast_type = 0
if g . inside_ternary == 0 {
g . writeln ( ' } ' )
g . set_current_pos_as_last_stmt_pos ( )
}
sumtype_index ++
if branch . exprs . len == 0 || sumtype_index == branch . exprs . len {
break
}
}
// reset global field for next use
g . aggregate_type_idx = 0
}
}
fn ( mut g Gen ) match_expr_switch ( node ast . MatchExpr , is_expr bool , cond_var string , tmp_var string , enum_typ ast . TypeSymbol ) {
2022-03-19 20:43:42 +01:00
cname := ' $ { enum_typ . cname } _ _ '
2022-02-13 05:52:49 +01:00
mut covered_enum := [ ] string { cap : ( enum_typ . info as ast . Enum ) . vals . len } // collects missing enum variant branches to avoid cstrict errors
mut range_branches := [ ] ast . MatchBranch { cap : node . branches . len } // branches have RangeExpr cannot emit as switch case branch, we handle it in default branch
mut default_generated := false
g . empty_line = true
g . writeln ( ' s w i t c h ( $ cond_var ) { ' )
g . indent ++
for branch in node . branches {
if branch . is_else {
2022-03-19 20:43:42 +01:00
for val in ( enum_typ . info as ast . Enum ) . vals {
if val ! in covered_enum {
g . writeln ( ' c a s e $ cname $ val : ' )
}
}
2022-02-13 05:52:49 +01:00
g . writeln ( ' d e f a u l t : ' )
default_generated = true
if range_branches . len > 0 {
g . indent ++
for range_branch in range_branches {
g . write ( ' i f ( ' )
for i , expr in range_branch . exprs {
if i > 0 {
g . write ( ' | | ' )
}
if expr is ast . RangeExpr {
// if type is unsigned and low is 0, check is unneeded
mut skip_low := false
if expr . low is ast . IntegerLiteral {
if node . cond_type in [ ast . u16_type , ast . u32_type , ast . u64_type ]
&& expr . low . val == ' 0 ' {
skip_low = true
}
}
g . write ( ' ( ' )
if ! skip_low {
g . write ( ' $ cond_var > = ' )
g . expr ( expr . low )
g . write ( ' & & ' )
}
g . write ( ' $ cond_var < = ' )
g . expr ( expr . high )
g . write ( ' ) ' )
} else {
g . write ( ' $ cond_var = = ( ' )
g . expr ( expr )
g . write ( ' ) ' )
}
}
g . writeln ( ' ) { ' )
g . stmts_with_tmp_var ( range_branch . stmts , tmp_var )
2022-03-19 19:55:32 +01:00
g . writeln ( ' \t b r e a k ; ' )
2022-02-13 05:52:49 +01:00
g . writeln ( ' } ' )
}
g . indent --
}
} else {
if branch . exprs . any ( it is ast . RangeExpr ) {
range_branches << branch
continue
}
for expr in branch . exprs {
if expr is ast . EnumVal {
covered_enum << expr . val
g . write ( ' c a s e ' )
g . expr ( expr )
g . writeln ( ' : ' )
}
}
}
g . indent ++
g . writeln ( ' { ' )
if is_expr && tmp_var . len > 0 && g . table . sym ( node . return_type ) . kind == . sum_type {
g . expected_cast_type = node . return_type
}
g . stmts_with_tmp_var ( branch . stmts , tmp_var )
g . expected_cast_type = 0
2022-03-19 19:55:32 +01:00
g . writeln ( ' \t b r e a k ; ' )
g . writeln ( ' } ' )
2022-02-13 05:52:49 +01:00
g . indent --
}
if range_branches . len > 0 && ! default_generated {
g . writeln ( ' d e f a u l t : ' )
g . indent ++
for range_branch in range_branches {
g . write ( ' i f ( ' )
for i , expr in range_branch . exprs {
if i > 0 {
g . write ( ' | | ' )
}
if expr is ast . RangeExpr {
// if type is unsigned and low is 0, check is unneeded
mut skip_low := false
if expr . low is ast . IntegerLiteral {
if node . cond_type in [ ast . u16_type , ast . u32_type , ast . u64_type ]
&& expr . low . val == ' 0 ' {
skip_low = true
}
}
g . write ( ' ( ' )
if ! skip_low {
g . write ( ' $ cond_var > = ' )
g . expr ( expr . low )
g . write ( ' & & ' )
}
g . write ( ' $ cond_var < = ' )
g . expr ( expr . high )
g . write ( ' ) ' )
} else {
g . write ( ' $ cond_var = = ( ' )
g . expr ( expr )
g . write ( ' ) ' )
}
}
g . writeln ( ' ) { ' )
g . stmts_with_tmp_var ( range_branch . stmts , tmp_var )
2022-03-19 19:55:32 +01:00
g . writeln ( ' \t b r e a k ; ' )
2022-02-13 05:52:49 +01:00
g . writeln ( ' } ' )
}
g . indent --
}
g . indent --
g . writeln ( ' } ' )
}
fn ( mut g Gen ) match_expr_classic ( node ast . MatchExpr , is_expr bool , cond_var string , tmp_var string ) {
type_sym := g . table . sym ( node . cond_type )
for j , branch in node . branches {
is_last := j == node . branches . len - 1
if branch . is_else || ( node . is_expr && is_last && tmp_var . len == 0 ) {
if node . branches . len > 1 {
if is_expr && tmp_var . len == 0 {
// TODO too many branches. maybe separate ?: matches
g . write ( ' : ' )
} else {
g . writeln ( ' ' )
g . write_v_source_line_info ( branch . pos )
g . writeln ( ' e l s e { ' )
}
}
} else {
if j > 0 {
if is_expr && tmp_var . len == 0 {
g . write ( ' : ' )
} else {
g . writeln ( ' ' )
g . write_v_source_line_info ( branch . pos )
g . write ( ' e l s e ' )
}
}
if is_expr && tmp_var . len == 0 {
g . write ( ' ( ' )
} else {
if j == 0 {
g . writeln ( ' ' )
}
g . write_v_source_line_info ( branch . pos )
g . write ( ' i f ( ' )
}
for i , expr in branch . exprs {
if i > 0 {
g . write ( ' | | ' )
}
match type_sym . kind {
. array {
ptr_typ := g . equality_fn ( node . cond_type )
g . write ( ' $ { ptr_typ } _ a r r _ e q ( $ cond_var , ' )
g . expr ( expr )
g . write ( ' ) ' )
}
. array_fixed {
ptr_typ := g . equality_fn ( node . cond_type )
g . write ( ' $ { ptr_typ } _ a r r _ e q ( $ cond_var , ' )
g . expr ( expr )
g . write ( ' ) ' )
}
. map {
ptr_typ := g . equality_fn ( node . cond_type )
g . write ( ' $ { ptr_typ } _ m a p _ e q ( $ cond_var , ' )
g . expr ( expr )
g . write ( ' ) ' )
}
. string {
g . write ( ' s t r i n g _ _ e q ( $ cond_var , ' )
g . expr ( expr )
g . write ( ' ) ' )
}
. struct_ {
ptr_typ := g . equality_fn ( node . cond_type )
g . write ( ' $ { ptr_typ } _ s t r u c t _ e q ( $ cond_var , ' )
g . expr ( expr )
g . write ( ' ) ' )
}
else {
if expr is ast . RangeExpr {
// if type is unsigned and low is 0, check is unneeded
mut skip_low := false
if expr . low is ast . IntegerLiteral {
if node . cond_type in [ ast . u16_type , ast . u32_type , ast . u64_type ]
&& expr . low . val == ' 0 ' {
skip_low = true
}
}
g . write ( ' ( ' )
if ! skip_low {
g . write ( ' $ cond_var > = ' )
g . expr ( expr . low )
g . write ( ' & & ' )
}
g . write ( ' $ cond_var < = ' )
g . expr ( expr . high )
g . write ( ' ) ' )
} else {
g . write ( ' $ cond_var = = ( ' )
g . expr ( expr )
g . write ( ' ) ' )
}
}
}
}
if is_expr && tmp_var . len == 0 {
2022-05-13 05:56:21 +02:00
g . write ( ' ) ? ' )
2022-02-13 05:52:49 +01:00
} else {
g . writeln ( ' ) { ' )
}
}
if is_expr && tmp_var . len > 0 && g . table . sym ( node . return_type ) . kind == . sum_type {
g . expected_cast_type = node . return_type
}
g . stmts_with_tmp_var ( branch . stmts , tmp_var )
g . expected_cast_type = 0
if g . inside_ternary == 0 && node . branches . len >= 1 {
g . write ( ' } ' )
}
}
}