Merge branch 'docs-type-aliases' of github.com:rtfeldman/roc into docs-type-aliases

This commit is contained in:
Chadtech 2021-04-05 19:40:05 -04:00
commit b5656328e6
44 changed files with 961 additions and 197 deletions

View file

@ -23,6 +23,22 @@ For debugging LLVM IR, we use [DebugIR](https://github.com/vaivaswatha/debugir).
MacOS systems should already have `libunwind`, but other systems will need to install it (On Ubuntu, this can be donw with `sudo apt-get install libunwind-dev`).
Some systems may already have `libc++-dev` on them, but if not, you may need to install it. (On Ubuntu, this can be done with `sudo apt-get install libc++-dev`.)
### libcxb libraries
You may see an error like this during builds:
```
/usr/bin/ld: cannot find -lxcb-render
/usr/bin/ld: cannot find -lxcb-shape
/usr/bin/ld: cannot find -lxcb-xfixes
```
If so, you can fix it like so:
```
sudo apt-get install libxcb-render0-dev libxcb-shape0-dev libxcb-xfixes0-dev
```
### Zig
**version: 0.7.x**

View file

@ -7,6 +7,7 @@ const Allocator = mem.Allocator;
const TAG_WIDTH = 8;
const EqFn = fn (?[*]u8, ?[*]u8) callconv(.C) bool;
const CompareFn = fn (?[*]u8, ?[*]u8, ?[*]u8) callconv(.C) u8;
const Opaque = ?[*]u8;
const Inc = fn (?[*]u8) callconv(.C) void;
@ -118,6 +119,59 @@ const Caller1 = fn (?[*]u8, ?[*]u8, ?[*]u8) callconv(.C) void;
const Caller2 = fn (?[*]u8, ?[*]u8, ?[*]u8, ?[*]u8) callconv(.C) void;
const Caller3 = fn (?[*]u8, ?[*]u8, ?[*]u8, ?[*]u8, ?[*]u8) callconv(.C) void;
pub fn listReverse(list: RocList, alignment: usize, element_width: usize) callconv(.C) RocList {
if (list.bytes) |source_ptr| {
const size = list.len();
var i: usize = 0;
var end: usize = size - 1;
if (list.isUnique()) {
const temp: [*]u8 = @ptrCast([*]u8, std.heap.c_allocator.alloc(u8, element_width) catch unreachable);
// Working from the front and back so
// we only need to go ~(n / 2) iterations.
// If the length is an odd number the middle
// element stays in the same place anyways.
while (i < (end - i)) : (i += 1) {
const last_position = end - i;
const last_element = source_ptr + (last_position * element_width);
const first_element = source_ptr + (i * element_width);
// Store Last Element in temp
@memcpy(temp, last_element, element_width);
// Swap Last Element with First Element
@memcpy(last_element, first_element, element_width);
// Swap First Element with temp
@memcpy(first_element, temp, element_width);
}
std.heap.c_allocator.free(temp[0..element_width]);
return list;
} else {
const output = RocList.allocate(std.heap.c_allocator, alignment, size, element_width);
const target_ptr = output.bytes orelse unreachable;
while (i < size) : (i += 1) {
const last_position = end - i;
@memcpy(target_ptr + (i * element_width), source_ptr + (last_position * element_width), element_width);
}
utils.decref(std.heap.c_allocator, alignment, list.bytes, size * element_width);
return output;
}
} else {
return RocList.empty();
}
}
pub fn listMap(list: RocList, transform: Opaque, caller: Caller1, alignment: usize, old_element_width: usize, new_element_width: usize) callconv(.C) RocList {
if (list.bytes) |source_ptr| {
const size = list.len();
@ -688,3 +742,81 @@ fn listRangeHelp(allocator: *Allocator, comptime T: type, low: T, high: T) RocLi
},
}
}
inline fn swapHelp(width: usize, temporary: [*]u8, ptr1: [*]u8, ptr2: [*]u8) void {
@memcpy(temporary, ptr1, width);
@memcpy(ptr1, ptr2, width);
@memcpy(ptr2, temporary, width);
}
fn swap(source_ptr: [*]u8, element_width_initial: usize, index_1: usize, index_2: usize) void {
const threshold: comptime usize = 64;
var element_width = element_width_initial;
var buffer_actual: [threshold]u8 = undefined;
var buffer: [*]u8 = buffer_actual[0..];
var element_at_i = source_ptr + (index_1 * element_width);
var element_at_j = source_ptr + (index_2 * element_width);
while (true) {
if (element_width < threshold) {
swapHelp(element_width, buffer, element_at_i, element_at_j);
return;
} else {
swapHelp(threshold, buffer, element_at_i, element_at_j);
element_at_i += threshold;
element_at_j += threshold;
element_width -= threshold;
}
}
}
fn partition(source_ptr: [*]u8, transform: Opaque, wrapper: CompareFn, element_width: usize, low: isize, high: isize) isize {
const pivot = source_ptr + (@intCast(usize, high) * element_width);
var i = (low - 1); // Index of smaller element and indicates the right position of pivot found so far
var j = low;
while (j <= high - 1) : (j += 1) {
const current_elem = source_ptr + (@intCast(usize, j) * element_width);
const ordering = wrapper(transform, current_elem, pivot);
const order = @intToEnum(utils.Ordering, ordering);
switch (order) {
utils.Ordering.LT => {
// the current element is smaller than the pivot; swap it
i += 1;
swap(source_ptr, element_width, @intCast(usize, i), @intCast(usize, j));
},
utils.Ordering.EQ, utils.Ordering.GT => {},
}
}
swap(source_ptr, element_width, @intCast(usize, i + 1), @intCast(usize, high));
return (i + 1);
}
fn quicksort(source_ptr: [*]u8, transform: Opaque, wrapper: CompareFn, element_width: usize, low: isize, high: isize) void {
if (low < high) {
// partition index
const pi = partition(source_ptr, transform, wrapper, element_width, low, high);
_ = quicksort(source_ptr, transform, wrapper, element_width, low, pi - 1); // before pi
_ = quicksort(source_ptr, transform, wrapper, element_width, pi + 1, high); // after pi
}
}
pub fn listSortWith(input: RocList, transform: Opaque, wrapper: CompareFn, alignment: usize, element_width: usize) callconv(.C) RocList {
var list = input.makeUnique(std.heap.c_allocator, alignment, element_width);
if (list.bytes) |source_ptr| {
const low = 0;
const high: isize = @intCast(isize, list.len()) - 1;
quicksort(source_ptr, transform, wrapper, element_width, low, high);
}
return list;
}

View file

@ -20,6 +20,8 @@ comptime {
exportListFn(list.listRepeat, "repeat");
exportListFn(list.listAppend, "append");
exportListFn(list.listRange, "range");
exportListFn(list.listReverse, "reverse");
exportListFn(list.listSortWith, "sort_with");
}
// Dict Module

View file

@ -157,3 +157,9 @@ pub const RocResult = extern struct {
return !self.isOk();
}
};
pub const Ordering = packed enum(u8) {
EQ = 0,
GT = 1,
LT = 2,
};

View file

@ -76,3 +76,5 @@ pub const LIST_CONTAINS: &str = "roc_builtins.list.contains";
pub const LIST_REPEAT: &str = "roc_builtins.list.repeat";
pub const LIST_APPEND: &str = "roc_builtins.list.append";
pub const LIST_RANGE: &str = "roc_builtins.list.range";
pub const LIST_REVERSE: &str = "roc_builtins.list.reverse";
pub const LIST_SORT_WITH: &str = "roc_builtins.list.sort_with";

View file

@ -958,6 +958,22 @@ pub fn types() -> MutMap<Symbol, (SolvedType, Region)> {
top_level_function(vec![list_type(flex(TVAR1))], Box::new(bool_type())),
);
// sortWith : List a, (a, a -> Ordering) -> List a
add_type(
Symbol::LIST_SORT_WITH,
top_level_function(
vec![
list_type(flex(TVAR1)),
closure(
vec![flex(TVAR1), flex(TVAR1)],
TVAR2,
Box::new(ordering_type()),
),
],
Box::new(list_type(flex(TVAR1))),
),
);
// Dict module
// Dict.hashTestOnly : Nat, v -> Nat

View file

@ -91,6 +91,7 @@ pub fn builtin_defs_map(symbol: Symbol, var_store: &mut VarStore) -> Option<Def>
LIST_WALK => list_walk,
LIST_WALK_BACKWARDS => list_walk_backwards,
LIST_WALK_UNTIL => list_walk_until,
LIST_SORT_WITH => list_sort_with,
DICT_TEST_HASH => dict_hash_test_only,
DICT_LEN => dict_len,
DICT_EMPTY => dict_empty,
@ -2118,6 +2119,11 @@ fn list_map3(symbol: Symbol, var_store: &mut VarStore) -> Def {
lowlevel_4(symbol, LowLevel::ListMap3, var_store)
}
/// List.sortWith : List a, (a, a -> Ordering) -> List a
fn list_sort_with(symbol: Symbol, var_store: &mut VarStore) -> Def {
lowlevel_2(symbol, LowLevel::ListSortWith, var_store)
}
/// Dict.hashTestOnly : k, v -> Nat
fn dict_hash_test_only(symbol: Symbol, var_store: &mut VarStore) -> Def {
lowlevel_2(symbol, LowLevel::Hash, var_store)

View file

@ -1,9 +1,9 @@
/// Helpers for interacting with the zig that generates bitcode
use crate::debug_info_init;
use crate::llvm::build::{set_name, Env, FAST_CALL_CONV};
use crate::llvm::build::{set_name, Env, C_CALL_CONV, FAST_CALL_CONV};
use crate::llvm::convert::basic_type_from_layout;
use crate::llvm::refcounting::{decrement_refcount_layout, increment_refcount_layout, Mode};
use inkwell::attributes::{Attribute, AttributeLoc};
/// Helpers for interacting with the zig that generates bitcode
use inkwell::types::{BasicType, BasicTypeEnum};
use inkwell::values::{BasicValueEnum, CallSiteValue, FunctionValue, InstructionValue};
use inkwell::AddressSpace;
@ -383,3 +383,101 @@ pub fn build_eq_wrapper<'a, 'ctx, 'env>(
function_value
}
pub fn build_compare_wrapper<'a, 'ctx, 'env>(
env: &Env<'a, 'ctx, 'env>,
layout_ids: &mut LayoutIds<'a>,
layout: &Layout<'a>,
) -> FunctionValue<'ctx> {
let block = env.builder.get_insert_block().expect("to be in a function");
let di_location = env.builder.get_current_debug_location().unwrap();
let symbol = Symbol::GENERIC_COMPARE_REF;
let fn_name = layout_ids
.get(symbol, &layout)
.to_symbol_string(symbol, &env.interns);
let function_value = match env.module.get_function(fn_name.as_str()) {
Some(function_value) => function_value,
None => {
let arg_type = env.context.i8_type().ptr_type(AddressSpace::Generic);
let function_value = crate::llvm::refcounting::build_header_help(
env,
&fn_name,
env.context.i8_type().into(),
&[arg_type.into(), arg_type.into(), arg_type.into()],
);
// we expose this function to zig; must use c calling convention
function_value.set_call_conventions(C_CALL_CONV);
let kind_id = Attribute::get_named_enum_kind_id("alwaysinline");
debug_assert!(kind_id > 0);
let attr = env.context.create_enum_attribute(kind_id, 1);
function_value.add_attribute(AttributeLoc::Function, attr);
let entry = env.context.append_basic_block(function_value, "entry");
env.builder.position_at_end(entry);
debug_info_init!(env, function_value);
let mut it = function_value.get_param_iter();
let function_ptr = it.next().unwrap().into_pointer_value();
let value_ptr1 = it.next().unwrap().into_pointer_value();
let value_ptr2 = it.next().unwrap().into_pointer_value();
set_name(
function_ptr.into(),
Symbol::ARG_1.ident_string(&env.interns),
);
set_name(value_ptr1.into(), Symbol::ARG_2.ident_string(&env.interns));
set_name(value_ptr2.into(), Symbol::ARG_3.ident_string(&env.interns));
let value_type = basic_type_from_layout(env.arena, env.context, layout, env.ptr_bytes);
let function_type = env
.context
.i8_type()
.fn_type(&[value_type, value_type], false)
.ptr_type(AddressSpace::Generic);
let value_ptr_type = value_type.ptr_type(AddressSpace::Generic);
let function_cast =
env.builder
.build_bitcast(function_ptr, function_type, "load_opaque");
let value_cast1 = env
.builder
.build_bitcast(value_ptr1, value_ptr_type, "load_opaque")
.into_pointer_value();
let value_cast2 = env
.builder
.build_bitcast(value_ptr2, value_ptr_type, "load_opaque")
.into_pointer_value();
let value1 = env.builder.build_load(value_cast1, "load_opaque");
let value2 = env.builder.build_load(value_cast2, "load_opaque");
let call = env.builder.build_call(
function_cast.into_pointer_value(),
&[value1, value2],
"call_user_defined_function",
);
let result = call.try_as_basic_value().left().unwrap();
// IMPORTANT! we call a user function, so it has the fast calling convention
call.set_call_convention(FAST_CALL_CONV);
env.builder.build_return(Some(&result));
function_value
}
};
env.builder.position_at_end(block);
env.builder
.set_current_debug_location(env.context, di_location);
function_value
}

View file

@ -8,7 +8,7 @@ use crate::llvm::build_list::{
allocate_list, empty_list, empty_polymorphic_list, list_append, list_concat, list_contains,
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_walk_help,
list_set, list_single, list_sort_with, list_walk_help,
};
use crate::llvm::build_str::{
str_concat, str_count_graphemes, str_ends_with, str_from_float, str_from_int, str_from_utf8,
@ -3693,7 +3693,7 @@ fn run_low_level<'a, 'ctx, 'env>(
let inplace = get_inplace_from_layout(layout);
list_reverse(env, parent, inplace, list, list_layout)
list_reverse(env, inplace, list, list_layout)
}
ListConcat => {
debug_assert_eq!(args.len(), 2);
@ -3949,6 +3949,22 @@ fn run_low_level<'a, 'ctx, 'env>(
list_join(env, inplace, parent, list, outer_list_layout)
}
ListSortWith => {
// List.sortWith : List a, (a, a -> Ordering) -> List a
debug_assert_eq!(args.len(), 2);
let (list, list_layout) = load_symbol_and_layout(scope, &args[0]);
let func = load_symbol(scope, &args[1]);
match list_layout {
Layout::Builtin(Builtin::EmptyList) => empty_list(env),
Layout::Builtin(Builtin::List(_, element_layout)) => {
list_sort_with(env, layout_ids, func, list, element_layout)
}
_ => unreachable!("invalid list layout"),
}
}
NumAbs | NumNeg | NumRound | NumSqrtUnchecked | NumLogUnchecked | NumSin | NumCos
| NumCeiling | NumFloor | NumToFloat | NumIsFinite | NumAtan | NumAcos | NumAsin => {
debug_assert_eq!(args.len(), 1);

View file

@ -1,7 +1,7 @@
#![allow(clippy::too_many_arguments)]
use crate::llvm::bitcode::{
build_dec_wrapper, build_eq_wrapper, build_inc_wrapper, build_transform_caller,
call_bitcode_fn, call_void_bitcode_fn,
build_compare_wrapper, build_dec_wrapper, build_eq_wrapper, build_inc_wrapper,
build_transform_caller, call_bitcode_fn, call_void_bitcode_fn,
};
use crate::llvm::build::{
allocate_with_refcount_help, cast_basic_basic, complex_bitcast, Env, InPlace,
@ -12,7 +12,6 @@ use crate::llvm::refcounting::{
};
use inkwell::builder::Builder;
use inkwell::context::Context;
use inkwell::types::BasicType;
use inkwell::types::{BasicTypeEnum, PointerType};
use inkwell::values::{BasicValueEnum, FunctionValue, IntValue, PointerValue, StructValue};
use inkwell::{AddressSpace, IntPredicate};
@ -337,93 +336,14 @@ pub fn list_join<'a, 'ctx, 'env>(
}
}
/// List.reverse : List elem -> List elem
pub fn list_reverse_help<'a, 'ctx, 'env>(
env: &Env<'a, 'ctx, 'env>,
parent: FunctionValue<'ctx>,
inplace: InPlace,
length: IntValue<'ctx>,
source_ptr: PointerValue<'ctx>,
dest_ptr: PointerValue<'ctx>,
) {
let builder = env.builder;
let ctx = env.context;
// constant 1i64
let one = ctx.i64_type().const_int(1, false);
let low_alloca = builder.build_alloca(ctx.i64_type(), "low");
let high_alloca = builder.build_alloca(ctx.i64_type(), "high");
let high_val = builder.build_int_sub(length, one, "subtract 1");
builder.build_store(low_alloca, ctx.i64_type().const_zero());
builder.build_store(high_alloca, high_val);
// while (high > low)
let condition_bb = ctx.append_basic_block(parent, "condition");
builder.build_unconditional_branch(condition_bb);
builder.position_at_end(condition_bb);
let high = builder.build_load(high_alloca, "high").into_int_value();
let low = builder.build_load(low_alloca, "low").into_int_value();
// if updating in-place, then the "middle element" can be left untouched
// otherwise, the middle element needs to be copied over from the source to the target
let predicate = match inplace {
InPlace::InPlace => IntPredicate::SGT,
InPlace::Clone => IntPredicate::SGE,
};
let condition = builder.build_int_compare(predicate, high, low, "loopcond");
let body_bb = ctx.append_basic_block(parent, "body");
let cont_bb = ctx.append_basic_block(parent, "cont");
builder.build_conditional_branch(condition, body_bb, cont_bb);
// loop body
builder.position_at_end(body_bb);
// assumption: calculating pointer offsets for both the source and target is
let mut low_ptr = unsafe { builder.build_in_bounds_gep(source_ptr, &[low], "low_ptr") };
let mut high_ptr = unsafe { builder.build_in_bounds_gep(source_ptr, &[high], "high_ptr") };
// TODO use memmove?
let low_value = builder.build_load(low_ptr, "load_low");
let high_value = builder.build_load(high_ptr, "load_high");
// swap the two values
if let InPlace::Clone = inplace {
low_ptr = unsafe { builder.build_in_bounds_gep(dest_ptr, &[low], "low_ptr") };
high_ptr = unsafe { builder.build_in_bounds_gep(dest_ptr, &[high], "high_ptr") };
}
builder.build_store(high_ptr, low_value);
builder.build_store(low_ptr, high_value);
builder.build_store(low_alloca, builder.build_int_add(low, one, "increment"));
builder.build_store(high_alloca, builder.build_int_sub(high, one, "decrement"));
builder.build_unconditional_branch(condition_bb);
// continuation
builder.position_at_end(cont_bb);
}
/// List.reverse : List elem -> List elem
pub fn list_reverse<'a, 'ctx, 'env>(
env: &Env<'a, 'ctx, 'env>,
parent: FunctionValue<'ctx>,
output_inplace: InPlace,
_output_inplace: InPlace,
list: BasicValueEnum<'ctx>,
list_layout: &Layout<'a>,
) -> BasicValueEnum<'ctx> {
let builder = env.builder;
let ctx = env.context;
let wrapper_struct = list.into_struct_value();
let (input_inplace, element_layout) = match *list_layout {
let (_, element_layout) = match *list_layout {
Layout::Builtin(Builtin::EmptyList) => (
InPlace::InPlace,
// this pointer will never actually be dereferenced
@ -440,74 +360,27 @@ pub fn list_reverse<'a, 'ctx, 'env>(
_ => unreachable!("Invalid layout {:?} in List.reverse", list_layout),
};
let list_type = basic_type_from_layout(env.arena, env.context, &element_layout, env.ptr_bytes);
let ptr_type = list_type.ptr_type(AddressSpace::Generic);
let list_i128 = complex_bitcast(env.builder, list, env.context.i128_type().into(), "to_i128");
let list_ptr = load_list_ptr(builder, wrapper_struct, ptr_type);
let length = list_len(builder, list.into_struct_value());
let element_width = env
.ptr_int()
.const_int(element_layout.stack_size(env.ptr_bytes) as u64, false);
match input_inplace {
InPlace::InPlace => {
list_reverse_help(env, parent, input_inplace, length, list_ptr, list_ptr);
let alignment = element_layout.alignment_bytes(env.ptr_bytes);
let alignment_iv = env.ptr_int().const_int(alignment as u64, false);
list
}
InPlace::Clone => {
let len_0_block = ctx.append_basic_block(parent, "len_0_block");
let len_1_block = ctx.append_basic_block(parent, "len_1_block");
let len_n_block = ctx.append_basic_block(parent, "len_n_block");
let cont_block = ctx.append_basic_block(parent, "cont_block");
let one = ctx.i64_type().const_int(1, false);
let zero = ctx.i64_type().const_zero();
let result = builder.build_alloca(ptr_type, "result");
builder.build_switch(
length,
len_n_block,
&[(zero, len_0_block), (one, len_1_block)],
let output = call_bitcode_fn(
env,
&[list_i128, alignment_iv.into(), element_width.into()],
&bitcode::LIST_REVERSE,
);
// build block for length 0
{
builder.position_at_end(len_0_block);
// store NULL pointer there
builder.build_store(result, ptr_type.const_zero());
builder.build_unconditional_branch(cont_block);
}
// build block for length 1
{
builder.position_at_end(len_1_block);
let new_list_ptr = clone_list(env, output_inplace, &element_layout, one, list_ptr);
builder.build_store(result, new_list_ptr);
builder.build_unconditional_branch(cont_block);
}
// build block for length > 1
{
builder.position_at_end(len_n_block);
let new_list_ptr = allocate_list(env, output_inplace, &element_layout, length);
list_reverse_help(env, parent, InPlace::Clone, length, list_ptr, new_list_ptr);
// store new list pointer there
builder.build_store(result, new_list_ptr);
builder.build_unconditional_branch(cont_block);
}
builder.position_at_end(cont_block);
let new_list_ptr = builder.build_load(result, "result").into_pointer_value();
store_list(env, new_list_ptr, length)
}
}
complex_bitcast(
env.builder,
output,
collection(env.context, env.ptr_bytes).into(),
"from_i128",
)
}
pub fn list_get_unsafe<'a, 'ctx, 'env>(
@ -1132,6 +1005,52 @@ pub fn list_keep_result<'a, 'ctx, 'env>(
)
}
/// List.sortWith : List a, (a, a -> Ordering) -> List a
pub fn list_sort_with<'a, 'ctx, 'env>(
env: &Env<'a, 'ctx, 'env>,
layout_ids: &mut LayoutIds<'a>,
transform: BasicValueEnum<'ctx>,
list: BasicValueEnum<'ctx>,
element_layout: &Layout<'a>,
) -> BasicValueEnum<'ctx> {
let u8_ptr = env.context.i8_type().ptr_type(AddressSpace::Generic);
let list_i128 = complex_bitcast(env.builder, list, env.context.i128_type().into(), "to_i128");
let transform_ptr = transform.into_pointer_value();
let compare_wrapper = build_compare_wrapper(env, layout_ids, element_layout)
.as_global_value()
.as_pointer_value();
let element_width = env
.ptr_int()
.const_int(element_layout.stack_size(env.ptr_bytes) as u64, false);
let alignment = element_layout.alignment_bytes(env.ptr_bytes);
let alignment_iv = env.ptr_int().const_int(alignment as u64, false);
let output = call_bitcode_fn(
env,
&[
list_i128,
env.builder
.build_bitcast(transform_ptr, u8_ptr, "to_opaque"),
compare_wrapper.into(),
alignment_iv.into(),
element_width.into(),
],
bitcode::LIST_SORT_WITH,
);
complex_bitcast(
env.builder,
output,
collection(env.context, env.ptr_bytes).into(),
"from_i128",
)
}
/// List.map : List before, (before -> after) -> List after
pub fn list_map<'a, 'ctx, 'env>(
env: &Env<'a, 'ctx, 'env>,

View file

@ -37,6 +37,7 @@ pub enum LowLevel {
ListWalkBackwards,
ListKeepOks,
ListKeepErrs,
ListSortWith,
DictSize,
DictEmpty,
DictInsert,

View file

@ -756,6 +756,9 @@ define_builtins! {
// A caller (wrapper) that we pass to zig for it to be able to call Roc functions
20 ZIG_FUNCTION_CALLER: "#zig_function_caller"
// a caller (wrapper) for comparison
21 GENERIC_COMPARE_REF: "#generic_compare_ref"
}
1 NUM: "Num" => {
0 NUM_NUM: "Num" imported // the Num.Num type alias
@ -919,6 +922,7 @@ define_builtins! {
28 LIST_PRODUCT_MUL: "#productmul"
29 LIST_WALK_UNTIL: "walkUntil"
30 LIST_RANGE: "range"
31 LIST_SORT_WITH: "sortWith"
}
5 RESULT: "Result" => {
0 RESULT_RESULT: "Result" imported // the Result.Result type alias

View file

@ -659,6 +659,7 @@ pub fn lowlevel_borrow_signature(arena: &Bump, op: LowLevel) -> &[bool] {
ListWalk | ListWalkUntil | ListWalkBackwards => {
arena.alloc_slice_copy(&[owned, irrelevant, owned])
}
ListSortWith => arena.alloc_slice_copy(&[owned, irrelevant]),
// TODO when we have lists with capacity (if ever)
// List.append should own its first argument

View file

@ -1839,19 +1839,30 @@ fn cleanup_because_exception() {
#[test]
fn list_range() {
assert_evals_to!("List.range 0 -1", RocList::from_slice(&[]), RocList<i64>);
assert_evals_to!("List.range 0 0", RocList::from_slice(&[0]), RocList<i64>);
assert_evals_to!(
indoc!("List.range 0 -1"),
RocList::from_slice(&[]),
RocList<i64>
);
assert_evals_to!(
indoc!("List.range 0 0"),
RocList::from_slice(&[0]),
RocList<i64>
);
assert_evals_to!(
indoc!("List.range 0 5"),
"List.range 0 5",
RocList::from_slice(&[0, 1, 2, 3, 4]),
RocList<i64>
);
}
#[test]
fn list_sort_with() {
assert_evals_to!(
"List.sortWith [] Num.compare",
RocList::from_slice(&[]),
RocList<i64>
);
assert_evals_to!(
"List.sortWith [ 4,3,2,1 ] Num.compare",
RocList::from_slice(&[1, 2, 3, 4]),
RocList<i64>
);
assert_evals_to!(
"List.sortWith [ 1,2,3,4] (\\a,b -> Num.compare b a)",
RocList::from_slice(&[4, 3, 2, 1]),
RocList<i64>
);
}

View file

@ -4,6 +4,7 @@ use crate::ui::util::slice_get;
use crate::ui::util::slice_get_mut;
use bumpalo::collections::String as BumpString;
use bumpalo::Bump;
use std::fmt;
#[derive(Debug)]
pub struct CodeLines {
@ -104,3 +105,21 @@ impl Lines for CodeLines {
Ok(self.get_line(line_nr)?.chars().last())
}
}
impl fmt::Display for CodeLines {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
for row in &self.lines {
let row_str = row
.chars()
.map(|code_char| format!("'{}'", code_char))
.collect::<Vec<String>>()
.join(", ");
write!(f, "\n{}", row_str)?;
}
write!(f, " (code_lines)")?;
Ok(())
}
}

View file

@ -4,6 +4,7 @@ use crate::editor::util::index_of;
use crate::ui::text::text_pos::TextPos;
use crate::ui::ui_error::UIResult;
use crate::ui::util::{slice_get, slice_get_mut};
use std::fmt;
#[derive(Debug)]
pub struct GridNodeMap {
@ -72,3 +73,21 @@ impl GridNodeMap {
Ok(caret_pos.column - first_node_index)
}
}
impl fmt::Display for GridNodeMap {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
for row in &self.lines {
let row_str = row
.iter()
.map(|mark_node_id| format!(" {} ", mark_node_id))
.collect::<Vec<String>>()
.join(", ");
write!(f, "{}", row_str)?;
}
write!(f, " (grid_node_map)")?;
Ok(())
}
}

View file

@ -42,6 +42,8 @@ pub fn handle_keydown(
A | Home | End => pass_keydown_to_focused(&modifiers, virtual_keycode, app_model)?,
F11 => pass_keydown_to_focused(&modifiers, virtual_keycode, app_model)?,
_ => (),
}

View file

@ -307,9 +307,11 @@ fn run_event_loop(file_path_opt: Option<&Path>) -> Result<(), Box<dyn Error>> {
}
if let Some(ref rendered_wgpu) = rendered_wgpu_opt {
let borrowed_text = rendered_wgpu.text.to_borrowed();
for text_section in &rendered_wgpu.text_sections {
let borrowed_text = text_section.to_borrowed();
glyph_brush.queue(borrowed_text);
}
draw_all_rects(
&rendered_wgpu.rects,

View file

@ -12,6 +12,7 @@ use crate::lang::{
pool::{NodeId, PoolStr},
};
use bumpalo::Bump;
use std::fmt;
#[derive(Debug)]
pub enum MarkupNode {
@ -342,3 +343,49 @@ pub fn set_parent_for_all_helper(
MarkupNode::Blank { parent_id_opt, .. } => *parent_id_opt = Some(parent_node_id),
}
}
impl fmt::Display for MarkupNode {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
writeln!(
f,
"{} ({})",
self.node_type_as_string(),
self.get_content().unwrap_or_else(|_| "".to_string())
)
}
}
pub fn tree_as_string(root_node_id: MarkNodeId, mark_node_pool: &SlowPool) -> String {
let mut full_string = "\n\n(mark_node_tree)\n".to_owned();
let node = mark_node_pool.get(root_node_id);
full_string.push_str(&format!("{}", node));
tree_as_string_helper(node, 1, &mut full_string, mark_node_pool);
full_string
}
fn tree_as_string_helper(
node: &MarkupNode,
level: usize,
tree_string: &mut String,
mark_node_pool: &SlowPool,
) {
for child_id in node.get_children_ids() {
let mut full_str = std::iter::repeat("|--- ")
.take(level)
.collect::<Vec<&str>>()
.join("")
.to_owned();
let child = mark_node_pool.get(child_id);
full_str.push_str(&format!("{}", child));
tree_string.push_str(&full_str);
tree_as_string_helper(child, level + 1, tree_string, mark_node_pool);
}
}

View file

@ -7,6 +7,7 @@ pub mod main;
mod markup;
mod mvc;
mod render_ast;
mod render_debug;
mod resources;
mod slow_pool;
mod style;

View file

@ -34,6 +34,7 @@ pub struct EdModel<'a> {
pub has_focus: bool,
// Option<MarkNodeId>: MarkupNode that corresponds to caret position, Option because this MarkNodeId is only calculated when it needs to be used.
pub caret_w_select_vec: NonEmpty<(CaretWSelect, Option<MarkNodeId>)>,
pub show_debug_view: bool,
// EdModel is dirty if it has changed since the previous render.
pub dirty: bool,
}
@ -83,6 +84,7 @@ pub fn init_model<'a>(
glyph_dim_rect_opt: None,
has_focus: true,
caret_w_select_vec: NonEmpty::new((CaretWSelect::default(), None)),
show_debug_view: false,
dirty: true,
})
}

View file

@ -281,6 +281,11 @@ impl<'a> SelectableLines for EdModel<'a> {
}
Home => self.move_caret_home(modifiers),
End => self.move_caret_end(modifiers),
F11 => {
self.show_debug_view = !self.show_debug_view;
self.dirty = true;
Ok(())
}
_ => Ok(()),
}
}
@ -371,9 +376,6 @@ pub fn handle_new_char(received_char: &char, ed_model: &mut EdModel) -> EdResult
};
let ast_node_id = curr_mark_node.get_ast_node_id();
let ast_node_ref = ed_model.module.env.pool.get(ast_node_id);
match ast_node_ref {

View file

@ -2,6 +2,7 @@ use super::ed_model::EdModel;
use crate::editor::config::Config;
use crate::editor::ed_error::EdResult;
use crate::editor::render_ast::build_code_graphics;
use crate::editor::render_debug::build_debug_graphics;
use crate::graphics::primitives::rect::Rect;
use crate::ui::text::caret_w_select::make_caret_rect;
use crate::ui::text::caret_w_select::CaretWSelect;
@ -12,7 +13,7 @@ use winit::dpi::PhysicalSize;
#[derive(Debug)]
pub struct RenderedWgpu {
pub text: glyph_brush::OwnedSection,
pub text_sections: Vec<glyph_brush::OwnedSection>,
pub rects: Vec<Rect>,
}
@ -25,7 +26,9 @@ pub fn model_to_wgpu<'a>(
) -> EdResult<RenderedWgpu> {
let glyph_dim_rect = ed_model.glyph_dim_rect_opt.context(MissingGlyphDims {})?;
let (section, mut rects) = build_code_graphics(
let mut all_text_sections = Vec::new();
let (code_section, mut rects) = build_code_graphics(
ed_model.markup_node_pool.get(ed_model.markup_root_id),
size,
txt_coords,
@ -34,6 +37,8 @@ pub fn model_to_wgpu<'a>(
&ed_model.markup_node_pool,
)?;
all_text_sections.push(code_section);
let caret_w_sel_vec = ed_model
.caret_w_select_vec
.iter()
@ -45,8 +50,12 @@ pub fn model_to_wgpu<'a>(
rects.append(&mut sel_rects);
if ed_model.show_debug_view {
all_text_sections.push(build_debug_graphics(size, txt_coords, config, ed_model)?);
}
Ok(RenderedWgpu {
text: section,
text_sections: all_text_sections,
rects,
})
}

View file

@ -11,9 +11,7 @@ use crate::editor::slow_pool::MarkNodeId;
use crate::editor::syntax_highlight::HighlightStyle;
use crate::editor::util::index_of;
use crate::lang::ast::Expr2;
use crate::lang::pool::NodeId;
use crate::lang::pool::PoolStr;
use crate::lang::pool::PoolVec;
use crate::lang::pool::{NodeId, PoolStr, PoolVec};
use crate::ui::text::text_pos::TextPos;
use roc_types::subs::Variable;
use snafu::OptionExt;
@ -202,17 +200,29 @@ pub fn update_record_field(
let mut new_field_name = String::new();
first_field.0.as_str(ed_model.module.env.pool).to_string();
// -push old field name
new_field_name.push_str(first_field.0.as_str(ed_model.module.env.pool));
new_field_name.push_str(new_input);
// -clone to prevent borrow issues
let field_val_id = first_field.2;
let new_pool_str = PoolStr::new(&new_field_name, &mut ed_model.module.env.pool);
if let Expr2::InvalidLookup(_) = ed_model.module.env.pool.get(field_val_id) {
ed_model
.module
.env
.pool
.set(field_val_id, Expr2::InvalidLookup(new_pool_str));
}
let first_field_mut = record_fields
.iter_mut(ed_model.module.env.pool)
.next()
.with_context(|| RecordWithoutFields {})?;
// -update field name
first_field_mut.0 = new_pool_str;
Ok(())
@ -239,6 +249,17 @@ pub fn update_record_colon(ed_model: &mut EdModel) -> EdResult<()> {
record_var: _,
fields,
} => {
// update AST node
let new_field_val = Expr2::Blank;
let new_field_val_id = ed_model.module.env.pool.add(new_field_val);
let first_field_mut = fields
.iter_mut(ed_model.module.env.pool)
.next()
.with_context(|| RecordWithoutFields {})?;
first_field_mut.2 = new_field_val_id;
// update Markup
let record_colon = nodes::COLON;
@ -257,7 +278,7 @@ pub fn update_record_colon(ed_model: &mut EdModel) -> EdResult<()> {
.add_child_at_index(new_child_index, record_colon_node_id)?;
let record_blank_node = MarkupNode::Blank {
ast_node_id,
ast_node_id: new_field_val_id,
syn_high_style: HighlightStyle::Blank,
attributes: Attributes::new(),
parent_id_opt: Some(parent_id),
@ -288,17 +309,6 @@ pub fn update_record_colon(ed_model: &mut EdModel) -> EdResult<()> {
nodes::BLANK_PLACEHOLDER,
record_blank_node_id,
)?;
// update AST node
let new_field_val = Expr2::Blank;
let new_field_val_id = ed_model.module.env.pool.add(new_field_val);
let first_field_mut = fields
.iter_mut(ed_model.module.env.pool)
.next()
.with_context(|| RecordWithoutFields {})?;
first_field_mut.2 = new_field_val_id;
}
other => unimplemented!("TODO implement updating of Expr2 {:?}.", other),
}

View file

@ -0,0 +1,64 @@
use crate::editor::ed_error::EdResult;
use crate::editor::markup::nodes::tree_as_string;
use crate::editor::mvc::ed_model::EdModel;
use crate::graphics::colors;
use crate::graphics::colors::from_hsb;
use crate::graphics::primitives::text as gr_text;
use crate::lang::ast::expr2_to_string;
use cgmath::Vector2;
use winit::dpi::PhysicalSize;
use crate::editor::config::Config;
pub fn build_debug_graphics(
size: &PhysicalSize<u32>,
txt_coords: Vector2<f32>,
config: &Config,
ed_model: &EdModel,
) -> EdResult<glyph_brush::OwnedSection> {
let area_bounds = (size.width as f32, size.height as f32);
let layout = wgpu_glyph::Layout::default().h_align(wgpu_glyph::HorizontalAlign::Left);
let debug_txt_coords: Vector2<f32> = (txt_coords.x, txt_coords.y * 6.0).into();
let grid_node_map_text = glyph_brush::OwnedText::new(format!("{}", ed_model.grid_node_map))
.with_color(colors::to_slice(from_hsb(20, 41, 100)))
.with_scale(config.code_font_size);
let code_lines_text = glyph_brush::OwnedText::new(format!("{}", ed_model.code_lines))
.with_color(colors::to_slice(from_hsb(0, 49, 96)))
.with_scale(config.code_font_size);
let mark_node_tree_text = glyph_brush::OwnedText::new(tree_as_string(
ed_model.markup_root_id,
&ed_model.markup_node_pool,
))
.with_color(colors::to_slice(from_hsb(266, 31, 96)))
.with_scale(config.code_font_size);
let mark_node_pool_text = glyph_brush::OwnedText::new(format!("{}", ed_model.markup_node_pool))
.with_color(colors::to_slice(from_hsb(110, 45, 82)))
.with_scale(config.code_font_size);
let ast_node_text = glyph_brush::OwnedText::new(format!(
"\n\n(ast_root)\n{}",
expr2_to_string(ed_model.module.ast_root_id, ed_model.module.env.pool)
))
.with_color(colors::to_slice(from_hsb(211, 80, 100)))
.with_scale(config.code_font_size);
let section = gr_text::section_from_glyph_text(
vec![
grid_node_map_text,
code_lines_text,
mark_node_tree_text,
mark_node_pool_text,
ast_node_text,
],
debug_txt_coords.into(),
area_bounds,
layout,
);
Ok(section)
}

View file

@ -1,4 +1,5 @@
use crate::editor::markup::nodes::MarkupNode;
use std::fmt;
pub type MarkNodeId = usize;
@ -34,3 +35,21 @@ impl SlowPool {
self.nodes[node_id] = new_node;
}
}
impl fmt::Display for SlowPool {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "\n\n(mark_node_pool)\n")?;
for (index, node) in self.nodes.iter().enumerate() {
writeln!(
f,
"{}: {} ({})",
index,
node.node_type_as_string(),
node.get_content().unwrap_or_else(|_| "".to_string()),
)?;
}
Ok(())
}
}

View file

@ -1,4 +1,5 @@
use crate::lang::pattern::{Pattern2, PatternId};
use crate::lang::pool::Pool;
use crate::lang::pool::{NodeId, PoolStr, PoolVec, ShallowClone};
use crate::lang::types::{Type2, TypeId};
use arraystring::{typenum::U30, ArrayString};
@ -301,6 +302,83 @@ pub struct WhenBranch {
pub type ExprId = NodeId<Expr2>;
pub fn expr2_to_string(node_id: NodeId<Expr2>, pool: &Pool) -> String {
let mut full_string = String::new();
expr2_to_string_helper(node_id, 0, pool, &mut full_string);
full_string
}
fn get_spacing(indent_level: usize) -> String {
std::iter::repeat(" ")
.take(indent_level)
.collect::<Vec<&str>>()
.join("")
}
fn expr2_to_string_helper(
node_id: NodeId<Expr2>,
indent_level: usize,
pool: &Pool,
out_string: &mut String,
) {
let expr2 = pool.get(node_id);
out_string.push_str(&get_spacing(indent_level));
match expr2 {
Expr2::SmallStr(arr_string) => out_string.push_str(&format!(
"{}{}{}",
"SmallStr(\"",
arr_string.as_str(),
"\")",
)),
Expr2::Str(pool_str) => {
out_string.push_str(&format!("{}{}{}", "Str(\"", pool_str.as_str(pool), "\")",))
}
Expr2::Blank => out_string.push_str("Blank"),
Expr2::EmptyRecord => out_string.push_str("EmptyRecord"),
Expr2::Record { record_var, fields } => {
out_string.push_str("Record:\n");
out_string.push_str(&format!(
"{}Var({:?})\n",
get_spacing(indent_level + 1),
record_var
));
out_string.push_str(&format!("{}fields: [\n", get_spacing(indent_level + 1)));
let mut first_child = true;
for (pool_str, var, val_node_id) in fields.iter(pool) {
if !first_child {
out_string.push_str(", ")
} else {
first_child = false;
}
out_string.push_str(&format!(
"{}({}, Var({:?}), Expr2(\n",
get_spacing(indent_level + 2),
pool_str.as_str(pool),
var,
));
expr2_to_string_helper(*val_node_id, indent_level + 3, pool, out_string);
out_string.push_str(&format!("{})\n", get_spacing(indent_level + 2)));
}
out_string.push_str(&format!("{}]\n", get_spacing(indent_level + 1)));
}
Expr2::InvalidLookup(pool_str) => {
out_string.push_str(&format!("InvalidLookup({})", pool_str.as_str(pool)));
}
other => todo!("Implement for {:?}", other),
}
out_string.push('\n');
}
#[test]
fn size_of_expr() {
assert_eq!(std::mem::size_of::<Expr2>(), crate::lang::pool::NODE_BYTES);

View file

@ -205,7 +205,7 @@ impl Drop for Pool {
}
/// A string containing at most 2^32 pool-allocated bytes.
#[derive(Debug)]
#[derive(Debug, Copy, Clone)]
pub struct PoolStr {
first_node_id: NodeId<()>,
len: u32,

1
examples/cli/.gitignore vendored Normal file
View file

@ -0,0 +1 @@
echo

16
examples/cli/Echo.roc Normal file
View file

@ -0,0 +1,16 @@
app "echo"
packages { base: "platform" }
imports [ base.Task.{ await }, base.Stdout, base.Stdin ]
provides [ main ] to base
main : Task.Task {} *
main =
{} <- await (Stdout.line "What's your first name?")
firstName <- await Stdin.line
{} <- await (Stdout.line "What's your last name?")
lastName <- await Stdin.line
Stdout.line "Hi, \(firstName) \(lastName)!"

BIN
examples/cli/cli-example Executable file

Binary file not shown.

23
examples/cli/platform/Cargo.lock generated Normal file
View file

@ -0,0 +1,23 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
[[package]]
name = "host"
version = "0.1.0"
dependencies = [
"roc_std 0.1.0",
]
[[package]]
name = "libc"
version = "0.2.79"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "roc_std"
version = "0.1.0"
dependencies = [
"libc 0.2.79 (registry+https://github.com/rust-lang/crates.io-index)",
]
[metadata]
"checksum libc 0.2.79 (registry+https://github.com/rust-lang/crates.io-index)" = "2448f6066e80e3bfc792e9c98bf705b4b0fc6e8ef5b43e5889aff0eaa9c58743"

View file

@ -0,0 +1,13 @@
[package]
name = "host"
version = "0.1.0"
authors = ["Richard Feldman <oss@rtfeldman.com>"]
edition = "2018"
[lib]
crate-type = ["staticlib"]
[dependencies]
roc_std = { path = "../../../roc_std" }
[workspace]

View file

@ -0,0 +1,16 @@
platform rtfeldman/roc-cli
requires { main : Task.Task {} * } # TODO FIXME
exposes [] # TODO FIXME actually expose modules
packages {}
imports [ Task ] # TODO FIXME Task.{ Task }
provides [ mainForHost ]
effects fx.Effect
{
putChar : I64 -> Effect {},
putLine : Str -> Effect {},
getLine : Effect Str
}
mainForHost : Task.Task {} * as Fx # TODO FIXME Task.Task {} []
mainForHost = main

View file

@ -0,0 +1,6 @@
interface Stdin
exposes [ line ]
imports [ fx.Effect, Task ]
line : Task.Task Str *
line = Effect.after Effect.getLine Task.succeed # TODO FIXME Effect.getLine should suffice

View file

@ -0,0 +1,6 @@
interface Stdout
exposes [ line ]
imports [ fx.Effect, Task ] # TODO FIXME Task.{ Task }
line : Str -> Task.Task {} *
line = \str -> Effect.map (Effect.putLine str) (\_ -> Ok {})

View file

@ -0,0 +1,34 @@
interface Task
exposes [ Task, succeed, fail, await, map, putLine ]
imports [ fx.Effect ]
Task ok err : Effect.Effect (Result ok err)
succeed : val -> Task val *
succeed = \val ->
Effect.always (Ok val)
fail : err -> Task * err
fail = \val ->
Effect.always (Err val)
await : Task a err, (a -> Task b err) -> Task b err
await = \effect, transform ->
Effect.after effect \result ->
when result is
Ok a -> transform a
Err err -> Task.fail err
map : Task a err, (a -> b) -> Task b err
map = \effect, transform ->
Effect.after effect \result ->
when result is
Ok a -> Task.succeed (transform a)
Err err -> Task.fail err
putLine : Str -> Task {} *
putLine = \line -> Effect.map (Effect.putLine line) (\_ -> Ok {})

View file

@ -0,0 +1,7 @@
#include <stdio.h>
extern int rust_main();
int main() {
return rust_main();
}

View file

@ -0,0 +1,133 @@
#![allow(non_snake_case)]
use roc_std::alloca;
use roc_std::RocCallResult;
use roc_std::RocStr;
use std::alloc::Layout;
use std::time::SystemTime;
extern "C" {
#[link_name = "roc__mainForHost_1_exposed"]
fn roc_main(output: *mut u8) -> ();
#[link_name = "roc__mainForHost_1_size"]
fn roc_main_size() -> i64;
#[link_name = "roc__mainForHost_1_Fx_caller"]
fn call_Fx(
flags: &(),
function_pointer: *const u8,
closure_data: *const u8,
output: *mut u8,
) -> ();
#[link_name = "roc__mainForHost_1_Fx_size"]
fn size_Fx() -> i64;
#[link_name = "roc__mainForHost_1_Fx_result_size"]
fn size_Fx_result() -> i64;
}
#[no_mangle]
pub fn roc_fx_putChar(foo: i64) -> () {
let character = foo as u8 as char;
print!("{}", character);
()
}
#[no_mangle]
pub fn roc_fx_putLine(line: RocStr) -> () {
let bytes = line.as_slice();
let string = unsafe { std::str::from_utf8_unchecked(bytes) };
println!("{}", string);
()
}
#[no_mangle]
pub fn roc_fx_getLine() -> RocStr {
use std::io::{self, BufRead};
let stdin = io::stdin();
let line1 = stdin.lock().lines().next().unwrap().unwrap();
RocStr::from_slice(line1.as_bytes())
}
unsafe fn call_the_closure(function_pointer: *const u8, closure_data_ptr: *const u8) -> i64 {
let size = size_Fx_result() as usize;
alloca::with_stack_bytes(size, |buffer| {
let buffer: *mut std::ffi::c_void = buffer;
let buffer: *mut u8 = buffer as *mut u8;
call_Fx(
&(),
function_pointer,
closure_data_ptr as *const u8,
buffer as *mut u8,
);
let output = &*(buffer as *mut RocCallResult<()>);
match output.into() {
Ok(_) => 0,
Err(e) => panic!("failed with {}", e),
}
})
}
#[no_mangle]
pub fn rust_main() -> isize {
println!("Running Roc closure");
let start_time = SystemTime::now();
let size = unsafe { roc_main_size() } as usize;
let layout = Layout::array::<u8>(size).unwrap();
let answer = unsafe {
let buffer = std::alloc::alloc(layout);
roc_main(buffer);
let output = &*(buffer as *mut RocCallResult<()>);
match output.into() {
Ok(()) => {
let function_pointer = {
// this is a pointer to the location where the function pointer is stored
// we pass just the function pointer
let temp = buffer.offset(8) as *const i64;
(*temp) as *const u8
};
let closure_data_ptr = buffer.offset(16);
let result =
call_the_closure(function_pointer as *const u8, closure_data_ptr as *const u8);
std::alloc::dealloc(buffer, layout);
result
}
Err(msg) => {
std::alloc::dealloc(buffer, layout);
panic!("Roc failed with message: {}", msg);
}
}
};
let end_time = SystemTime::now();
let duration = end_time.duration_since(start_time).unwrap();
println!(
"Roc closure took {:.4} ms to compute this answer: {:?}",
duration.as_secs_f64() * 1000.0,
// truncate the answer, so stdout is not swamped
answer
);
// Exit code
0
}

1
examples/task/.gitignore vendored Normal file
View file

@ -0,0 +1 @@
task-example

View file

@ -1,12 +1,15 @@
app "effect-example"
app "task-example"
packages { base: "platform" }
imports [ base.Task.{ Task, after }, base.File, base.Path ]
imports [ base.Task.{ Task }, base.File, base.Path ]
provides [ main ] to base
main : Task.Task {} (File.FileReadErr [BadUtf8])
main =
when Path.fromStr "Cargo.toml" is
Ok path ->
Task.after (Task.putLine "Our Cargo.toml:") \_ ->
Task.after (File.readUtf8 path) (\line -> Task.putLine line)
{} <- Task.await (Task.putLine "Our Cargo.toml:")
line <- Task.await (File.readUtf8 path)
Task.putLine line
_ -> Task.putLine "invalid path"

View file

@ -2,6 +2,7 @@ interface File
exposes [ FileReadErr, FileOpenErr, FileWriteErr, DirReadErr, readUtf8 ]
imports [ Task.{ Task }, fx.Effect.{ after }, Path ]
# TODO FIXME should be able to import this as Path.{ Path }, but there's a bug.
Path : Path.Path
# These various file errors come from the POSIX errno values - see

View file

@ -1,5 +1,5 @@
platform folkertdev/foo
requires { main : Effect {} }
requires { main : Task {} * }
exposes []
packages {}
imports [ Task, File ]

View file

@ -1,5 +1,5 @@
interface Task
exposes [ Task, succeed, fail, after, map, putLine ]
exposes [ Task, succeed, fail, await, map, putLine ]
imports [ fx.Effect ]
@ -16,8 +16,8 @@ fail = \val ->
Effect.always (Err val)
after : Task a err, (a -> Task b err) -> Task b err
after = \effect, transform ->
await : Task a err, (a -> Task b err) -> Task b err
await = \effect, transform ->
Effect.after effect \result ->
when result is
Ok a -> transform a