add first version of List.dropAt

* adds an implementation with no uniqueness/mutability
This commit is contained in:
Dan Knutson 2021-10-02 20:03:07 -05:00
parent ff9866420b
commit 3baff93a97
11 changed files with 145 additions and 13 deletions

View file

@ -833,6 +833,54 @@ pub fn listDrop(
}
}
// GIESCH add type inference test
// GIESCH add unit tests
// GIESCH do a uniqueness check, and reuse the same array if possible
// GIESCH figure out where to specify uniqueness of output, update builtins readme
pub fn listDropAt(
list: RocList,
alignment: u32,
element_width: usize,
drop_index: usize,
dec: Dec,
) callconv(.C) RocList {
if (list.bytes) |source_ptr| {
const size = list.len();
if (drop_index >= size) {
return RocList.empty();
}
if (drop_index < size) {
const element = source_ptr + drop_index * element_width;
dec(element);
}
// GIESCH is this necessary?
if (size < 2 and drop_index == 0) {
return RocList.empty();
}
const output = RocList.allocate(alignment, (size - 1), element_width);
const target_ptr = output.bytes orelse unreachable;
const head_size = drop_index * element_width;
@memcpy(target_ptr, source_ptr, head_size);
const tail_target = target_ptr + drop_index * element_width;
const tail_source = source_ptr + (drop_index + 1) * element_width;
const tail_size = (size - drop_index - 1) * element_width;
@memcpy(tail_target, tail_source, tail_size);
// GIESCH what's the difference between this and Dec?
utils.decref(list.bytes, size * element_width, alignment);
return output;
} else {
return RocList.empty();
}
}
pub fn listRange(width: utils.IntWidth, low: Opaque, high: Opaque) callconv(.C) RocList {
return switch (width) {
.U8 => helper1(u8, low, high),

View file

@ -39,6 +39,7 @@ comptime {
exportListFn(list.listSortWith, "sort_with");
exportListFn(list.listConcat, "concat");
exportListFn(list.listDrop, "drop");
exportListFn(list.listDropAt, "drop_at");
exportListFn(list.listSet, "set");
exportListFn(list.listSetInPlace, "set_in_place");
exportListFn(list.listSwap, "swap");

View file

@ -422,15 +422,18 @@ min : List (Num a) -> Result (Num a) [ ListWasEmpty ]*
## If the given index is outside the bounds of the list, returns the original
## list unmodified.
##
## To drop the element at a given index, instead of replacing it, see [List.drop].
## To drop the element at a given index, instead of replacing it, see [List.dropAt].
set : List elem, Nat, elem -> List elem
# GIESCH figure out if we should add docs for List.drop;
# what's the relationship with List.dropFirst, below?
# GIESCH add docs re: uniqueness and performance
## Drops the element at the given index from the list.
##
## This has no effect if the given index is outside the bounds of the list.
##
## To replace the element at a given index, instead of dropping it, see [List.set].
drop : List elem, Nat -> List elem
dropAt : List elem, Nat -> List elem
## Adds a new element to the end of the list.
##

View file

@ -63,6 +63,7 @@ pub const LIST_REPEAT: &str = "roc_builtins.list.repeat";
pub const LIST_APPEND: &str = "roc_builtins.list.append";
pub const LIST_PREPEND: &str = "roc_builtins.list.prepend";
pub const LIST_DROP: &str = "roc_builtins.list.drop";
pub const LIST_DROP_AT: &str = "roc_builtins.list.drop_at";
pub const LIST_SWAP: &str = "roc_builtins.list.swap";
pub const LIST_SINGLE: &str = "roc_builtins.list.single";
pub const LIST_JOIN: &str = "roc_builtins.list.join";

View file

@ -920,6 +920,13 @@ pub fn types() -> MutMap<Symbol, (SolvedType, Region)> {
Box::new(list_type(flex(TVAR1))),
);
// dropAt : List elem, Nat -> List elem
add_top_level_function_type!(
Symbol::LIST_DROP_AT,
vec![list_type(flex(TVAR1)), nat_type()],
Box::new(list_type(flex(TVAR1))),
);
// swap : List elem, Nat, Nat -> List elem
add_top_level_function_type!(
Symbol::LIST_SWAP,

View file

@ -87,6 +87,7 @@ pub fn builtin_defs_map(symbol: Symbol, var_store: &mut VarStore) -> Option<Def>
LIST_MAP2 => list_map2,
LIST_MAP3 => list_map3,
LIST_DROP => list_drop,
LIST_DROP_AT => list_drop_at,
LIST_SWAP => list_swap,
LIST_MAP_WITH_INDEX => list_map_with_index,
LIST_KEEP_IF => list_keep_if,
@ -1979,6 +1980,29 @@ fn list_drop(symbol: Symbol, var_store: &mut VarStore) -> Def {
)
}
/// List.dropAt : List elem, Nat -> List elem
fn list_drop_at(symbol: Symbol, var_store: &mut VarStore) -> Def {
let list_var = var_store.fresh();
let index_var = var_store.fresh();
let body = RunLowLevel {
op: LowLevel::ListDropAt,
args: vec![
(list_var, Var(Symbol::ARG_1)),
(index_var, Var(Symbol::ARG_2)),
],
ret_var: list_var,
};
defn(
symbol,
vec![(list_var, Symbol::ARG_1), (index_var, Symbol::ARG_2)],
var_store,
body,
list_var,
)
}
/// List.append : List elem, elem -> List elem
fn list_append(symbol: Symbol, var_store: &mut VarStore) -> Def {
let list_var = var_store.fresh();

View file

@ -9,9 +9,10 @@ use crate::llvm::build_dict::{
use crate::llvm::build_hash::generic_hash;
use crate::llvm::build_list::{
self, allocate_list, empty_list, empty_polymorphic_list, list_append, list_concat,
list_contains, list_drop, list_get_unsafe, list_join, list_keep_errs, list_keep_if,
list_keep_oks, list_len, list_map, list_map2, list_map3, list_map_with_index, list_prepend,
list_range, list_repeat, list_reverse, list_set, list_single, list_sort_with, list_swap,
list_contains, list_drop, list_drop_at, list_get_unsafe, list_join, list_keep_errs,
list_keep_if, list_keep_oks, list_len, list_map, list_map2, list_map3, list_map_with_index,
list_prepend, list_range, list_repeat, list_reverse, list_set, list_single, list_sort_with,
list_swap,
};
use crate::llvm::build_str::{
empty_str, str_concat, str_count_graphemes, str_ends_with, str_from_float, str_from_int,
@ -5018,6 +5019,27 @@ fn run_low_level<'a, 'ctx, 'env>(
_ => unreachable!("Invalid layout {:?} in List.drop", list_layout),
}
}
ListDropAt => {
// List.dropAt : List elem, Nat -> List elem
debug_assert_eq!(args.len(), 2);
let (list, list_layout) = load_symbol_and_layout(scope, &args[0]);
let original_wrapper = list.into_struct_value();
let count = load_symbol(scope, &args[1]);
match list_layout {
Layout::Builtin(Builtin::EmptyList) => empty_list(env),
Layout::Builtin(Builtin::List(element_layout)) => list_drop_at(
env,
layout_ids,
original_wrapper,
count.into_int_value(),
element_layout,
),
_ => unreachable!("Invalid layout {:?} in List.dropAt", list_layout),
}
}
ListPrepend => {
// List.prepend : List elem, elem -> List elem
debug_assert_eq!(args.len(), 2);

View file

@ -297,7 +297,7 @@ pub fn list_swap<'a, 'ctx, 'env>(
)
}
/// List.drop : List elem, Nat, Nat -> List elem
/// List.drop : List elem, Nat -> List elem
pub fn list_drop<'a, 'ctx, 'env>(
env: &Env<'a, 'ctx, 'env>,
layout_ids: &mut LayoutIds<'a>,
@ -319,6 +319,29 @@ pub fn list_drop<'a, 'ctx, 'env>(
)
}
// GIESCH ask about how this calling/linking to compiled zig works
/// List.dropAt : List elem, Nat -> List elem
pub fn list_drop_at<'a, 'ctx, 'env>(
env: &Env<'a, 'ctx, 'env>,
layout_ids: &mut LayoutIds<'a>,
original_wrapper: StructValue<'ctx>,
count: IntValue<'ctx>,
element_layout: &Layout<'a>,
) -> BasicValueEnum<'ctx> {
let dec_element_fn = build_dec_wrapper(env, layout_ids, element_layout);
call_bitcode_fn_returns_list(
env,
&[
pass_list_cc(env, original_wrapper.into()),
env.alignment_intvalue(element_layout),
layout_width(env, element_layout),
count.into(),
dec_element_fn.as_global_value().as_pointer_value().into(),
],
bitcode::LIST_DROP_AT,
)
}
/// List.set : List elem, Nat, elem -> List elem
pub fn list_set<'a, 'ctx, 'env>(
env: &Env<'a, 'ctx, 'env>,

View file

@ -41,6 +41,7 @@ pub enum LowLevel {
ListKeepErrs,
ListSortWith,
ListDrop,
ListDropAt,
ListSwap,
DictSize,
DictEmpty,
@ -116,13 +117,13 @@ impl LowLevel {
StrConcat | StrJoinWith | StrIsEmpty | StrStartsWith | StrStartsWithCodePt
| StrEndsWith | StrSplit | StrCountGraphemes | StrFromInt | StrFromUtf8
| StrFromUtf8Range | StrToUtf8 | StrRepeat | StrFromFloat | ListLen | ListGetUnsafe
| ListSet | ListDrop | ListSingle | ListRepeat | ListReverse | ListConcat
| ListContains | ListAppend | ListPrepend | ListJoin | ListRange | ListSwap
| DictSize | DictEmpty | DictInsert | DictRemove | DictContains | DictGetUnsafe
| DictKeys | DictValues | DictUnion | DictIntersection | DictDifference
| SetFromList | NumAdd | NumAddWrap | NumAddChecked | NumSub | NumSubWrap
| NumSubChecked | NumMul | NumMulWrap | NumMulChecked | NumGt | NumGte | NumLt
| NumLte | NumCompare | NumDivUnchecked | NumRemUnchecked | NumIsMultipleOf
| ListSet | ListDrop | ListDropAt | ListSingle | ListRepeat | ListReverse
| ListConcat | ListContains | ListAppend | ListPrepend | ListJoin | ListRange
| ListSwap | DictSize | DictEmpty | DictInsert | DictRemove | DictContains
| DictGetUnsafe | DictKeys | DictValues | DictUnion | DictIntersection
| DictDifference | SetFromList | NumAdd | NumAddWrap | NumAddChecked | NumSub
| NumSubWrap | NumSubChecked | NumMul | NumMulWrap | NumMulChecked | NumGt | NumGte
| NumLt | NumLte | NumCompare | NumDivUnchecked | NumRemUnchecked | NumIsMultipleOf
| NumAbs | NumNeg | NumSin | NumCos | NumSqrtUnchecked | NumLogUnchecked | NumRound
| NumToFloat | NumPow | NumCeiling | NumPowInt | NumFloor | NumIsFinite | NumAtan
| NumAcos | NumAsin | NumBitwiseAnd | NumBitwiseXor | NumBitwiseOr | NumShiftLeftBy

View file

@ -964,6 +964,7 @@ define_builtins! {
31 LIST_SORT_WITH: "sortWith"
32 LIST_DROP: "drop"
33 LIST_SWAP: "swap"
34 LIST_DROP_AT: "dropAt"
}
5 RESULT: "Result" => {
0 RESULT_RESULT: "Result" imported // the Result.Result type alias

View file

@ -993,6 +993,7 @@ pub fn lowlevel_borrow_signature(arena: &Bump, op: LowLevel) -> &[bool] {
// List.append should own its first argument
ListAppend => arena.alloc_slice_copy(&[owned, owned]),
ListDrop => arena.alloc_slice_copy(&[owned, irrelevant]),
ListDropAt => arena.alloc_slice_copy(&[owned, irrelevant]),
ListSwap => arena.alloc_slice_copy(&[owned, irrelevant, irrelevant]),
Eq | NotEq => arena.alloc_slice_copy(&[borrowed, borrowed]),