mirror of
https://github.com/roc-lang/roc.git
synced 2025-09-30 15:21:12 +00:00
Merge remote-tracking branch 'origin/trunk' into backpassing
This commit is contained in:
commit
dbb4758cb9
40 changed files with 3279 additions and 2638 deletions
84
Cargo.lock
generated
84
Cargo.lock
generated
|
@ -1690,7 +1690,7 @@ dependencies = [
|
||||||
"kernel32-sys",
|
"kernel32-sys",
|
||||||
"libc",
|
"libc",
|
||||||
"log",
|
"log",
|
||||||
"miow 0.2.2",
|
"miow",
|
||||||
"net2",
|
"net2",
|
||||||
"slab",
|
"slab",
|
||||||
"winapi 0.2.8",
|
"winapi 0.2.8",
|
||||||
|
@ -1708,29 +1708,6 @@ dependencies = [
|
||||||
"slab",
|
"slab",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "mio-named-pipes"
|
|
||||||
version = "0.1.7"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "0840c1c50fd55e521b247f949c241c9997709f23bd7f023b9762cd561e935656"
|
|
||||||
dependencies = [
|
|
||||||
"log",
|
|
||||||
"mio",
|
|
||||||
"miow 0.3.6",
|
|
||||||
"winapi 0.3.9",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "mio-uds"
|
|
||||||
version = "0.6.8"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "afcb699eb26d4332647cc848492bbc15eafb26f08d0304550d5aa1f612e066f0"
|
|
||||||
dependencies = [
|
|
||||||
"iovec",
|
|
||||||
"libc",
|
|
||||||
"mio",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "miow"
|
name = "miow"
|
||||||
version = "0.2.2"
|
version = "0.2.2"
|
||||||
|
@ -1743,16 +1720,6 @@ dependencies = [
|
||||||
"ws2_32-sys",
|
"ws2_32-sys",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "miow"
|
|
||||||
version = "0.3.6"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "5a33c1b55807fbed163481b5ba66db4b2fa6cde694a5027be10fb724206c5897"
|
|
||||||
dependencies = [
|
|
||||||
"socket2",
|
|
||||||
"winapi 0.3.9",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "naga"
|
name = "naga"
|
||||||
version = "0.2.0"
|
version = "0.2.0"
|
||||||
|
@ -2738,7 +2705,6 @@ dependencies = [
|
||||||
"pretty_assertions",
|
"pretty_assertions",
|
||||||
"quickcheck",
|
"quickcheck",
|
||||||
"quickcheck_macros",
|
"quickcheck_macros",
|
||||||
"roc_can",
|
|
||||||
"roc_collections",
|
"roc_collections",
|
||||||
"roc_module",
|
"roc_module",
|
||||||
"roc_region",
|
"roc_region",
|
||||||
|
@ -2811,7 +2777,6 @@ dependencies = [
|
||||||
"strip-ansi-escapes",
|
"strip-ansi-escapes",
|
||||||
"target-lexicon",
|
"target-lexicon",
|
||||||
"tempfile",
|
"tempfile",
|
||||||
"tokio",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -2859,9 +2824,7 @@ dependencies = [
|
||||||
"im",
|
"im",
|
||||||
"im-rc",
|
"im-rc",
|
||||||
"indoc",
|
"indoc",
|
||||||
"inkwell",
|
|
||||||
"inlinable_string",
|
"inlinable_string",
|
||||||
"itertools 0.9.0",
|
|
||||||
"libc",
|
"libc",
|
||||||
"log",
|
"log",
|
||||||
"maplit",
|
"maplit",
|
||||||
|
@ -2873,25 +2836,16 @@ dependencies = [
|
||||||
"quickcheck",
|
"quickcheck",
|
||||||
"quickcheck_macros",
|
"quickcheck_macros",
|
||||||
"rand 0.8.3",
|
"rand 0.8.3",
|
||||||
"roc_builtins",
|
|
||||||
"roc_can",
|
"roc_can",
|
||||||
"roc_collections",
|
"roc_collections",
|
||||||
"roc_constrain",
|
|
||||||
"roc_fmt",
|
"roc_fmt",
|
||||||
"roc_gen",
|
|
||||||
"roc_load",
|
|
||||||
"roc_module",
|
"roc_module",
|
||||||
"roc_mono",
|
|
||||||
"roc_parse",
|
"roc_parse",
|
||||||
"roc_problem",
|
"roc_problem",
|
||||||
"roc_region",
|
"roc_region",
|
||||||
"roc_reporting",
|
|
||||||
"roc_solve",
|
|
||||||
"roc_types",
|
"roc_types",
|
||||||
"roc_unify",
|
|
||||||
"ropey",
|
"ropey",
|
||||||
"snafu",
|
"snafu",
|
||||||
"target-lexicon",
|
|
||||||
"ven_graph",
|
"ven_graph",
|
||||||
"wgpu",
|
"wgpu",
|
||||||
"wgpu_glyph",
|
"wgpu_glyph",
|
||||||
|
@ -2915,9 +2869,7 @@ dependencies = [
|
||||||
"roc_collections",
|
"roc_collections",
|
||||||
"roc_module",
|
"roc_module",
|
||||||
"roc_parse",
|
"roc_parse",
|
||||||
"roc_problem",
|
|
||||||
"roc_region",
|
"roc_region",
|
||||||
"roc_types",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -2932,7 +2884,6 @@ dependencies = [
|
||||||
"inkwell",
|
"inkwell",
|
||||||
"inlinable_string",
|
"inlinable_string",
|
||||||
"libc",
|
"libc",
|
||||||
"libloading",
|
|
||||||
"maplit",
|
"maplit",
|
||||||
"pretty_assertions",
|
"pretty_assertions",
|
||||||
"quickcheck",
|
"quickcheck",
|
||||||
|
@ -2941,7 +2892,6 @@ dependencies = [
|
||||||
"roc_builtins",
|
"roc_builtins",
|
||||||
"roc_can",
|
"roc_can",
|
||||||
"roc_collections",
|
"roc_collections",
|
||||||
"roc_constrain",
|
|
||||||
"roc_load",
|
"roc_load",
|
||||||
"roc_module",
|
"roc_module",
|
||||||
"roc_mono",
|
"roc_mono",
|
||||||
|
@ -3054,7 +3004,6 @@ dependencies = [
|
||||||
"roc_builtins",
|
"roc_builtins",
|
||||||
"roc_can",
|
"roc_can",
|
||||||
"roc_collections",
|
"roc_collections",
|
||||||
"roc_constrain",
|
|
||||||
"roc_load",
|
"roc_load",
|
||||||
"roc_module",
|
"roc_module",
|
||||||
"roc_parse",
|
"roc_parse",
|
||||||
|
@ -3144,7 +3093,6 @@ dependencies = [
|
||||||
"roc_builtins",
|
"roc_builtins",
|
||||||
"roc_can",
|
"roc_can",
|
||||||
"roc_collections",
|
"roc_collections",
|
||||||
"roc_constrain",
|
|
||||||
"roc_load",
|
"roc_load",
|
||||||
"roc_module",
|
"roc_module",
|
||||||
"roc_parse",
|
"roc_parse",
|
||||||
|
@ -3175,8 +3123,6 @@ dependencies = [
|
||||||
"quickcheck_macros",
|
"quickcheck_macros",
|
||||||
"roc_collections",
|
"roc_collections",
|
||||||
"roc_module",
|
"roc_module",
|
||||||
"roc_parse",
|
|
||||||
"roc_problem",
|
|
||||||
"roc_region",
|
"roc_region",
|
||||||
"ven_ena",
|
"ven_ena",
|
||||||
]
|
]
|
||||||
|
@ -3405,15 +3351,6 @@ dependencies = [
|
||||||
"opaque-debug",
|
"opaque-debug",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "signal-hook-registry"
|
|
||||||
version = "1.3.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "16f1d0fef1604ba8f7a073c7e701f213e056707210e9020af4528e0101ce11a6"
|
|
||||||
dependencies = [
|
|
||||||
"libc",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "siphasher"
|
name = "siphasher"
|
||||||
version = "0.3.3"
|
version = "0.3.3"
|
||||||
|
@ -3507,17 +3444,6 @@ dependencies = [
|
||||||
"syn 1.0.60",
|
"syn 1.0.60",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "socket2"
|
|
||||||
version = "0.3.19"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "122e570113d28d773067fab24266b66753f6ea915758651696b6e35e49f88d6e"
|
|
||||||
dependencies = [
|
|
||||||
"cfg-if 1.0.0",
|
|
||||||
"libc",
|
|
||||||
"winapi 0.3.9",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "spirv_cross"
|
name = "spirv_cross"
|
||||||
version = "0.22.2"
|
version = "0.22.2"
|
||||||
|
@ -3666,7 +3592,6 @@ dependencies = [
|
||||||
"libc",
|
"libc",
|
||||||
"libloading",
|
"libloading",
|
||||||
"maplit",
|
"maplit",
|
||||||
"pretty_assertions",
|
|
||||||
"quickcheck",
|
"quickcheck",
|
||||||
"quickcheck_macros",
|
"quickcheck_macros",
|
||||||
"roc_build",
|
"roc_build",
|
||||||
|
@ -3763,17 +3688,10 @@ checksum = "6703a273949a90131b290be1fe7b039d0fc884aa1935860dfcbe056f28cd8092"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bytes",
|
"bytes",
|
||||||
"fnv",
|
"fnv",
|
||||||
"lazy_static",
|
|
||||||
"libc",
|
|
||||||
"memchr",
|
"memchr",
|
||||||
"mio",
|
|
||||||
"mio-named-pipes",
|
|
||||||
"mio-uds",
|
|
||||||
"num_cpus",
|
"num_cpus",
|
||||||
"pin-project-lite 0.1.11",
|
"pin-project-lite 0.1.11",
|
||||||
"signal-hook-registry",
|
|
||||||
"slab",
|
"slab",
|
||||||
"winapi 0.3.9",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
|
@ -58,7 +58,6 @@ im = "14" # im and im-rc should always have the same version!
|
||||||
im-rc = "14" # im and im-rc should always have the same version!
|
im-rc = "14" # im and im-rc should always have the same version!
|
||||||
bumpalo = { version = "3.2", features = ["collections"] }
|
bumpalo = { version = "3.2", features = ["collections"] }
|
||||||
inlinable_string = "0.1"
|
inlinable_string = "0.1"
|
||||||
tokio = { version = "0.2", features = ["blocking", "fs", "sync", "rt-threaded", "process", "io-driver"] }
|
|
||||||
libc = "0.2"
|
libc = "0.2"
|
||||||
libloading = "0.6"
|
libloading = "0.6"
|
||||||
|
|
||||||
|
|
|
@ -246,7 +246,7 @@ mod cli_run {
|
||||||
&example_file("benchmarks", "TestBase64.roc"),
|
&example_file("benchmarks", "TestBase64.roc"),
|
||||||
"test-base64",
|
"test-base64",
|
||||||
&[],
|
&[],
|
||||||
"SGVsbG8gV29ybGQ=\n",
|
"encoded: SGVsbG8gV29ybGQ=\ndecoded: Hello World\n",
|
||||||
true,
|
true,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -36,7 +36,14 @@ pub fn gen_from_mono_module(
|
||||||
let code_gen_start = SystemTime::now();
|
let code_gen_start = SystemTime::now();
|
||||||
|
|
||||||
for (home, (module_path, src)) in loaded.sources {
|
for (home, (module_path, src)) in loaded.sources {
|
||||||
let src_lines: Vec<&str> = src.split('\n').collect();
|
let mut src_lines: Vec<&str> = Vec::new();
|
||||||
|
|
||||||
|
if let Some((_, header_src)) = loaded.header_sources.get(&home) {
|
||||||
|
src_lines.extend(header_src.split('\n'));
|
||||||
|
src_lines.extend(src.split('\n').skip(1));
|
||||||
|
} else {
|
||||||
|
src_lines.extend(src.split('\n'));
|
||||||
|
}
|
||||||
let palette = DEFAULT_PALETTE;
|
let palette = DEFAULT_PALETTE;
|
||||||
|
|
||||||
// Report parsing and canonicalization problems
|
// Report parsing and canonicalization problems
|
||||||
|
|
|
@ -10,8 +10,6 @@ roc_collections = { path = "../collections" }
|
||||||
roc_region = { path = "../region" }
|
roc_region = { path = "../region" }
|
||||||
roc_module = { path = "../module" }
|
roc_module = { path = "../module" }
|
||||||
roc_types = { path = "../types" }
|
roc_types = { path = "../types" }
|
||||||
roc_can = { path = "../can" }
|
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
pretty_assertions = "0.5.1"
|
pretty_assertions = "0.5.1"
|
||||||
maplit = "1.0.1"
|
maplit = "1.0.1"
|
||||||
|
|
|
@ -86,21 +86,22 @@ pub const RocList = extern struct {
|
||||||
const old_length = self.length;
|
const old_length = self.length;
|
||||||
const delta_length = new_length - old_length;
|
const delta_length = new_length - old_length;
|
||||||
|
|
||||||
const data_bytes = new_capacity * slot_size;
|
const data_bytes = new_length * element_width;
|
||||||
const first_slot = allocateWithRefcount(allocator, alignment, data_bytes);
|
const first_slot = utils.allocateWithRefcount(allocator, alignment, data_bytes);
|
||||||
|
|
||||||
// transfer the memory
|
// transfer the memory
|
||||||
|
|
||||||
if (self.bytes) |source_ptr| {
|
if (self.bytes) |source_ptr| {
|
||||||
const dest_ptr = first_slot;
|
const dest_ptr = first_slot;
|
||||||
|
|
||||||
@memcpy(dest_ptr, source_ptr, old_length);
|
@memcpy(dest_ptr, source_ptr, old_length * element_width);
|
||||||
|
@memset(dest_ptr + old_length * element_width, 0, delta_length * element_width);
|
||||||
}
|
}
|
||||||
|
|
||||||
// NOTE the newly added elements are left uninitialized
|
// NOTE the newly added elements are left uninitialized
|
||||||
|
|
||||||
const result = RocList{
|
const result = RocList{
|
||||||
.dict_bytes = first_slot,
|
.bytes = first_slot,
|
||||||
.length = new_length,
|
.length = new_length,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -152,6 +153,66 @@ pub fn listMapWithIndex(list: RocList, transform: Opaque, caller: Caller2, align
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn listMap2(list1: RocList, list2: RocList, transform: Opaque, caller: Caller2, alignment: usize, a_width: usize, b_width: usize, c_width: usize, dec_a: Dec, dec_b: Dec) callconv(.C) RocList {
|
||||||
|
const output_length = std.math.min(list1.len(), list2.len());
|
||||||
|
|
||||||
|
if (list1.bytes) |source_a| {
|
||||||
|
if (list2.bytes) |source_b| {
|
||||||
|
const output = RocList.allocate(std.heap.c_allocator, alignment, output_length, c_width);
|
||||||
|
const target_ptr = output.bytes orelse unreachable;
|
||||||
|
|
||||||
|
var i: usize = 0;
|
||||||
|
while (i < output_length) : (i += 1) {
|
||||||
|
const element_a = source_a + i * a_width;
|
||||||
|
const element_b = source_b + i * b_width;
|
||||||
|
const target = target_ptr + i * c_width;
|
||||||
|
caller(transform, element_a, element_b, target);
|
||||||
|
}
|
||||||
|
|
||||||
|
// if the lists don't have equal length, we must consume the remaining elements
|
||||||
|
// In this case we consume by (recursively) decrementing the elements
|
||||||
|
if (list1.len() > output_length) {
|
||||||
|
while (i < list1.len()) : (i += 1) {
|
||||||
|
const element_a = source_a + i * a_width;
|
||||||
|
dec_a(element_a);
|
||||||
|
}
|
||||||
|
} else if (list2.len() > output_length) {
|
||||||
|
while (i < list2.len()) : (i += 1) {
|
||||||
|
const element_b = source_b + i * b_width;
|
||||||
|
dec_b(element_b);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
utils.decref(std.heap.c_allocator, alignment, list1.bytes, list1.len() * a_width);
|
||||||
|
utils.decref(std.heap.c_allocator, alignment, list2.bytes, list2.len() * b_width);
|
||||||
|
|
||||||
|
return output;
|
||||||
|
} else {
|
||||||
|
// consume list1 elements (we know there is at least one because the list1.bytes pointer is non-null
|
||||||
|
var i: usize = 0;
|
||||||
|
while (i < list1.len()) : (i += 1) {
|
||||||
|
const element_a = source_a + i * a_width;
|
||||||
|
dec_a(element_a);
|
||||||
|
}
|
||||||
|
utils.decref(std.heap.c_allocator, alignment, list1.bytes, list1.len() * a_width);
|
||||||
|
|
||||||
|
return RocList.empty();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// consume list2 elements (if any)
|
||||||
|
if (list2.bytes) |source_b| {
|
||||||
|
var i: usize = 0;
|
||||||
|
while (i < list2.len()) : (i += 1) {
|
||||||
|
const element_b = source_b + i * b_width;
|
||||||
|
dec_b(element_b);
|
||||||
|
}
|
||||||
|
utils.decref(std.heap.c_allocator, alignment, list2.bytes, list2.len() * b_width);
|
||||||
|
}
|
||||||
|
|
||||||
|
return RocList.empty();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn listKeepIf(list: RocList, transform: Opaque, caller: Caller1, alignment: usize, element_width: usize, inc: Inc, dec: Dec) callconv(.C) RocList {
|
pub fn listKeepIf(list: RocList, transform: Opaque, caller: Caller1, alignment: usize, element_width: usize, inc: Inc, dec: Dec) callconv(.C) RocList {
|
||||||
if (list.bytes) |source_ptr| {
|
if (list.bytes) |source_ptr| {
|
||||||
const size = list.len();
|
const size = list.len();
|
||||||
|
@ -246,27 +307,52 @@ pub fn listWalk(list: RocList, stepper: Opaque, stepper_caller: Caller2, accum:
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (list.isEmpty()) {
|
||||||
@memcpy(output orelse unreachable, accum orelse unreachable, accum_width);
|
@memcpy(output orelse unreachable, accum orelse unreachable, accum_width);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const alloc: [*]u8 = @ptrCast([*]u8, std.heap.c_allocator.alloc(u8, accum_width) catch unreachable);
|
||||||
|
var b1 = output orelse unreachable;
|
||||||
|
var b2 = alloc;
|
||||||
|
|
||||||
|
@memcpy(b2, accum orelse unreachable, accum_width);
|
||||||
|
|
||||||
if (list.bytes) |source_ptr| {
|
if (list.bytes) |source_ptr| {
|
||||||
var i: usize = 0;
|
var i: usize = 0;
|
||||||
const size = list.len();
|
const size = list.len();
|
||||||
while (i < size) : (i += 1) {
|
while (i < size) : (i += 1) {
|
||||||
const element = source_ptr + i * element_width;
|
const element = source_ptr + i * element_width;
|
||||||
stepper_caller(stepper, element, output, output);
|
stepper_caller(stepper, element, b2, b1);
|
||||||
|
|
||||||
|
const temp = b1;
|
||||||
|
b2 = b1;
|
||||||
|
b1 = temp;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@memcpy(output orelse unreachable, b2, accum_width);
|
||||||
|
std.heap.c_allocator.free(alloc[0..accum_width]);
|
||||||
|
|
||||||
const data_bytes = list.len() * element_width;
|
const data_bytes = list.len() * element_width;
|
||||||
utils.decref(std.heap.c_allocator, alignment, list.bytes, data_bytes);
|
utils.decref(std.heap.c_allocator, alignment, list.bytes, data_bytes);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
pub fn listWalkBackwards(list: RocList, stepper: Opaque, stepper_caller: Caller2, accum: Opaque, alignment: usize, element_width: usize, accum_width: usize, output: Opaque) callconv(.C) void {
|
pub fn listWalkBackwards(list: RocList, stepper: Opaque, stepper_caller: Caller2, accum: Opaque, alignment: usize, element_width: usize, accum_width: usize, output: Opaque) callconv(.C) void {
|
||||||
if (accum_width == 0) {
|
if (accum_width == 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (list.isEmpty()) {
|
||||||
@memcpy(output orelse unreachable, accum orelse unreachable, accum_width);
|
@memcpy(output orelse unreachable, accum orelse unreachable, accum_width);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const alloc: [*]u8 = @ptrCast([*]u8, std.heap.c_allocator.alloc(u8, accum_width) catch unreachable);
|
||||||
|
var b1 = output orelse unreachable;
|
||||||
|
var b2 = alloc;
|
||||||
|
|
||||||
|
@memcpy(b2, accum orelse unreachable, accum_width);
|
||||||
|
|
||||||
if (list.bytes) |source_ptr| {
|
if (list.bytes) |source_ptr| {
|
||||||
const size = list.len();
|
const size = list.len();
|
||||||
|
@ -274,12 +360,22 @@ pub fn listWalkBackwards(list: RocList, stepper: Opaque, stepper_caller: Caller2
|
||||||
while (i > 0) {
|
while (i > 0) {
|
||||||
i -= 1;
|
i -= 1;
|
||||||
const element = source_ptr + i * element_width;
|
const element = source_ptr + i * element_width;
|
||||||
stepper_caller(stepper, element, output, output);
|
stepper_caller(stepper, element, b2, b1);
|
||||||
|
|
||||||
|
const temp = b1;
|
||||||
|
b2 = b1;
|
||||||
|
b1 = temp;
|
||||||
}
|
}
|
||||||
|
|
||||||
const data_bytes = list.len() * element_width;
|
const data_bytes = list.len() * element_width;
|
||||||
utils.decref(std.heap.c_allocator, alignment, list.bytes, data_bytes);
|
utils.decref(std.heap.c_allocator, alignment, list.bytes, data_bytes);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@memcpy(output orelse unreachable, b2, accum_width);
|
||||||
|
std.heap.c_allocator.free(alloc[0..accum_width]);
|
||||||
|
|
||||||
|
const data_bytes = list.len() * element_width;
|
||||||
|
utils.decref(std.heap.c_allocator, alignment, list.bytes, data_bytes);
|
||||||
}
|
}
|
||||||
|
|
||||||
// List.contains : List k, k -> Bool
|
// List.contains : List k, k -> Bool
|
||||||
|
@ -324,3 +420,16 @@ pub fn listRepeat(count: usize, alignment: usize, element: Opaque, element_width
|
||||||
unreachable;
|
unreachable;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn listAppend(list: RocList, alignment: usize, element: Opaque, element_width: usize) callconv(.C) RocList {
|
||||||
|
const old_length = list.len();
|
||||||
|
var output = list.reallocate(std.heap.c_allocator, alignment, old_length + 1, element_width);
|
||||||
|
|
||||||
|
if (output.bytes) |target| {
|
||||||
|
if (element) |source| {
|
||||||
|
@memcpy(target + old_length * element_width, source, element_width);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|
|
@ -7,6 +7,7 @@ const list = @import("list.zig");
|
||||||
|
|
||||||
comptime {
|
comptime {
|
||||||
exportListFn(list.listMap, "map");
|
exportListFn(list.listMap, "map");
|
||||||
|
exportListFn(list.listMap2, "map2");
|
||||||
exportListFn(list.listMapWithIndex, "map_with_index");
|
exportListFn(list.listMapWithIndex, "map_with_index");
|
||||||
exportListFn(list.listKeepIf, "keep_if");
|
exportListFn(list.listKeepIf, "keep_if");
|
||||||
exportListFn(list.listWalk, "walk");
|
exportListFn(list.listWalk, "walk");
|
||||||
|
@ -15,6 +16,7 @@ comptime {
|
||||||
exportListFn(list.listKeepErrs, "keep_errs");
|
exportListFn(list.listKeepErrs, "keep_errs");
|
||||||
exportListFn(list.listContains, "contains");
|
exportListFn(list.listContains, "contains");
|
||||||
exportListFn(list.listRepeat, "repeat");
|
exportListFn(list.listRepeat, "repeat");
|
||||||
|
exportListFn(list.listAppend, "append");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Dict Module
|
// Dict Module
|
||||||
|
|
|
@ -63,6 +63,7 @@ pub const DICT_WALK: &str = "roc_builtins.dict.walk";
|
||||||
pub const SET_FROM_LIST: &str = "roc_builtins.dict.set_from_list";
|
pub const SET_FROM_LIST: &str = "roc_builtins.dict.set_from_list";
|
||||||
|
|
||||||
pub const LIST_MAP: &str = "roc_builtins.list.map";
|
pub const LIST_MAP: &str = "roc_builtins.list.map";
|
||||||
|
pub const LIST_MAP2: &str = "roc_builtins.list.map2";
|
||||||
pub const LIST_MAP_WITH_INDEX: &str = "roc_builtins.list.map_with_index";
|
pub const LIST_MAP_WITH_INDEX: &str = "roc_builtins.list.map_with_index";
|
||||||
pub const LIST_KEEP_IF: &str = "roc_builtins.list.keep_if";
|
pub const LIST_KEEP_IF: &str = "roc_builtins.list.keep_if";
|
||||||
pub const LIST_KEEP_OKS: &str = "roc_builtins.list.keep_oks";
|
pub const LIST_KEEP_OKS: &str = "roc_builtins.list.keep_oks";
|
||||||
|
@ -71,3 +72,4 @@ pub const LIST_WALK: &str = "roc_builtins.list.walk";
|
||||||
pub const LIST_WALK_BACKWARDS: &str = "roc_builtins.list.walk_backwards";
|
pub const LIST_WALK_BACKWARDS: &str = "roc_builtins.list.walk_backwards";
|
||||||
pub const LIST_CONTAINS: &str = "roc_builtins.list.contains";
|
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";
|
||||||
|
|
|
@ -804,6 +804,19 @@ pub fn types() -> MutMap<Symbol, (SolvedType, Region)> {
|
||||||
)
|
)
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// map2 : List a, List b, (a, b -> c) -> List c
|
||||||
|
add_type(Symbol::LIST_MAP2, {
|
||||||
|
let_tvars! {a, b, c, cvar};
|
||||||
|
top_level_function(
|
||||||
|
vec![
|
||||||
|
list_type(flex(a)),
|
||||||
|
list_type(flex(b)),
|
||||||
|
closure(vec![flex(a), flex(b)], cvar, Box::new(flex(c))),
|
||||||
|
],
|
||||||
|
Box::new(list_type(flex(c))),
|
||||||
|
)
|
||||||
|
});
|
||||||
|
|
||||||
// append : List elem, elem -> List elem
|
// append : List elem, elem -> List elem
|
||||||
add_type(
|
add_type(
|
||||||
Symbol::LIST_APPEND,
|
Symbol::LIST_APPEND,
|
||||||
|
|
|
@ -80,6 +80,7 @@ pub fn builtin_defs_map(symbol: Symbol, var_store: &mut VarStore) -> Option<Def>
|
||||||
LIST_PREPEND => list_prepend,
|
LIST_PREPEND => list_prepend,
|
||||||
LIST_JOIN => list_join,
|
LIST_JOIN => list_join,
|
||||||
LIST_MAP => list_map,
|
LIST_MAP => list_map,
|
||||||
|
LIST_MAP2 => list_map2,
|
||||||
LIST_MAP_WITH_INDEX => list_map_with_index,
|
LIST_MAP_WITH_INDEX => list_map_with_index,
|
||||||
LIST_KEEP_IF => list_keep_if,
|
LIST_KEEP_IF => list_keep_if,
|
||||||
LIST_KEEP_OKS => list_keep_oks,
|
LIST_KEEP_OKS => list_keep_oks,
|
||||||
|
@ -216,6 +217,7 @@ pub fn builtin_defs(var_store: &mut VarStore) -> MutMap<Symbol, Def> {
|
||||||
Symbol::LIST_PREPEND => list_prepend,
|
Symbol::LIST_PREPEND => list_prepend,
|
||||||
Symbol::LIST_JOIN => list_join,
|
Symbol::LIST_JOIN => list_join,
|
||||||
Symbol::LIST_MAP => list_map,
|
Symbol::LIST_MAP => list_map,
|
||||||
|
Symbol::LIST_MAP2 => list_map2,
|
||||||
Symbol::LIST_MAP_WITH_INDEX => list_map_with_index,
|
Symbol::LIST_MAP_WITH_INDEX => list_map_with_index,
|
||||||
Symbol::LIST_KEEP_IF => list_keep_if,
|
Symbol::LIST_KEEP_IF => list_keep_if,
|
||||||
Symbol::LIST_KEEP_OKS => list_keep_oks,
|
Symbol::LIST_KEEP_OKS => list_keep_oks,
|
||||||
|
@ -2115,6 +2117,11 @@ fn list_map_with_index(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
||||||
lowlevel_2(symbol, LowLevel::ListMapWithIndex, var_store)
|
lowlevel_2(symbol, LowLevel::ListMapWithIndex, var_store)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// List.map2 : List a, List b, (a, b -> c) -> List c
|
||||||
|
fn list_map2(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
||||||
|
lowlevel_3(symbol, LowLevel::ListMap2, var_store)
|
||||||
|
}
|
||||||
|
|
||||||
/// Dict.hashTestOnly : k, v -> Nat
|
/// Dict.hashTestOnly : k, v -> Nat
|
||||||
pub fn dict_hash_test_only(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
pub 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)
|
||||||
|
|
|
@ -122,8 +122,19 @@ where
|
||||||
} else {
|
} else {
|
||||||
// This is a type alias
|
// This is a type alias
|
||||||
|
|
||||||
// the should already be added to the scope when this module is canonicalized
|
// the symbol should already be added to the scope when this module is canonicalized
|
||||||
debug_assert!(scope.contains_alias(symbol));
|
debug_assert!(scope.contains_alias(symbol));
|
||||||
|
|
||||||
|
// but now we know this symbol by a different identifier, so we still need to add it to
|
||||||
|
// the scope
|
||||||
|
match scope.import(ident, symbol, region) {
|
||||||
|
Ok(()) => {
|
||||||
|
// here we do nothing special
|
||||||
|
}
|
||||||
|
Err((_shadowed_symbol, _region)) => {
|
||||||
|
panic!("TODO gracefully handle shadowing in imports.")
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -10,8 +10,6 @@ roc_collections = { path = "../collections" }
|
||||||
roc_region = { path = "../region" }
|
roc_region = { path = "../region" }
|
||||||
roc_module = { path = "../module" }
|
roc_module = { path = "../module" }
|
||||||
roc_parse = { path = "../parse" }
|
roc_parse = { path = "../parse" }
|
||||||
roc_problem = { path = "../problem" }
|
|
||||||
roc_types = { path = "../types" }
|
|
||||||
im = "14" # im and im-rc should always have the same version!
|
im = "14" # im and im-rc should always have the same version!
|
||||||
im-rc = "14" # im and im-rc should always have the same version!
|
im-rc = "14" # im and im-rc should always have the same version!
|
||||||
bumpalo = { version = "3.2", features = ["collections"] }
|
bumpalo = { version = "3.2", features = ["collections"] }
|
||||||
|
|
|
@ -12,7 +12,6 @@ roc_module = { path = "../module" }
|
||||||
roc_problem = { path = "../problem" }
|
roc_problem = { path = "../problem" }
|
||||||
roc_types = { path = "../types" }
|
roc_types = { path = "../types" }
|
||||||
roc_builtins = { path = "../builtins" }
|
roc_builtins = { path = "../builtins" }
|
||||||
roc_constrain = { path = "../constrain" }
|
|
||||||
roc_unify = { path = "../unify" }
|
roc_unify = { path = "../unify" }
|
||||||
roc_solve = { path = "../solve" }
|
roc_solve = { path = "../solve" }
|
||||||
roc_mono = { path = "../mono" }
|
roc_mono = { path = "../mono" }
|
||||||
|
@ -40,7 +39,6 @@ either = "1.6.1"
|
||||||
# This way, GitHub Actions works and nobody's builds get broken.
|
# This way, GitHub Actions works and nobody's builds get broken.
|
||||||
inkwell = { git = "https://github.com/rtfeldman/inkwell", tag = "llvm10-0.release3" }
|
inkwell = { git = "https://github.com/rtfeldman/inkwell", tag = "llvm10-0.release3" }
|
||||||
target-lexicon = "0.10"
|
target-lexicon = "0.10"
|
||||||
libloading = "0.6"
|
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
roc_can = { path = "../can" }
|
roc_can = { path = "../can" }
|
||||||
|
|
|
@ -7,8 +7,8 @@ use crate::llvm::build_hash::generic_hash;
|
||||||
use crate::llvm::build_list::{
|
use crate::llvm::build_list::{
|
||||||
allocate_list, empty_list, empty_polymorphic_list, list_append, list_concat, list_contains,
|
allocate_list, empty_list, empty_polymorphic_list, list_append, list_concat, list_contains,
|
||||||
list_get_unsafe, list_join, list_keep_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_map_with_index, list_prepend, list_repeat, list_reverse, list_set, list_single, list_sum,
|
list_map2, list_map_with_index, list_prepend, list_repeat, list_reverse, list_set, list_single,
|
||||||
list_walk, list_walk_backwards,
|
list_sum, list_walk, list_walk_backwards,
|
||||||
};
|
};
|
||||||
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,
|
||||||
|
@ -1104,7 +1104,7 @@ pub fn build_exp_expr<'a, 'ctx, 'env>(
|
||||||
let tag_field_layouts = &fields[*tag_id as usize];
|
let tag_field_layouts = &fields[*tag_id as usize];
|
||||||
|
|
||||||
for (field_symbol, tag_field_layout) in arguments.iter().zip(tag_field_layouts.iter()) {
|
for (field_symbol, tag_field_layout) in arguments.iter().zip(tag_field_layouts.iter()) {
|
||||||
let (val, val_layout) = load_symbol_and_layout(scope, field_symbol);
|
let (val, _val_layout) = load_symbol_and_layout(scope, field_symbol);
|
||||||
|
|
||||||
// Zero-sized fields have no runtime representation.
|
// Zero-sized fields have no runtime representation.
|
||||||
// The layout of the struct expects them to be dropped!
|
// The layout of the struct expects them to be dropped!
|
||||||
|
@ -1127,7 +1127,7 @@ pub fn build_exp_expr<'a, 'ctx, 'env>(
|
||||||
field_vals.push(ptr);
|
field_vals.push(ptr);
|
||||||
} else {
|
} else {
|
||||||
// this check fails for recursive tag unions, but can be helpful while debugging
|
// this check fails for recursive tag unions, but can be helpful while debugging
|
||||||
debug_assert_eq!(tag_field_layout, val_layout);
|
// debug_assert_eq!(tag_field_layout, val_layout);
|
||||||
|
|
||||||
field_vals.push(val);
|
field_vals.push(val);
|
||||||
}
|
}
|
||||||
|
@ -3719,6 +3719,33 @@ fn run_low_level<'a, 'ctx, 'env>(
|
||||||
_ => unreachable!("invalid list layout"),
|
_ => unreachable!("invalid list layout"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
ListMap2 => {
|
||||||
|
debug_assert_eq!(args.len(), 3);
|
||||||
|
|
||||||
|
let (list1, list1_layout) = load_symbol_and_layout(scope, &args[0]);
|
||||||
|
let (list2, list2_layout) = load_symbol_and_layout(scope, &args[1]);
|
||||||
|
|
||||||
|
let (func, func_layout) = load_symbol_and_layout(scope, &args[2]);
|
||||||
|
|
||||||
|
match (list1_layout, list2_layout) {
|
||||||
|
(
|
||||||
|
Layout::Builtin(Builtin::List(_, element1_layout)),
|
||||||
|
Layout::Builtin(Builtin::List(_, element2_layout)),
|
||||||
|
) => list_map2(
|
||||||
|
env,
|
||||||
|
layout_ids,
|
||||||
|
func,
|
||||||
|
func_layout,
|
||||||
|
list1,
|
||||||
|
list2,
|
||||||
|
element1_layout,
|
||||||
|
element2_layout,
|
||||||
|
),
|
||||||
|
(Layout::Builtin(Builtin::EmptyList), _)
|
||||||
|
| (_, Layout::Builtin(Builtin::EmptyList)) => empty_list(env),
|
||||||
|
_ => unreachable!("invalid list layout"),
|
||||||
|
}
|
||||||
|
}
|
||||||
ListMapWithIndex => {
|
ListMapWithIndex => {
|
||||||
// List.map : List before, (before -> after) -> List after
|
// List.map : List before, (before -> after) -> List after
|
||||||
debug_assert_eq!(args.len(), 2);
|
debug_assert_eq!(args.len(), 2);
|
||||||
|
|
|
@ -551,63 +551,51 @@ pub fn list_get_unsafe<'a, 'ctx, 'env>(
|
||||||
/// List.append : List elem, elem -> List elem
|
/// List.append : List elem, elem -> List elem
|
||||||
pub fn list_append<'a, 'ctx, 'env>(
|
pub fn list_append<'a, 'ctx, 'env>(
|
||||||
env: &Env<'a, 'ctx, 'env>,
|
env: &Env<'a, 'ctx, 'env>,
|
||||||
inplace: InPlace,
|
_inplace: InPlace,
|
||||||
original_wrapper: StructValue<'ctx>,
|
original_wrapper: StructValue<'ctx>,
|
||||||
elem: BasicValueEnum<'ctx>,
|
element: BasicValueEnum<'ctx>,
|
||||||
elem_layout: &Layout<'a>,
|
element_layout: &Layout<'a>,
|
||||||
) -> BasicValueEnum<'ctx> {
|
) -> BasicValueEnum<'ctx> {
|
||||||
let builder = env.builder;
|
let builder = env.builder;
|
||||||
let ctx = env.context;
|
|
||||||
|
|
||||||
// Load the usize length from the wrapper.
|
let list_i128 = complex_bitcast(
|
||||||
let list_len = list_len(builder, original_wrapper);
|
env.builder,
|
||||||
let elem_type = basic_type_from_layout(env.arena, ctx, elem_layout, env.ptr_bytes);
|
original_wrapper.into(),
|
||||||
let ptr_type = get_ptr_type(&elem_type, AddressSpace::Generic);
|
env.context.i128_type().into(),
|
||||||
|
"to_i128",
|
||||||
let list_ptr = load_list_ptr(builder, original_wrapper, ptr_type);
|
|
||||||
|
|
||||||
// The output list length, which is the old list length + 1
|
|
||||||
let new_list_len = env.builder.build_int_add(
|
|
||||||
ctx.i64_type().const_int(1_u64, false),
|
|
||||||
list_len,
|
|
||||||
"new_list_length",
|
|
||||||
);
|
);
|
||||||
|
|
||||||
let ptr_bytes = env.ptr_bytes;
|
let element_width = env
|
||||||
|
|
||||||
// Calculate the number of bytes we'll need to allocate.
|
|
||||||
let elem_bytes = env
|
|
||||||
.ptr_int()
|
.ptr_int()
|
||||||
.const_int(elem_layout.stack_size(env.ptr_bytes) as u64, false);
|
.const_int(element_layout.stack_size(env.ptr_bytes) as u64, false);
|
||||||
|
|
||||||
// This is the size of the list coming in, before we have added an element
|
let element_ptr = builder.build_alloca(element.get_type(), "element");
|
||||||
// to the end.
|
builder.build_store(element_ptr, element);
|
||||||
let list_size = env
|
|
||||||
.builder
|
|
||||||
.build_int_mul(elem_bytes, list_len, "mul_old_len_by_elem_bytes");
|
|
||||||
|
|
||||||
// Allocate space for the new array that we'll copy into.
|
let alignment = element_layout.alignment_bytes(env.ptr_bytes);
|
||||||
let clone_ptr = allocate_list(env, inplace, elem_layout, new_list_len);
|
let alignment_iv = env.ptr_int().const_int(alignment as u64, false);
|
||||||
|
|
||||||
// TODO check if malloc returned null; if so, runtime error for OOM!
|
let output = call_bitcode_fn(
|
||||||
|
env,
|
||||||
|
&[
|
||||||
|
list_i128,
|
||||||
|
alignment_iv.into(),
|
||||||
|
builder.build_bitcast(
|
||||||
|
element_ptr,
|
||||||
|
env.context.i8_type().ptr_type(AddressSpace::Generic),
|
||||||
|
"to_opaque",
|
||||||
|
),
|
||||||
|
element_width.into(),
|
||||||
|
],
|
||||||
|
&bitcode::LIST_APPEND,
|
||||||
|
);
|
||||||
|
|
||||||
if elem_layout.safe_to_memcpy() {
|
complex_bitcast(
|
||||||
// Copy the bytes from the original array into the new
|
env.builder,
|
||||||
// one we just malloc'd.
|
output,
|
||||||
//
|
collection(env.context, env.ptr_bytes).into(),
|
||||||
// TODO how do we decide when to do the small memcpy vs the normal one?
|
"from_i128",
|
||||||
builder
|
)
|
||||||
.build_memcpy(clone_ptr, ptr_bytes, list_ptr, ptr_bytes, list_size)
|
|
||||||
.unwrap();
|
|
||||||
} else {
|
|
||||||
panic!("TODO Cranelift currently only knows how to clone list elements that are Copy.");
|
|
||||||
}
|
|
||||||
|
|
||||||
let elem_ptr = unsafe { builder.build_in_bounds_gep(clone_ptr, &[list_len], "load_index") };
|
|
||||||
|
|
||||||
builder.build_store(elem_ptr, elem);
|
|
||||||
|
|
||||||
store_list(env, clone_ptr, new_list_len)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// List.set : List elem, Int, elem -> List elem
|
/// List.set : List elem, Int, elem -> List elem
|
||||||
|
@ -1230,6 +1218,93 @@ fn list_map_generic<'a, 'ctx, 'env>(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn list_map2<'a, 'ctx, 'env>(
|
||||||
|
env: &Env<'a, 'ctx, 'env>,
|
||||||
|
layout_ids: &mut LayoutIds<'a>,
|
||||||
|
transform: BasicValueEnum<'ctx>,
|
||||||
|
transform_layout: &Layout<'a>,
|
||||||
|
list1: BasicValueEnum<'ctx>,
|
||||||
|
list2: BasicValueEnum<'ctx>,
|
||||||
|
element1_layout: &Layout<'a>,
|
||||||
|
element2_layout: &Layout<'a>,
|
||||||
|
) -> BasicValueEnum<'ctx> {
|
||||||
|
let builder = env.builder;
|
||||||
|
|
||||||
|
let return_layout = match transform_layout {
|
||||||
|
Layout::FunctionPointer(_, ret) => ret,
|
||||||
|
Layout::Closure(_, _, ret) => ret,
|
||||||
|
_ => unreachable!("not a callable layout"),
|
||||||
|
};
|
||||||
|
|
||||||
|
let u8_ptr = env.context.i8_type().ptr_type(AddressSpace::Generic);
|
||||||
|
|
||||||
|
let list1_i128 = complex_bitcast(
|
||||||
|
env.builder,
|
||||||
|
list1,
|
||||||
|
env.context.i128_type().into(),
|
||||||
|
"to_i128",
|
||||||
|
);
|
||||||
|
|
||||||
|
let list2_i128 = complex_bitcast(
|
||||||
|
env.builder,
|
||||||
|
list2,
|
||||||
|
env.context.i128_type().into(),
|
||||||
|
"to_i128",
|
||||||
|
);
|
||||||
|
|
||||||
|
let transform_ptr = builder.build_alloca(transform.get_type(), "transform_ptr");
|
||||||
|
env.builder.build_store(transform_ptr, transform);
|
||||||
|
|
||||||
|
let argument_layouts = [element1_layout.clone(), element2_layout.clone()];
|
||||||
|
let stepper_caller =
|
||||||
|
build_transform_caller(env, layout_ids, transform_layout, &argument_layouts)
|
||||||
|
.as_global_value()
|
||||||
|
.as_pointer_value();
|
||||||
|
|
||||||
|
let a_width = env
|
||||||
|
.ptr_int()
|
||||||
|
.const_int(element1_layout.stack_size(env.ptr_bytes) as u64, false);
|
||||||
|
|
||||||
|
let b_width = env
|
||||||
|
.ptr_int()
|
||||||
|
.const_int(element2_layout.stack_size(env.ptr_bytes) as u64, false);
|
||||||
|
|
||||||
|
let c_width = env
|
||||||
|
.ptr_int()
|
||||||
|
.const_int(return_layout.stack_size(env.ptr_bytes) as u64, false);
|
||||||
|
|
||||||
|
let alignment = return_layout.alignment_bytes(env.ptr_bytes);
|
||||||
|
let alignment_iv = env.ptr_int().const_int(alignment as u64, false);
|
||||||
|
|
||||||
|
let dec_a = build_dec_wrapper(env, layout_ids, element1_layout);
|
||||||
|
let dec_b = build_dec_wrapper(env, layout_ids, element2_layout);
|
||||||
|
|
||||||
|
let output = call_bitcode_fn(
|
||||||
|
env,
|
||||||
|
&[
|
||||||
|
list1_i128,
|
||||||
|
list2_i128,
|
||||||
|
env.builder
|
||||||
|
.build_bitcast(transform_ptr, u8_ptr, "to_opaque"),
|
||||||
|
stepper_caller.into(),
|
||||||
|
alignment_iv.into(),
|
||||||
|
a_width.into(),
|
||||||
|
b_width.into(),
|
||||||
|
c_width.into(),
|
||||||
|
dec_a.as_global_value().as_pointer_value().into(),
|
||||||
|
dec_b.as_global_value().as_pointer_value().into(),
|
||||||
|
],
|
||||||
|
bitcode::LIST_MAP2,
|
||||||
|
);
|
||||||
|
|
||||||
|
complex_bitcast(
|
||||||
|
env.builder,
|
||||||
|
output,
|
||||||
|
collection(env.context, env.ptr_bytes).into(),
|
||||||
|
"from_i128",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
/// List.concat : List elem, List elem -> List elem
|
/// List.concat : List elem, List elem -> List elem
|
||||||
pub fn list_concat<'a, 'ctx, 'env>(
|
pub fn list_concat<'a, 'ctx, 'env>(
|
||||||
env: &Env<'a, 'ctx, 'env>,
|
env: &Env<'a, 'ctx, 'env>,
|
||||||
|
|
|
@ -366,6 +366,7 @@ struct ModuleCache<'a> {
|
||||||
mono_problems: MutMap<ModuleId, Vec<roc_mono::ir::MonoProblem>>,
|
mono_problems: MutMap<ModuleId, Vec<roc_mono::ir::MonoProblem>>,
|
||||||
|
|
||||||
sources: MutMap<ModuleId, (PathBuf, &'a str)>,
|
sources: MutMap<ModuleId, (PathBuf, &'a str)>,
|
||||||
|
header_sources: MutMap<ModuleId, (PathBuf, &'a str)>,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn start_phase<'a>(module_id: ModuleId, phase: Phase, state: &mut State<'a>) -> Vec<BuildTask<'a>> {
|
fn start_phase<'a>(module_id: ModuleId, phase: Phase, state: &mut State<'a>) -> Vec<BuildTask<'a>> {
|
||||||
|
@ -616,6 +617,7 @@ pub struct LoadedModule {
|
||||||
pub type_problems: MutMap<ModuleId, Vec<solve::TypeError>>,
|
pub type_problems: MutMap<ModuleId, Vec<solve::TypeError>>,
|
||||||
pub declarations_by_id: MutMap<ModuleId, Vec<Declaration>>,
|
pub declarations_by_id: MutMap<ModuleId, Vec<Declaration>>,
|
||||||
pub exposed_to_host: MutMap<Symbol, Variable>,
|
pub exposed_to_host: MutMap<Symbol, Variable>,
|
||||||
|
pub header_sources: MutMap<ModuleId, (PathBuf, Box<str>)>,
|
||||||
pub sources: MutMap<ModuleId, (PathBuf, Box<str>)>,
|
pub sources: MutMap<ModuleId, (PathBuf, Box<str>)>,
|
||||||
pub timings: MutMap<ModuleId, ModuleTiming>,
|
pub timings: MutMap<ModuleId, ModuleTiming>,
|
||||||
pub documentation: MutMap<ModuleId, ModuleDocumentation>,
|
pub documentation: MutMap<ModuleId, ModuleDocumentation>,
|
||||||
|
@ -639,7 +641,8 @@ struct ModuleHeader<'a> {
|
||||||
package_qualified_imported_modules: MutSet<PackageQualified<'a, ModuleId>>,
|
package_qualified_imported_modules: MutSet<PackageQualified<'a, ModuleId>>,
|
||||||
exposes: Vec<Symbol>,
|
exposes: Vec<Symbol>,
|
||||||
exposed_imports: MutMap<Ident, (Symbol, Region)>,
|
exposed_imports: MutMap<Ident, (Symbol, Region)>,
|
||||||
src: &'a [u8],
|
header_src: &'a str,
|
||||||
|
parse_state: roc_parse::parser::State<'a>,
|
||||||
module_timing: ModuleTiming,
|
module_timing: ModuleTiming,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -698,6 +701,7 @@ pub struct MonomorphizedModule<'a> {
|
||||||
pub mono_problems: MutMap<ModuleId, Vec<roc_mono::ir::MonoProblem>>,
|
pub mono_problems: MutMap<ModuleId, Vec<roc_mono::ir::MonoProblem>>,
|
||||||
pub procedures: MutMap<(Symbol, Layout<'a>), Proc<'a>>,
|
pub procedures: MutMap<(Symbol, Layout<'a>), Proc<'a>>,
|
||||||
pub exposed_to_host: MutMap<Symbol, Variable>,
|
pub exposed_to_host: MutMap<Symbol, Variable>,
|
||||||
|
pub header_sources: MutMap<ModuleId, (PathBuf, Box<str>)>,
|
||||||
pub sources: MutMap<ModuleId, (PathBuf, Box<str>)>,
|
pub sources: MutMap<ModuleId, (PathBuf, Box<str>)>,
|
||||||
pub timings: MutMap<ModuleId, ModuleTiming>,
|
pub timings: MutMap<ModuleId, ModuleTiming>,
|
||||||
}
|
}
|
||||||
|
@ -914,26 +918,19 @@ impl ModuleTiming {
|
||||||
end_time,
|
end_time,
|
||||||
} = self;
|
} = self;
|
||||||
|
|
||||||
end_time
|
let calculate = |t: Result<Duration, std::time::SystemTimeError>| -> Option<Duration> {
|
||||||
.duration_since(*start_time)
|
t.ok()?
|
||||||
.ok()
|
.checked_sub(*make_specializations)?
|
||||||
.and_then(|t| {
|
.checked_sub(*find_specializations)?
|
||||||
t.checked_sub(*make_specializations).and_then(|t| {
|
.checked_sub(*solve)?
|
||||||
t.checked_sub(*find_specializations).and_then(|t| {
|
.checked_sub(*constrain)?
|
||||||
t.checked_sub(*solve).and_then(|t| {
|
.checked_sub(*canonicalize)?
|
||||||
t.checked_sub(*constrain).and_then(|t| {
|
.checked_sub(*parse_body)?
|
||||||
t.checked_sub(*canonicalize).and_then(|t| {
|
.checked_sub(*parse_header)?
|
||||||
t.checked_sub(*parse_body).and_then(|t| {
|
.checked_sub(*read_roc_file)
|
||||||
t.checked_sub(*parse_header)
|
};
|
||||||
.and_then(|t| t.checked_sub(*read_roc_file))
|
|
||||||
})
|
calculate(end_time.duration_since(*start_time)).unwrap_or_else(Duration::default)
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
|
||||||
.unwrap_or_else(Duration::default)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1678,6 +1675,11 @@ fn update<'a>(
|
||||||
.exposed_symbols_by_module
|
.exposed_symbols_by_module
|
||||||
.insert(home, exposed_symbols);
|
.insert(home, exposed_symbols);
|
||||||
|
|
||||||
|
state
|
||||||
|
.module_cache
|
||||||
|
.header_sources
|
||||||
|
.insert(home, (header.module_path.clone(), header.header_src));
|
||||||
|
|
||||||
state
|
state
|
||||||
.module_cache
|
.module_cache
|
||||||
.imports
|
.imports
|
||||||
|
@ -2115,6 +2117,7 @@ fn finish_specialization(
|
||||||
type_problems,
|
type_problems,
|
||||||
can_problems,
|
can_problems,
|
||||||
sources,
|
sources,
|
||||||
|
header_sources,
|
||||||
..
|
..
|
||||||
} = module_cache;
|
} = module_cache;
|
||||||
|
|
||||||
|
@ -2123,6 +2126,11 @@ fn finish_specialization(
|
||||||
.map(|(id, (path, src))| (id, (path, src.into())))
|
.map(|(id, (path, src))| (id, (path, src.into())))
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
|
let header_sources: MutMap<ModuleId, (PathBuf, Box<str>)> = header_sources
|
||||||
|
.into_iter()
|
||||||
|
.map(|(id, (path, src))| (id, (path, src.into())))
|
||||||
|
.collect();
|
||||||
|
|
||||||
let path_to_platform = {
|
let path_to_platform = {
|
||||||
use PlatformPath::*;
|
use PlatformPath::*;
|
||||||
let package_or_path = match platform_path {
|
let package_or_path = match platform_path {
|
||||||
|
@ -2224,6 +2232,7 @@ fn finish_specialization(
|
||||||
interns,
|
interns,
|
||||||
procedures,
|
procedures,
|
||||||
sources,
|
sources,
|
||||||
|
header_sources,
|
||||||
timings: state.timings,
|
timings: state.timings,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -2251,6 +2260,13 @@ fn finish(
|
||||||
.map(|(id, (path, src))| (id, (path, src.into())))
|
.map(|(id, (path, src))| (id, (path, src.into())))
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
|
let header_sources = state
|
||||||
|
.module_cache
|
||||||
|
.header_sources
|
||||||
|
.into_iter()
|
||||||
|
.map(|(id, (path, src))| (id, (path, src.into())))
|
||||||
|
.collect();
|
||||||
|
|
||||||
LoadedModule {
|
LoadedModule {
|
||||||
module_id: state.root_id,
|
module_id: state.root_id,
|
||||||
interns,
|
interns,
|
||||||
|
@ -2259,6 +2275,7 @@ fn finish(
|
||||||
type_problems: state.module_cache.type_problems,
|
type_problems: state.module_cache.type_problems,
|
||||||
declarations_by_id: state.declarations_by_id,
|
declarations_by_id: state.declarations_by_id,
|
||||||
exposed_to_host: exposed_vars_by_symbol.into_iter().collect(),
|
exposed_to_host: exposed_vars_by_symbol.into_iter().collect(),
|
||||||
|
header_sources,
|
||||||
sources,
|
sources,
|
||||||
timings: state.timings,
|
timings: state.timings,
|
||||||
documentation,
|
documentation,
|
||||||
|
@ -2468,41 +2485,63 @@ fn parse_header<'a>(
|
||||||
module_timing.parse_header = parse_header_duration;
|
module_timing.parse_header = parse_header_duration;
|
||||||
|
|
||||||
match parsed {
|
match parsed {
|
||||||
Ok((_, ast::Module::Interface { header }, parse_state)) => Ok(send_header(
|
Ok((_, ast::Module::Interface { header }, parse_state)) => {
|
||||||
Located {
|
let header_src = unsafe {
|
||||||
|
let chomped = src_bytes.len() - parse_state.bytes.len();
|
||||||
|
std::str::from_utf8_unchecked(&src_bytes[..chomped])
|
||||||
|
};
|
||||||
|
|
||||||
|
let info = HeaderInfo {
|
||||||
|
loc_name: Located {
|
||||||
region: header.name.region,
|
region: header.name.region,
|
||||||
value: ModuleNameEnum::Interface(header.name.value),
|
value: ModuleNameEnum::Interface(header.name.value),
|
||||||
},
|
},
|
||||||
filename,
|
filename,
|
||||||
is_root_module,
|
is_root_module,
|
||||||
opt_shorthand,
|
opt_shorthand,
|
||||||
&[],
|
header_src,
|
||||||
header.exposes.into_bump_slice(),
|
packages: &[],
|
||||||
header.imports.into_bump_slice(),
|
exposes: header.exposes.into_bump_slice(),
|
||||||
None,
|
imports: header.imports.into_bump_slice(),
|
||||||
|
to_platform: None,
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(send_header(
|
||||||
|
info,
|
||||||
parse_state,
|
parse_state,
|
||||||
module_ids,
|
module_ids,
|
||||||
ident_ids_by_module,
|
ident_ids_by_module,
|
||||||
module_timing,
|
module_timing,
|
||||||
)),
|
))
|
||||||
|
}
|
||||||
Ok((_, ast::Module::App { header }, parse_state)) => {
|
Ok((_, ast::Module::App { header }, parse_state)) => {
|
||||||
let mut pkg_config_dir = filename.clone();
|
let mut pkg_config_dir = filename.clone();
|
||||||
pkg_config_dir.pop();
|
pkg_config_dir.pop();
|
||||||
|
|
||||||
|
let header_src = unsafe {
|
||||||
|
let chomped = src_bytes.len() - parse_state.bytes.len();
|
||||||
|
std::str::from_utf8_unchecked(&src_bytes[..chomped])
|
||||||
|
};
|
||||||
|
|
||||||
let packages = header.packages.into_bump_slice();
|
let packages = header.packages.into_bump_slice();
|
||||||
|
|
||||||
let (module_id, app_module_header_msg) = send_header(
|
let info = HeaderInfo {
|
||||||
Located {
|
loc_name: Located {
|
||||||
region: header.name.region,
|
region: header.name.region,
|
||||||
value: ModuleNameEnum::App(header.name.value),
|
value: ModuleNameEnum::App(header.name.value),
|
||||||
},
|
},
|
||||||
filename,
|
filename,
|
||||||
is_root_module,
|
is_root_module,
|
||||||
opt_shorthand,
|
opt_shorthand,
|
||||||
|
header_src,
|
||||||
packages,
|
packages,
|
||||||
header.provides.into_bump_slice(),
|
exposes: header.provides.into_bump_slice(),
|
||||||
header.imports.into_bump_slice(),
|
imports: header.imports.into_bump_slice(),
|
||||||
Some(header.to.value.clone()),
|
to_platform: Some(header.to.value.clone()),
|
||||||
|
};
|
||||||
|
|
||||||
|
let (module_id, app_module_header_msg) = send_header(
|
||||||
|
info,
|
||||||
parse_state,
|
parse_state,
|
||||||
module_ids.clone(),
|
module_ids.clone(),
|
||||||
ident_ids_by_module.clone(),
|
ident_ids_by_module.clone(),
|
||||||
|
@ -2673,16 +2712,22 @@ enum ModuleNameEnum<'a> {
|
||||||
PkgConfig,
|
PkgConfig,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(clippy::too_many_arguments)]
|
#[derive(Debug)]
|
||||||
fn send_header<'a>(
|
struct HeaderInfo<'a> {
|
||||||
loc_name: Located<ModuleNameEnum<'a>>,
|
loc_name: Located<ModuleNameEnum<'a>>,
|
||||||
filename: PathBuf,
|
filename: PathBuf,
|
||||||
is_root_module: bool,
|
is_root_module: bool,
|
||||||
opt_shorthand: Option<&'a str>,
|
opt_shorthand: Option<&'a str>,
|
||||||
|
header_src: &'a str,
|
||||||
packages: &'a [Located<PackageEntry<'a>>],
|
packages: &'a [Located<PackageEntry<'a>>],
|
||||||
exposes: &'a [Located<ExposesEntry<'a, &'a str>>],
|
exposes: &'a [Located<ExposesEntry<'a, &'a str>>],
|
||||||
imports: &'a [Located<ImportsEntry<'a>>],
|
imports: &'a [Located<ImportsEntry<'a>>],
|
||||||
to_platform: Option<To<'a>>,
|
to_platform: Option<To<'a>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(clippy::too_many_arguments)]
|
||||||
|
fn send_header<'a>(
|
||||||
|
info: HeaderInfo<'a>,
|
||||||
parse_state: parser::State<'a>,
|
parse_state: parser::State<'a>,
|
||||||
module_ids: Arc<Mutex<PackageModuleIds<'a>>>,
|
module_ids: Arc<Mutex<PackageModuleIds<'a>>>,
|
||||||
ident_ids_by_module: Arc<Mutex<MutMap<ModuleId, IdentIds>>>,
|
ident_ids_by_module: Arc<Mutex<MutMap<ModuleId, IdentIds>>>,
|
||||||
|
@ -2690,6 +2735,18 @@ fn send_header<'a>(
|
||||||
) -> (ModuleId, Msg<'a>) {
|
) -> (ModuleId, Msg<'a>) {
|
||||||
use ModuleNameEnum::*;
|
use ModuleNameEnum::*;
|
||||||
|
|
||||||
|
let HeaderInfo {
|
||||||
|
loc_name,
|
||||||
|
filename,
|
||||||
|
is_root_module,
|
||||||
|
opt_shorthand,
|
||||||
|
packages,
|
||||||
|
exposes,
|
||||||
|
imports,
|
||||||
|
to_platform,
|
||||||
|
header_src,
|
||||||
|
} = info;
|
||||||
|
|
||||||
let declared_name: ModuleName = match &loc_name.value {
|
let declared_name: ModuleName = match &loc_name.value {
|
||||||
PkgConfig => unreachable!(),
|
PkgConfig => unreachable!(),
|
||||||
App(_) => ModuleName::APP.into(),
|
App(_) => ModuleName::APP.into(),
|
||||||
|
@ -2872,7 +2929,8 @@ fn send_header<'a>(
|
||||||
package_qualified_imported_modules,
|
package_qualified_imported_modules,
|
||||||
deps_by_name,
|
deps_by_name,
|
||||||
exposes: exposed,
|
exposes: exposed,
|
||||||
src: parse_state.bytes,
|
header_src,
|
||||||
|
parse_state,
|
||||||
exposed_imports: scope,
|
exposed_imports: scope,
|
||||||
module_timing,
|
module_timing,
|
||||||
},
|
},
|
||||||
|
@ -3091,7 +3149,8 @@ fn send_header_two<'a>(
|
||||||
package_qualified_imported_modules,
|
package_qualified_imported_modules,
|
||||||
deps_by_name,
|
deps_by_name,
|
||||||
exposes: exposed,
|
exposes: exposed,
|
||||||
src: parse_state.bytes,
|
header_src: "#builtin effect header",
|
||||||
|
parse_state,
|
||||||
exposed_imports: scope,
|
exposed_imports: scope,
|
||||||
module_timing,
|
module_timing,
|
||||||
},
|
},
|
||||||
|
@ -3619,12 +3678,13 @@ where
|
||||||
fn parse<'a>(arena: &'a Bump, header: ModuleHeader<'a>) -> Result<Msg<'a>, LoadingProblem<'a>> {
|
fn parse<'a>(arena: &'a Bump, header: ModuleHeader<'a>) -> Result<Msg<'a>, LoadingProblem<'a>> {
|
||||||
let mut module_timing = header.module_timing;
|
let mut module_timing = header.module_timing;
|
||||||
let parse_start = SystemTime::now();
|
let parse_start = SystemTime::now();
|
||||||
let parse_state = parser::State::new_in(arena, &header.src, Attempting::Module);
|
let source = header.parse_state.bytes;
|
||||||
|
let parse_state = header.parse_state;
|
||||||
let parsed_defs = match module_defs().parse(&arena, parse_state) {
|
let parsed_defs = match module_defs().parse(&arena, parse_state) {
|
||||||
Ok((_, success, _state)) => success,
|
Ok((_, success, _state)) => success,
|
||||||
Err((_, fail, _)) => {
|
Err((_, fail, _)) => {
|
||||||
return Err(LoadingProblem::ParsingFailed(
|
return Err(LoadingProblem::ParsingFailed(
|
||||||
fail.into_parse_problem(header.module_path, header.src),
|
fail.into_parse_problem(header.module_path, source),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -3642,7 +3702,7 @@ fn parse<'a>(arena: &'a Bump, header: ModuleHeader<'a>) -> Result<Msg<'a>, Loadi
|
||||||
// SAFETY: By this point we've already incrementally verified that there
|
// SAFETY: By this point we've already incrementally verified that there
|
||||||
// are no UTF-8 errors in these bytes. If there had been any UTF-8 errors,
|
// are no UTF-8 errors in these bytes. If there had been any UTF-8 errors,
|
||||||
// we'd have bailed out before now.
|
// we'd have bailed out before now.
|
||||||
let src = unsafe { from_utf8_unchecked(header.src) };
|
let src = unsafe { from_utf8_unchecked(source) };
|
||||||
|
|
||||||
let ModuleHeader {
|
let ModuleHeader {
|
||||||
module_id,
|
module_id,
|
||||||
|
|
|
@ -27,6 +27,7 @@ pub enum LowLevel {
|
||||||
ListPrepend,
|
ListPrepend,
|
||||||
ListJoin,
|
ListJoin,
|
||||||
ListMap,
|
ListMap,
|
||||||
|
ListMap2,
|
||||||
ListMapWithIndex,
|
ListMapWithIndex,
|
||||||
ListKeepIf,
|
ListKeepIf,
|
||||||
ListWalk,
|
ListWalk,
|
||||||
|
|
|
@ -909,6 +909,7 @@ define_builtins! {
|
||||||
21 LIST_KEEP_OKS: "keepOks"
|
21 LIST_KEEP_OKS: "keepOks"
|
||||||
22 LIST_KEEP_ERRS: "keepErrs"
|
22 LIST_KEEP_ERRS: "keepErrs"
|
||||||
23 LIST_MAP_WITH_INDEX: "mapWithIndex"
|
23 LIST_MAP_WITH_INDEX: "mapWithIndex"
|
||||||
|
24 LIST_MAP2: "map2"
|
||||||
}
|
}
|
||||||
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
|
||||||
|
|
|
@ -12,7 +12,6 @@ roc_module = { path = "../module" }
|
||||||
roc_types = { path = "../types" }
|
roc_types = { path = "../types" }
|
||||||
roc_can = { path = "../can" }
|
roc_can = { path = "../can" }
|
||||||
roc_unify = { path = "../unify" }
|
roc_unify = { path = "../unify" }
|
||||||
roc_constrain = { path = "../constrain" }
|
|
||||||
roc_solve = { path = "../solve" }
|
roc_solve = { path = "../solve" }
|
||||||
roc_problem = { path = "../problem" }
|
roc_problem = { path = "../problem" }
|
||||||
ven_pretty = { path = "../../vendor/pretty" }
|
ven_pretty = { path = "../../vendor/pretty" }
|
||||||
|
@ -21,7 +20,6 @@ ven_ena = { path = "../../vendor/ena" }
|
||||||
linked-hash-map = "0.5.4"
|
linked-hash-map = "0.5.4"
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
roc_constrain = { path = "../constrain" }
|
|
||||||
roc_load= { path = "../load" }
|
roc_load= { path = "../load" }
|
||||||
roc_builtins = { path = "../builtins" }
|
roc_builtins = { path = "../builtins" }
|
||||||
roc_parse = { path = "../parse" }
|
roc_parse = { path = "../parse" }
|
||||||
|
|
|
@ -651,6 +651,7 @@ pub fn lowlevel_borrow_signature(arena: &Bump, op: LowLevel) -> &[bool] {
|
||||||
StrJoinWith => arena.alloc_slice_copy(&[borrowed, borrowed]),
|
StrJoinWith => arena.alloc_slice_copy(&[borrowed, borrowed]),
|
||||||
ListJoin => arena.alloc_slice_copy(&[irrelevant]),
|
ListJoin => arena.alloc_slice_copy(&[irrelevant]),
|
||||||
ListMap | ListMapWithIndex => arena.alloc_slice_copy(&[owned, irrelevant]),
|
ListMap | ListMapWithIndex => arena.alloc_slice_copy(&[owned, irrelevant]),
|
||||||
|
ListMap2 => arena.alloc_slice_copy(&[owned, owned, irrelevant]),
|
||||||
ListKeepIf | ListKeepOks | ListKeepErrs => arena.alloc_slice_copy(&[owned, borrowed]),
|
ListKeepIf | ListKeepOks | ListKeepErrs => arena.alloc_slice_copy(&[owned, borrowed]),
|
||||||
ListContains => arena.alloc_slice_copy(&[borrowed, irrelevant]),
|
ListContains => arena.alloc_slice_copy(&[borrowed, irrelevant]),
|
||||||
ListWalk => arena.alloc_slice_copy(&[owned, irrelevant, owned]),
|
ListWalk => arena.alloc_slice_copy(&[owned, irrelevant, owned]),
|
||||||
|
@ -659,7 +660,7 @@ pub fn lowlevel_borrow_signature(arena: &Bump, op: LowLevel) -> &[bool] {
|
||||||
|
|
||||||
// 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
|
||||||
ListAppend => arena.alloc_slice_copy(&[borrowed, owned]),
|
ListAppend => arena.alloc_slice_copy(&[owned, owned]),
|
||||||
|
|
||||||
Eq | NotEq => arena.alloc_slice_copy(&[borrowed, borrowed]),
|
Eq | NotEq => arena.alloc_slice_copy(&[borrowed, borrowed]),
|
||||||
|
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
use self::InProgressProc::*;
|
use self::InProgressProc::*;
|
||||||
use crate::exhaustive::{Ctor, Guard, RenderAs, TagId};
|
use crate::exhaustive::{Ctor, Guard, RenderAs, TagId};
|
||||||
use crate::layout::{
|
use crate::layout::{
|
||||||
BuildClosureData, Builtin, ClosureLayout, Layout, LayoutCache, LayoutProblem, UnionLayout,
|
BuildClosureData, Builtin, ClosureLayout, Layout, LayoutCache, LayoutProblem, MemoryMode,
|
||||||
WrappedVariant, TAG_SIZE,
|
UnionLayout, WrappedVariant, TAG_SIZE,
|
||||||
};
|
};
|
||||||
use bumpalo::collections::Vec;
|
use bumpalo::collections::Vec;
|
||||||
use bumpalo::Bump;
|
use bumpalo::Bump;
|
||||||
|
@ -277,6 +277,7 @@ pub struct Procs<'a> {
|
||||||
pub module_thunks: MutSet<Symbol>,
|
pub module_thunks: MutSet<Symbol>,
|
||||||
pub pending_specializations: Option<MutMap<Symbol, MutMap<Layout<'a>, PendingSpecialization>>>,
|
pub pending_specializations: Option<MutMap<Symbol, MutMap<Layout<'a>, PendingSpecialization>>>,
|
||||||
pub specialized: MutMap<(Symbol, Layout<'a>), InProgressProc<'a>>,
|
pub specialized: MutMap<(Symbol, Layout<'a>), InProgressProc<'a>>,
|
||||||
|
pub call_by_pointer_wrappers: MutMap<Symbol, Symbol>,
|
||||||
pub runtime_errors: MutMap<Symbol, &'a str>,
|
pub runtime_errors: MutMap<Symbol, &'a str>,
|
||||||
pub externals_others_need: ExternalSpecializations,
|
pub externals_others_need: ExternalSpecializations,
|
||||||
pub externals_we_need: MutMap<ModuleId, ExternalSpecializations>,
|
pub externals_we_need: MutMap<ModuleId, ExternalSpecializations>,
|
||||||
|
@ -291,6 +292,7 @@ impl<'a> Default for Procs<'a> {
|
||||||
pending_specializations: Some(MutMap::default()),
|
pending_specializations: Some(MutMap::default()),
|
||||||
specialized: MutMap::default(),
|
specialized: MutMap::default(),
|
||||||
runtime_errors: MutMap::default(),
|
runtime_errors: MutMap::default(),
|
||||||
|
call_by_pointer_wrappers: MutMap::default(),
|
||||||
externals_we_need: MutMap::default(),
|
externals_we_need: MutMap::default(),
|
||||||
externals_others_need: ExternalSpecializations::default(),
|
externals_others_need: ExternalSpecializations::default(),
|
||||||
}
|
}
|
||||||
|
@ -1741,7 +1743,7 @@ pub fn specialize_all<'a>(
|
||||||
partial_proc,
|
partial_proc,
|
||||||
) {
|
) {
|
||||||
Ok((proc, layout)) => {
|
Ok((proc, layout)) => {
|
||||||
debug_assert_eq!(outside_layout, layout);
|
debug_assert_eq!(outside_layout, layout, " in {:?}", name);
|
||||||
|
|
||||||
if let Layout::Closure(args, closure, ret) = layout {
|
if let Layout::Closure(args, closure, ret) = layout {
|
||||||
procs.specialized.remove(&(name, outside_layout));
|
procs.specialized.remove(&(name, outside_layout));
|
||||||
|
@ -3355,11 +3357,34 @@ pub fn with_hole<'a>(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
List { loc_elems, .. } if loc_elems.is_empty() => {
|
List {
|
||||||
|
loc_elems,
|
||||||
|
elem_var,
|
||||||
|
..
|
||||||
|
} if loc_elems.is_empty() => {
|
||||||
// because an empty list has an unknown element type, it is handled differently
|
// because an empty list has an unknown element type, it is handled differently
|
||||||
|
let opt_elem_layout = layout_cache.from_var(env.arena, elem_var, env.subs);
|
||||||
|
|
||||||
|
match opt_elem_layout {
|
||||||
|
Ok(elem_layout) => {
|
||||||
|
let expr = Expr::EmptyArray;
|
||||||
|
Stmt::Let(
|
||||||
|
assigned,
|
||||||
|
expr,
|
||||||
|
Layout::Builtin(Builtin::List(
|
||||||
|
MemoryMode::Refcounted,
|
||||||
|
env.arena.alloc(elem_layout),
|
||||||
|
)),
|
||||||
|
hole,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
Err(LayoutProblem::UnresolvedTypeVar(_)) => {
|
||||||
let expr = Expr::EmptyArray;
|
let expr = Expr::EmptyArray;
|
||||||
Stmt::Let(assigned, expr, Layout::Builtin(Builtin::EmptyList), hole)
|
Stmt::Let(assigned, expr, Layout::Builtin(Builtin::EmptyList), hole)
|
||||||
}
|
}
|
||||||
|
Err(LayoutProblem::Erroneous) => panic!("list element is error type"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
List {
|
List {
|
||||||
list_var,
|
list_var,
|
||||||
|
@ -3654,8 +3679,7 @@ pub fn with_hole<'a>(
|
||||||
captured_symbols.sort();
|
captured_symbols.sort();
|
||||||
let captured_symbols = captured_symbols.into_bump_slice();
|
let captured_symbols = captured_symbols.into_bump_slice();
|
||||||
|
|
||||||
procs
|
let inserted = procs.insert_anonymous(
|
||||||
.insert_anonymous(
|
|
||||||
env,
|
env,
|
||||||
name,
|
name,
|
||||||
function_type,
|
function_type,
|
||||||
|
@ -3664,8 +3688,18 @@ pub fn with_hole<'a>(
|
||||||
CapturedSymbols::Captured(captured_symbols),
|
CapturedSymbols::Captured(captured_symbols),
|
||||||
return_type,
|
return_type,
|
||||||
layout_cache,
|
layout_cache,
|
||||||
)
|
);
|
||||||
.unwrap();
|
|
||||||
|
if let Err(runtime_error) = inserted {
|
||||||
|
return Stmt::RuntimeError(env.arena.alloc(format!(
|
||||||
|
"RuntimeError {} line {} {:?}",
|
||||||
|
file!(),
|
||||||
|
line!(),
|
||||||
|
runtime_error,
|
||||||
|
)));
|
||||||
|
} else {
|
||||||
|
drop(inserted);
|
||||||
|
}
|
||||||
|
|
||||||
let closure_data_layout = closure_layout.as_block_of_memory_layout();
|
let closure_data_layout = closure_layout.as_block_of_memory_layout();
|
||||||
// define the function pointer
|
// define the function pointer
|
||||||
|
@ -4652,27 +4686,11 @@ fn from_can_when<'a>(
|
||||||
}
|
}
|
||||||
let opt_branches = to_opt_branches(env, region, branches, layout_cache);
|
let opt_branches = to_opt_branches(env, region, branches, layout_cache);
|
||||||
|
|
||||||
let cond_layout = match layout_cache.from_var(env.arena, cond_var, env.subs) {
|
let cond_layout =
|
||||||
Ok(cached) => cached,
|
return_on_layout_error!(env, layout_cache.from_var(env.arena, cond_var, env.subs));
|
||||||
Err(LayoutProblem::UnresolvedTypeVar(_)) => {
|
|
||||||
return Stmt::RuntimeError(env.arena.alloc(format!(
|
|
||||||
"UnresolvedTypeVar {} line {}",
|
|
||||||
file!(),
|
|
||||||
line!()
|
|
||||||
)));
|
|
||||||
}
|
|
||||||
Err(LayoutProblem::Erroneous) => {
|
|
||||||
return Stmt::RuntimeError(env.arena.alloc(format!(
|
|
||||||
"Erroneous {} line {}",
|
|
||||||
file!(),
|
|
||||||
line!()
|
|
||||||
)));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
let ret_layout = layout_cache
|
let ret_layout =
|
||||||
.from_var(env.arena, expr_var, env.subs)
|
return_on_layout_error!(env, layout_cache.from_var(env.arena, expr_var, env.subs));
|
||||||
.unwrap_or_else(|err| panic!("TODO turn this into a RuntimeError {:?}", err));
|
|
||||||
|
|
||||||
let arena = env.arena;
|
let arena = env.arena;
|
||||||
let it = opt_branches
|
let it = opt_branches
|
||||||
|
@ -5733,6 +5751,12 @@ fn call_by_pointer<'a>(
|
||||||
match layout {
|
match layout {
|
||||||
Layout::FunctionPointer(arg_layouts, ret_layout) if !is_thunk => {
|
Layout::FunctionPointer(arg_layouts, ret_layout) if !is_thunk => {
|
||||||
if arg_layouts.iter().any(|l| l.contains_refcounted()) {
|
if arg_layouts.iter().any(|l| l.contains_refcounted()) {
|
||||||
|
if let Some(wrapper) = procs.call_by_pointer_wrappers.get(&symbol) {
|
||||||
|
if procs.specialized.contains_key(&(*wrapper, layout.clone())) {
|
||||||
|
return Expr::FunctionPointer(*wrapper, layout);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let name = env.unique_symbol();
|
let name = env.unique_symbol();
|
||||||
let mut args = Vec::with_capacity_in(arg_layouts.len(), env.arena);
|
let mut args = Vec::with_capacity_in(arg_layouts.len(), env.arena);
|
||||||
let mut arg_symbols = Vec::with_capacity_in(arg_layouts.len(), env.arena);
|
let mut arg_symbols = Vec::with_capacity_in(arg_layouts.len(), env.arena);
|
||||||
|
@ -5777,6 +5801,9 @@ fn call_by_pointer<'a>(
|
||||||
procs
|
procs
|
||||||
.specialized
|
.specialized
|
||||||
.insert((name, layout.clone()), InProgressProc::Done(proc));
|
.insert((name, layout.clone()), InProgressProc::Done(proc));
|
||||||
|
|
||||||
|
procs.call_by_pointer_wrappers.insert(symbol, name);
|
||||||
|
|
||||||
Expr::FunctionPointer(name, layout)
|
Expr::FunctionPointer(name, layout)
|
||||||
} else {
|
} else {
|
||||||
// if none of the arguments is refcounted, then owning the arguments has no
|
// if none of the arguments is refcounted, then owning the arguments has no
|
||||||
|
@ -5786,6 +5813,12 @@ fn call_by_pointer<'a>(
|
||||||
}
|
}
|
||||||
Layout::FunctionPointer(arg_layouts, ret_layout) => {
|
Layout::FunctionPointer(arg_layouts, ret_layout) => {
|
||||||
if arg_layouts.iter().any(|l| l.contains_refcounted()) {
|
if arg_layouts.iter().any(|l| l.contains_refcounted()) {
|
||||||
|
if let Some(wrapper) = procs.call_by_pointer_wrappers.get(&symbol) {
|
||||||
|
if procs.specialized.contains_key(&(*wrapper, layout.clone())) {
|
||||||
|
return Expr::FunctionPointer(*wrapper, layout);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let name = env.unique_symbol();
|
let name = env.unique_symbol();
|
||||||
let mut args = Vec::with_capacity_in(arg_layouts.len(), env.arena);
|
let mut args = Vec::with_capacity_in(arg_layouts.len(), env.arena);
|
||||||
let mut arg_symbols = Vec::with_capacity_in(arg_layouts.len(), env.arena);
|
let mut arg_symbols = Vec::with_capacity_in(arg_layouts.len(), env.arena);
|
||||||
|
@ -5834,6 +5867,9 @@ fn call_by_pointer<'a>(
|
||||||
procs
|
procs
|
||||||
.specialized
|
.specialized
|
||||||
.insert((name, layout.clone()), InProgressProc::Done(proc));
|
.insert((name, layout.clone()), InProgressProc::Done(proc));
|
||||||
|
|
||||||
|
procs.call_by_pointer_wrappers.insert(symbol, name);
|
||||||
|
|
||||||
Expr::FunctionPointer(name, layout)
|
Expr::FunctionPointer(name, layout)
|
||||||
} else {
|
} else {
|
||||||
// if none of the arguments is refcounted, then owning the arguments has no
|
// if none of the arguments is refcounted, then owning the arguments has no
|
||||||
|
@ -6201,8 +6237,7 @@ fn call_by_name<'a>(
|
||||||
|
|
||||||
procs.runtime_errors.insert(proc_name, error_msg);
|
procs.runtime_errors.insert(proc_name, error_msg);
|
||||||
|
|
||||||
panic!();
|
Stmt::RuntimeError(error_msg)
|
||||||
// Stmt::RuntimeError(error_msg)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -683,7 +683,6 @@ mod test_mono {
|
||||||
let Test.9 = 2i64;
|
let Test.9 = 2i64;
|
||||||
let Test.4 = Array [Test.8, Test.9];
|
let Test.4 = Array [Test.8, Test.9];
|
||||||
let Test.3 = CallByName Test.1 Test.4;
|
let Test.3 = CallByName Test.1 Test.4;
|
||||||
dec Test.4;
|
|
||||||
ret Test.3;
|
ret Test.3;
|
||||||
"#
|
"#
|
||||||
),
|
),
|
||||||
|
@ -709,7 +708,6 @@ mod test_mono {
|
||||||
let Test.2 = Array [Test.5];
|
let Test.2 = Array [Test.5];
|
||||||
let Test.3 = 2i64;
|
let Test.3 = 2i64;
|
||||||
let Test.1 = CallByName List.5 Test.2 Test.3;
|
let Test.1 = CallByName List.5 Test.2 Test.3;
|
||||||
dec Test.2;
|
|
||||||
ret Test.1;
|
ret Test.1;
|
||||||
"#
|
"#
|
||||||
),
|
),
|
||||||
|
|
|
@ -246,7 +246,7 @@ fn to_expr_report<'a>(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
EExpr::Start(row, col) => {
|
EExpr::Start(row, col) | EExpr::IndentStart(row, col) => {
|
||||||
let (context_row, context_col, a_thing) = match context {
|
let (context_row, context_col, a_thing) = match context {
|
||||||
Context::InNode(node, r, c, _) => match node {
|
Context::InNode(node, r, c, _) => match node {
|
||||||
Node::WhenCondition | Node::WhenBranch | Node::WhenIfGuard => (
|
Node::WhenCondition | Node::WhenBranch | Node::WhenIfGuard => (
|
||||||
|
|
|
@ -146,12 +146,12 @@ pub const DEFAULT_PALETTE: Palette = Palette {
|
||||||
};
|
};
|
||||||
|
|
||||||
pub const RED_CODE: &str = "\u{001b}[31m";
|
pub const RED_CODE: &str = "\u{001b}[31m";
|
||||||
pub const WHITE_CODE: &str = "\u{001b}[37m";
|
pub const GREEN_CODE: &str = "\u{001b}[32m";
|
||||||
pub const BLUE_CODE: &str = "\u{001b}[34m";
|
|
||||||
pub const YELLOW_CODE: &str = "\u{001b}[33m";
|
pub const YELLOW_CODE: &str = "\u{001b}[33m";
|
||||||
pub const GREEN_CODE: &str = "\u{001b}[42m";
|
pub const BLUE_CODE: &str = "\u{001b}[34m";
|
||||||
pub const CYAN_CODE: &str = "\u{001b}[36m";
|
|
||||||
pub const MAGENTA_CODE: &str = "\u{001b}[35m";
|
pub const MAGENTA_CODE: &str = "\u{001b}[35m";
|
||||||
|
pub const CYAN_CODE: &str = "\u{001b}[36m";
|
||||||
|
pub const WHITE_CODE: &str = "\u{001b}[37m";
|
||||||
|
|
||||||
pub const BOLD_CODE: &str = "\u{001b}[1m";
|
pub const BOLD_CODE: &str = "\u{001b}[1m";
|
||||||
|
|
||||||
|
|
|
@ -5572,4 +5572,38 @@ mod test_reporting {
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[ignore]
|
||||||
|
fn double_binop() {
|
||||||
|
report_problem_as(
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
key >= 97 && <= 122
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[ignore]
|
||||||
|
fn case_of() {
|
||||||
|
report_problem_as(
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
case 1 of
|
||||||
|
1 -> True
|
||||||
|
_ -> False
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,11 +12,8 @@ roc_module = { path = "../module" }
|
||||||
roc_types = { path = "../types" }
|
roc_types = { path = "../types" }
|
||||||
roc_can = { path = "../can" }
|
roc_can = { path = "../can" }
|
||||||
roc_unify = { path = "../unify" }
|
roc_unify = { path = "../unify" }
|
||||||
roc_constrain = { path = "../constrain" }
|
|
||||||
roc_builtins = { path = "../builtins" }
|
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
roc_constrain = { path = "../constrain" }
|
|
||||||
roc_load = { path = "../load" }
|
roc_load = { path = "../load" }
|
||||||
roc_builtins = { path = "../builtins" }
|
roc_builtins = { path = "../builtins" }
|
||||||
roc_problem = { path = "../problem" }
|
roc_problem = { path = "../problem" }
|
||||||
|
|
|
@ -29,7 +29,6 @@ bumpalo = { version = "3.2", features = ["collections"] }
|
||||||
inlinable_string = "0.1"
|
inlinable_string = "0.1"
|
||||||
either = "1.6.1"
|
either = "1.6.1"
|
||||||
indoc = "0.3.3"
|
indoc = "0.3.3"
|
||||||
pretty_assertions = "0.5.1"
|
|
||||||
libc = "0.2"
|
libc = "0.2"
|
||||||
# NOTE: rtfeldman/inkwell is a fork of TheDan64/inkwell which does not change anything.
|
# NOTE: rtfeldman/inkwell is a fork of TheDan64/inkwell which does not change anything.
|
||||||
#
|
#
|
||||||
|
|
|
@ -568,6 +568,35 @@ fn list_map_closure() {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn list_map2_pair() {
|
||||||
|
assert_evals_to!(
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
List.map2 [1,2,3] [3,2,1] (\a,b -> Pair a b)
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
RocList::from_slice(&[(1, 3), (2, 2), (3, 1)]),
|
||||||
|
RocList<(i64, i64)>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn list_map2_different_lengths() {
|
||||||
|
assert_evals_to!(
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
List.map2
|
||||||
|
["a", "b", "lllllllllllllongnggg" ]
|
||||||
|
["b"]
|
||||||
|
Str.concat
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
RocList::from_slice(&[RocStr::from_slice("ab".as_bytes()),]),
|
||||||
|
RocList<RocStr>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn list_join_empty_list() {
|
fn list_join_empty_list() {
|
||||||
assert_evals_to!("List.join []", RocList::from_slice(&[]), RocList<i64>);
|
assert_evals_to!("List.join []", RocList::from_slice(&[]), RocList<i64>);
|
||||||
|
|
|
@ -1,11 +1,9 @@
|
||||||
#[cfg(test)]
|
#![cfg(test)]
|
||||||
mod gen_primitives {
|
|
||||||
|
|
||||||
use crate::assert_evals_to;
|
use crate::assert_evals_to;
|
||||||
use crate::assert_llvm_evals_to;
|
use crate::assert_llvm_evals_to;
|
||||||
use crate::assert_non_opt_evals_to;
|
use crate::assert_non_opt_evals_to;
|
||||||
use indoc::indoc;
|
use indoc::indoc;
|
||||||
use pretty_assertions::assert_eq;
|
|
||||||
use roc_std::RocStr;
|
use roc_std::RocStr;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -2128,6 +2126,7 @@ mod gen_primitives {
|
||||||
r#"
|
r#"
|
||||||
app "test" provides [ main ] to "./platform"
|
app "test" provides [ main ] to "./platform"
|
||||||
|
|
||||||
|
|
||||||
Color : [ Red, Black ]
|
Color : [ Red, Black ]
|
||||||
|
|
||||||
Tree a b : [ Leaf, Node Color (Tree a b) a b (Tree a b) ]
|
Tree a b : [ Leaf, Node Color (Tree a b) a b (Tree a b) ]
|
||||||
|
@ -2259,4 +2258,3 @@ mod gen_primitives {
|
||||||
i64
|
i64
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
|
@ -9,8 +9,6 @@ license = "Apache-2.0"
|
||||||
roc_collections = { path = "../collections" }
|
roc_collections = { path = "../collections" }
|
||||||
roc_region = { path = "../region" }
|
roc_region = { path = "../region" }
|
||||||
roc_module = { path = "../module" }
|
roc_module = { path = "../module" }
|
||||||
roc_parse = { path = "../parse" }
|
|
||||||
roc_problem = { path = "../problem" }
|
|
||||||
ven_ena = { path = "../../vendor/ena" }
|
ven_ena = { path = "../../vendor/ena" }
|
||||||
inlinable_string = "0.1"
|
inlinable_string = "0.1"
|
||||||
|
|
||||||
|
|
|
@ -371,7 +371,10 @@ impl SolvedType {
|
||||||
|
|
||||||
match subs.get_without_compacting(var).content {
|
match subs.get_without_compacting(var).content {
|
||||||
FlexVar(_) => SolvedType::Flex(VarId::from_var(var, subs)),
|
FlexVar(_) => SolvedType::Flex(VarId::from_var(var, subs)),
|
||||||
RecursionVar { .. } => SolvedType::Flex(VarId::from_var(var, subs)),
|
RecursionVar { structure, .. } => {
|
||||||
|
// TODO should there be a SolvedType RecursionVar variant?
|
||||||
|
Self::from_var_help(subs, recursion_vars, structure)
|
||||||
|
}
|
||||||
RigidVar(name) => SolvedType::Rigid(name),
|
RigidVar(name) => SolvedType::Rigid(name),
|
||||||
Structure(flat_type) => Self::from_flat_type(subs, recursion_vars, flat_type),
|
Structure(flat_type) => Self::from_flat_type(subs, recursion_vars, flat_type),
|
||||||
Alias(symbol, args, actual_var) => {
|
Alias(symbol, args, actual_var) => {
|
||||||
|
|
|
@ -867,7 +867,11 @@ fn unify_shared_tags(
|
||||||
|
|
||||||
merge(subs, ctx, Structure(flat_type))
|
merge(subs, ctx, Structure(flat_type))
|
||||||
} else {
|
} else {
|
||||||
mismatch!("Problem with Tag Union")
|
mismatch!(
|
||||||
|
"Problem with Tag Union\nThere should be {:?} matching tags, but I only got \n{:?}",
|
||||||
|
num_shared_tags,
|
||||||
|
&matching_tags
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -15,16 +15,8 @@ roc_region = { path = "../compiler/region" }
|
||||||
roc_module = { path = "../compiler/module" }
|
roc_module = { path = "../compiler/module" }
|
||||||
roc_problem = { path = "../compiler/problem" }
|
roc_problem = { path = "../compiler/problem" }
|
||||||
roc_types = { path = "../compiler/types" }
|
roc_types = { path = "../compiler/types" }
|
||||||
roc_builtins = { path = "../compiler/builtins" }
|
|
||||||
roc_constrain = { path = "../compiler/constrain" }
|
|
||||||
roc_unify = { path = "../compiler/unify" }
|
|
||||||
roc_solve = { path = "../compiler/solve" }
|
|
||||||
roc_mono = { path = "../compiler/mono" }
|
|
||||||
roc_load = { path = "../compiler/load" }
|
|
||||||
roc_gen = { path = "../compiler/gen" }
|
|
||||||
roc_fmt = { path = "../compiler/fmt" }
|
roc_fmt = { path = "../compiler/fmt" }
|
||||||
|
|
||||||
roc_reporting = { path = "../compiler/reporting" }
|
|
||||||
# TODO switch to clap 3.0.0 once it's out. Tried adding clap = "~3.0.0-beta.1" and cargo wouldn't accept it
|
# TODO switch to clap 3.0.0 once it's out. Tried adding clap = "~3.0.0-beta.1" and cargo wouldn't accept it
|
||||||
ven_graph = { path = "../vendor/pathfinding" }
|
ven_graph = { path = "../vendor/pathfinding" }
|
||||||
im = "14" # im and im-rc should always have the same version!
|
im = "14" # im and im-rc should always have the same version!
|
||||||
|
@ -34,25 +26,6 @@ inlinable_string = "0.1"
|
||||||
arraystring = "0.3.0"
|
arraystring = "0.3.0"
|
||||||
libc = "0.2"
|
libc = "0.2"
|
||||||
page_size = "0.4"
|
page_size = "0.4"
|
||||||
# NOTE: rtfeldman/inkwell is a fork of TheDan64/inkwell which does not change anything.
|
|
||||||
#
|
|
||||||
# The reason for this fork is that the way Inkwell is designed, you have to use
|
|
||||||
# a particular branch (e.g. "llvm8-0") in Cargo.toml. That would be fine, except that
|
|
||||||
# breaking changes get pushed directly to that branch, which breaks our build
|
|
||||||
# without warning.
|
|
||||||
#
|
|
||||||
# We tried referencing a specific rev on TheDan64/inkwell directly (instead of branch),
|
|
||||||
# but although that worked locally, it did not work on GitHub Actions. (After a few
|
|
||||||
# hours of investigation, gave up trying to figure out why.) So this is the workaround:
|
|
||||||
# having an immutable tag on the rtfeldman/inkwell fork which points to
|
|
||||||
# a particular "release" of Inkwell.
|
|
||||||
#
|
|
||||||
# When we want to update Inkwell, we can sync up rtfeldman/inkwell to the latest
|
|
||||||
# commit of TheDan64/inkwell, push a new tag which points to the latest commit,
|
|
||||||
# change the tag value in this Cargo.toml to point to that tag, and `cargo update`.
|
|
||||||
# This way, GitHub Actions works and nobody's builds get broken.
|
|
||||||
inkwell = { git = "https://github.com/rtfeldman/inkwell", tag = "llvm10-0.release3" }
|
|
||||||
target-lexicon = "0.10"
|
|
||||||
winit = "0.22"
|
winit = "0.22"
|
||||||
wgpu = "0.6"
|
wgpu = "0.6"
|
||||||
glyph_brush = "0.7"
|
glyph_brush = "0.7"
|
||||||
|
@ -62,7 +35,6 @@ env_logger = "0.7"
|
||||||
futures = "0.3"
|
futures = "0.3"
|
||||||
wgpu_glyph = "0.10"
|
wgpu_glyph = "0.10"
|
||||||
cgmath = "0.17.0"
|
cgmath = "0.17.0"
|
||||||
itertools = "0.9.0"
|
|
||||||
snafu = { version = "0.6", features = ["backtraces"] }
|
snafu = { version = "0.6", features = ["backtraces"] }
|
||||||
colored = "2"
|
colored = "2"
|
||||||
pest = "2.1"
|
pest = "2.1"
|
||||||
|
|
|
@ -1,141 +1,28 @@
|
||||||
interface Base64 exposes [ fromBytes ] imports [ Bytes.Decode ]
|
interface Base64 exposes [ fromBytes, fromStr, toBytes, toStr ] imports [ Base64.Decode, Base64.Encode ]
|
||||||
|
|
||||||
Decoder a : Bytes.Decode.Decoder a
|
# base 64 encoding from a sequence of bytes
|
||||||
|
fromBytes : List U8 -> Result Str [ InvalidInput ]*
|
||||||
|
|
||||||
fromBytes : List U8 -> Result Str Bytes.Decode.DecodeError
|
|
||||||
fromBytes = \bytes ->
|
fromBytes = \bytes ->
|
||||||
Bytes.Decode.decode bytes (decodeBase64 (List.len bytes))
|
when Base64.Decode.fromBytes bytes is
|
||||||
|
Ok v -> Ok v
|
||||||
|
Err _ -> Err InvalidInput
|
||||||
|
|
||||||
|
# base 64 encoding from a string
|
||||||
|
fromStr : Str -> Result Str [ InvalidInput ]*
|
||||||
|
fromStr = \str ->
|
||||||
|
fromBytes (Str.toBytes str)
|
||||||
|
|
||||||
decodeBase64 : Nat -> Bytes.Decode.Decoder Str
|
# base64-encode bytes to the original
|
||||||
decodeBase64 = \width -> Bytes.Decode.loop loopHelp { remaining: width, string: "" }
|
toBytes : Str -> Result (List U8) [ InvalidInput ]*
|
||||||
|
toBytes = \str ->
|
||||||
|
Ok (Base64.Encode.toBytes str)
|
||||||
|
|
||||||
loopHelp : { remaining : Nat, string : Str } -> Decoder (Bytes.Decode.Step { remaining : Nat, string : Str } Str)
|
toStr : Str -> Result Str [ InvalidInput ]*
|
||||||
loopHelp = \{ remaining, string } ->
|
toStr = \str ->
|
||||||
if remaining >= 3 then
|
when toBytes str is
|
||||||
Bytes.Decode.map3
|
Ok bytes ->
|
||||||
Bytes.Decode.u8
|
when Str.fromUtf8 bytes is
|
||||||
Bytes.Decode.u8
|
Ok v -> Ok v
|
||||||
Bytes.Decode.u8
|
Err _ -> Err InvalidInput
|
||||||
\x, y, z ->
|
|
||||||
a : U32
|
|
||||||
a = Num.intCast x
|
|
||||||
b : U32
|
|
||||||
b = Num.intCast y
|
|
||||||
c : U32
|
|
||||||
c = Num.intCast z
|
|
||||||
|
|
||||||
combined = Num.bitwiseOr (Num.bitwiseOr (Num.shiftLeftBy 16 a) (Num.shiftLeftBy 8 b)) c
|
Err _ -> Err InvalidInput
|
||||||
Loop
|
|
||||||
{
|
|
||||||
remaining: remaining - 3,
|
|
||||||
string: Str.concat string (bitsToChars combined 0)
|
|
||||||
}
|
|
||||||
|
|
||||||
else if remaining == 0 then
|
|
||||||
Bytes.Decode.succeed (Done string)
|
|
||||||
|
|
||||||
else if remaining == 2 then
|
|
||||||
Bytes.Decode.map2
|
|
||||||
Bytes.Decode.u8
|
|
||||||
Bytes.Decode.u8
|
|
||||||
\x, y ->
|
|
||||||
a : U32
|
|
||||||
a = Num.intCast x
|
|
||||||
b : U32
|
|
||||||
b = Num.intCast y
|
|
||||||
combined = Num.bitwiseOr (Num.shiftLeftBy 16 a) (Num.shiftLeftBy 8 b)
|
|
||||||
Done (Str.concat string (bitsToChars combined 1))
|
|
||||||
|
|
||||||
else
|
|
||||||
# remaining = 1
|
|
||||||
Bytes.Decode.map
|
|
||||||
Bytes.Decode.u8
|
|
||||||
\x ->
|
|
||||||
a : U32
|
|
||||||
a = Num.intCast x
|
|
||||||
Done (Str.concat string (bitsToChars (Num.shiftLeftBy 16 a) 2))
|
|
||||||
|
|
||||||
|
|
||||||
bitsToChars : U32, Int * -> Str
|
|
||||||
bitsToChars = \bits, missing ->
|
|
||||||
when Str.fromUtf8 (bitsToCharsHelp bits missing) is
|
|
||||||
Ok str -> str
|
|
||||||
Err _ -> ""
|
|
||||||
|
|
||||||
# Mask that can be used to get the lowest 6 bits of a binary number
|
|
||||||
lowest6BitsMask : Int *
|
|
||||||
lowest6BitsMask = 63
|
|
||||||
|
|
||||||
|
|
||||||
bitsToCharsHelp : U32, Int * -> List U8
|
|
||||||
bitsToCharsHelp = \bits, missing ->
|
|
||||||
# The input is 24 bits, which we have to partition into 4 6-bit segments. We achieve this by
|
|
||||||
# shifting to the right by (a multiple of) 6 to remove unwanted bits on the right, then `Num.bitwiseAnd`
|
|
||||||
# with `0b111111` (which is 2^6 - 1 or 63) (so, 6 1s) to remove unwanted bits on the left.
|
|
||||||
|
|
||||||
# any 6-bit number is a valid base64 digit, so this is actually safe
|
|
||||||
p =
|
|
||||||
Num.shiftRightZfBy 18 bits
|
|
||||||
|> Num.intCast
|
|
||||||
|> unsafeToChar
|
|
||||||
|
|
||||||
q =
|
|
||||||
Num.bitwiseAnd (Num.shiftRightZfBy 12 bits) lowest6BitsMask
|
|
||||||
|> Num.intCast
|
|
||||||
|> unsafeToChar
|
|
||||||
|
|
||||||
r =
|
|
||||||
Num.bitwiseAnd (Num.shiftRightZfBy 6 bits) lowest6BitsMask
|
|
||||||
|> Num.intCast
|
|
||||||
|> unsafeToChar
|
|
||||||
|
|
||||||
s =
|
|
||||||
Num.bitwiseAnd bits lowest6BitsMask
|
|
||||||
|> Num.intCast
|
|
||||||
|> unsafeToChar
|
|
||||||
|
|
||||||
equals : U8
|
|
||||||
equals = 61
|
|
||||||
|
|
||||||
when missing is
|
|
||||||
0 ->
|
|
||||||
[ p, q, r, s ]
|
|
||||||
1 ->
|
|
||||||
[ p, q, r, equals ]
|
|
||||||
2 ->
|
|
||||||
[ p, q, equals , equals ]
|
|
||||||
_ ->
|
|
||||||
# unreachable
|
|
||||||
[]
|
|
||||||
|
|
||||||
# Base64 index to character/digit
|
|
||||||
unsafeToChar : U8 -> U8
|
|
||||||
unsafeToChar = \n ->
|
|
||||||
if n <= 25 then
|
|
||||||
# uppercase characters
|
|
||||||
65 + n
|
|
||||||
|
|
||||||
else if n <= 51 then
|
|
||||||
# lowercase characters
|
|
||||||
97 + (n - 26)
|
|
||||||
|
|
||||||
else if n <= 61 then
|
|
||||||
# digit characters
|
|
||||||
48 + (n - 52)
|
|
||||||
|
|
||||||
else
|
|
||||||
# special cases
|
|
||||||
when n is
|
|
||||||
62 ->
|
|
||||||
# '+'
|
|
||||||
43
|
|
||||||
|
|
||||||
63 ->
|
|
||||||
# '/'
|
|
||||||
47
|
|
||||||
|
|
||||||
_ ->
|
|
||||||
# anything else is invalid '\u{0000}'
|
|
||||||
0
|
|
||||||
|
|
137
examples/benchmarks/Base64/Decode.roc
Normal file
137
examples/benchmarks/Base64/Decode.roc
Normal file
|
@ -0,0 +1,137 @@
|
||||||
|
interface Base64.Decode exposes [ fromBytes ] imports [ Bytes.Decode.{ Decoder, DecodeProblem } ]
|
||||||
|
|
||||||
|
fromBytes : List U8 -> Result Str DecodeProblem
|
||||||
|
fromBytes = \bytes ->
|
||||||
|
Bytes.Decode.decode bytes (decodeBase64 (List.len bytes))
|
||||||
|
|
||||||
|
|
||||||
|
decodeBase64 : Nat -> Decoder Str
|
||||||
|
decodeBase64 = \width -> Bytes.Decode.loop loopHelp { remaining: width, string: "" }
|
||||||
|
|
||||||
|
loopHelp : { remaining : Nat, string : Str } -> Decoder (Bytes.Decode.Step { remaining : Nat, string : Str } Str)
|
||||||
|
loopHelp = \{ remaining, string } ->
|
||||||
|
if remaining >= 3 then
|
||||||
|
Bytes.Decode.map3
|
||||||
|
Bytes.Decode.u8
|
||||||
|
Bytes.Decode.u8
|
||||||
|
Bytes.Decode.u8
|
||||||
|
\x, y, z ->
|
||||||
|
a : U32
|
||||||
|
a = Num.intCast x
|
||||||
|
b : U32
|
||||||
|
b = Num.intCast y
|
||||||
|
c : U32
|
||||||
|
c = Num.intCast z
|
||||||
|
combined = Num.bitwiseOr (Num.bitwiseOr (Num.shiftLeftBy 16 a) (Num.shiftLeftBy 8 b)) c
|
||||||
|
Loop
|
||||||
|
{
|
||||||
|
remaining: remaining - 3,
|
||||||
|
string: Str.concat string (bitsToChars combined 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
else if remaining == 0 then
|
||||||
|
Bytes.Decode.succeed (Done string)
|
||||||
|
|
||||||
|
else if remaining == 2 then
|
||||||
|
Bytes.Decode.map2
|
||||||
|
Bytes.Decode.u8
|
||||||
|
Bytes.Decode.u8
|
||||||
|
\x, y ->
|
||||||
|
a : U32
|
||||||
|
a = Num.intCast x
|
||||||
|
b : U32
|
||||||
|
b = Num.intCast y
|
||||||
|
combined = Num.bitwiseOr (Num.shiftLeftBy 16 a) (Num.shiftLeftBy 8 b)
|
||||||
|
Done (Str.concat string (bitsToChars combined 1))
|
||||||
|
|
||||||
|
else
|
||||||
|
# remaining = 1
|
||||||
|
Bytes.Decode.map
|
||||||
|
Bytes.Decode.u8
|
||||||
|
\x ->
|
||||||
|
a : U32
|
||||||
|
a = Num.intCast x
|
||||||
|
Done (Str.concat string (bitsToChars (Num.shiftLeftBy 16 a) 2))
|
||||||
|
|
||||||
|
|
||||||
|
bitsToChars : U32, Int * -> Str
|
||||||
|
bitsToChars = \bits, missing ->
|
||||||
|
when Str.fromUtf8 (bitsToCharsHelp bits missing) is
|
||||||
|
Ok str -> str
|
||||||
|
Err _ -> ""
|
||||||
|
|
||||||
|
# Mask that can be used to get the lowest 6 bits of a binary number
|
||||||
|
lowest6BitsMask : Int *
|
||||||
|
lowest6BitsMask = 63
|
||||||
|
|
||||||
|
|
||||||
|
bitsToCharsHelp : U32, Int * -> List U8
|
||||||
|
bitsToCharsHelp = \bits, missing ->
|
||||||
|
# The input is 24 bits, which we have to partition into 4 6-bit segments. We achieve this by
|
||||||
|
# shifting to the right by (a multiple of) 6 to remove unwanted bits on the right, then `Num.bitwiseAnd`
|
||||||
|
# with `0b111111` (which is 2^6 - 1 or 63) (so, 6 1s) to remove unwanted bits on the left.
|
||||||
|
|
||||||
|
# any 6-bit number is a valid base64 digit, so this is actually safe
|
||||||
|
p =
|
||||||
|
Num.shiftRightZfBy 18 bits
|
||||||
|
|> Num.intCast
|
||||||
|
|> unsafeToChar
|
||||||
|
|
||||||
|
q =
|
||||||
|
Num.bitwiseAnd (Num.shiftRightZfBy 12 bits) lowest6BitsMask
|
||||||
|
|> Num.intCast
|
||||||
|
|> unsafeToChar
|
||||||
|
|
||||||
|
r =
|
||||||
|
Num.bitwiseAnd (Num.shiftRightZfBy 6 bits) lowest6BitsMask
|
||||||
|
|> Num.intCast
|
||||||
|
|> unsafeToChar
|
||||||
|
|
||||||
|
s =
|
||||||
|
Num.bitwiseAnd bits lowest6BitsMask
|
||||||
|
|> Num.intCast
|
||||||
|
|> unsafeToChar
|
||||||
|
|
||||||
|
equals : U8
|
||||||
|
equals = 61
|
||||||
|
|
||||||
|
when missing is
|
||||||
|
0 ->
|
||||||
|
[ p, q, r, s ]
|
||||||
|
1 ->
|
||||||
|
[ p, q, r, equals ]
|
||||||
|
2 ->
|
||||||
|
[ p, q, equals , equals ]
|
||||||
|
_ ->
|
||||||
|
# unreachable
|
||||||
|
[]
|
||||||
|
|
||||||
|
# Base64 index to character/digit
|
||||||
|
unsafeToChar : U8 -> U8
|
||||||
|
unsafeToChar = \n ->
|
||||||
|
if n <= 25 then
|
||||||
|
# uppercase characters
|
||||||
|
65 + n
|
||||||
|
|
||||||
|
else if n <= 51 then
|
||||||
|
# lowercase characters
|
||||||
|
97 + (n - 26)
|
||||||
|
|
||||||
|
else if n <= 61 then
|
||||||
|
# digit characters
|
||||||
|
48 + (n - 52)
|
||||||
|
|
||||||
|
else
|
||||||
|
# special cases
|
||||||
|
when n is
|
||||||
|
62 ->
|
||||||
|
# '+'
|
||||||
|
43
|
||||||
|
|
||||||
|
63 ->
|
||||||
|
# '/'
|
||||||
|
47
|
||||||
|
|
||||||
|
_ ->
|
||||||
|
# anything else is invalid '\u{0000}'
|
||||||
|
0
|
195
examples/benchmarks/Base64/Encode.roc
Normal file
195
examples/benchmarks/Base64/Encode.roc
Normal file
|
@ -0,0 +1,195 @@
|
||||||
|
interface Base64.Encode
|
||||||
|
exposes [ toBytes ]
|
||||||
|
imports [ Bytes.Encode.{ Encoder } ]
|
||||||
|
|
||||||
|
InvalidChar : U8
|
||||||
|
|
||||||
|
# State : [ None, One U8, Two U8, Three U8 ]
|
||||||
|
|
||||||
|
toBytes : Str -> List U8
|
||||||
|
toBytes = \str ->
|
||||||
|
str
|
||||||
|
|> Str.toBytes
|
||||||
|
|> encodeChunks
|
||||||
|
|> Bytes.Encode.sequence
|
||||||
|
|> Bytes.Encode.encode
|
||||||
|
|
||||||
|
|
||||||
|
encodeChunks : List U8 -> List Encoder
|
||||||
|
encodeChunks = \bytes ->
|
||||||
|
List.walk bytes folder { output: [], accum: None }
|
||||||
|
|> encodeResidual
|
||||||
|
|
||||||
|
coerce : Nat, a -> a
|
||||||
|
coerce = \_, x -> x
|
||||||
|
|
||||||
|
# folder : U8, { output : List Encoder, accum : State } -> { output : List Encoder, accum : State }
|
||||||
|
folder = \char, { output, accum } ->
|
||||||
|
when accum is
|
||||||
|
Unreachable n -> coerce n { output, accum: Unreachable n }
|
||||||
|
None -> { output, accum: One char }
|
||||||
|
One a -> { output, accum: Two a char }
|
||||||
|
Two a b -> { output, accum: Three a b char }
|
||||||
|
Three a b c ->
|
||||||
|
when encodeCharacters a b c char is
|
||||||
|
Ok encoder ->
|
||||||
|
{
|
||||||
|
output: List.append output encoder,
|
||||||
|
accum: None
|
||||||
|
}
|
||||||
|
|
||||||
|
Err _ ->
|
||||||
|
{ output, accum: None }
|
||||||
|
|
||||||
|
# SGVs bG8g V29y bGQ=
|
||||||
|
|
||||||
|
# encodeResidual : { output : List Encoder, accum : State } -> List Encoder
|
||||||
|
encodeResidual = \{ output, accum } ->
|
||||||
|
when accum is
|
||||||
|
Unreachable _ -> output
|
||||||
|
None -> output
|
||||||
|
One _ -> output
|
||||||
|
Two a b ->
|
||||||
|
when encodeCharacters a b equals equals is
|
||||||
|
Ok encoder ->
|
||||||
|
List.append output encoder
|
||||||
|
|
||||||
|
Err _ ->
|
||||||
|
output
|
||||||
|
|
||||||
|
Three a b c ->
|
||||||
|
when encodeCharacters a b c equals is
|
||||||
|
Ok encoder ->
|
||||||
|
List.append output encoder
|
||||||
|
|
||||||
|
Err _ ->
|
||||||
|
output
|
||||||
|
|
||||||
|
equals : U8
|
||||||
|
equals = 61
|
||||||
|
|
||||||
|
# Convert 4 characters to 24 bits (as an Encoder)
|
||||||
|
encodeCharacters : U8, U8, U8, U8 -> Result Encoder InvalidChar
|
||||||
|
encodeCharacters = \a,b,c,d ->
|
||||||
|
if !(isValidChar a) then
|
||||||
|
Err a
|
||||||
|
else if !(isValidChar b) then
|
||||||
|
Err b
|
||||||
|
else
|
||||||
|
# `=` is the padding character, and must be special-cased
|
||||||
|
# only the `c` and `d` char are allowed to be padding
|
||||||
|
n1 = unsafeConvertChar a
|
||||||
|
n2 = unsafeConvertChar b
|
||||||
|
|
||||||
|
x : U32
|
||||||
|
x = Num.intCast n1
|
||||||
|
|
||||||
|
y : U32
|
||||||
|
y = Num.intCast n2
|
||||||
|
|
||||||
|
if d == equals then
|
||||||
|
if c == equals then
|
||||||
|
n = Num.bitwiseOr (Num.shiftLeftBy 18 x) (Num.shiftLeftBy 12 y)
|
||||||
|
|
||||||
|
# masking higher bits is not needed, Encode.unsignedInt8 ignores higher bits
|
||||||
|
b1 : U8
|
||||||
|
b1 = Num.intCast (Num.shiftRightBy 16 n)
|
||||||
|
|
||||||
|
Ok (Bytes.Encode.u8 b1)
|
||||||
|
|
||||||
|
else if !(isValidChar c) then
|
||||||
|
Err c
|
||||||
|
|
||||||
|
else
|
||||||
|
|
||||||
|
n3 = unsafeConvertChar c
|
||||||
|
|
||||||
|
z : U32
|
||||||
|
z = Num.intCast n3
|
||||||
|
|
||||||
|
n = Num.bitwiseOr (Num.bitwiseOr (Num.shiftLeftBy 18 x) (Num.shiftLeftBy 12 y)) (Num.shiftLeftBy 6 z)
|
||||||
|
|
||||||
|
combined : U16
|
||||||
|
combined = Num.intCast (Num.shiftRightBy 8 n)
|
||||||
|
|
||||||
|
Ok (Bytes.Encode.u16 BE combined)
|
||||||
|
|
||||||
|
else if !(isValidChar d) then
|
||||||
|
Err d
|
||||||
|
|
||||||
|
else
|
||||||
|
n3 = unsafeConvertChar c
|
||||||
|
n4 = unsafeConvertChar d
|
||||||
|
|
||||||
|
z : U32
|
||||||
|
z = Num.intCast n3
|
||||||
|
|
||||||
|
w : U32
|
||||||
|
w = Num.intCast n4
|
||||||
|
|
||||||
|
n =
|
||||||
|
Num.bitwiseOr
|
||||||
|
(Num.bitwiseOr (Num.shiftLeftBy 18 x) (Num.shiftLeftBy 12 y))
|
||||||
|
(Num.bitwiseOr (Num.shiftLeftBy 6 z) w)
|
||||||
|
|
||||||
|
b3 : U8
|
||||||
|
b3 = Num.intCast n
|
||||||
|
|
||||||
|
combined : U16
|
||||||
|
combined = Num.intCast (Num.shiftRightBy 8 n)
|
||||||
|
|
||||||
|
Ok (Bytes.Encode.sequence [ Bytes.Encode.u16 BE combined, Bytes.Encode.u8 b3 ])
|
||||||
|
|
||||||
|
# is the character a base64 digit?
|
||||||
|
# The base16 digits are: A-Z, a-z, 0-1, '+' and '/'
|
||||||
|
isValidChar : U8 -> Bool
|
||||||
|
isValidChar = \c ->
|
||||||
|
if isAlphaNum c then
|
||||||
|
True
|
||||||
|
|
||||||
|
else
|
||||||
|
when c is
|
||||||
|
43 ->
|
||||||
|
# '+'
|
||||||
|
True
|
||||||
|
|
||||||
|
47 ->
|
||||||
|
# '/'
|
||||||
|
True
|
||||||
|
|
||||||
|
_ ->
|
||||||
|
False
|
||||||
|
|
||||||
|
isAlphaNum : U8 -> Bool
|
||||||
|
isAlphaNum = \key ->
|
||||||
|
(key >= 48 && key <= 57) || (key >= 64 && key <= 90) || (key >= 97 && key <= 122)
|
||||||
|
|
||||||
|
|
||||||
|
# Convert a base64 character/digit to its index
|
||||||
|
# See also [Wikipedia](https://en.wikipedia.org/wiki/Base64#Base64_table)
|
||||||
|
unsafeConvertChar : U8 -> U8
|
||||||
|
unsafeConvertChar = \key ->
|
||||||
|
if key >= 65 && key <= 90 then
|
||||||
|
# A-Z
|
||||||
|
key - 65
|
||||||
|
|
||||||
|
else if key >= 97 && key <= 122 then
|
||||||
|
# a-z
|
||||||
|
(key - 97) + 26
|
||||||
|
|
||||||
|
else if key >= 48 && key <= 57 then
|
||||||
|
# 0-9
|
||||||
|
(key - 48) + 26 + 26
|
||||||
|
|
||||||
|
else
|
||||||
|
when key is
|
||||||
|
43 ->
|
||||||
|
# '+'
|
||||||
|
62
|
||||||
|
|
||||||
|
47 ->
|
||||||
|
# '/'
|
||||||
|
63
|
||||||
|
|
||||||
|
_ ->
|
||||||
|
0
|
|
@ -1,13 +1,13 @@
|
||||||
interface Bytes.Decode exposes [ Decoder, decode, map, map2, u8, loop, Step, succeed, DecodeError, after, map3 ] imports []
|
interface Bytes.Decode exposes [ Decoder, decode, map, map2, u8, loop, Step, succeed, DecodeProblem, after, map3 ] imports []
|
||||||
|
|
||||||
State : { bytes: List U8, cursor : Nat }
|
State : { bytes: List U8, cursor : Nat }
|
||||||
|
|
||||||
DecodeError : [ OutOfBytes ]
|
DecodeProblem : [ OutOfBytes ]
|
||||||
|
|
||||||
|
|
||||||
Decoder a : [ @Decoder (State -> [Good State a, Bad DecodeError]) ]
|
Decoder a : [ @Decoder (State -> [Good State a, Bad DecodeProblem]) ]
|
||||||
|
|
||||||
decode : List U8, Decoder a -> Result a DecodeError
|
decode : List U8, Decoder a -> Result a DecodeProblem
|
||||||
decode = \bytes, @Decoder decoder ->
|
decode = \bytes, @Decoder decoder ->
|
||||||
when decoder { bytes, cursor: 0 } is
|
when decoder { bytes, cursor: 0 } is
|
||||||
Good _ value ->
|
Good _ value ->
|
||||||
|
|
124
examples/benchmarks/Bytes/Encode.roc
Normal file
124
examples/benchmarks/Bytes/Encode.roc
Normal file
|
@ -0,0 +1,124 @@
|
||||||
|
interface Bytes.Encode exposes [ Encoder, sequence, u8, u16, bytes, empty, encode ] imports []
|
||||||
|
|
||||||
|
Endianness : [ BE, LE ]
|
||||||
|
|
||||||
|
Encoder : [ Signed8 I8, Unsigned8 U8, Signed16 Endianness I16, Unsigned16 Endianness U16, Sequence Nat (List Encoder), Bytes (List U8) ]
|
||||||
|
|
||||||
|
|
||||||
|
u8 : U8 -> Encoder
|
||||||
|
u8 = \value -> Unsigned8 value
|
||||||
|
|
||||||
|
empty : Encoder
|
||||||
|
empty =
|
||||||
|
foo : List Encoder
|
||||||
|
foo = []
|
||||||
|
|
||||||
|
Sequence 0 foo
|
||||||
|
|
||||||
|
u16 : Endianness, U16 -> Encoder
|
||||||
|
u16 = \endianness, value -> Unsigned16 endianness value
|
||||||
|
|
||||||
|
bytes : List U8 -> Encoder
|
||||||
|
bytes = \bs -> Bytes bs
|
||||||
|
|
||||||
|
|
||||||
|
sequence : List Encoder -> Encoder
|
||||||
|
sequence = \encoders ->
|
||||||
|
Sequence (getWidths encoders 0) encoders
|
||||||
|
|
||||||
|
getWidth : Encoder -> Nat
|
||||||
|
getWidth = \encoder ->
|
||||||
|
when encoder is
|
||||||
|
Signed8 _ -> 1
|
||||||
|
Unsigned8 _ -> 1
|
||||||
|
Signed16 _ _ -> 2
|
||||||
|
Unsigned16 _ _ -> 2
|
||||||
|
# Signed32 _ -> 4
|
||||||
|
# Unsigned32 _ -> 4
|
||||||
|
# Signed64 _ -> 8
|
||||||
|
# Unsigned64 _ -> 8
|
||||||
|
# Signed128 _ -> 16
|
||||||
|
# Unsigned128 _ -> 16
|
||||||
|
Sequence w _ -> w
|
||||||
|
Bytes bs -> List.len bs
|
||||||
|
|
||||||
|
getWidths : List Encoder, Nat -> Nat
|
||||||
|
getWidths = \encoders, initial -> List.walk encoders (\encoder, accum -> accum + getWidth encoder) initial
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
encode : Encoder -> List U8
|
||||||
|
encode = \encoder ->
|
||||||
|
output = List.repeat (getWidth encoder) 0
|
||||||
|
|
||||||
|
encodeHelp encoder 0 output
|
||||||
|
|> .output
|
||||||
|
|
||||||
|
encodeHelp = \encoder, offset, output ->
|
||||||
|
when encoder is
|
||||||
|
Unsigned8 value ->
|
||||||
|
{
|
||||||
|
output: List.set output offset value,
|
||||||
|
offset: offset + 1
|
||||||
|
}
|
||||||
|
|
||||||
|
Signed8 value ->
|
||||||
|
cast : U8
|
||||||
|
cast = Num.intCast value
|
||||||
|
|
||||||
|
{
|
||||||
|
output: List.set output offset cast,
|
||||||
|
offset: offset + 1
|
||||||
|
}
|
||||||
|
|
||||||
|
Unsigned16 endianness value ->
|
||||||
|
a : U8
|
||||||
|
a = Num.intCast (Num.shiftRightBy 8 value)
|
||||||
|
|
||||||
|
b : U8
|
||||||
|
b = Num.intCast value
|
||||||
|
|
||||||
|
newOutput =
|
||||||
|
when endianness is
|
||||||
|
BE ->
|
||||||
|
output
|
||||||
|
|> List.set (offset + 0) a
|
||||||
|
|> List.set (offset + 1) b
|
||||||
|
LE ->
|
||||||
|
output
|
||||||
|
|> List.set (offset + 0) b
|
||||||
|
|> List.set (offset + 1) a
|
||||||
|
|
||||||
|
{
|
||||||
|
output: newOutput,
|
||||||
|
offset: offset + 2
|
||||||
|
}
|
||||||
|
|
||||||
|
Signed16 endianness value ->
|
||||||
|
a : U8
|
||||||
|
a = Num.intCast (Num.shiftRightBy 8 value)
|
||||||
|
|
||||||
|
b : U8
|
||||||
|
b = Num.intCast value
|
||||||
|
|
||||||
|
newOutput =
|
||||||
|
when endianness is
|
||||||
|
BE ->
|
||||||
|
output
|
||||||
|
|> List.set (offset + 0) a
|
||||||
|
|> List.set (offset + 1) b
|
||||||
|
LE ->
|
||||||
|
output
|
||||||
|
|> List.set (offset + 0) b
|
||||||
|
|> List.set (offset + 1) a
|
||||||
|
|
||||||
|
{
|
||||||
|
output: newOutput,
|
||||||
|
offset: offset + 1
|
||||||
|
}
|
||||||
|
|
||||||
|
Bytes bs ->
|
||||||
|
List.walk bs (\byte, accum -> { offset: accum.offset + 1, output : List.set accum.output offset byte }) { output, offset }
|
||||||
|
|
||||||
|
Sequence _ encoders ->
|
||||||
|
List.walk encoders (\single, accum -> encodeHelp single accum.offset accum.output) { output, offset }
|
|
@ -8,8 +8,8 @@ app "closure"
|
||||||
main : Task.Task {} []
|
main : Task.Task {} []
|
||||||
main = closure1 {}
|
main = closure1 {}
|
||||||
|> Task.after (\_ -> closure2 {})
|
|> Task.after (\_ -> closure2 {})
|
||||||
|> Task.after (\_ -> closure2 {})
|
|> Task.after (\_ -> closure3 {})
|
||||||
|> Task.after (\_ -> closure2 {})
|
|> Task.after (\_ -> closure4 {})
|
||||||
|
|
||||||
# ---
|
# ---
|
||||||
|
|
||||||
|
|
|
@ -8,8 +8,14 @@ IO a : Task.Task a []
|
||||||
main : IO {}
|
main : IO {}
|
||||||
main =
|
main =
|
||||||
when Base64.fromBytes (Str.toBytes "Hello World") is
|
when Base64.fromBytes (Str.toBytes "Hello World") is
|
||||||
Ok str ->
|
Err _ ->
|
||||||
Task.putLine str
|
Task.putLine "sadness"
|
||||||
|
|
||||||
|
Ok encoded ->
|
||||||
|
Task.after (Task.putLine (Str.concat "encoded: " encoded)) \_ ->
|
||||||
|
when Base64.toStr encoded is
|
||||||
|
Ok decoded ->
|
||||||
|
Task.putLine (Str.concat "decoded: " decoded)
|
||||||
|
|
||||||
Err _ ->
|
Err _ ->
|
||||||
Task.putLine "sadness"
|
Task.putLine "sadness"
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue