From c721a1cc1f0b355cc36ce541e29d2c76e1ce4da5 Mon Sep 17 00:00:00 2001 From: Tim Whiting Date: Wed, 27 Oct 2021 20:28:34 -0600 Subject: [PATCH] add builtin List.min --- compiler/builtins/src/std.rs | 9 ++- compiler/can/src/builtins.rs | 124 ++++++++++++++++++++++++++++++ compiler/module/src/symbol.rs | 2 + compiler/test_gen/src/gen_list.rs | 25 ++++++ 4 files changed, 159 insertions(+), 1 deletion(-) diff --git a/compiler/builtins/src/std.rs b/compiler/builtins/src/std.rs index 38230bf0e3..b39bd4f6e8 100644 --- a/compiler/builtins/src/std.rs +++ b/compiler/builtins/src/std.rs @@ -722,7 +722,7 @@ pub fn types() -> MutMap { add_top_level_function_type!( Symbol::LIST_LAST, vec![list_type(flex(TVAR1))], - Box::new(result_type(flex(TVAR1), list_was_empty)), + Box::new(result_type(flex(TVAR1), list_was_empty.clone())), ); // set : List elem, Nat, elem -> List elem @@ -746,6 +746,13 @@ pub fn types() -> MutMap { Box::new(bool_type()), ); + // min : List (Num a) -> Result (Num a) [ ListWasEmpty ]* + add_top_level_function_type!( + Symbol::LIST_MIN, + vec![list_type(num_type(flex(TVAR1)))], + Box::new(result_type(num_type(flex(TVAR1)), list_was_empty)), + ); + // sum : List (Num a) -> Num a add_top_level_function_type!( Symbol::LIST_SUM, diff --git a/compiler/can/src/builtins.rs b/compiler/can/src/builtins.rs index 6403c2d25e..75986c8d66 100644 --- a/compiler/can/src/builtins.rs +++ b/compiler/can/src/builtins.rs @@ -79,6 +79,7 @@ pub fn builtin_defs_map(symbol: Symbol, var_store: &mut VarStore) -> Option LIST_REVERSE => list_reverse, LIST_CONCAT => list_concat, LIST_CONTAINS => list_contains, + LIST_MIN => list_min, LIST_SUM => list_sum, LIST_PRODUCT => list_product, LIST_PREPEND => list_prepend, @@ -2131,6 +2132,129 @@ fn list_walk_until(symbol: Symbol, var_store: &mut VarStore) -> Def { lowlevel_3(symbol, LowLevel::ListWalkUntil, var_store) } +// min : List (Num a) -> Result (Num a) [ ListWasEmpty ]* +fn list_min(symbol: Symbol, var_store: &mut VarStore) -> Def { + let arg_var = var_store.fresh(); + let bool_var = var_store.fresh(); + let list_var = var_store.fresh(); + let len_var = Variable::NAT; + let num_var = len_var; + let num_precision_var = Variable::NATURAL; + let list_elem_var = var_store.fresh(); + let ret_var = var_store.fresh(); + let closure_var = var_store.fresh(); + + // Perform a bounds check. If it passes, delegate to List.getUnsafe. + let body = If { + cond_var: bool_var, + branch_var: var_store.fresh(), + branches: vec![( + // if-condition + no_region( + // List.len list != 0 + RunLowLevel { + op: LowLevel::NotEq, + args: vec![ + (len_var, int(num_var, num_precision_var, 0)), + ( + len_var, + RunLowLevel { + op: LowLevel::ListLen, + args: vec![(list_var, Var(Symbol::ARG_1))], + ret_var: len_var, + }, + ), + ], + ret_var: bool_var, + }, + ), + // list was not empty + no_region( + // Ok ( List.walk list (List.getUnsafe list 0) \acc,elem -> if elem < acc then elem else acc ) + tag( + "Ok", + vec![ + // List.walk list (List.getUnsafe list 0) \acc,elem -> if elem < acc then elem else acc + RunLowLevel { + op: LowLevel::ListWalk, + args: vec![ + (list_var, Var(Symbol::ARG_1)), + // (List.getUnsafe list 0) + ( + list_elem_var, + RunLowLevel { + op: LowLevel::ListGetUnsafe, + args: vec![ + (list_var, Var(Symbol::ARG_1)), + (arg_var, int(num_var, num_precision_var, 0)), + ], + ret_var: list_elem_var, + }, + ), + // \acc,elem -> if elem < acc then elem else acc + (closure_var, list_min_lt(list_elem_var, var_store)), + ], + ret_var: list_elem_var, + }, + ], + var_store, + ), + ), + )], + final_else: Box::new( + // list was empty + no_region( + // Err ListWasEmpty + tag( + "Err", + vec![tag("ListWasEmpty", Vec::new(), var_store)], + var_store, + ), + ), + ), + }; + + defn( + symbol, + vec![(list_var, Symbol::ARG_1)], + var_store, + body, + ret_var, + ) +} + +// \acc,elem -> if elem < acc then elem else acc +fn list_min_lt(list_elem_var: Variable, var_store: &mut VarStore) -> Expr { + let bool_var = var_store.fresh(); + // if elem < acc then elem else acc + let body = If { + cond_var: bool_var, + branch_var: list_elem_var, + branches: vec![( + // if-condition + no_region( + // elem < acc + RunLowLevel { + op: LowLevel::NumLt, + args: vec![(list_elem_var, Var(Symbol::ARG_4)), (list_elem_var, Var(Symbol::ARG_3))], + ret_var: bool_var, + }, + ), + // list was not empty + no_region(Var(Symbol::ARG_4)), + )], + final_else: Box::new(no_region(Var(Symbol::ARG_3))), + }; + + defn_help( + Symbol::LIST_MIN_LT, + vec![(list_elem_var, Symbol::ARG_3), (list_elem_var, Symbol::ARG_4)], + var_store, + body, + list_elem_var, + ) +} + /// List.sum : List (Num a) -> Num a fn list_sum(symbol: Symbol, var_store: &mut VarStore) -> Def { let num_var = var_store.fresh(); diff --git a/compiler/module/src/symbol.rs b/compiler/module/src/symbol.rs index 1b358231ed..d6acdf24db 100644 --- a/compiler/module/src/symbol.rs +++ b/compiler/module/src/symbol.rs @@ -1053,6 +1053,8 @@ define_builtins! { 33 LIST_SWAP: "swap" 34 LIST_DROP_AT: "dropAt" 35 LIST_DROP_LAST: "dropLast" + 36 LIST_MIN: "min" + 37 LIST_MIN_LT: "#minlt" } 5 RESULT: "Result" => { 0 RESULT_RESULT: "Result" imported // the Result.Result type alias diff --git a/compiler/test_gen/src/gen_list.rs b/compiler/test_gen/src/gen_list.rs index 6ba248d673..4dddce1997 100644 --- a/compiler/test_gen/src/gen_list.rs +++ b/compiler/test_gen/src/gen_list.rs @@ -1949,6 +1949,31 @@ fn list_contains() { assert_evals_to!(indoc!("List.contains [] 4"), false, bool); } +#[test] +fn list_min() { + assert_evals_to!( + indoc!( + r#" + when List.min [] is + Ok val -> val + Err _ -> -1 + "# + ), + -1, + i64 + ); + assert_evals_to!( + indoc!( + r#" + when List.min [3, 1, 2] is + Ok val -> val + Err _ -> -1 + "# + ), + 1, + i64 + ); +} #[test] fn list_sum() {