mirror of
https://github.com/roc-lang/roc.git
synced 2025-09-28 14:24:45 +00:00
Merge branch 'docs-type-aliases' of github.com:rtfeldman/roc into docs-type-aliases
This commit is contained in:
commit
b5656328e6
44 changed files with 961 additions and 197 deletions
|
@ -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`).
|
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`.)
|
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
|
### Zig
|
||||||
**version: 0.7.x**
|
**version: 0.7.x**
|
||||||
|
|
||||||
|
|
|
@ -7,6 +7,7 @@ const Allocator = mem.Allocator;
|
||||||
const TAG_WIDTH = 8;
|
const TAG_WIDTH = 8;
|
||||||
|
|
||||||
const EqFn = fn (?[*]u8, ?[*]u8) callconv(.C) bool;
|
const EqFn = fn (?[*]u8, ?[*]u8) callconv(.C) bool;
|
||||||
|
const CompareFn = fn (?[*]u8, ?[*]u8, ?[*]u8) callconv(.C) u8;
|
||||||
const Opaque = ?[*]u8;
|
const Opaque = ?[*]u8;
|
||||||
|
|
||||||
const Inc = fn (?[*]u8) callconv(.C) void;
|
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 Caller2 = fn (?[*]u8, ?[*]u8, ?[*]u8, ?[*]u8) callconv(.C) void;
|
||||||
const Caller3 = fn (?[*]u8, ?[*]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 {
|
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| {
|
if (list.bytes) |source_ptr| {
|
||||||
const size = list.len();
|
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;
|
||||||
|
}
|
||||||
|
|
|
@ -20,6 +20,8 @@ comptime {
|
||||||
exportListFn(list.listRepeat, "repeat");
|
exportListFn(list.listRepeat, "repeat");
|
||||||
exportListFn(list.listAppend, "append");
|
exportListFn(list.listAppend, "append");
|
||||||
exportListFn(list.listRange, "range");
|
exportListFn(list.listRange, "range");
|
||||||
|
exportListFn(list.listReverse, "reverse");
|
||||||
|
exportListFn(list.listSortWith, "sort_with");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Dict Module
|
// Dict Module
|
||||||
|
|
|
@ -157,3 +157,9 @@ pub const RocResult = extern struct {
|
||||||
return !self.isOk();
|
return !self.isOk();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
pub const Ordering = packed enum(u8) {
|
||||||
|
EQ = 0,
|
||||||
|
GT = 1,
|
||||||
|
LT = 2,
|
||||||
|
};
|
||||||
|
|
|
@ -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_REPEAT: &str = "roc_builtins.list.repeat";
|
||||||
pub const LIST_APPEND: &str = "roc_builtins.list.append";
|
pub const LIST_APPEND: &str = "roc_builtins.list.append";
|
||||||
pub const LIST_RANGE: &str = "roc_builtins.list.range";
|
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";
|
||||||
|
|
|
@ -958,6 +958,22 @@ pub fn types() -> MutMap<Symbol, (SolvedType, Region)> {
|
||||||
top_level_function(vec![list_type(flex(TVAR1))], Box::new(bool_type())),
|
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 module
|
||||||
|
|
||||||
// Dict.hashTestOnly : Nat, v -> Nat
|
// Dict.hashTestOnly : Nat, v -> Nat
|
||||||
|
|
|
@ -91,6 +91,7 @@ pub fn builtin_defs_map(symbol: Symbol, var_store: &mut VarStore) -> Option<Def>
|
||||||
LIST_WALK => list_walk,
|
LIST_WALK => list_walk,
|
||||||
LIST_WALK_BACKWARDS => list_walk_backwards,
|
LIST_WALK_BACKWARDS => list_walk_backwards,
|
||||||
LIST_WALK_UNTIL => list_walk_until,
|
LIST_WALK_UNTIL => list_walk_until,
|
||||||
|
LIST_SORT_WITH => list_sort_with,
|
||||||
DICT_TEST_HASH => dict_hash_test_only,
|
DICT_TEST_HASH => dict_hash_test_only,
|
||||||
DICT_LEN => dict_len,
|
DICT_LEN => dict_len,
|
||||||
DICT_EMPTY => dict_empty,
|
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)
|
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
|
/// Dict.hashTestOnly : k, v -> Nat
|
||||||
fn dict_hash_test_only(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
fn dict_hash_test_only(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
||||||
lowlevel_2(symbol, LowLevel::Hash, var_store)
|
lowlevel_2(symbol, LowLevel::Hash, var_store)
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
|
/// Helpers for interacting with the zig that generates bitcode
|
||||||
use crate::debug_info_init;
|
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::convert::basic_type_from_layout;
|
||||||
use crate::llvm::refcounting::{decrement_refcount_layout, increment_refcount_layout, Mode};
|
use crate::llvm::refcounting::{decrement_refcount_layout, increment_refcount_layout, Mode};
|
||||||
use inkwell::attributes::{Attribute, AttributeLoc};
|
use inkwell::attributes::{Attribute, AttributeLoc};
|
||||||
/// Helpers for interacting with the zig that generates bitcode
|
|
||||||
use inkwell::types::{BasicType, BasicTypeEnum};
|
use inkwell::types::{BasicType, BasicTypeEnum};
|
||||||
use inkwell::values::{BasicValueEnum, CallSiteValue, FunctionValue, InstructionValue};
|
use inkwell::values::{BasicValueEnum, CallSiteValue, FunctionValue, InstructionValue};
|
||||||
use inkwell::AddressSpace;
|
use inkwell::AddressSpace;
|
||||||
|
@ -383,3 +383,101 @@ pub fn build_eq_wrapper<'a, 'ctx, 'env>(
|
||||||
|
|
||||||
function_value
|
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
|
||||||
|
}
|
||||||
|
|
|
@ -8,7 +8,7 @@ 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_errs, list_keep_if, list_keep_oks, list_len, list_map,
|
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_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::{
|
use crate::llvm::build_str::{
|
||||||
str_concat, str_count_graphemes, str_ends_with, str_from_float, str_from_int, str_from_utf8,
|
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);
|
let inplace = get_inplace_from_layout(layout);
|
||||||
|
|
||||||
list_reverse(env, parent, inplace, list, list_layout)
|
list_reverse(env, inplace, list, list_layout)
|
||||||
}
|
}
|
||||||
ListConcat => {
|
ListConcat => {
|
||||||
debug_assert_eq!(args.len(), 2);
|
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)
|
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
|
NumAbs | NumNeg | NumRound | NumSqrtUnchecked | NumLogUnchecked | NumSin | NumCos
|
||||||
| NumCeiling | NumFloor | NumToFloat | NumIsFinite | NumAtan | NumAcos | NumAsin => {
|
| NumCeiling | NumFloor | NumToFloat | NumIsFinite | NumAtan | NumAcos | NumAsin => {
|
||||||
debug_assert_eq!(args.len(), 1);
|
debug_assert_eq!(args.len(), 1);
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
#![allow(clippy::too_many_arguments)]
|
#![allow(clippy::too_many_arguments)]
|
||||||
use crate::llvm::bitcode::{
|
use crate::llvm::bitcode::{
|
||||||
build_dec_wrapper, build_eq_wrapper, build_inc_wrapper, build_transform_caller,
|
build_compare_wrapper, build_dec_wrapper, build_eq_wrapper, build_inc_wrapper,
|
||||||
call_bitcode_fn, call_void_bitcode_fn,
|
build_transform_caller, call_bitcode_fn, call_void_bitcode_fn,
|
||||||
};
|
};
|
||||||
use crate::llvm::build::{
|
use crate::llvm::build::{
|
||||||
allocate_with_refcount_help, cast_basic_basic, complex_bitcast, Env, InPlace,
|
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::builder::Builder;
|
||||||
use inkwell::context::Context;
|
use inkwell::context::Context;
|
||||||
use inkwell::types::BasicType;
|
|
||||||
use inkwell::types::{BasicTypeEnum, PointerType};
|
use inkwell::types::{BasicTypeEnum, PointerType};
|
||||||
use inkwell::values::{BasicValueEnum, FunctionValue, IntValue, PointerValue, StructValue};
|
use inkwell::values::{BasicValueEnum, FunctionValue, IntValue, PointerValue, StructValue};
|
||||||
use inkwell::{AddressSpace, IntPredicate};
|
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
|
/// List.reverse : List elem -> List elem
|
||||||
pub fn list_reverse<'a, 'ctx, 'env>(
|
pub fn list_reverse<'a, 'ctx, 'env>(
|
||||||
env: &Env<'a, 'ctx, 'env>,
|
env: &Env<'a, 'ctx, 'env>,
|
||||||
parent: FunctionValue<'ctx>,
|
_output_inplace: InPlace,
|
||||||
output_inplace: InPlace,
|
|
||||||
list: BasicValueEnum<'ctx>,
|
list: BasicValueEnum<'ctx>,
|
||||||
list_layout: &Layout<'a>,
|
list_layout: &Layout<'a>,
|
||||||
) -> BasicValueEnum<'ctx> {
|
) -> BasicValueEnum<'ctx> {
|
||||||
let builder = env.builder;
|
let (_, element_layout) = match *list_layout {
|
||||||
let ctx = env.context;
|
|
||||||
|
|
||||||
let wrapper_struct = list.into_struct_value();
|
|
||||||
let (input_inplace, element_layout) = match *list_layout {
|
|
||||||
Layout::Builtin(Builtin::EmptyList) => (
|
Layout::Builtin(Builtin::EmptyList) => (
|
||||||
InPlace::InPlace,
|
InPlace::InPlace,
|
||||||
// this pointer will never actually be dereferenced
|
// 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),
|
_ => 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 list_i128 = complex_bitcast(env.builder, list, env.context.i128_type().into(), "to_i128");
|
||||||
let ptr_type = list_type.ptr_type(AddressSpace::Generic);
|
|
||||||
|
|
||||||
let list_ptr = load_list_ptr(builder, wrapper_struct, ptr_type);
|
let element_width = env
|
||||||
let length = list_len(builder, list.into_struct_value());
|
.ptr_int()
|
||||||
|
.const_int(element_layout.stack_size(env.ptr_bytes) as u64, false);
|
||||||
|
|
||||||
match input_inplace {
|
let alignment = element_layout.alignment_bytes(env.ptr_bytes);
|
||||||
InPlace::InPlace => {
|
let alignment_iv = env.ptr_int().const_int(alignment as u64, false);
|
||||||
list_reverse_help(env, parent, input_inplace, length, list_ptr, list_ptr);
|
|
||||||
|
|
||||||
list
|
let output = call_bitcode_fn(
|
||||||
}
|
env,
|
||||||
|
&[list_i128, alignment_iv.into(), element_width.into()],
|
||||||
InPlace::Clone => {
|
&bitcode::LIST_REVERSE,
|
||||||
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)],
|
|
||||||
);
|
);
|
||||||
|
|
||||||
// build block for length 0
|
complex_bitcast(
|
||||||
{
|
env.builder,
|
||||||
builder.position_at_end(len_0_block);
|
output,
|
||||||
|
collection(env.context, env.ptr_bytes).into(),
|
||||||
// store NULL pointer there
|
"from_i128",
|
||||||
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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn list_get_unsafe<'a, 'ctx, 'env>(
|
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
|
/// List.map : List before, (before -> after) -> List after
|
||||||
pub fn list_map<'a, 'ctx, 'env>(
|
pub fn list_map<'a, 'ctx, 'env>(
|
||||||
env: &Env<'a, 'ctx, 'env>,
|
env: &Env<'a, 'ctx, 'env>,
|
||||||
|
|
|
@ -37,6 +37,7 @@ pub enum LowLevel {
|
||||||
ListWalkBackwards,
|
ListWalkBackwards,
|
||||||
ListKeepOks,
|
ListKeepOks,
|
||||||
ListKeepErrs,
|
ListKeepErrs,
|
||||||
|
ListSortWith,
|
||||||
DictSize,
|
DictSize,
|
||||||
DictEmpty,
|
DictEmpty,
|
||||||
DictInsert,
|
DictInsert,
|
||||||
|
|
|
@ -756,6 +756,9 @@ define_builtins! {
|
||||||
|
|
||||||
// A caller (wrapper) that we pass to zig for it to be able to call Roc functions
|
// A caller (wrapper) that we pass to zig for it to be able to call Roc functions
|
||||||
20 ZIG_FUNCTION_CALLER: "#zig_function_caller"
|
20 ZIG_FUNCTION_CALLER: "#zig_function_caller"
|
||||||
|
|
||||||
|
// a caller (wrapper) for comparison
|
||||||
|
21 GENERIC_COMPARE_REF: "#generic_compare_ref"
|
||||||
}
|
}
|
||||||
1 NUM: "Num" => {
|
1 NUM: "Num" => {
|
||||||
0 NUM_NUM: "Num" imported // the Num.Num type alias
|
0 NUM_NUM: "Num" imported // the Num.Num type alias
|
||||||
|
@ -919,6 +922,7 @@ define_builtins! {
|
||||||
28 LIST_PRODUCT_MUL: "#productmul"
|
28 LIST_PRODUCT_MUL: "#productmul"
|
||||||
29 LIST_WALK_UNTIL: "walkUntil"
|
29 LIST_WALK_UNTIL: "walkUntil"
|
||||||
30 LIST_RANGE: "range"
|
30 LIST_RANGE: "range"
|
||||||
|
31 LIST_SORT_WITH: "sortWith"
|
||||||
}
|
}
|
||||||
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
|
||||||
|
|
|
@ -659,6 +659,7 @@ pub fn lowlevel_borrow_signature(arena: &Bump, op: LowLevel) -> &[bool] {
|
||||||
ListWalk | ListWalkUntil | ListWalkBackwards => {
|
ListWalk | ListWalkUntil | ListWalkBackwards => {
|
||||||
arena.alloc_slice_copy(&[owned, irrelevant, owned])
|
arena.alloc_slice_copy(&[owned, irrelevant, owned])
|
||||||
}
|
}
|
||||||
|
ListSortWith => arena.alloc_slice_copy(&[owned, irrelevant]),
|
||||||
|
|
||||||
// TODO when we have lists with capacity (if ever)
|
// TODO when we have lists with capacity (if ever)
|
||||||
// List.append should own its first argument
|
// List.append should own its first argument
|
||||||
|
|
|
@ -1839,19 +1839,30 @@ fn cleanup_because_exception() {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn list_range() {
|
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!(
|
assert_evals_to!(
|
||||||
indoc!("List.range 0 -1"),
|
"List.range 0 5",
|
||||||
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"),
|
|
||||||
RocList::from_slice(&[0, 1, 2, 3, 4]),
|
RocList::from_slice(&[0, 1, 2, 3, 4]),
|
||||||
RocList<i64>
|
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>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
|
@ -4,6 +4,7 @@ use crate::ui::util::slice_get;
|
||||||
use crate::ui::util::slice_get_mut;
|
use crate::ui::util::slice_get_mut;
|
||||||
use bumpalo::collections::String as BumpString;
|
use bumpalo::collections::String as BumpString;
|
||||||
use bumpalo::Bump;
|
use bumpalo::Bump;
|
||||||
|
use std::fmt;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct CodeLines {
|
pub struct CodeLines {
|
||||||
|
@ -104,3 +105,21 @@ impl Lines for CodeLines {
|
||||||
Ok(self.get_line(line_nr)?.chars().last())
|
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(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -4,6 +4,7 @@ use crate::editor::util::index_of;
|
||||||
use crate::ui::text::text_pos::TextPos;
|
use crate::ui::text::text_pos::TextPos;
|
||||||
use crate::ui::ui_error::UIResult;
|
use crate::ui::ui_error::UIResult;
|
||||||
use crate::ui::util::{slice_get, slice_get_mut};
|
use crate::ui::util::{slice_get, slice_get_mut};
|
||||||
|
use std::fmt;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct GridNodeMap {
|
pub struct GridNodeMap {
|
||||||
|
@ -72,3 +73,21 @@ impl GridNodeMap {
|
||||||
Ok(caret_pos.column - first_node_index)
|
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(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -42,6 +42,8 @@ pub fn handle_keydown(
|
||||||
|
|
||||||
A | Home | End => pass_keydown_to_focused(&modifiers, virtual_keycode, app_model)?,
|
A | Home | End => pass_keydown_to_focused(&modifiers, virtual_keycode, app_model)?,
|
||||||
|
|
||||||
|
F11 => pass_keydown_to_focused(&modifiers, virtual_keycode, app_model)?,
|
||||||
|
|
||||||
_ => (),
|
_ => (),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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 {
|
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);
|
glyph_brush.queue(borrowed_text);
|
||||||
|
}
|
||||||
|
|
||||||
draw_all_rects(
|
draw_all_rects(
|
||||||
&rendered_wgpu.rects,
|
&rendered_wgpu.rects,
|
||||||
|
|
|
@ -12,6 +12,7 @@ use crate::lang::{
|
||||||
pool::{NodeId, PoolStr},
|
pool::{NodeId, PoolStr},
|
||||||
};
|
};
|
||||||
use bumpalo::Bump;
|
use bumpalo::Bump;
|
||||||
|
use std::fmt;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum MarkupNode {
|
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),
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -7,6 +7,7 @@ pub mod main;
|
||||||
mod markup;
|
mod markup;
|
||||||
mod mvc;
|
mod mvc;
|
||||||
mod render_ast;
|
mod render_ast;
|
||||||
|
mod render_debug;
|
||||||
mod resources;
|
mod resources;
|
||||||
mod slow_pool;
|
mod slow_pool;
|
||||||
mod style;
|
mod style;
|
||||||
|
|
|
@ -34,6 +34,7 @@ pub struct EdModel<'a> {
|
||||||
pub has_focus: bool,
|
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.
|
// 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 caret_w_select_vec: NonEmpty<(CaretWSelect, Option<MarkNodeId>)>,
|
||||||
|
pub show_debug_view: bool,
|
||||||
// EdModel is dirty if it has changed since the previous render.
|
// EdModel is dirty if it has changed since the previous render.
|
||||||
pub dirty: bool,
|
pub dirty: bool,
|
||||||
}
|
}
|
||||||
|
@ -83,6 +84,7 @@ pub fn init_model<'a>(
|
||||||
glyph_dim_rect_opt: None,
|
glyph_dim_rect_opt: None,
|
||||||
has_focus: true,
|
has_focus: true,
|
||||||
caret_w_select_vec: NonEmpty::new((CaretWSelect::default(), None)),
|
caret_w_select_vec: NonEmpty::new((CaretWSelect::default(), None)),
|
||||||
|
show_debug_view: false,
|
||||||
dirty: true,
|
dirty: true,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -281,6 +281,11 @@ impl<'a> SelectableLines for EdModel<'a> {
|
||||||
}
|
}
|
||||||
Home => self.move_caret_home(modifiers),
|
Home => self.move_caret_home(modifiers),
|
||||||
End => self.move_caret_end(modifiers),
|
End => self.move_caret_end(modifiers),
|
||||||
|
F11 => {
|
||||||
|
self.show_debug_view = !self.show_debug_view;
|
||||||
|
self.dirty = true;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
_ => 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_id = curr_mark_node.get_ast_node_id();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
let ast_node_ref = ed_model.module.env.pool.get(ast_node_id);
|
let ast_node_ref = ed_model.module.env.pool.get(ast_node_id);
|
||||||
|
|
||||||
match ast_node_ref {
|
match ast_node_ref {
|
||||||
|
|
|
@ -2,6 +2,7 @@ use super::ed_model::EdModel;
|
||||||
use crate::editor::config::Config;
|
use crate::editor::config::Config;
|
||||||
use crate::editor::ed_error::EdResult;
|
use crate::editor::ed_error::EdResult;
|
||||||
use crate::editor::render_ast::build_code_graphics;
|
use crate::editor::render_ast::build_code_graphics;
|
||||||
|
use crate::editor::render_debug::build_debug_graphics;
|
||||||
use crate::graphics::primitives::rect::Rect;
|
use crate::graphics::primitives::rect::Rect;
|
||||||
use crate::ui::text::caret_w_select::make_caret_rect;
|
use crate::ui::text::caret_w_select::make_caret_rect;
|
||||||
use crate::ui::text::caret_w_select::CaretWSelect;
|
use crate::ui::text::caret_w_select::CaretWSelect;
|
||||||
|
@ -12,7 +13,7 @@ use winit::dpi::PhysicalSize;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct RenderedWgpu {
|
pub struct RenderedWgpu {
|
||||||
pub text: glyph_brush::OwnedSection,
|
pub text_sections: Vec<glyph_brush::OwnedSection>,
|
||||||
pub rects: Vec<Rect>,
|
pub rects: Vec<Rect>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -25,7 +26,9 @@ pub fn model_to_wgpu<'a>(
|
||||||
) -> EdResult<RenderedWgpu> {
|
) -> EdResult<RenderedWgpu> {
|
||||||
let glyph_dim_rect = ed_model.glyph_dim_rect_opt.context(MissingGlyphDims {})?;
|
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),
|
ed_model.markup_node_pool.get(ed_model.markup_root_id),
|
||||||
size,
|
size,
|
||||||
txt_coords,
|
txt_coords,
|
||||||
|
@ -34,6 +37,8 @@ pub fn model_to_wgpu<'a>(
|
||||||
&ed_model.markup_node_pool,
|
&ed_model.markup_node_pool,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
|
all_text_sections.push(code_section);
|
||||||
|
|
||||||
let caret_w_sel_vec = ed_model
|
let caret_w_sel_vec = ed_model
|
||||||
.caret_w_select_vec
|
.caret_w_select_vec
|
||||||
.iter()
|
.iter()
|
||||||
|
@ -45,8 +50,12 @@ pub fn model_to_wgpu<'a>(
|
||||||
|
|
||||||
rects.append(&mut sel_rects);
|
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 {
|
Ok(RenderedWgpu {
|
||||||
text: section,
|
text_sections: all_text_sections,
|
||||||
rects,
|
rects,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,9 +11,7 @@ use crate::editor::slow_pool::MarkNodeId;
|
||||||
use crate::editor::syntax_highlight::HighlightStyle;
|
use crate::editor::syntax_highlight::HighlightStyle;
|
||||||
use crate::editor::util::index_of;
|
use crate::editor::util::index_of;
|
||||||
use crate::lang::ast::Expr2;
|
use crate::lang::ast::Expr2;
|
||||||
use crate::lang::pool::NodeId;
|
use crate::lang::pool::{NodeId, PoolStr, PoolVec};
|
||||||
use crate::lang::pool::PoolStr;
|
|
||||||
use crate::lang::pool::PoolVec;
|
|
||||||
use crate::ui::text::text_pos::TextPos;
|
use crate::ui::text::text_pos::TextPos;
|
||||||
use roc_types::subs::Variable;
|
use roc_types::subs::Variable;
|
||||||
use snafu::OptionExt;
|
use snafu::OptionExt;
|
||||||
|
@ -202,17 +200,29 @@ pub fn update_record_field(
|
||||||
|
|
||||||
let mut new_field_name = String::new();
|
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);
|
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);
|
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
|
let first_field_mut = record_fields
|
||||||
.iter_mut(ed_model.module.env.pool)
|
.iter_mut(ed_model.module.env.pool)
|
||||||
.next()
|
.next()
|
||||||
.with_context(|| RecordWithoutFields {})?;
|
.with_context(|| RecordWithoutFields {})?;
|
||||||
|
|
||||||
|
// -update field name
|
||||||
first_field_mut.0 = new_pool_str;
|
first_field_mut.0 = new_pool_str;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -239,6 +249,17 @@ pub fn update_record_colon(ed_model: &mut EdModel) -> EdResult<()> {
|
||||||
record_var: _,
|
record_var: _,
|
||||||
fields,
|
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
|
// update Markup
|
||||||
let record_colon = nodes::COLON;
|
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)?;
|
.add_child_at_index(new_child_index, record_colon_node_id)?;
|
||||||
|
|
||||||
let record_blank_node = MarkupNode::Blank {
|
let record_blank_node = MarkupNode::Blank {
|
||||||
ast_node_id,
|
ast_node_id: new_field_val_id,
|
||||||
syn_high_style: HighlightStyle::Blank,
|
syn_high_style: HighlightStyle::Blank,
|
||||||
attributes: Attributes::new(),
|
attributes: Attributes::new(),
|
||||||
parent_id_opt: Some(parent_id),
|
parent_id_opt: Some(parent_id),
|
||||||
|
@ -288,17 +309,6 @@ pub fn update_record_colon(ed_model: &mut EdModel) -> EdResult<()> {
|
||||||
nodes::BLANK_PLACEHOLDER,
|
nodes::BLANK_PLACEHOLDER,
|
||||||
record_blank_node_id,
|
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),
|
other => unimplemented!("TODO implement updating of Expr2 {:?}.", other),
|
||||||
}
|
}
|
||||||
|
|
64
editor/src/editor/render_debug.rs
Normal file
64
editor/src/editor/render_debug.rs
Normal 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)
|
||||||
|
}
|
|
@ -1,4 +1,5 @@
|
||||||
use crate::editor::markup::nodes::MarkupNode;
|
use crate::editor::markup::nodes::MarkupNode;
|
||||||
|
use std::fmt;
|
||||||
|
|
||||||
pub type MarkNodeId = usize;
|
pub type MarkNodeId = usize;
|
||||||
|
|
||||||
|
@ -34,3 +35,21 @@ impl SlowPool {
|
||||||
self.nodes[node_id] = new_node;
|
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(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
use crate::lang::pattern::{Pattern2, PatternId};
|
use crate::lang::pattern::{Pattern2, PatternId};
|
||||||
|
use crate::lang::pool::Pool;
|
||||||
use crate::lang::pool::{NodeId, PoolStr, PoolVec, ShallowClone};
|
use crate::lang::pool::{NodeId, PoolStr, PoolVec, ShallowClone};
|
||||||
use crate::lang::types::{Type2, TypeId};
|
use crate::lang::types::{Type2, TypeId};
|
||||||
use arraystring::{typenum::U30, ArrayString};
|
use arraystring::{typenum::U30, ArrayString};
|
||||||
|
@ -301,6 +302,83 @@ pub struct WhenBranch {
|
||||||
|
|
||||||
pub type ExprId = NodeId<Expr2>;
|
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]
|
#[test]
|
||||||
fn size_of_expr() {
|
fn size_of_expr() {
|
||||||
assert_eq!(std::mem::size_of::<Expr2>(), crate::lang::pool::NODE_BYTES);
|
assert_eq!(std::mem::size_of::<Expr2>(), crate::lang::pool::NODE_BYTES);
|
||||||
|
|
|
@ -205,7 +205,7 @@ impl Drop for Pool {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A string containing at most 2^32 pool-allocated bytes.
|
/// A string containing at most 2^32 pool-allocated bytes.
|
||||||
#[derive(Debug)]
|
#[derive(Debug, Copy, Clone)]
|
||||||
pub struct PoolStr {
|
pub struct PoolStr {
|
||||||
first_node_id: NodeId<()>,
|
first_node_id: NodeId<()>,
|
||||||
len: u32,
|
len: u32,
|
||||||
|
|
1
examples/cli/.gitignore
vendored
Normal file
1
examples/cli/.gitignore
vendored
Normal file
|
@ -0,0 +1 @@
|
||||||
|
echo
|
16
examples/cli/Echo.roc
Normal file
16
examples/cli/Echo.roc
Normal 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
BIN
examples/cli/cli-example
Executable file
Binary file not shown.
23
examples/cli/platform/Cargo.lock
generated
Normal file
23
examples/cli/platform/Cargo.lock
generated
Normal 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"
|
13
examples/cli/platform/Cargo.toml
Normal file
13
examples/cli/platform/Cargo.toml
Normal 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]
|
16
examples/cli/platform/Pkg-Config.roc
Normal file
16
examples/cli/platform/Pkg-Config.roc
Normal 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
|
6
examples/cli/platform/Stdin.roc
Normal file
6
examples/cli/platform/Stdin.roc
Normal 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
|
6
examples/cli/platform/Stdout.roc
Normal file
6
examples/cli/platform/Stdout.roc
Normal 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 {})
|
34
examples/cli/platform/Task.roc
Normal file
34
examples/cli/platform/Task.roc
Normal 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 {})
|
7
examples/cli/platform/host.c
Normal file
7
examples/cli/platform/host.c
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
extern int rust_main();
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
return rust_main();
|
||||||
|
}
|
133
examples/cli/platform/src/lib.rs
Normal file
133
examples/cli/platform/src/lib.rs
Normal 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
1
examples/task/.gitignore
vendored
Normal file
|
@ -0,0 +1 @@
|
||||||
|
task-example
|
|
@ -1,12 +1,15 @@
|
||||||
app "effect-example"
|
app "task-example"
|
||||||
packages { base: "platform" }
|
packages { base: "platform" }
|
||||||
imports [ base.Task.{ Task, after }, base.File, base.Path ]
|
imports [ base.Task.{ Task }, base.File, base.Path ]
|
||||||
provides [ main ] to base
|
provides [ main ] to base
|
||||||
|
|
||||||
main : Task.Task {} (File.FileReadErr [BadUtf8])
|
main : Task.Task {} (File.FileReadErr [BadUtf8])
|
||||||
main =
|
main =
|
||||||
when Path.fromStr "Cargo.toml" is
|
when Path.fromStr "Cargo.toml" is
|
||||||
Ok path ->
|
Ok path ->
|
||||||
Task.after (Task.putLine "Our Cargo.toml:") \_ ->
|
{} <- Task.await (Task.putLine "Our Cargo.toml:")
|
||||||
Task.after (File.readUtf8 path) (\line -> Task.putLine line)
|
|
||||||
|
line <- Task.await (File.readUtf8 path)
|
||||||
|
|
||||||
|
Task.putLine line
|
||||||
_ -> Task.putLine "invalid path"
|
_ -> Task.putLine "invalid path"
|
||||||
|
|
|
@ -2,6 +2,7 @@ interface File
|
||||||
exposes [ FileReadErr, FileOpenErr, FileWriteErr, DirReadErr, readUtf8 ]
|
exposes [ FileReadErr, FileOpenErr, FileWriteErr, DirReadErr, readUtf8 ]
|
||||||
imports [ Task.{ Task }, fx.Effect.{ after }, Path ]
|
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
|
Path : Path.Path
|
||||||
|
|
||||||
# These various file errors come from the POSIX errno values - see
|
# These various file errors come from the POSIX errno values - see
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
platform folkertdev/foo
|
platform folkertdev/foo
|
||||||
requires { main : Effect {} }
|
requires { main : Task {} * }
|
||||||
exposes []
|
exposes []
|
||||||
packages {}
|
packages {}
|
||||||
imports [ Task, File ]
|
imports [ Task, File ]
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
interface Task
|
interface Task
|
||||||
exposes [ Task, succeed, fail, after, map, putLine ]
|
exposes [ Task, succeed, fail, await, map, putLine ]
|
||||||
imports [ fx.Effect ]
|
imports [ fx.Effect ]
|
||||||
|
|
||||||
|
|
||||||
|
@ -16,8 +16,8 @@ fail = \val ->
|
||||||
Effect.always (Err val)
|
Effect.always (Err val)
|
||||||
|
|
||||||
|
|
||||||
after : Task a err, (a -> Task b err) -> Task b err
|
await : Task a err, (a -> Task b err) -> Task b err
|
||||||
after = \effect, transform ->
|
await = \effect, transform ->
|
||||||
Effect.after effect \result ->
|
Effect.after effect \result ->
|
||||||
when result is
|
when result is
|
||||||
Ok a -> transform a
|
Ok a -> transform a
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue