conditionally unwrapping while loops
This commit is contained in:
@@ -5258,6 +5258,70 @@ ct_exec_assignment :: proc(state: ^Ct_State, statement: ast.Stmt, depth: int) ->
|
||||
return ct_flow(.Normal), true
|
||||
}
|
||||
|
||||
ct_eval_conditional_unwrap :: proc(
|
||||
state: ^Ct_State,
|
||||
statement: ast.Stmt,
|
||||
keyword: string,
|
||||
depth: int,
|
||||
) -> (matched: bool, scope_start: int, flow: Ct_Flow, ok: bool) {
|
||||
checker := state.checker
|
||||
operands: [dynamic]ast.Expr_Id
|
||||
operands.allocator = checker.allocator
|
||||
defer delete(operands)
|
||||
flatten_conditional_unwrap_operands(checker.ast_module, statement.expr, &operands)
|
||||
if len(operands) != len(statement.captures) {
|
||||
return false, len(state.bindings), ct_flow(.Normal), ct_failf(
|
||||
state, .Not_Comptime, statement.span,
|
||||
"'%s' unwrap capture count mismatch",
|
||||
keyword,
|
||||
)
|
||||
}
|
||||
scope_start = len(state.bindings)
|
||||
matched = true
|
||||
for operand, index in operands {
|
||||
value, operand_flow, operand_ok := ct_eval_expr(state, operand, types.INVALID, depth+1)
|
||||
if !operand_ok || operand_flow.kind != .Normal {
|
||||
ct_pop_bindings(state, scope_start)
|
||||
return false, scope_start, operand_flow, operand_ok
|
||||
}
|
||||
v := state.values[value]
|
||||
if v.kind == .Null {
|
||||
matched = false
|
||||
break
|
||||
}
|
||||
if v.kind != .Optional_Some {
|
||||
ct_pop_bindings(state, scope_start)
|
||||
return false, scope_start, ct_flow(.Normal), ct_failf(
|
||||
state, .Not_Comptime, statement.span,
|
||||
"'%s' unwrap requires an optional value",
|
||||
keyword,
|
||||
)
|
||||
}
|
||||
children := ct_child_slice(state, v)
|
||||
if len(children) > 0 && statement.captures[index] != checker.sink_symbol {
|
||||
ct_bind_value(state, statement.captures[index], state.values[children[0]].type, children[0], false)
|
||||
}
|
||||
}
|
||||
if matched && statement.guard != ast.INVALID_EXPR {
|
||||
guard, guard_flow, guard_ok := ct_eval_expr(state, statement.guard, types.BOOL, depth+1)
|
||||
if !guard_ok || guard_flow.kind != .Normal {
|
||||
ct_pop_bindings(state, scope_start)
|
||||
return false, scope_start, guard_flow, guard_ok
|
||||
}
|
||||
guard_value, bool_ok := ct_bool_value(state, guard)
|
||||
if !bool_ok {
|
||||
ct_pop_bindings(state, scope_start)
|
||||
return false, scope_start, ct_flow(.Normal), ct_failf(
|
||||
state, .Not_Comptime, statement.span,
|
||||
"'%s' unwrap guard must be a bool",
|
||||
keyword,
|
||||
)
|
||||
}
|
||||
matched = guard_value
|
||||
}
|
||||
return matched, scope_start, ct_flow(.Normal), true
|
||||
}
|
||||
|
||||
ct_exec_if :: proc(state: ^Ct_State, statement: ast.Stmt, yield_returns: bool, depth: int) -> (Ct_Flow, bool) {
|
||||
checker := state.checker
|
||||
if yield_returns && statement.else_body == nil {
|
||||
@@ -5284,47 +5348,9 @@ ct_exec_if :: proc(state: ^Ct_State, statement: ast.Stmt, yield_returns: bool, d
|
||||
}
|
||||
return ct_exec_statements(state, body, false, depth+1)
|
||||
}
|
||||
operands: [dynamic]ast.Expr_Id
|
||||
operands.allocator = checker.allocator
|
||||
defer delete(operands)
|
||||
flatten_conditional_unwrap_operands(checker.ast_module, statement.expr, &operands)
|
||||
if len(operands) != len(statement.captures) {
|
||||
return ct_flow(.Normal), ct_fail(state, .Not_Comptime, statement.span, "'if' unwrap capture count mismatch")
|
||||
}
|
||||
scope_start := len(state.bindings)
|
||||
matched := true
|
||||
for operand, index in operands {
|
||||
value, flow, ok := ct_eval_expr(state, operand, types.INVALID, depth+1)
|
||||
if !ok || flow.kind != .Normal {
|
||||
ct_pop_bindings(state, scope_start)
|
||||
return flow, ok
|
||||
}
|
||||
v := state.values[value]
|
||||
if v.kind == .Null {
|
||||
matched = false
|
||||
break
|
||||
}
|
||||
if v.kind != .Optional_Some {
|
||||
ct_pop_bindings(state, scope_start)
|
||||
return ct_flow(.Normal), ct_fail(state, .Not_Comptime, statement.span, "'if' unwrap requires an optional value")
|
||||
}
|
||||
children := ct_child_slice(state, v)
|
||||
if len(children) > 0 && statement.captures[index] != checker.sink_symbol {
|
||||
ct_bind_value(state, statement.captures[index], state.values[children[0]].type, children[0], false)
|
||||
}
|
||||
}
|
||||
if matched && statement.guard != ast.INVALID_EXPR {
|
||||
guard, flow, ok := ct_eval_expr(state, statement.guard, types.BOOL, depth+1)
|
||||
if !ok || flow.kind != .Normal {
|
||||
ct_pop_bindings(state, scope_start)
|
||||
return flow, ok
|
||||
}
|
||||
guard_value, guard_ok := ct_bool_value(state, guard)
|
||||
if !guard_ok {
|
||||
ct_pop_bindings(state, scope_start)
|
||||
return ct_flow(.Normal), ct_fail(state, .Not_Comptime, statement.span, "'if' unwrap guard must be a bool")
|
||||
}
|
||||
matched = guard_value
|
||||
matched, scope_start, unwrap_flow, unwrap_ok := ct_eval_conditional_unwrap(state, statement, "if", depth)
|
||||
if !unwrap_ok || unwrap_flow.kind != .Normal {
|
||||
return unwrap_flow, unwrap_ok
|
||||
}
|
||||
body := statement.body if matched else statement.else_body
|
||||
flow := ct_flow(.Normal)
|
||||
@@ -5357,34 +5383,53 @@ ct_exec_while :: proc(state: ^Ct_State, statement: ast.Stmt, yield_returns: bool
|
||||
if !ct_step(state, statement.span) {
|
||||
return ct_flow(.Normal), false
|
||||
}
|
||||
condition, flow, ok := ct_eval_expr(state, statement.expr, types.BOOL, depth+1)
|
||||
if !ok || flow.kind != .Normal {
|
||||
return flow, ok
|
||||
}
|
||||
value, bool_ok := ct_bool_value(state, condition)
|
||||
if !bool_ok {
|
||||
return ct_flow(.Normal), ct_fail(state, .Not_Comptime, statement.span, "'while' condition must be a bool")
|
||||
}
|
||||
if !value {
|
||||
return ct_flow(.Normal), true
|
||||
scope_start := len(state.bindings)
|
||||
if len(statement.captures) > 0 {
|
||||
matched, unwrap_scope_start, flow, ok :=
|
||||
ct_eval_conditional_unwrap(state, statement, "while", depth)
|
||||
scope_start = unwrap_scope_start
|
||||
if !ok || flow.kind != .Normal {
|
||||
return flow, ok
|
||||
}
|
||||
if !matched {
|
||||
ct_pop_bindings(state, scope_start)
|
||||
return ct_flow(.Normal), true
|
||||
}
|
||||
} else {
|
||||
condition, flow, ok := ct_eval_expr(state, statement.expr, types.BOOL, depth+1)
|
||||
if !ok || flow.kind != .Normal {
|
||||
return flow, ok
|
||||
}
|
||||
value, bool_ok := ct_bool_value(state, condition)
|
||||
if !bool_ok {
|
||||
return ct_flow(.Normal), ct_fail(state, .Not_Comptime, statement.span, "'while' condition must be a bool")
|
||||
}
|
||||
if !value {
|
||||
return ct_flow(.Normal), true
|
||||
}
|
||||
}
|
||||
body_flow, body_ok := ct_exec_statements(state, statement.body, yield_returns, depth+1)
|
||||
if !body_ok {
|
||||
ct_pop_bindings(state, scope_start)
|
||||
return body_flow, false
|
||||
}
|
||||
if ct_loop_consumes_flow(body_flow, statement.label, false) {
|
||||
ct_pop_bindings(state, scope_start)
|
||||
return ct_flow(.Normal), true
|
||||
}
|
||||
if body_flow.kind != .Normal && !ct_loop_consumes_flow(body_flow, statement.label, true) {
|
||||
ct_pop_bindings(state, scope_start)
|
||||
return body_flow, true
|
||||
}
|
||||
if statement.update != ast.INVALID_STMT {
|
||||
update := [1]ast.Stmt_Id{statement.update}
|
||||
update_flow, update_ok := ct_exec_statements(state, update[:], false, depth+1)
|
||||
if !update_ok || update_flow.kind != .Normal {
|
||||
ct_pop_bindings(state, scope_start)
|
||||
return update_flow, update_ok
|
||||
}
|
||||
}
|
||||
ct_pop_bindings(state, scope_start)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user