mirror of
https://github.com/roc-lang/roc.git
synced 2025-09-28 14:24:45 +00:00
feat(List): add walk function and fix walkBackwards
This commit is contained in:
parent
d9e906b8fb
commit
8feab843ea
9 changed files with 210 additions and 26 deletions
|
@ -489,7 +489,20 @@ pub fn types() -> MutMap<Symbol, (SolvedType, Region)> {
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
// walkRight : List elem, (elem -> accum -> accum), accum -> accum
|
// walk : List elem, (elem -> accum -> accum), accum -> accum
|
||||||
|
add_type(
|
||||||
|
Symbol::LIST_WALK,
|
||||||
|
top_level_function(
|
||||||
|
vec![
|
||||||
|
list_type(flex(TVAR1)),
|
||||||
|
closure(vec![flex(TVAR1), flex(TVAR2)], TVAR3, Box::new(flex(TVAR2))),
|
||||||
|
flex(TVAR2),
|
||||||
|
],
|
||||||
|
Box::new(flex(TVAR2)),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
// walkBackwards : List elem, (elem -> accum -> accum), accum -> accum
|
||||||
add_type(
|
add_type(
|
||||||
Symbol::LIST_WALK_BACKWARDS,
|
Symbol::LIST_WALK_BACKWARDS,
|
||||||
top_level_function(
|
top_level_function(
|
||||||
|
|
|
@ -777,7 +777,34 @@ pub fn types() -> MutMap<Symbol, (SolvedType, Region)> {
|
||||||
)
|
)
|
||||||
});
|
});
|
||||||
|
|
||||||
// walkRight : Attr (* | u) (List (Attr u a))
|
// walk : Attr (* | u) (List (Attr u a))
|
||||||
|
// , Attr Shared (Attr u a -> b -> b)
|
||||||
|
// , b
|
||||||
|
// -> b
|
||||||
|
add_type(Symbol::LIST_WALK, {
|
||||||
|
let_tvars! { u, a, b, star1, closure };
|
||||||
|
|
||||||
|
unique_function(
|
||||||
|
vec![
|
||||||
|
SolvedType::Apply(
|
||||||
|
Symbol::ATTR_ATTR,
|
||||||
|
vec![
|
||||||
|
container(star1, vec![u]),
|
||||||
|
SolvedType::Apply(Symbol::LIST_LIST, vec![attr_type(u, a)]),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
shared(SolvedType::Func(
|
||||||
|
vec![attr_type(u, a), flex(b)],
|
||||||
|
Box::new(flex(closure)),
|
||||||
|
Box::new(flex(b)),
|
||||||
|
)),
|
||||||
|
flex(b),
|
||||||
|
],
|
||||||
|
flex(b),
|
||||||
|
)
|
||||||
|
});
|
||||||
|
|
||||||
|
// walkBackwards : Attr (* | u) (List (Attr u a))
|
||||||
// , Attr Shared (Attr u a -> b -> b)
|
// , Attr Shared (Attr u a -> b -> b)
|
||||||
// , b
|
// , b
|
||||||
// -> b
|
// -> b
|
||||||
|
|
|
@ -71,6 +71,7 @@ pub fn builtin_defs(var_store: &mut VarStore) -> MutMap<Symbol, Def> {
|
||||||
Symbol::LIST_JOIN => list_join,
|
Symbol::LIST_JOIN => list_join,
|
||||||
Symbol::LIST_MAP => list_map,
|
Symbol::LIST_MAP => list_map,
|
||||||
Symbol::LIST_KEEP_IF => list_keep_if,
|
Symbol::LIST_KEEP_IF => list_keep_if,
|
||||||
|
Symbol::LIST_WALK => list_walk,
|
||||||
Symbol::LIST_WALK_BACKWARDS => list_walk_backwards,
|
Symbol::LIST_WALK_BACKWARDS => list_walk_backwards,
|
||||||
Symbol::NUM_ADD => num_add,
|
Symbol::NUM_ADD => num_add,
|
||||||
Symbol::NUM_ADD_CHECKED => num_add_checked,
|
Symbol::NUM_ADD_CHECKED => num_add_checked,
|
||||||
|
@ -1313,14 +1314,43 @@ fn list_join(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// List.walkRight : List elem, (elem -> accum -> accum), accum -> accum
|
/// List.walk : List elem, (elem -> accum -> accum), accum -> accum
|
||||||
|
fn list_walk(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
||||||
|
let list_var = var_store.fresh();
|
||||||
|
let func_var = var_store.fresh();
|
||||||
|
let accum_var = var_store.fresh();
|
||||||
|
|
||||||
|
let body = RunLowLevel {
|
||||||
|
op: LowLevel::ListWalk,
|
||||||
|
args: vec![
|
||||||
|
(list_var, Var(Symbol::ARG_1)),
|
||||||
|
(func_var, Var(Symbol::ARG_2)),
|
||||||
|
(accum_var, Var(Symbol::ARG_3)),
|
||||||
|
],
|
||||||
|
ret_var: accum_var,
|
||||||
|
};
|
||||||
|
|
||||||
|
defn(
|
||||||
|
symbol,
|
||||||
|
vec![
|
||||||
|
(list_var, Symbol::ARG_1),
|
||||||
|
(func_var, Symbol::ARG_2),
|
||||||
|
(accum_var, Symbol::ARG_3),
|
||||||
|
],
|
||||||
|
var_store,
|
||||||
|
body,
|
||||||
|
accum_var,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// List.walkBackwards : List elem, (elem -> accum -> accum), accum -> accum
|
||||||
fn list_walk_backwards(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
fn list_walk_backwards(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
||||||
let list_var = var_store.fresh();
|
let list_var = var_store.fresh();
|
||||||
let func_var = var_store.fresh();
|
let func_var = var_store.fresh();
|
||||||
let accum_var = var_store.fresh();
|
let accum_var = var_store.fresh();
|
||||||
|
|
||||||
let body = RunLowLevel {
|
let body = RunLowLevel {
|
||||||
op: LowLevel::ListWalkRight,
|
op: LowLevel::ListWalkBackwards,
|
||||||
args: vec![
|
args: vec![
|
||||||
(list_var, Var(Symbol::ARG_1)),
|
(list_var, Var(Symbol::ARG_1)),
|
||||||
(func_var, Var(Symbol::ARG_2)),
|
(func_var, Var(Symbol::ARG_2)),
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
use crate::llvm::build_list::{
|
use crate::llvm::build_list::{
|
||||||
allocate_list, empty_list, empty_polymorphic_list, list_append, list_concat, list_contains,
|
allocate_list, empty_list, empty_polymorphic_list, list_append, list_concat, list_contains,
|
||||||
list_get_unsafe, list_join, list_keep_if, list_len, list_map, list_prepend, list_repeat,
|
list_get_unsafe, list_join, list_keep_if, list_len, list_map, list_prepend, list_repeat,
|
||||||
list_reverse, list_set, list_single, list_sum, list_walk_backwards,
|
list_reverse, list_set, list_single, list_sum, list_walk, list_walk_backwards,
|
||||||
};
|
};
|
||||||
use crate::llvm::build_str::{
|
use crate::llvm::build_str::{
|
||||||
str_concat, str_count_graphemes, str_len, str_split, str_starts_with, CHAR_LAYOUT,
|
str_concat, str_count_graphemes, str_len, str_split, str_starts_with, CHAR_LAYOUT,
|
||||||
|
@ -2491,8 +2491,28 @@ fn run_low_level<'a, 'ctx, 'env>(
|
||||||
|
|
||||||
list_contains(env, parent, elem, elem_layout, list, list_layout)
|
list_contains(env, parent, elem, elem_layout, list, list_layout)
|
||||||
}
|
}
|
||||||
ListWalkRight => {
|
ListWalk => {
|
||||||
// List.walkRight : List elem, (elem -> accum -> accum), accum -> accum
|
debug_assert_eq!(args.len(), 3);
|
||||||
|
|
||||||
|
let (list, list_layout) = load_symbol_and_layout(env, scope, &args[0]);
|
||||||
|
|
||||||
|
let (func, func_layout) = load_symbol_and_layout(env, scope, &args[1]);
|
||||||
|
|
||||||
|
let (default, default_layout) = load_symbol_and_layout(env, scope, &args[2]);
|
||||||
|
|
||||||
|
list_walk(
|
||||||
|
env,
|
||||||
|
parent,
|
||||||
|
list,
|
||||||
|
list_layout,
|
||||||
|
func,
|
||||||
|
func_layout,
|
||||||
|
default,
|
||||||
|
default_layout,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
ListWalkBackwards => {
|
||||||
|
// List.walkBackwards : List elem, (elem -> accum -> accum), accum -> accum
|
||||||
debug_assert_eq!(args.len(), 3);
|
debug_assert_eq!(args.len(), 3);
|
||||||
|
|
||||||
let (list, list_layout) = load_symbol_and_layout(env, scope, &args[0]);
|
let (list, list_layout) = load_symbol_and_layout(env, scope, &args[0]);
|
||||||
|
|
|
@ -809,7 +809,98 @@ pub fn list_sum<'a, 'ctx, 'env>(
|
||||||
builder.build_load(accum_alloca, "load_final_acum")
|
builder.build_load(accum_alloca, "load_final_acum")
|
||||||
}
|
}
|
||||||
|
|
||||||
/// List.walkRight : List elem, (elem -> accum -> accum), accum -> accum
|
/// List.walk : List elem, (elem -> accum -> accum), accum -> accum
|
||||||
|
pub fn list_walk<'a, 'ctx, 'env>(
|
||||||
|
env: &Env<'a, 'ctx, 'env>,
|
||||||
|
parent: FunctionValue<'ctx>,
|
||||||
|
list: BasicValueEnum<'ctx>,
|
||||||
|
list_layout: &Layout<'a>,
|
||||||
|
func: BasicValueEnum<'ctx>,
|
||||||
|
func_layout: &Layout<'a>,
|
||||||
|
default: BasicValueEnum<'ctx>,
|
||||||
|
default_layout: &Layout<'a>,
|
||||||
|
) -> BasicValueEnum<'ctx> {
|
||||||
|
let ctx = env.context;
|
||||||
|
let builder = env.builder;
|
||||||
|
|
||||||
|
let list_wrapper = list.into_struct_value();
|
||||||
|
let len = list_len(env.builder, list_wrapper);
|
||||||
|
|
||||||
|
let accum_type = basic_type_from_layout(env.arena, ctx, default_layout, env.ptr_bytes);
|
||||||
|
let accum_alloca = builder.build_alloca(accum_type, "alloca_walk_right_accum");
|
||||||
|
builder.build_store(accum_alloca, default);
|
||||||
|
|
||||||
|
let then_block = ctx.append_basic_block(parent, "then");
|
||||||
|
let cont_block = ctx.append_basic_block(parent, "branchcont");
|
||||||
|
|
||||||
|
let condition = builder.build_int_compare(
|
||||||
|
IntPredicate::UGT,
|
||||||
|
len,
|
||||||
|
ctx.i64_type().const_zero(),
|
||||||
|
"list_non_empty",
|
||||||
|
);
|
||||||
|
|
||||||
|
builder.build_conditional_branch(condition, then_block, cont_block);
|
||||||
|
|
||||||
|
builder.position_at_end(then_block);
|
||||||
|
|
||||||
|
match (func, func_layout) {
|
||||||
|
(BasicValueEnum::PointerValue(func_ptr), Layout::FunctionPointer(_, _)) => {
|
||||||
|
let elem_layout = match list_layout {
|
||||||
|
Layout::Builtin(Builtin::List(_, layout)) => layout,
|
||||||
|
_ => unreachable!("can only fold over a list"),
|
||||||
|
};
|
||||||
|
|
||||||
|
let elem_type = basic_type_from_layout(env.arena, ctx, elem_layout, env.ptr_bytes);
|
||||||
|
let elem_ptr_type = get_ptr_type(&elem_type, AddressSpace::Generic);
|
||||||
|
|
||||||
|
let list_ptr = load_list_ptr(builder, list_wrapper, elem_ptr_type);
|
||||||
|
|
||||||
|
let walk_right_loop = |_, elem: BasicValueEnum<'ctx>| {
|
||||||
|
// load current accumulator
|
||||||
|
let current = builder.build_load(accum_alloca, "retrieve_accum");
|
||||||
|
|
||||||
|
let call_site_value =
|
||||||
|
builder.build_call(func_ptr, &[elem, current], "#walk_right_func");
|
||||||
|
|
||||||
|
// set the calling convention explicitly for this call
|
||||||
|
call_site_value.set_call_convention(crate::llvm::build::FAST_CALL_CONV);
|
||||||
|
|
||||||
|
let new_current = call_site_value
|
||||||
|
.try_as_basic_value()
|
||||||
|
.left()
|
||||||
|
.unwrap_or_else(|| panic!("LLVM error: Invalid call by pointer."));
|
||||||
|
|
||||||
|
builder.build_store(accum_alloca, new_current);
|
||||||
|
};
|
||||||
|
|
||||||
|
incrementing_elem_loop(
|
||||||
|
builder,
|
||||||
|
ctx,
|
||||||
|
parent,
|
||||||
|
list_ptr,
|
||||||
|
len,
|
||||||
|
"#index",
|
||||||
|
walk_right_loop,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
_ => {
|
||||||
|
unreachable!(
|
||||||
|
"Invalid function basic value enum or layout for List.keepIf : {:?}",
|
||||||
|
(func, func_layout)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
builder.build_unconditional_branch(cont_block);
|
||||||
|
|
||||||
|
builder.position_at_end(cont_block);
|
||||||
|
|
||||||
|
builder.build_load(accum_alloca, "load_final_acum")
|
||||||
|
}
|
||||||
|
|
||||||
|
/// List.walkBackwards : List elem, (elem -> accum -> accum), accum -> accum
|
||||||
#[allow(clippy::too_many_arguments)]
|
#[allow(clippy::too_many_arguments)]
|
||||||
pub fn list_walk_backwards<'a, 'ctx, 'env>(
|
pub fn list_walk_backwards<'a, 'ctx, 'env>(
|
||||||
env: &Env<'a, 'ctx, 'env>,
|
env: &Env<'a, 'ctx, 'env>,
|
||||||
|
@ -875,7 +966,7 @@ pub fn list_walk_backwards<'a, 'ctx, 'env>(
|
||||||
builder.build_store(accum_alloca, new_current);
|
builder.build_store(accum_alloca, new_current);
|
||||||
};
|
};
|
||||||
|
|
||||||
incrementing_elem_loop(
|
decrementing_elem_loop(
|
||||||
builder,
|
builder,
|
||||||
ctx,
|
ctx,
|
||||||
parent,
|
parent,
|
||||||
|
@ -1537,6 +1628,7 @@ where
|
||||||
let current_index = builder
|
let current_index = builder
|
||||||
.build_load(index_alloca, index_name)
|
.build_load(index_alloca, index_name)
|
||||||
.into_int_value();
|
.into_int_value();
|
||||||
|
|
||||||
let next_index = builder.build_int_sub(current_index, one, "nextindex");
|
let next_index = builder.build_int_sub(current_index, one, "nextindex");
|
||||||
|
|
||||||
builder.build_store(index_alloca, next_index);
|
builder.build_store(index_alloca, next_index);
|
||||||
|
@ -1546,7 +1638,7 @@ where
|
||||||
|
|
||||||
// #index >= 0
|
// #index >= 0
|
||||||
let condition = builder.build_int_compare(
|
let condition = builder.build_int_compare(
|
||||||
IntPredicate::UGE,
|
IntPredicate::SGE,
|
||||||
next_index,
|
next_index,
|
||||||
ctx.i64_type().const_zero(),
|
ctx.i64_type().const_zero(),
|
||||||
"bounds_check",
|
"bounds_check",
|
||||||
|
|
|
@ -267,13 +267,13 @@ mod gen_list {
|
||||||
fn list_walk_backwards_with_str() {
|
fn list_walk_backwards_with_str() {
|
||||||
assert_evals_to!(
|
assert_evals_to!(
|
||||||
r#"List.walkBackwards [ "x", "y", "z" ] Str.concat "<""#,
|
r#"List.walkBackwards [ "x", "y", "z" ] Str.concat "<""#,
|
||||||
RocStr::from("zyx<"),
|
RocStr::from("xyz<"),
|
||||||
RocStr
|
RocStr
|
||||||
);
|
);
|
||||||
|
|
||||||
assert_evals_to!(
|
assert_evals_to!(
|
||||||
r#"List.walkBackwards [ "Third", "Second", "First" ] Str.concat "Fourth""#,
|
r#"List.walkBackwards [ "Third", "Second", "First" ] Str.concat "Fourth""#,
|
||||||
RocStr::from("FirstSecondThirdFourth"),
|
RocStr::from("ThirdSecondFirstFourth"),
|
||||||
RocStr
|
RocStr
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,7 +22,8 @@ pub enum LowLevel {
|
||||||
ListJoin,
|
ListJoin,
|
||||||
ListMap,
|
ListMap,
|
||||||
ListKeepIf,
|
ListKeepIf,
|
||||||
ListWalkRight,
|
ListWalk,
|
||||||
|
ListWalkBackwards,
|
||||||
ListSum,
|
ListSum,
|
||||||
NumAdd,
|
NumAdd,
|
||||||
NumAddWrap,
|
NumAddWrap,
|
||||||
|
|
|
@ -683,18 +683,18 @@ define_builtins! {
|
||||||
5 LIST_APPEND: "append"
|
5 LIST_APPEND: "append"
|
||||||
6 LIST_MAP: "map"
|
6 LIST_MAP: "map"
|
||||||
7 LIST_LEN: "len"
|
7 LIST_LEN: "len"
|
||||||
8 LIST_FOLDL: "foldl"
|
8 LIST_WALK_BACKWARDS: "walkBackwards"
|
||||||
9 LIST_WALK_BACKWARDS: "walkBackwards"
|
9 LIST_CONCAT: "concat"
|
||||||
10 LIST_CONCAT: "concat"
|
10 LIST_FIRST: "first"
|
||||||
11 LIST_FIRST: "first"
|
11 LIST_SINGLE: "single"
|
||||||
12 LIST_SINGLE: "single"
|
12 LIST_REPEAT: "repeat"
|
||||||
13 LIST_REPEAT: "repeat"
|
13 LIST_REVERSE: "reverse"
|
||||||
14 LIST_REVERSE: "reverse"
|
14 LIST_PREPEND: "prepend"
|
||||||
15 LIST_PREPEND: "prepend"
|
15 LIST_JOIN: "join"
|
||||||
16 LIST_JOIN: "join"
|
16 LIST_KEEP_IF: "keepIf"
|
||||||
17 LIST_KEEP_IF: "keepIf"
|
17 LIST_CONTAINS: "contains"
|
||||||
18 LIST_CONTAINS: "contains"
|
18 LIST_SUM: "sum"
|
||||||
19 LIST_SUM: "sum"
|
19 LIST_WALK: "walk"
|
||||||
}
|
}
|
||||||
5 RESULT: "Result" => {
|
5 RESULT: "Result" => {
|
||||||
0 RESULT_RESULT: "Result" imported // the Result.Result type alias
|
0 RESULT_RESULT: "Result" imported // the Result.Result type alias
|
||||||
|
|
|
@ -535,7 +535,8 @@ pub fn lowlevel_borrow_signature(arena: &Bump, op: LowLevel) -> &[bool] {
|
||||||
ListMap => arena.alloc_slice_copy(&[owned, irrelevant]),
|
ListMap => arena.alloc_slice_copy(&[owned, irrelevant]),
|
||||||
ListKeepIf => arena.alloc_slice_copy(&[owned, irrelevant]),
|
ListKeepIf => arena.alloc_slice_copy(&[owned, irrelevant]),
|
||||||
ListContains => arena.alloc_slice_copy(&[borrowed, irrelevant]),
|
ListContains => arena.alloc_slice_copy(&[borrowed, irrelevant]),
|
||||||
ListWalkRight => arena.alloc_slice_copy(&[borrowed, irrelevant, owned]),
|
ListWalk => arena.alloc_slice_copy(&[borrowed, irrelevant, owned]),
|
||||||
|
ListWalkBackwards => arena.alloc_slice_copy(&[borrowed, irrelevant, owned]),
|
||||||
ListSum => arena.alloc_slice_copy(&[borrowed]),
|
ListSum => arena.alloc_slice_copy(&[borrowed]),
|
||||||
|
|
||||||
Eq | NotEq | And | Or | NumAdd | NumAddWrap | NumAddChecked | NumSub | NumMul | NumGt
|
Eq | NotEq | And | Or | NumAdd | NumAddWrap | NumAddChecked | NumSub | NumMul | NumGt
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue