Merge remote-tracking branch 'upstream' into annotate-type-signatures

This commit is contained in:
snobee 2025-01-27 14:21:24 -08:00
commit 4f7729c044
No known key found for this signature in database
GPG key ID: ABF756C92D69FDF1
330 changed files with 10591 additions and 7447 deletions

View file

@ -203,6 +203,7 @@ comptime {
exportStrFn(str.reserveC, "reserve");
exportStrFn(str.strToUtf8C, "to_utf8");
exportStrFn(str.fromUtf8C, "from_utf8");
exportStrFn(str.fromUtf8Lossy, "from_utf8_lossy");
exportStrFn(str.repeatC, "repeat");
exportStrFn(str.strTrim, "trim");
exportStrFn(str.strTrimStart, "trim_start");
@ -211,6 +212,9 @@ comptime {
exportStrFn(str.withCapacityC, "with_capacity");
exportStrFn(str.strAllocationPtr, "allocation_ptr");
exportStrFn(str.strReleaseExcessCapacity, "release_excess_capacity");
exportStrFn(str.strWithAsciiLowercased, "with_ascii_lowercased");
exportStrFn(str.strWithAsciiUppercased, "with_ascii_uppercased");
exportStrFn(str.strCaselessAsciiEquals, "caseless_ascii_equals");
for (INTEGERS) |T| {
str.exportFromInt(T, ROC_BUILTINS ++ "." ++ STR ++ ".from_int.");

View file

@ -2,6 +2,7 @@ const utils = @import("utils.zig");
const RocList = @import("list.zig").RocList;
const UpdateMode = utils.UpdateMode;
const std = @import("std");
const ascii = std.ascii;
const mem = std.mem;
const unicode = std.unicode;
const testing = std.testing;
@ -178,7 +179,7 @@ pub const RocStr = extern struct {
pub fn eq(self: RocStr, other: RocStr) bool {
// If they are byte-for-byte equal, they're definitely equal!
if (self.bytes == other.bytes and self.length == other.length and self.capacity_or_alloc_ptr == other.capacity_or_alloc_ptr) {
if (self.bytes == other.bytes and self.length == other.length) {
return true;
}
@ -370,11 +371,17 @@ pub const RocStr = extern struct {
}
fn refcount(self: RocStr) usize {
if ((self.getCapacity() == 0 and !self.isSeamlessSlice()) or self.isSmallStr()) {
const is_seamless_slice = self.isSeamlessSlice();
if ((self.getCapacity() == 0 and !is_seamless_slice) or self.isSmallStr()) {
return 1;
}
const ptr: [*]usize = @as([*]usize, @ptrCast(@alignCast(self.bytes)));
const data_ptr = if (is_seamless_slice)
self.getAllocationPtr()
else
self.bytes;
const ptr: [*]usize = @as([*]usize, @ptrCast(@alignCast(data_ptr)));
return (ptr - 1)[0];
}
@ -611,16 +618,6 @@ fn initFromSmallStr(slice_bytes: [*]u8, len: usize, _: usize) RocStr {
return RocStr.init(slice_bytes, len);
}
// The alloc_ptr must already be shifted to be ready for storing in a seamless slice.
fn initFromBigStr(slice_bytes: [*]u8, len: usize, alloc_ptr: usize) RocStr {
// Here we can make seamless slices instead of copying to a new small str.
return RocStr{
.bytes = slice_bytes,
.length = len | SEAMLESS_SLICE_BIT,
.capacity_or_alloc_ptr = alloc_ptr,
};
}
fn strSplitOnHelp(array: [*]RocStr, string: RocStr, delimiter: RocStr) void {
if (delimiter.len() == 0) {
string.incref(1);
@ -1449,6 +1446,105 @@ pub fn fromUtf8C(
return fromUtf8(list, update_mode);
}
const UNICODE_REPLACEMENT: u21 = 0xfffd;
const Utf8Iterator = struct {
bytes: []u8,
i: usize,
pub fn init(list: RocList) Utf8Iterator {
const bytes = @as([*]u8, @ptrCast(list.bytes))[0..list.length];
return Utf8Iterator{
.bytes = bytes,
.i = 0,
};
}
pub fn nextLossy(it: *Utf8Iterator) ?u32 {
if (it.bytes.len <= it.i) {
return null;
}
const rest = it.bytes[it.i..];
const n = unicode.utf8ByteSequenceLength(rest[0]) catch {
// invalid start byte
it.i += 1;
return UNICODE_REPLACEMENT;
};
for (1..n) |i| {
if (rest.len == i) {
// unexpected end
it.i += i;
return UNICODE_REPLACEMENT;
}
if (rest[i] < 0x70) {
// expected continuation byte (>= 0x70)
it.i += i;
return UNICODE_REPLACEMENT;
}
}
it.i += n;
return unicode.utf8Decode(rest[0..n]) catch {
return UNICODE_REPLACEMENT;
};
}
pub fn reset(it: *Utf8Iterator) void {
it.i = 0;
}
};
fn codepointSeqLengthLossy(c: u32) u3 {
if (c < 0x110000) {
if (unicode.utf8CodepointSequenceLength(@intCast(c))) |n| {
return n;
} else |_| {
// fallthrough
}
}
return unicode.utf8CodepointSequenceLength(UNICODE_REPLACEMENT) catch unreachable;
}
fn utf8EncodeLossy(c: u32, out: []u8) u3 {
if (c < 0x110000) {
if (unicode.utf8Encode(@intCast(c), out)) |n| {
return n;
} else |_| {
// fallthrough
}
}
return unicode.utf8Encode(UNICODE_REPLACEMENT, out) catch unreachable;
}
pub fn fromUtf8Lossy(
list: RocList,
) callconv(.C) RocStr {
if (list.len() == 0) {
return RocStr.empty();
}
// PERF: we could try to reuse the input list if it's already valid utf-8, similar to fromUtf8
var it = Utf8Iterator.init(list);
var enc_len: usize = 0;
while (it.nextLossy()) |c| {
enc_len += codepointSeqLengthLossy(c);
}
var str = RocStr.allocate(enc_len);
const ptr = str.asU8ptrMut()[0..enc_len];
var end_index: usize = 0;
it.reset();
while (it.nextLossy()) |c| {
end_index += utf8EncodeLossy(c, ptr[end_index..]);
}
str.setLen(end_index);
return str;
}
pub fn fromUtf8(
list: RocList,
update_mode: UpdateMode,
@ -1667,6 +1763,17 @@ test "validateUtf8Bytes: unicode ∆ in middle of array" {
try expectOk(str_result);
}
test "fromUtf8Lossy: ascii, emoji" {
var list = RocList.fromSlice(u8, "r💖c", false);
defer list.decref(@alignOf(u8), @sizeOf(u8), false, rcNone);
const res = fromUtf8Lossy(list);
defer res.decref();
const expected = RocStr.fromSlice("r💖c");
defer expected.decref();
try expect(expected.eq(res));
}
fn expectErr(list: RocList, index: usize, err: Utf8DecodeError, problem: Utf8ByteProblem) !void {
const str_ptr = @as([*]u8, @ptrCast(list.bytes));
const len = list.length;
@ -1765,6 +1872,66 @@ test "validateUtf8Bytes: surrogate halves" {
try expectErr(list, 3, error.Utf8EncodesSurrogateHalf, Utf8ByteProblem.EncodesSurrogateHalf);
}
test "fromUtf8Lossy: invalid start byte" {
var list = RocList.fromSlice(u8, "r\x80c", false);
defer list.decref(@alignOf(u8), @sizeOf(u8), false, rcNone);
const res = fromUtf8Lossy(list);
defer res.decref();
const expected = RocStr.fromSlice("r<EFBFBD>c");
defer expected.decref();
try expect(expected.eq(res));
}
test "fromUtf8Lossy: overlong encoding" {
var list = RocList.fromSlice(u8, "r\xF0\x9F\x92\x96\x80c", false);
defer list.decref(@alignOf(u8), @sizeOf(u8), false, rcNone);
const res = fromUtf8Lossy(list);
defer res.decref();
const expected = RocStr.fromSlice("r💖<EFBFBD>c");
defer expected.decref();
try expect(expected.eq(res));
}
test "fromUtf8Lossy: expected continuation" {
var list = RocList.fromSlice(u8, "r\xCFc", false);
defer list.decref(@alignOf(u8), @sizeOf(u8), false, rcNone);
const res = fromUtf8Lossy(list);
defer res.decref();
const expected = RocStr.fromSlice("r<EFBFBD>c");
defer expected.decref();
try expect(expected.eq(res));
}
test "fromUtf8Lossy: unexpected end" {
var list = RocList.fromSlice(u8, "r\xCF", false);
defer list.decref(@alignOf(u8), @sizeOf(u8), false, rcNone);
const res = fromUtf8Lossy(list);
defer res.decref();
const expected = RocStr.fromSlice("r<EFBFBD>");
defer expected.decref();
try expect(expected.eq(res));
}
test "fromUtf8Lossy: encodes surrogate" {
// 0xd83d == 0b1101_1000_0011_1101
// wwww xxxx yyyy zzzz
// becomes 0b1110_1101 0b10_1000_00 0b10_11_1101
// 1110_wwww 10_xxxx_yy 10_yy_zzzz
// 0xED 0x90 0xBD
var list = RocList.fromSlice(u8, "r\xED\xA0\xBDc", false);
defer list.decref(@alignOf(u8), @sizeOf(u8), false, rcNone);
const res = fromUtf8Lossy(list);
defer res.decref();
const expected = RocStr.fromSlice("r<EFBFBD>c");
defer expected.decref();
try expect(expected.eq(res));
}
fn isWhitespace(codepoint: u21) bool {
// https://www.unicode.org/Public/UCD/latest/ucd/PropList.txt
return switch (codepoint) {
@ -1968,6 +2135,194 @@ fn countTrailingWhitespaceBytes(string: RocStr) usize {
return byte_count;
}
// Str.with_ascii_lowercased
pub fn strWithAsciiLowercased(string: RocStr) callconv(.C) RocStr {
var new_str = if (string.isUnique())
string
else blk: {
string.decref();
break :blk RocStr.fromSlice(string.asSlice());
};
const new_str_bytes = new_str.asU8ptrMut()[0..string.len()];
for (new_str_bytes) |*c| {
c.* = ascii.toLower(c.*);
}
return new_str;
}
test "withAsciiLowercased: small str" {
const original = RocStr.fromSlice("cOFFÉ");
try expect(original.isSmallStr());
const expected = RocStr.fromSlice("coffÉ");
defer expected.decref();
const str_result = strWithAsciiLowercased(original);
defer str_result.decref();
try expect(str_result.isSmallStr());
try expect(str_result.eq(expected));
}
test "withAsciiLowercased: non small str" {
const original = RocStr.fromSlice("cOFFÉ cOFFÉ cOFFÉ cOFFÉ cOFFÉ cOFFÉ");
defer original.decref();
try expect(!original.isSmallStr());
const expected = RocStr.fromSlice("coffÉ coffÉ coffÉ coffÉ coffÉ coffÉ");
defer expected.decref();
const str_result = strWithAsciiLowercased(original);
try expect(!str_result.isSmallStr());
try expect(str_result.eq(expected));
}
test "withAsciiLowercased: seamless slice" {
const l = RocStr.fromSlice("cOFFÉ cOFFÉ cOFFÉ cOFFÉ cOFFÉ cOFFÉ");
const original = substringUnsafeC(l, 1, l.len() - 1);
defer original.decref();
try expect(original.isSeamlessSlice());
const expected = RocStr.fromSlice("offÉ coffÉ coffÉ coffÉ coffÉ coffÉ");
defer expected.decref();
const str_result = strWithAsciiLowercased(original);
try expect(!str_result.isSmallStr());
try expect(str_result.eq(expected));
}
// Str.with_ascii_uppercased
pub fn strWithAsciiUppercased(string: RocStr) callconv(.C) RocStr {
var new_str = if (string.isUnique())
string
else blk: {
string.decref();
break :blk RocStr.fromSlice(string.asSlice());
};
const new_str_bytes = new_str.asU8ptrMut()[0..string.len()];
for (new_str_bytes) |*c| {
c.* = ascii.toUpper(c.*);
}
return new_str;
}
test "withAsciiUppercased: small str" {
const original = RocStr.fromSlice("coffé");
try expect(original.isSmallStr());
const expected = RocStr.fromSlice("COFFé");
defer expected.decref();
const str_result = strWithAsciiUppercased(original);
defer str_result.decref();
try expect(str_result.isSmallStr());
try expect(str_result.eq(expected));
}
test "withAsciiUppercased: non small str" {
const original = RocStr.fromSlice("coffé coffé coffé coffé coffé coffé");
defer original.decref();
try expect(!original.isSmallStr());
const expected = RocStr.fromSlice("COFFé COFFé COFFé COFFé COFFé COFFé");
defer expected.decref();
const str_result = strWithAsciiUppercased(original);
try expect(!str_result.isSmallStr());
try expect(str_result.eq(expected));
}
test "withAsciiUppercased: seamless slice" {
const l = RocStr.fromSlice("coffé coffé coffé coffé coffé coffé");
const original = substringUnsafeC(l, 1, l.len() - 1);
defer original.decref();
try expect(original.isSeamlessSlice());
const expected = RocStr.fromSlice("OFFé COFFé COFFé COFFé COFFé COFFé");
defer expected.decref();
const str_result = strWithAsciiUppercased(original);
try expect(!str_result.isSmallStr());
try expect(str_result.eq(expected));
}
pub fn strCaselessAsciiEquals(self: RocStr, other: RocStr) callconv(.C) bool {
if (self.bytes == other.bytes and self.length == other.length) {
return true;
}
return ascii.eqlIgnoreCase(self.asSlice(), other.asSlice());
}
test "caselessAsciiEquals: same str" {
const str1 = RocStr.fromSlice("coFféÉ");
defer str1.decref();
const are_equal = strCaselessAsciiEquals(str1, str1);
try expect(are_equal);
}
test "caselessAsciiEquals: differently capitalized non-ascii char" {
const str1 = RocStr.fromSlice("coffé");
defer str1.decref();
try expect(str1.isSmallStr());
const str2 = RocStr.fromSlice("coffÉ");
defer str2.decref();
const are_equal = strCaselessAsciiEquals(str1, str2);
try expect(!are_equal);
}
test "caselessAsciiEquals: small str" {
const str1 = RocStr.fromSlice("coffé");
defer str1.decref();
try expect(str1.isSmallStr());
const str2 = RocStr.fromSlice("COFFé");
defer str2.decref();
const are_equal = strCaselessAsciiEquals(str1, str2);
try expect(are_equal);
}
test "caselessAsciiEquals: non small str" {
const str1 = RocStr.fromSlice("coffé coffé coffé coffé coffé coffé");
defer str1.decref();
try expect(!str1.isSmallStr());
const str2 = RocStr.fromSlice("COFFé COFFé COFFé COFFé COFFé COFFé");
defer str2.decref();
const are_equal = strCaselessAsciiEquals(str1, str2);
try expect(are_equal);
}
test "caselessAsciiEquals: seamless slice" {
const l = RocStr.fromSlice("coffé coffé coffé coffé coffé coffé");
const str1 = substringUnsafeC(l, 1, l.len() - 1);
defer str1.decref();
try expect(str1.isSeamlessSlice());
const str2 = RocStr.fromSlice("OFFé COFFé COFFé COFFé COFFé COFFé");
defer str2.decref();
const are_equal = strCaselessAsciiEquals(str1, str2);
try expect(are_equal);
}
fn rcNone(_: ?[*]u8) callconv(.C) void {}
fn decStr(ptr: ?[*]u8) callconv(.C) void {

View file

@ -1,4 +1,4 @@
module [Bool, Eq, true, false, and, or, not, is_eq, is_not_eq]
module [Bool, Eq, true, false, not, is_eq, is_not_eq]
## Defines a type that can be compared for total equality.
##
@ -34,7 +34,7 @@ Eq implements
## `Bool` implements the `Eq` ability.
Bool := [True, False] implements [Eq { is_eq: bool_is_eq }]
bool_is_eq = \@Bool(b1), @Bool(b2) -> structural_eq(b1, b2)
bool_is_eq = |@Bool(b1), @Bool(b2)| structural_eq(b1, b2)
## The boolean true value.
true : Bool
@ -44,55 +44,6 @@ true = @Bool(True)
false : Bool
false = @Bool(False)
## Returns `Bool.true` when both inputs are `Bool.true`. This is equivalent to
## the logic [AND](https://en.wikipedia.org/wiki/Logical_conjunction)
## gate. The infix operator `&&` can also be used as shorthand for
## `Bool.and`.
##
## ```roc
## expect Bool.and(Bool.true, Bool.true) == Bool.true
## expect (Bool.true && Bool.true) == Bool.true
## expect (Bool.false && Bool.true) == Bool.false
## expect (Bool.true && Bool.false) == Bool.false
## expect (Bool.false && Bool.false) == Bool.false
## ```
##
## ## Performance Details
##
## In Roc the `&&` and `||` work the same way as any
## other function. However, in some languages `&&` and `||` are special-cased.
## In these languages the compiler will skip evaluating the expression after the
## first operator under certain circumstances. For example an expression like
## `enable_pets && likes_dogs(user)` would compile to.
## ```roc
## if enable_pets then
## likes_dogs(user)
## else
## Bool.false
## ```
## Roc does not do this because conditionals like `if` and `when` have a
## performance cost. Calling a function can sometimes be faster across the board
## than doing an `if` to decide whether to skip calling it.
and : Bool, Bool -> Bool
## Returns `Bool.true` when either input is a `Bool.true`. This is equivalent to
## the logic [OR](https://en.wikipedia.org/wiki/Logical_disjunction) gate.
## The infix operator `||` can also be used as shorthand for `Bool.or`.
## ```roc
## expect Bool.or(Bool.false, Bool.true) == Bool.true
## expect (Bool.true || Bool.true) == Bool.true
## expect (Bool.false || Bool.true) == Bool.true
## expect (Bool.true || Bool.false) == Bool.true
## expect (Bool.false || Bool.false) == Bool.false
## ```
##
## ## Performance Details
##
## In Roc the `&&` and `||` work the same way as any
## other functions. However, in some languages `&&` and `||` are special-cased.
## Refer to the note in `Bool.and` for more detail.
or : Bool, Bool -> Bool
## Returns `Bool.false` when given `Bool.true`, and vice versa. This is
## equivalent to the logic [NOT](https://en.wikipedia.org/wiki/Negation)
## gate. The operator `!` can also be used as shorthand for `Bool.not`.
@ -115,7 +66,7 @@ not : Bool -> Bool
## expect "Apples" != "Oranges"
## ```
is_not_eq : a, a -> Bool where a implements Eq
is_not_eq = \a, b -> structural_not_eq(a, b)
is_not_eq = |a, b| structural_not_eq(a, b)
# INTERNAL COMPILER USE ONLY: used to lower calls to `is_eq` to structural
# equality via the `Eq` low-level for derived types.

View file

@ -123,11 +123,11 @@ DecoderFormatting implements
## _ -> { result: Err(TooShort), rest: bytes }
## ```
custom : (List U8, fmt -> DecodeResult val) -> Decoder val fmt where fmt implements DecoderFormatting
custom = \decode -> @Decoder(decode)
custom = |decode| @Decoder(decode)
## Decode a `List U8` utf-8 bytes using a specific [Decoder] function
decode_with : List U8, Decoder val fmt, fmt -> DecodeResult val where fmt implements DecoderFormatting
decode_with = \bytes, @Decoder(decode), fmt -> decode(bytes, fmt)
decode_with = |bytes, @Decoder(decode), fmt| decode(bytes, fmt)
## Decode a `List U8` utf-8 bytes and return a [DecodeResult](#DecodeResult)
## ```roc
@ -139,7 +139,7 @@ decode_with = \bytes, @Decoder(decode), fmt -> decode(bytes, fmt)
## actual.result == expected
## ```
from_bytes_partial : List U8, fmt -> DecodeResult val where val implements Decoding, fmt implements DecoderFormatting
from_bytes_partial = \bytes, fmt -> decode_with(bytes, decoder, fmt)
from_bytes_partial = |bytes, fmt| decode_with(bytes, decoder, fmt)
## Decode a `List U8` utf-8 bytes and return a [Result] with no leftover bytes
## expected. If successful returns `Ok val`, however, if there are bytes
@ -153,7 +153,7 @@ from_bytes_partial = \bytes, fmt -> decode_with(bytes, decoder, fmt)
## actual == expected
## ```
from_bytes : List U8, fmt -> Result val [Leftover (List U8)]DecodeError where val implements Decoding, fmt implements DecoderFormatting
from_bytes = \bytes, fmt ->
from_bytes = |bytes, fmt|
when from_bytes_partial(bytes, fmt) is
{ result, rest } ->
if List.is_empty(rest) then
@ -165,4 +165,4 @@ from_bytes = \bytes, fmt ->
## Transform the `val` of a [DecodeResult]
map_result : DecodeResult a, (a -> b) -> DecodeResult b
map_result = \{ result, rest }, mapper -> { result: Result.map_ok(result, mapper), rest }
map_result = |{ result, rest }, mapper| { result: Result.map_ok(result, mapper), rest }

View file

@ -112,14 +112,14 @@ Dict k v := {
]
is_eq : Dict k v, Dict k v -> Bool where v implements Eq
is_eq = \xs, ys ->
is_eq = |xs, ys|
if len(xs) != len(ys) then
Bool.false
else
walk_until(
xs,
Bool.true,
\_, k, x_val ->
|_, k, x_val|
when get(ys, k) is
Ok(y_val) if y_val == x_val ->
Continue(Bool.true)
@ -129,12 +129,12 @@ is_eq = \xs, ys ->
)
hash_dict : hasher, Dict k v -> hasher where v implements Hash, hasher implements Hasher
hash_dict = \hasher, dict -> Hash.hash_unordered(hasher, to_list(dict), List.walk)
hash_dict = |hasher, dict| Hash.hash_unordered(hasher, to_list(dict), List.walk)
to_inspector_dict : Dict k v -> Inspector f where k implements Inspect & Hash & Eq, v implements Inspect, f implements InspectFormatter
to_inspector_dict = \dict ->
to_inspector_dict = |dict|
Inspect.custom(
\fmt ->
|fmt|
Inspect.apply(Inspect.dict(dict, walk, Inspect.to_inspector, Inspect.to_inspector), fmt),
)
@ -143,7 +143,7 @@ to_inspector_dict = \dict ->
## empty_dict = Dict.empty({})
## ```
empty : {} -> Dict * *
empty = \{} ->
empty = |{}|
@Dict(
{
buckets: [],
@ -158,19 +158,19 @@ empty = \{} ->
## may provide a performance optimization if you know how many entries will be
## inserted.
with_capacity : U64 -> Dict * *
with_capacity = \requested ->
with_capacity = |requested|
empty({})
|> reserve(requested)
## Enlarge the dictionary for at least capacity additional elements
reserve : Dict k v, U64 -> Dict k v
reserve = \@Dict({ buckets, data, max_bucket_capacity: original_max_bucket_capacity, max_load_factor, shifts }), requested ->
reserve = |@Dict({ buckets, data, max_bucket_capacity: original_max_bucket_capacity, max_load_factor, shifts }), requested|
current_size = List.len(data)
requested_size = Num.add_wrap(current_size, requested)
size = Num.min(requested_size, max_size)
requested_shifts = calc_shifts_for_size(size, max_load_factor)
if List.is_empty(buckets) || requested_shifts > shifts then
if List.is_empty(buckets) or requested_shifts > shifts then
(buckets0, max_bucket_capacity) = alloc_buckets_from_shift(requested_shifts, max_load_factor)
buckets1 = fill_buckets_from_data(buckets0, data, requested_shifts)
@Dict(
@ -189,7 +189,7 @@ reserve = \@Dict({ buckets, data, max_bucket_capacity: original_max_bucket_capac
## This function will require regenerating the metadata if the size changes.
## There will still be some overhead due to dictionary metadata always being a power of 2.
release_excess_capacity : Dict k v -> Dict k v
release_excess_capacity = \@Dict({ buckets, data, max_bucket_capacity: original_max_bucket_capacity, max_load_factor, shifts }) ->
release_excess_capacity = |@Dict({ buckets, data, max_bucket_capacity: original_max_bucket_capacity, max_load_factor, shifts })|
size = List.len(data)
# NOTE: If we want, we technically could increase the load factor here to potentially minimize size more.
@ -218,7 +218,7 @@ release_excess_capacity = \@Dict({ buckets, data, max_bucket_capacity: original_
## capacity_of_dict = Dict.capacity(food_dict)
## ```
capacity : Dict * * -> U64
capacity = \@Dict({ max_bucket_capacity }) ->
capacity = |@Dict({ max_bucket_capacity })|
max_bucket_capacity
## Returns a dictionary containing the key and value provided as input.
@ -228,7 +228,7 @@ capacity = \@Dict({ max_bucket_capacity }) ->
## |> Bool.is_eq(Dict.empty({}) |> Dict.insert("A", "B"))
## ```
single : k, v -> Dict k v
single = \k, v ->
single = |k, v|
insert(empty({}), k, v)
## Returns dictionary with the keys and values specified by the input [List].
@ -247,8 +247,8 @@ single = \k, v ->
## If the list has few duplicate keys, it would be faster to allocate a dictionary
## with the same capacity of the list and walk it calling [Dict.insert]
from_list : List (k, v) -> Dict k v
from_list = \data ->
List.walk(data, empty({}), \dict, (k, v) -> insert(dict, k, v))
from_list = |data|
List.walk(data, empty({}), |dict, (k, v)| insert(dict, k, v))
## Returns the number of values in the dictionary.
## ```roc
@ -261,7 +261,7 @@ from_list = \data ->
## |> Bool.is_eq(3)
## ```
len : Dict * * -> U64
len = \@Dict({ data }) ->
len = |@Dict({ data })|
List.len(data)
## Check if the dictionary is empty.
@ -271,7 +271,7 @@ len = \@Dict({ data }) ->
## Dict.is_empty(Dict.empty({}))
## ```
is_empty : Dict * * -> Bool
is_empty = \@Dict({ data }) ->
is_empty = |@Dict({ data })|
List.is_empty(data)
## Clears all elements from a dictionary keeping around the allocation if it isn't huge.
@ -287,10 +287,10 @@ is_empty = \@Dict({ data }) ->
## expect Dict.len(clear_songs) == 0
## ```
clear : Dict k v -> Dict k v
clear = \@Dict({ buckets, data, max_bucket_capacity, max_load_factor, shifts }) ->
clear = |@Dict({ buckets, data, max_bucket_capacity, max_load_factor, shifts })|
@Dict(
{
buckets: List.map(buckets, \_ -> empty_bucket),
buckets: List.map(buckets, |_| empty_bucket),
# use take_first to keep around the capacity
data: List.take_first(data, 0),
max_bucket_capacity,
@ -303,13 +303,13 @@ clear = \@Dict({ buckets, data, max_bucket_capacity, max_load_factor, shifts })
## function on each of them which receives both the key and the old value. Then return a
## new dictionary containing the same keys and the converted values.
map : Dict k a, (k, a -> b) -> Dict k b
map = \dict, transform ->
map = |dict, transform|
init = with_capacity(capacity(dict))
walk(
dict,
init,
\answer, k, v ->
|answer, k, v|
insert(answer, k, transform(k, v)),
)
@ -319,13 +319,13 @@ map = \dict, transform ->
##
## You may know a similar function named `concat_map` in other languages.
join_map : Dict a b, (a, b -> Dict x y) -> Dict x y
join_map = \dict, transform ->
join_map = |dict, transform|
init = with_capacity(capacity(dict)) # Might be a pessimization
walk(
dict,
init,
\answer, k, v ->
|answer, k, v|
insert_all(answer, transform(k, v)),
)
@ -341,8 +341,8 @@ join_map = \dict, transform ->
## |> Bool.is_eq(36)
## ```
walk : Dict k v, state, (state, k, v -> state) -> state
walk = \@Dict({ data }), initial_state, transform ->
List.walk(data, initial_state, \state, (k, v) -> transform(state, k, v))
walk = |@Dict({ data }), initial_state, transform|
List.walk(data, initial_state, |state, (k, v)| transform(state, k, v))
## Same as [Dict.walk], except you can stop walking early.
##
@ -373,8 +373,8 @@ walk = \@Dict({ data }), initial_state, transform ->
## expect someone_is_an_adult == Bool.true
## ```
walk_until : Dict k v, state, (state, k, v -> [Continue state, Break state]) -> state
walk_until = \@Dict({ data }), initial_state, transform ->
List.walk_until(data, initial_state, \state, (k, v) -> transform(state, k, v))
walk_until = |@Dict({ data }), initial_state, transform|
List.walk_until(data, initial_state, |state, (k, v)| transform(state, k, v))
## Run the given function on each key-value pair of a dictionary, and return
## a dictionary with just the pairs for which the function returned `Bool.true`.
@ -388,11 +388,11 @@ walk_until = \@Dict({ data }), initial_state, transform ->
## |> Bool.is_eq(2)
## ```
keep_if : Dict k v, ((k, v) -> Bool) -> Dict k v
keep_if = \dict, predicate ->
keep_if = |dict, predicate|
keep_if_help(dict, predicate, 0, Dict.len(dict))
keep_if_help : Dict k v, ((k, v) -> Bool), U64, U64 -> Dict k v
keep_if_help = \@Dict(dict), predicate, index, length ->
keep_if_help = |@Dict(dict), predicate, index, length|
if index < length then
(key, value) = list_get_unsafe(dict.data, index)
if predicate((key, value)) then
@ -414,8 +414,8 @@ keep_if_help = \@Dict(dict), predicate, index, length ->
## |> Bool.is_eq(1)
## ```
drop_if : Dict k v, ((k, v) -> Bool) -> Dict k v
drop_if = \dict, predicate ->
Dict.keep_if(dict, \e -> Bool.not(predicate(e)))
drop_if = |dict, predicate|
Dict.keep_if(dict, |e| Bool.not(predicate(e)))
## Get the value for a given key. If there is a value for the specified key it
## will return [Ok value], otherwise return [Err KeyNotFound].
@ -429,7 +429,7 @@ drop_if = \dict, predicate ->
## expect Dict.get(dictionary, 2000) == Err(KeyNotFound)
## ```
get : Dict k v, k -> Result v [KeyNotFound]
get = \dict, key ->
get = |dict, key|
find(dict, key)
|> .result
@ -442,7 +442,7 @@ get = \dict, key ->
## |> Bool.is_eq(Bool.true)
## ```
contains : Dict k v, k -> Bool
contains = \dict, key ->
contains = |dict, key|
find(dict, key)
|> .result
|> Result.is_ok
@ -456,7 +456,7 @@ contains = \dict, key ->
## |> Bool.is_eq(Ok(12))
## ```
insert : Dict k v, k, v -> Dict k v
insert = \dict, key, value ->
insert = |dict, key, value|
@Dict({ buckets, data, max_bucket_capacity, max_load_factor, shifts }) =
if len(dict) < capacity(dict) then
dict
@ -471,7 +471,7 @@ insert = \dict, key, value ->
insert_helper(buckets, data, bucket_index, dist_and_fingerprint, key, value, max_bucket_capacity, max_load_factor, shifts)
insert_helper : List Bucket, List (k, v), U64, U32, k, v, U64, F32, U8 -> Dict k v
insert_helper = \buckets0, data0, bucket_index0, dist_and_fingerprint0, key, value, max_bucket_capacity, max_load_factor, shifts ->
insert_helper = |buckets0, data0, bucket_index0, dist_and_fingerprint0, key, value, max_bucket_capacity, max_load_factor, shifts|
loaded = list_get_unsafe(buckets0, bucket_index0)
if dist_and_fingerprint0 == loaded.dist_and_fingerprint then
(found_key, _) = list_get_unsafe(data0, Num.to_u64(loaded.data_index))
@ -502,7 +502,7 @@ insert_helper = \buckets0, data0, bucket_index0, dist_and_fingerprint0, key, val
## |> Bool.is_eq(0)
## ```
remove : Dict k v, k -> Dict k v
remove = \@Dict({ buckets, data, max_bucket_capacity, max_load_factor, shifts }), key ->
remove = |@Dict({ buckets, data, max_bucket_capacity, max_load_factor, shifts }), key|
if !(List.is_empty(data)) then
(bucket_index0, dist_and_fingerprint0) = next_while_less(buckets, key, shifts)
(bucket_index1, dist_and_fingerprint1) = remove_helper(buckets, bucket_index0, dist_and_fingerprint0, data, key)
@ -516,7 +516,7 @@ remove = \@Dict({ buckets, data, max_bucket_capacity, max_load_factor, shifts })
@Dict({ buckets, data, max_bucket_capacity, max_load_factor, shifts })
remove_helper : List Bucket, U64, U32, List (k, *), k -> (U64, U32) where k implements Eq
remove_helper = \buckets, bucket_index, dist_and_fingerprint, data, key ->
remove_helper = |buckets, bucket_index, dist_and_fingerprint, data, key|
bucket = list_get_unsafe(buckets, bucket_index)
if dist_and_fingerprint == bucket.dist_and_fingerprint then
(found_key, _) = list_get_unsafe(data, Num.to_u64(bucket.data_index))
@ -543,7 +543,7 @@ remove_helper = \buckets, bucket_index, dist_and_fingerprint, data, key ->
## expect Dict.update(Dict.single("a", Bool.true), "a", alter_value) == Dict.empty({})
## ```
update : Dict k v, k, (Result v [Missing] -> Result v [Missing]) -> Dict k v
update = \@Dict({ buckets, data, max_bucket_capacity, max_load_factor, shifts }), key, alter ->
update = |@Dict({ buckets, data, max_bucket_capacity, max_load_factor, shifts }), key, alter|
{ bucket_index, result } = find(@Dict({ buckets, data, max_bucket_capacity, max_load_factor, shifts }), key)
when result is
Ok(value) ->
@ -582,7 +582,7 @@ update = \@Dict({ buckets, data, max_bucket_capacity, max_load_factor, shifts })
Err(Missing) ->
@Dict({ buckets, data, max_bucket_capacity, max_load_factor, shifts })
circular_dist = \start, end, size ->
circular_dist = |start, end, size|
correction =
if start > end then
size
@ -604,7 +604,7 @@ circular_dist = \start, end, size ->
## |> Bool.is_eq([(1, "One"), (2, "Two"), (3, "Three"), (4, "Four")])
## ```
to_list : Dict k v -> List (k, v)
to_list = \@Dict({ data }) ->
to_list = |@Dict({ data })|
data
## Returns the keys of a dictionary as a [List].
@ -619,8 +619,8 @@ to_list = \@Dict({ data }) ->
## |> Bool.is_eq([1,2,3,4])
## ```
keys : Dict k v -> List k
keys = \@Dict({ data }) ->
List.map(data, \(k, _) -> k)
keys = |@Dict({ data })|
List.map(data, |(k, _)| k)
## Returns the values of a dictionary as a [List].
## This requires allocating a temporary [List], prefer using [Dict.to_list] or [Dict.walk] instead.
@ -634,8 +634,8 @@ keys = \@Dict({ data }) ->
## |> Bool.is_eq(["One","Two","Three","Four"])
## ```
values : Dict k v -> List v
values = \@Dict({ data }) ->
List.map(data, \(_, v) -> v)
values = |@Dict({ data })|
List.map(data, |(_, v)| v)
## Combine two dictionaries by keeping the [union](https://en.wikipedia.org/wiki/Union_(set_theory))
## of all the key-value pairs. This means that all the key-value pairs in
@ -662,7 +662,7 @@ values = \@Dict({ data }) ->
## Dict.insert_all(first, second) == expected
## ```
insert_all : Dict k v, Dict k v -> Dict k v
insert_all = \xs, ys ->
insert_all = |xs, ys|
if len(ys) > len(xs) then
insert_all(ys, xs)
else
@ -690,7 +690,7 @@ insert_all = \xs, ys ->
## expect Dict.keep_shared(first, second) == expected
## ```
keep_shared : Dict k v, Dict k v -> Dict k v where v implements Eq
keep_shared = \xs0, ys0 ->
keep_shared = |xs0, ys0|
(xs1, ys1) =
if len(ys0) < len(xs0) then
(ys0, xs0)
@ -700,7 +700,7 @@ keep_shared = \xs0, ys0 ->
walk(
xs1,
with_capacity(len(xs1)),
\state, k, v ->
|state, k, v|
when get(ys1, k) is
Ok(yv) if v == yv ->
insert(state, k, v)
@ -730,8 +730,8 @@ keep_shared = \xs0, ys0 ->
## expect Dict.remove_all(first, second) == expected
## ```
remove_all : Dict k v, Dict k v -> Dict k v
remove_all = \xs, ys ->
walk(ys, xs, \state, k, _ -> remove(state, k))
remove_all = |xs, ys|
walk(ys, xs, |state, k, _| remove(state, k))
# Below here is a list of generic helpers and internal data types for Dict
Bucket : {
@ -747,17 +747,17 @@ initial_shifts = Num.sub_wrap(64, 3) # 2^(64-shifts) number of buckets
max_size = Num.shift_left_by(1u64, 32)
max_bucket_count = max_size
increment_dist = \dist_and_fingerprint ->
increment_dist = |dist_and_fingerprint|
Num.add_wrap(dist_and_fingerprint, dist_inc)
increment_dist_n = \dist_and_fingerprint, n ->
increment_dist_n = |dist_and_fingerprint, n|
Num.add_wrap(dist_and_fingerprint, Num.mul_wrap(n, dist_inc))
decrement_dist = \dist_and_fingerprint ->
decrement_dist = |dist_and_fingerprint|
Num.sub_wrap(dist_and_fingerprint, dist_inc)
find : Dict k v, k -> { bucket_index : U64, result : Result v [KeyNotFound] }
find = \@Dict({ buckets, data, shifts }), key ->
find = |@Dict({ buckets, data, shifts }), key|
hash = hash_key(key)
dist_and_fingerprint = dist_and_fingerprint_from_hash(hash)
bucket_index = bucket_index_from_hash(hash, shifts)
@ -772,7 +772,7 @@ find = \@Dict({ buckets, data, shifts }), key ->
find_manual_unrolls = 2
find_first_unroll : List Bucket, U64, U32, List (k, v), k -> { bucket_index : U64, result : Result v [KeyNotFound] } where k implements Eq
find_first_unroll = \buckets, bucket_index, dist_and_fingerprint, data, key ->
find_first_unroll = |buckets, bucket_index, dist_and_fingerprint, data, key|
# TODO: once we have short circuit evaluation, use it here and other similar locations in this file.
# Avoid the nested if with else block inconvenience.
bucket = list_get_unsafe(buckets, bucket_index)
@ -786,7 +786,7 @@ find_first_unroll = \buckets, bucket_index, dist_and_fingerprint, data, key ->
find_second_unroll(buckets, next_bucket_index(bucket_index, List.len(buckets)), increment_dist(dist_and_fingerprint), data, key)
find_second_unroll : List Bucket, U64, U32, List (k, v), k -> { bucket_index : U64, result : Result v [KeyNotFound] } where k implements Eq
find_second_unroll = \buckets, bucket_index, dist_and_fingerprint, data, key ->
find_second_unroll = |buckets, bucket_index, dist_and_fingerprint, data, key|
bucket = list_get_unsafe(buckets, bucket_index)
if dist_and_fingerprint == bucket.dist_and_fingerprint then
(found_key, value) = list_get_unsafe(data, Num.to_u64(bucket.data_index))
@ -798,7 +798,7 @@ find_second_unroll = \buckets, bucket_index, dist_and_fingerprint, data, key ->
find_helper(buckets, next_bucket_index(bucket_index, List.len(buckets)), increment_dist(dist_and_fingerprint), data, key)
find_helper : List Bucket, U64, U32, List (k, v), k -> { bucket_index : U64, result : Result v [KeyNotFound] } where k implements Eq
find_helper = \buckets, bucket_index, dist_and_fingerprint, data, key ->
find_helper = |buckets, bucket_index, dist_and_fingerprint, data, key|
bucket = list_get_unsafe(buckets, bucket_index)
if dist_and_fingerprint == bucket.dist_and_fingerprint then
(found_key, value) = list_get_unsafe(data, Num.to_u64(bucket.data_index))
@ -812,7 +812,7 @@ find_helper = \buckets, bucket_index, dist_and_fingerprint, data, key ->
find_helper(buckets, next_bucket_index(bucket_index, List.len(buckets)), increment_dist(dist_and_fingerprint), data, key)
remove_bucket : Dict k v, U64 -> Dict k v
remove_bucket = \@Dict({ buckets: buckets0, data: data0, max_bucket_capacity, max_load_factor, shifts }), bucket_index0 ->
remove_bucket = |@Dict({ buckets: buckets0, data: data0, max_bucket_capacity, max_load_factor, shifts }), bucket_index0|
data_index_to_remove = list_get_unsafe(buckets0, bucket_index0) |> .data_index
data_index_to_remove_u64 = Num.to_u64(data_index_to_remove)
@ -853,7 +853,7 @@ remove_bucket = \@Dict({ buckets: buckets0, data: data0, max_bucket_capacity, ma
)
scan_for_index : List Bucket, U64, U32 -> U64
scan_for_index = \buckets, bucket_index, data_index ->
scan_for_index = |buckets, bucket_index, data_index|
bucket = list_get_unsafe(buckets, bucket_index)
if bucket.data_index != data_index then
scan_for_index(buckets, next_bucket_index(bucket_index, List.len(buckets)), data_index)
@ -861,7 +861,7 @@ scan_for_index = \buckets, bucket_index, data_index ->
bucket_index
remove_bucket_helper : List Bucket, U64 -> (List Bucket, U64)
remove_bucket_helper = \buckets, bucket_index ->
remove_bucket_helper = |buckets, bucket_index|
next_index = next_bucket_index(bucket_index, List.len(buckets))
next_bucket = list_get_unsafe(buckets, next_index)
# shift down until either empty or an element with correct spot is found
@ -872,7 +872,7 @@ remove_bucket_helper = \buckets, bucket_index ->
(buckets, bucket_index)
increase_size : Dict k v -> Dict k v
increase_size = \@Dict({ data, max_bucket_capacity, max_load_factor, shifts }) ->
increase_size = |@Dict({ data, max_bucket_capacity, max_load_factor, shifts })|
if max_bucket_capacity != max_bucket_count then
new_shifts = shifts |> Num.sub_wrap(1)
(buckets0, new_max_bucket_capacity) = alloc_buckets_from_shift(new_shifts, max_load_factor)
@ -890,7 +890,7 @@ increase_size = \@Dict({ data, max_bucket_capacity, max_load_factor, shifts }) -
crash("Dict hit limit of ${Num.to_str(max_bucket_count)} elements. Unable to grow more.")
alloc_buckets_from_shift : U8, F32 -> (List Bucket, U64)
alloc_buckets_from_shift = \shifts, max_load_factor ->
alloc_buckets_from_shift = |shifts, max_load_factor|
bucket_count = calc_num_buckets(shifts)
if bucket_count == max_bucket_count then
# reached the maximum, make sure we can use each bucket
@ -905,49 +905,49 @@ alloc_buckets_from_shift = \shifts, max_load_factor ->
(List.repeat(empty_bucket, bucket_count), max_bucket_capacity)
calc_shifts_for_size : U64, F32 -> U8
calc_shifts_for_size = \size, max_load_factor ->
calc_shifts_for_size = |size, max_load_factor|
calc_shifts_for_size_helper(initial_shifts, size, max_load_factor)
calc_shifts_for_size_helper = \shifts, size, max_load_factor ->
calc_shifts_for_size_helper = |shifts, size, max_load_factor|
max_bucket_capacity =
shifts
|> calc_num_buckets
|> Num.to_f32
|> Num.mul(max_load_factor)
|> Num.floor
if shifts > 0 && max_bucket_capacity < size then
if shifts > 0 and max_bucket_capacity < size then
calc_shifts_for_size_helper(Num.sub_wrap(shifts, 1), size, max_load_factor)
else
shifts
calc_num_buckets = \shifts ->
calc_num_buckets = |shifts|
Num.min(Num.shift_left_by(1, Num.sub_wrap(64, shifts)), max_bucket_count)
fill_buckets_from_data = \buckets0, data, shifts ->
fill_buckets_from_data = |buckets0, data, shifts|
List.walk_with_index(
data,
buckets0,
\buckets1, (key, _), data_index ->
|buckets1, (key, _), data_index|
(bucket_index, dist_and_fingerprint) = next_while_less(buckets1, key, shifts)
place_and_shift_up(buckets1, { dist_and_fingerprint, data_index: Num.to_u32(data_index) }, bucket_index),
)
next_while_less : List Bucket, k, U8 -> (U64, U32) where k implements Hash & Eq
next_while_less = \buckets, key, shifts ->
next_while_less = |buckets, key, shifts|
hash = hash_key(key)
dist_and_fingerprint = dist_and_fingerprint_from_hash(hash)
bucket_index = bucket_index_from_hash(hash, shifts)
next_while_less_helper(buckets, bucket_index, dist_and_fingerprint)
next_while_less_helper = \buckets, bucket_index, dist_and_fingerprint ->
next_while_less_helper = |buckets, bucket_index, dist_and_fingerprint|
loaded = list_get_unsafe(buckets, bucket_index)
if dist_and_fingerprint < loaded.dist_and_fingerprint then
next_while_less_helper(buckets, next_bucket_index(bucket_index, List.len(buckets)), increment_dist(dist_and_fingerprint))
else
(bucket_index, dist_and_fingerprint)
place_and_shift_up = \buckets0, bucket, bucket_index ->
place_and_shift_up = |buckets0, bucket, bucket_index|
loaded = list_get_unsafe(buckets0, bucket_index)
if loaded.dist_and_fingerprint != 0 then
buckets1 = List.set(buckets0, bucket_index, bucket)
@ -959,7 +959,7 @@ place_and_shift_up = \buckets0, bucket, bucket_index ->
else
List.set(buckets0, bucket_index, bucket)
next_bucket_index = \bucket_index, max_buckets ->
next_bucket_index = |bucket_index, max_buckets|
# I just ported this impl directly.
# I am a bit confused why it is using an if over a mask.
# Maybe compilers are smart enough to optimize this well.
@ -969,20 +969,20 @@ next_bucket_index = \bucket_index, max_buckets ->
else
0
hash_key = \key ->
hash_key = |key|
create_low_level_hasher(PseudoRandSeed)
|> Hash.hash(key)
|> complete
dist_and_fingerprint_from_hash : U64 -> U32
dist_and_fingerprint_from_hash = \hash ->
dist_and_fingerprint_from_hash = |hash|
hash
|> Num.to_u32
|> Num.bitwise_and(fingerprint_mask)
|> Num.bitwise_or(dist_inc)
bucket_index_from_hash : U64, U8 -> U64
bucket_index_from_hash = \hash, shifts ->
bucket_index_from_hash = |hash, shifts|
Num.shift_right_zf_by(hash, shifts)
expect
@ -1087,7 +1087,7 @@ expect
|> insert("bar", {})
|> insert("baz", {})
contains(dict, "baz") && !(contains(dict, "other"))
contains(dict, "baz") and !(contains(dict, "other"))
expect
dict =
@ -1145,17 +1145,17 @@ expect
|> insert("l", 11)
(get(dict, "a") == Ok(0))
&& (get(dict, "b") == Ok(1))
&& (get(dict, "c") == Ok(2))
&& (get(dict, "d") == Ok(3))
&& (get(dict, "e") == Ok(4))
&& (get(dict, "f") == Ok(5))
&& (get(dict, "g") == Ok(6))
&& (get(dict, "h") == Ok(7))
&& (get(dict, "i") == Ok(8))
&& (get(dict, "j") == Ok(9))
&& (get(dict, "k") == Ok(10))
&& (get(dict, "l") == Ok(11))
and (get(dict, "b") == Ok(1))
and (get(dict, "c") == Ok(2))
and (get(dict, "d") == Ok(3))
and (get(dict, "e") == Ok(4))
and (get(dict, "f") == Ok(5))
and (get(dict, "g") == Ok(6))
and (get(dict, "h") == Ok(7))
and (get(dict, "i") == Ok(8))
and (get(dict, "j") == Ok(9))
and (get(dict, "k") == Ok(10))
and (get(dict, "l") == Ok(11))
# Force rehash.
expect
@ -1197,18 +1197,18 @@ expect
|> insert("m", 12)
(get(dict, "a") == Ok(0))
&& (get(dict, "b") == Ok(1))
&& (get(dict, "c") == Ok(2))
&& (get(dict, "d") == Ok(3))
&& (get(dict, "e") == Ok(4))
&& (get(dict, "f") == Ok(5))
&& (get(dict, "g") == Ok(6))
&& (get(dict, "h") == Ok(7))
&& (get(dict, "i") == Ok(8))
&& (get(dict, "j") == Ok(9))
&& (get(dict, "k") == Ok(10))
&& (get(dict, "l") == Ok(11))
&& (get(dict, "m") == Ok(12))
and (get(dict, "b") == Ok(1))
and (get(dict, "c") == Ok(2))
and (get(dict, "d") == Ok(3))
and (get(dict, "e") == Ok(4))
and (get(dict, "f") == Ok(5))
and (get(dict, "g") == Ok(6))
and (get(dict, "h") == Ok(7))
and (get(dict, "i") == Ok(8))
and (get(dict, "j") == Ok(9))
and (get(dict, "k") == Ok(10))
and (get(dict, "l") == Ok(11))
and (get(dict, "m") == Ok(12))
expect
empty({})
@ -1227,7 +1227,7 @@ BadKey := U64 implements [
]
hash_bad_key : hasher, BadKey -> hasher where hasher implements Hasher
hash_bad_key = \hasher, _ -> Hash.hash(hasher, 0)
hash_bad_key = |hasher, _| Hash.hash(hasher, 0)
expect
bad_keys = [
@ -1250,11 +1250,11 @@ expect
List.walk(
bad_keys,
Dict.empty({}),
\acc, k ->
|acc, k|
Dict.update(
acc,
k,
\val ->
|val|
when val is
Ok(p) -> Ok(Num.add_wrap(p, 1))
Err(Missing) -> Ok(0),
@ -1265,8 +1265,8 @@ expect
List.walk(
bad_keys,
Bool.true,
\acc, k ->
acc && Dict.contains(dict, k),
|acc, k|
acc and Dict.contains(dict, k),
)
all_inserted_correctly
@ -1299,7 +1299,7 @@ LowLevelHasher := { initialized_seed : U64, state : U64 } implements [
pseudo_seed : {} -> U64
create_low_level_hasher : [PseudoRandSeed, WithSeed U64] -> LowLevelHasher
create_low_level_hasher = \seed_opt ->
create_low_level_hasher = |seed_opt|
seed =
when seed_opt is
PseudoRandSeed -> pseudo_seed({})
@ -1307,7 +1307,7 @@ create_low_level_hasher = \seed_opt ->
@LowLevelHasher({ initialized_seed: init_seed(seed), state: seed })
combine_state : LowLevelHasher, { a : U64, b : U64, seed : U64, length : U64 } -> LowLevelHasher
combine_state = \@LowLevelHasher({ initialized_seed, state }), { a, b, seed, length } ->
combine_state = |@LowLevelHasher({ initialized_seed, state }), { a, b, seed, length }|
mum =
a
|> Num.bitwise_xor(wyp1)
@ -1323,20 +1323,20 @@ combine_state = \@LowLevelHasher({ initialized_seed, state }), { a, b, seed, len
@LowLevelHasher({ initialized_seed, state: wymix(state, hash) })
init_seed = \seed ->
init_seed = |seed|
seed
|> Num.bitwise_xor(wyp0)
|> wymix(wyp1)
|> Num.bitwise_xor(seed)
complete = \@LowLevelHasher({ state }) -> state
complete = |@LowLevelHasher({ state })| state
# These implementations hash each value individually with the seed and then mix
# the resulting hash with the state. There are other options that may be faster
# like using the output of the last hash as the seed to the current hash.
# I am simply not sure the tradeoffs here. Theoretically this method is more sound.
# Either way, the performance will be similar and we can change this later.
add_u8 = \@LowLevelHasher({ initialized_seed, state }), u8 ->
add_u8 = |@LowLevelHasher({ initialized_seed, state }), u8|
p0 = Num.to_u64(u8)
a =
Num.shift_left_by(p0, 16)
@ -1346,7 +1346,7 @@ add_u8 = \@LowLevelHasher({ initialized_seed, state }), u8 ->
combine_state(@LowLevelHasher({ initialized_seed, state }), { a, b, seed: initialized_seed, length: 1 })
add_u16 = \@LowLevelHasher({ initialized_seed, state }), u16 ->
add_u16 = |@LowLevelHasher({ initialized_seed, state }), u16|
p0 = Num.bitwise_and(u16, 0xFF) |> Num.to_u64
p1 = Num.shift_right_zf_by(u16, 8) |> Num.to_u64
a =
@ -1357,13 +1357,13 @@ add_u16 = \@LowLevelHasher({ initialized_seed, state }), u16 ->
combine_state(@LowLevelHasher({ initialized_seed, state }), { a, b, seed: initialized_seed, length: 2 })
add_u32 = \@LowLevelHasher({ initialized_seed, state }), u32 ->
add_u32 = |@LowLevelHasher({ initialized_seed, state }), u32|
p0 = Num.to_u64(u32)
a = Num.shift_left_by(p0, 32) |> Num.bitwise_or(p0)
combine_state(@LowLevelHasher({ initialized_seed, state }), { a, b: a, seed: initialized_seed, length: 4 })
add_u64 = \@LowLevelHasher({ initialized_seed, state }), u64 ->
add_u64 = |@LowLevelHasher({ initialized_seed, state }), u64|
p0 = Num.bitwise_and(0xFFFF_FFFF, u64)
p1 = Num.shift_right_zf_by(u64, 32)
a = Num.shift_left_by(p0, 32) |> Num.bitwise_or(p1)
@ -1371,7 +1371,7 @@ add_u64 = \@LowLevelHasher({ initialized_seed, state }), u64 ->
combine_state(@LowLevelHasher({ initialized_seed, state }), { a, b, seed: initialized_seed, length: 8 })
add_u128 = \@LowLevelHasher({ initialized_seed, state }), u128 ->
add_u128 = |@LowLevelHasher({ initialized_seed, state }), u128|
lower = u128 |> Num.to_u64
upper = Num.shift_right_zf_by(u128, 64) |> Num.to_u64
p0 = Num.bitwise_and(0xFFFF_FFFF, lower)
@ -1384,7 +1384,7 @@ add_u128 = \@LowLevelHasher({ initialized_seed, state }), u128 ->
combine_state(@LowLevelHasher({ initialized_seed, state }), { a, b, seed: initialized_seed, length: 16 })
add_bytes : LowLevelHasher, List U8 -> LowLevelHasher
add_bytes = \@LowLevelHasher({ initialized_seed, state }), list ->
add_bytes = |@LowLevelHasher({ initialized_seed, state }), list|
length = List.len(list)
abs =
if length <= 16 then
@ -1409,7 +1409,7 @@ add_bytes = \@LowLevelHasher({ initialized_seed, state }), list ->
combine_state(@LowLevelHasher({ initialized_seed, state }), { a: abs.a, b: abs.b, seed: abs.seed, length })
hash_bytes_helper48 : U64, U64, U64, List U8, U64, U64 -> { a : U64, b : U64, seed : U64 }
hash_bytes_helper48 = \seed, see1, see2, list, index, remaining ->
hash_bytes_helper48 = |seed, see1, see2, list, index, remaining|
new_seed = wymix(Num.bitwise_xor(wyr8(list, index), wyp1), Num.bitwise_xor(wyr8(list, Num.add_wrap(index, 8)), seed))
new_see1 = wymix(Num.bitwise_xor(wyr8(list, Num.add_wrap(index, 16)), wyp2), Num.bitwise_xor(wyr8(list, Num.add_wrap(index, 24)), see1))
new_see2 = wymix(Num.bitwise_xor(wyr8(list, Num.add_wrap(index, 32)), wyp3), Num.bitwise_xor(wyr8(list, Num.add_wrap(index, 40)), see2))
@ -1428,7 +1428,7 @@ hash_bytes_helper48 = \seed, see1, see2, list, index, remaining ->
{ a: wyr8(list, (Num.sub_wrap(new_remaining, 16) |> Num.add_wrap(new_index))), b: wyr8(list, (Num.sub_wrap(new_remaining, 8) |> Num.add_wrap(new_index))), seed: final_seed }
hash_bytes_helper16 : U64, List U8, U64, U64 -> { a : U64, b : U64, seed : U64 }
hash_bytes_helper16 = \seed, list, index, remaining ->
hash_bytes_helper16 = |seed, list, index, remaining|
new_seed = wymix(Num.bitwise_xor(wyr8(list, index), wyp1), Num.bitwise_xor(wyr8(list, Num.add_wrap(index, 8)), seed))
new_remaining = Num.sub_wrap(remaining, 16)
new_index = Num.add_wrap(index, 16)
@ -1448,13 +1448,13 @@ wyp3 : U64
wyp3 = 0x589965cc75374cc3
wymix : U64, U64 -> U64
wymix = \a, b ->
wymix = |a, b|
{ lower, upper } = wymum(a, b)
Num.bitwise_xor(lower, upper)
wymum : U64, U64 -> { lower : U64, upper : U64 }
wymum = \a, b ->
wymum = |a, b|
r = Num.mul_wrap(Num.to_u128(a), Num.to_u128(b))
lower = Num.to_u64(r)
upper = Num.shift_right_zf_by(r, 64) |> Num.to_u64
@ -1465,7 +1465,7 @@ wymum = \a, b ->
# Get the next 8 bytes as a U64
wyr8 : List U8, U64 -> U64
wyr8 = \list, index ->
wyr8 = |list, index|
# With seamless slices and Num.from_bytes, this should be possible to make faster and nicer.
# It would also deal with the fact that on big endian systems we want to invert the order here.
# Without seamless slices, we would need from_bytes to take an index.
@ -1486,7 +1486,7 @@ wyr8 = \list, index ->
# Get the next 4 bytes as a U64 with some shifting.
wyr4 : List U8, U64 -> U64
wyr4 = \list, index ->
wyr4 = |list, index|
p1 = list_get_unsafe(list, index) |> Num.to_u64
p2 = list_get_unsafe(list, Num.add_wrap(index, 1)) |> Num.to_u64
p3 = list_get_unsafe(list, Num.add_wrap(index, 2)) |> Num.to_u64
@ -1499,7 +1499,7 @@ wyr4 = \list, index ->
# Get the next K bytes with some shifting.
# K must be 3 or less.
wyr3 : List U8, U64, U64 -> U64
wyr3 = \list, index, k ->
wyr3 = |list, index, k|
# ((uint64_t)p[0])<<16)|(((uint64_t)p[k>>1])<<8)|p[k-1]
p1 = list_get_unsafe(list, index) |> Num.to_u64
p2 = list_get_unsafe(list, Num.shift_right_zf_by(k, 1) |> Num.add_wrap(index)) |> Num.to_u64
@ -1704,7 +1704,7 @@ expect
|> Dict.insert("Alice", 17)
|> Dict.insert("Bob", 18)
|> Dict.insert("Charlie", 19)
|> Dict.walk_until(Bool.false, \_, _, age -> if age >= 18 then Break(Bool.true) else Continue(Bool.false))
|> Dict.walk_until(Bool.false, |_, _, age| if age >= 18 then Break(Bool.true) else Continue(Bool.false))
|> Bool.is_eq(Bool.true)
expect
@ -1713,7 +1713,7 @@ expect
|> Dict.insert("Alice", 17)
|> Dict.insert("Bob", 18)
|> Dict.insert("Charlie", 19)
|> Dict.keep_if(\(_k, v) -> v >= 18)
|> Dict.keep_if(|(_k, v)| v >= 18)
d2 =
Dict.empty({})
@ -1728,7 +1728,7 @@ expect
|> Dict.insert("Alice", 17)
|> Dict.insert("Bob", 18)
|> Dict.insert("Charlie", 19)
|> Dict.keep_if(\(k, _v) -> Str.ends_with(k, "e"))
|> Dict.keep_if(|(k, _v)| Str.ends_with(k, "e"))
d2 =
Dict.empty({})
@ -1746,7 +1746,7 @@ expect
|> Dict.insert(2, 2)
|> Dict.insert(3, 3)
|> Dict.insert(4, 4)
|> Dict.keep_if(\(k, _v) -> !(List.contains(keys_to_delete, k)))
|> Dict.keep_if(|(k, _v)| !(List.contains(keys_to_delete, k)))
d2 =
Dict.empty({})
@ -1765,7 +1765,7 @@ expect
|> Dict.insert(2, 2)
|> Dict.insert(3, 3)
|> Dict.insert(4, 4)
|> Dict.keep_if(\(k, _v) -> !(List.contains(keys_to_delete, k)))
|> Dict.keep_if(|(k, _v)| !(List.contains(keys_to_delete, k)))
d2 =
Dict.empty({})

View file

@ -84,10 +84,10 @@ EncoderFormatting implements
## actual == expected
## ```
custom : (List U8, fmt -> List U8) -> Encoder fmt where fmt implements EncoderFormatting
custom = \encoder -> @Encoder(encoder)
custom = |encoder| @Encoder(encoder)
append_with : List U8, Encoder fmt, fmt -> List U8 where fmt implements EncoderFormatting
append_with = \lst, @Encoder(do_encoding), fmt -> do_encoding(lst, fmt)
append_with = |lst, @Encoder(do_encoding), fmt| do_encoding(lst, fmt)
## Appends the encoded representation of a value to an existing list of bytes.
##
@ -99,7 +99,7 @@ append_with = \lst, @Encoder(do_encoding), fmt -> do_encoding(lst, fmt)
## actual == expected
## ```
append : List U8, val, fmt -> List U8 where val implements Encoding, fmt implements EncoderFormatting
append = \lst, val, fmt -> append_with(lst, to_encoder(val), fmt)
append = |lst, val, fmt| append_with(lst, to_encoder(val), fmt)
## Encodes a value to a list of bytes (`List U8`) according to the specified format.
##
@ -113,4 +113,4 @@ append = \lst, val, fmt -> append_with(lst, to_encoder(val), fmt)
## actual == expected
## ```
to_bytes : val, fmt -> List U8 where val implements Encoding, fmt implements EncoderFormatting
to_bytes = \val, fmt -> append_with([], to_encoder(val), fmt)
to_bytes = |val, fmt| append_with([], to_encoder(val), fmt)

View file

@ -74,56 +74,56 @@ Hasher implements
complete : a -> U64 where a implements Hasher
## Adds a string into a [Hasher] by hashing its UTF-8 bytes.
hash_str_bytes = \hasher, s ->
hash_str_bytes = |hasher, s|
add_bytes(hasher, Str.to_utf8(s))
## Adds a list of [Hash]able elements to a [Hasher] by hashing each element.
hash_list = \hasher, lst ->
hash_list = |hasher, lst|
List.walk(
lst,
hasher,
\accum_hasher, elem ->
|accum_hasher, elem|
hash(accum_hasher, elem),
)
## Adds a single [Bool] to a hasher.
hash_bool : a, Bool -> a where a implements Hasher
hash_bool = \hasher, b ->
hash_bool = |hasher, b|
as_u8 = if b then 1 else 0
add_u8(hasher, as_u8)
## Adds a single I8 to a hasher.
hash_i8 : a, I8 -> a where a implements Hasher
hash_i8 = \hasher, n -> add_u8(hasher, Num.to_u8(n))
hash_i8 = |hasher, n| add_u8(hasher, Num.to_u8(n))
## Adds a single I16 to a hasher.
hash_i16 : a, I16 -> a where a implements Hasher
hash_i16 = \hasher, n -> add_u16(hasher, Num.to_u16(n))
hash_i16 = |hasher, n| add_u16(hasher, Num.to_u16(n))
## Adds a single I32 to a hasher.
hash_i32 : a, I32 -> a where a implements Hasher
hash_i32 = \hasher, n -> add_u32(hasher, Num.to_u32(n))
hash_i32 = |hasher, n| add_u32(hasher, Num.to_u32(n))
## Adds a single I64 to a hasher.
hash_i64 : a, I64 -> a where a implements Hasher
hash_i64 = \hasher, n -> add_u64(hasher, Num.to_u64(n))
hash_i64 = |hasher, n| add_u64(hasher, Num.to_u64(n))
## Adds a single I128 to a hasher.
hash_i128 : a, I128 -> a where a implements Hasher
hash_i128 = \hasher, n -> add_u128(hasher, Num.to_u128(n))
hash_i128 = |hasher, n| add_u128(hasher, Num.to_u128(n))
## Adds a single [Dec] to a hasher.
hash_dec : a, Dec -> a where a implements Hasher
hash_dec = \hasher, n -> hash_i128(hasher, Num.without_decimal_point(n))
hash_dec = |hasher, n| hash_i128(hasher, Num.without_decimal_point(n))
## Adds a container of [Hash]able elements to a [Hasher] by hashing each element.
## The container is iterated using the walk method passed in.
## The order of the elements does not affect the final hash.
hash_unordered = \hasher, container, walk ->
hash_unordered = |hasher, container, walk|
walk(
container,
0,
\accum, elem ->
|accum, elem|
x =
# Note, we intentionally copy the hasher in every iteration.
# Having the same base state is required for unordered hashing.
@ -138,4 +138,4 @@ hash_unordered = \hasher, container, walk ->
else
next_accum,
)
|> \accum -> add_u64(hasher, accum)
|> |accum| add_u64(hasher, accum)

View file

@ -81,21 +81,21 @@ InspectFormatter implements
Inspector f := f -> f where f implements InspectFormatter
custom : (f -> f) -> Inspector f where f implements InspectFormatter
custom = \fn -> @Inspector(fn)
custom = |fn| @Inspector(fn)
apply : Inspector f, f -> f where f implements InspectFormatter
apply = \@Inspector(fn), fmt -> fn(fmt)
apply = |@Inspector(fn), fmt| fn(fmt)
Inspect implements
to_inspector : val -> Inspector f where val implements Inspect, f implements InspectFormatter
inspect : val -> f where val implements Inspect, f implements InspectFormatter
inspect = \val ->
inspect = |val|
@Inspector(val_fn) = to_inspector(val)
val_fn(init({}))
to_str : val -> Str where val implements Inspect
to_str = \val ->
to_str = |val|
val
|> inspect
|> to_dbg_str
@ -134,16 +134,16 @@ DbgFormatter := { data : Str }
]
dbg_init : {} -> DbgFormatter
dbg_init = \{} -> @DbgFormatter({ data: "" })
dbg_init = |{}| @DbgFormatter({ data: "" })
dbg_list : list, ElemWalker (DbgFormatter, Bool) list elem, (elem -> Inspector DbgFormatter) -> Inspector DbgFormatter
dbg_list = \content, walk_fn, to_dbg_inspector ->
custom_list_dbg = \f0 ->
dbg_list = |content, walk_fn, to_dbg_inspector|
custom_list_dbg = |f0|
f1 = dbg_write(f0, "[")
(f5, _) = walk_fn(
content,
(f1, Bool.false),
\(f2, prepend_sep), elem ->
|(f2, prepend_sep), elem|
f3 =
if prepend_sep then
dbg_write(f2, ", ")
@ -153,7 +153,7 @@ dbg_list = \content, walk_fn, to_dbg_inspector ->
elem
|> to_dbg_inspector
|> apply(f3)
|> \f4 -> (f4, Bool.true),
|> |f4| (f4, Bool.true),
)
dbg_write(f5, "]")
@ -161,13 +161,13 @@ dbg_list = \content, walk_fn, to_dbg_inspector ->
custom(custom_list_dbg)
dbg_set : set, ElemWalker (DbgFormatter, Bool) set elem, (elem -> Inspector DbgFormatter) -> Inspector DbgFormatter
dbg_set = \content, walk_fn, to_dbg_inspector ->
custom_dbg_set = \f0 ->
dbg_set = |content, walk_fn, to_dbg_inspector|
custom_dbg_set = |f0|
f1 = dbg_write(f0, "{")
(f5, _) = walk_fn(
content,
(f1, Bool.false),
\(f2, prepend_sep), elem ->
|(f2, prepend_sep), elem|
f3 =
if prepend_sep then
dbg_write(f2, ", ")
@ -177,7 +177,7 @@ dbg_set = \content, walk_fn, to_dbg_inspector ->
elem
|> to_dbg_inspector
|> apply(f3)
|> \f4 -> (f4, Bool.true),
|> |f4| (f4, Bool.true),
)
dbg_write(f5, "}")
@ -185,13 +185,13 @@ dbg_set = \content, walk_fn, to_dbg_inspector ->
custom(custom_dbg_set)
dbg_dict : dict, KeyValWalker (DbgFormatter, Bool) dict key value, (key -> Inspector DbgFormatter), (value -> Inspector DbgFormatter) -> Inspector DbgFormatter
dbg_dict = \d, walk_fn, key_to_inspector, value_to_inspector ->
custom_dbg_dict = \f0 ->
dbg_dict = |d, walk_fn, key_to_inspector, value_to_inspector|
custom_dbg_dict = |f0|
f1 = dbg_write(f0, "{")
(f5, _) = walk_fn(
d,
(f1, Bool.false),
\(f2, prepend_sep), key, value ->
|(f2, prepend_sep), key, value|
f3 =
if prepend_sep then
dbg_write(f2, ", ")
@ -200,8 +200,8 @@ dbg_dict = \d, walk_fn, key_to_inspector, value_to_inspector ->
apply(key_to_inspector(key), f3)
|> dbg_write(": ")
|> \x -> apply(value_to_inspector(value), x)
|> \f4 -> (f4, Bool.true),
|> |x| apply(value_to_inspector(value), x)
|> |f4| (f4, Bool.true),
)
dbg_write(f5, "}")
@ -209,11 +209,11 @@ dbg_dict = \d, walk_fn, key_to_inspector, value_to_inspector ->
custom(custom_dbg_dict)
dbg_tag : Str, List (Inspector DbgFormatter) -> Inspector DbgFormatter
dbg_tag = \name, fields ->
dbg_tag = |name, fields|
if List.is_empty(fields) then
custom(\f0 -> dbg_write(f0, name))
custom(|f0| dbg_write(f0, name))
else
custom_dbg_tag = \f0 ->
custom_dbg_tag = |f0|
f1 =
dbg_write(f0, "(")
|> dbg_write(name)
@ -221,9 +221,9 @@ dbg_tag = \name, fields ->
f3 = List.walk(
fields,
f1,
\f2, inspector ->
|f2, inspector|
dbg_write(f2, " ")
|> \x -> apply(inspector, x),
|> |x| apply(inspector, x),
)
dbg_write(f3, ")")
@ -231,13 +231,13 @@ dbg_tag = \name, fields ->
custom(custom_dbg_tag)
dbg_tuple : List (Inspector DbgFormatter) -> Inspector DbgFormatter
dbg_tuple = \fields ->
custom_dbg_tuple = \f0 ->
dbg_tuple = |fields|
custom_dbg_tuple = |f0|
f1 = dbg_write(f0, "(")
(f5, _) = List.walk(
fields,
(f1, Bool.false),
\(f2, prepend_sep), inspector ->
|(f2, prepend_sep), inspector|
f3 =
if prepend_sep then
dbg_write(f2, ", ")
@ -245,7 +245,7 @@ dbg_tuple = \fields ->
f2
apply(inspector, f3)
|> \f4 -> (f4, Bool.true),
|> |f4| (f4, Bool.true),
)
dbg_write(f5, ")")
@ -253,13 +253,13 @@ dbg_tuple = \fields ->
custom(custom_dbg_tuple)
dbg_record : List { key : Str, value : Inspector DbgFormatter } -> Inspector DbgFormatter
dbg_record = \fields ->
custom_dbg_record = \f0 ->
dbg_record = |fields|
custom_dbg_record = |f0|
f1 = dbg_write(f0, "{")
(f5, _) = List.walk(
fields,
(f1, Bool.false),
\(f2, prepend_sep), { key, value } ->
|(f2, prepend_sep), { key, value }|
f3 =
if prepend_sep then
dbg_write(f2, ", ")
@ -268,8 +268,8 @@ dbg_record = \fields ->
dbg_write(f3, key)
|> dbg_write(": ")
|> \x -> apply(value, x)
|> \f4 -> (f4, Bool.true),
|> |x| apply(value, x)
|> |f4| (f4, Bool.true),
)
dbg_write(f5, "}")
@ -277,12 +277,12 @@ dbg_record = \fields ->
custom(custom_dbg_record)
dbg_bool : Bool -> Inspector DbgFormatter
dbg_bool = \b ->
dbg_bool = |b|
text = if b then "Bool.true" else "Bool.false"
custom(\f0 -> dbg_write(f0, text))
custom(|f0| dbg_write(f0, text))
dbg_str : Str -> Inspector DbgFormatter
dbg_str = \s ->
dbg_str = |s|
# escape invisible unicode characters as in fmt_str_body crates/compiler/fmt/src/expr.rs
escape_s =
Str.replace_each(s, "\u(feff)", "\\u(feff)")
@ -290,7 +290,7 @@ dbg_str = \s ->
|> Str.replace_each("\u(200c)", "\\u(200c)")
|> Str.replace_each("\u(200d)", "\\u(200d)")
custom_dbg_str = \f0 ->
custom_dbg_str = |f0|
dbg_write(f0, "\"")
|> dbg_write(escape_s)
|> dbg_write("\"")
@ -298,68 +298,68 @@ dbg_str = \s ->
custom(custom_dbg_str)
dbg_opaque : * -> Inspector DbgFormatter
dbg_opaque = \_ ->
custom(\f0 -> dbg_write(f0, "<opaque>"))
dbg_opaque = |_|
custom(|f0| dbg_write(f0, "<opaque>"))
dbg_function : * -> Inspector DbgFormatter
dbg_function = \_ ->
custom(\f0 -> dbg_write(f0, "<function>"))
dbg_function = |_|
custom(|f0| dbg_write(f0, "<function>"))
dbg_u8 : U8 -> Inspector DbgFormatter
dbg_u8 = \num ->
custom(\f0 -> dbg_write(f0, Num.to_str(num)))
dbg_u8 = |num|
custom(|f0| dbg_write(f0, Num.to_str(num)))
dbg_i8 : I8 -> Inspector DbgFormatter
dbg_i8 = \num ->
custom(\f0 -> dbg_write(f0, Num.to_str(num)))
dbg_i8 = |num|
custom(|f0| dbg_write(f0, Num.to_str(num)))
dbg_u16 : U16 -> Inspector DbgFormatter
dbg_u16 = \num ->
custom(\f0 -> dbg_write(f0, Num.to_str(num)))
dbg_u16 = |num|
custom(|f0| dbg_write(f0, Num.to_str(num)))
dbg_i16 : I16 -> Inspector DbgFormatter
dbg_i16 = \num ->
custom(\f0 -> dbg_write(f0, Num.to_str(num)))
dbg_i16 = |num|
custom(|f0| dbg_write(f0, Num.to_str(num)))
dbg_u32 : U32 -> Inspector DbgFormatter
dbg_u32 = \num ->
custom(\f0 -> dbg_write(f0, Num.to_str(num)))
dbg_u32 = |num|
custom(|f0| dbg_write(f0, Num.to_str(num)))
dbg_i32 : I32 -> Inspector DbgFormatter
dbg_i32 = \num ->
custom(\f0 -> dbg_write(f0, Num.to_str(num)))
dbg_i32 = |num|
custom(|f0| dbg_write(f0, Num.to_str(num)))
dbg_u64 : U64 -> Inspector DbgFormatter
dbg_u64 = \num ->
custom(\f0 -> dbg_write(f0, Num.to_str(num)))
dbg_u64 = |num|
custom(|f0| dbg_write(f0, Num.to_str(num)))
dbg_i64 : I64 -> Inspector DbgFormatter
dbg_i64 = \num ->
custom(\f0 -> dbg_write(f0, Num.to_str(num)))
dbg_i64 = |num|
custom(|f0| dbg_write(f0, Num.to_str(num)))
dbg_u128 : U128 -> Inspector DbgFormatter
dbg_u128 = \num ->
custom(\f0 -> dbg_write(f0, Num.to_str(num)))
dbg_u128 = |num|
custom(|f0| dbg_write(f0, Num.to_str(num)))
dbg_i128 : I128 -> Inspector DbgFormatter
dbg_i128 = \num ->
custom(\f0 -> dbg_write(f0, Num.to_str(num)))
dbg_i128 = |num|
custom(|f0| dbg_write(f0, Num.to_str(num)))
dbg_f32 : F32 -> Inspector DbgFormatter
dbg_f32 = \num ->
custom(\f0 -> dbg_write(f0, Num.to_str(num)))
dbg_f32 = |num|
custom(|f0| dbg_write(f0, Num.to_str(num)))
dbg_f64 : F64 -> Inspector DbgFormatter
dbg_f64 = \num ->
custom(\f0 -> dbg_write(f0, Num.to_str(num)))
dbg_f64 = |num|
custom(|f0| dbg_write(f0, Num.to_str(num)))
dbg_dec : Dec -> Inspector DbgFormatter
dbg_dec = \num ->
custom(\f0 -> dbg_write(f0, Num.to_str(num)))
dbg_dec = |num|
custom(|f0| dbg_write(f0, Num.to_str(num)))
dbg_write : DbgFormatter, Str -> DbgFormatter
dbg_write = \@DbgFormatter({ data }), added ->
dbg_write = |@DbgFormatter({ data }), added|
@DbgFormatter({ data: Str.concat(data, added) })
to_dbg_str : DbgFormatter -> Str
to_dbg_str = \@DbgFormatter({ data }) -> data
to_dbg_str = |@DbgFormatter({ data })| data

View file

@ -227,7 +227,7 @@ import Num exposing [U64, Num, U8]
## List.is_empty([])
## ```
is_empty : List * -> Bool
is_empty = \list ->
is_empty = |list|
List.len(list) == 0
# unsafe primitive that does not perform a bounds check
@ -242,7 +242,7 @@ get_unsafe : List a, U64 -> a
## expect List.get([100, 200, 300], 5) == Err(OutOfBounds)
## ```
get : List a, U64 -> Result a [OutOfBounds]
get = \list, index ->
get = |list, index|
if index < List.len(list) then
Ok(List.get_unsafe(list, index))
else
@ -253,7 +253,7 @@ get = \list, index ->
replace_unsafe : List a, U64, a -> { list : List a, value : a }
replace : List a, U64, a -> { list : List a, value : a }
replace = \list, index, new_value ->
replace = |list, index, new_value|
if index < List.len(list) then
List.replace_unsafe(list, index, new_value)
else
@ -268,7 +268,7 @@ replace = \list, index, new_value ->
##
## To drop the element at a given index, instead of replacing it, see [List.drop_at].
set : List a, U64, a -> List a
set = \list, index, value ->
set = |list, index, value|
(List.replace(list, index, value)).list
## Updates the element at the given index with the given function.
@ -281,7 +281,7 @@ set = \list, index, value ->
## To replace the element at a given index, instead of updating based on the current value,
## see [List.set] and [List.replace]
update : List a, U64, (a -> a) -> List a
update = \list, index, func ->
update = |list, index, func|
when List.get(list, index) is
Err(OutOfBounds) -> list
Ok(value) ->
@ -292,7 +292,7 @@ update = \list, index, func ->
expect
list : List U64
list = [1, 2, 3]
got = update(list, 1, \x -> x + 42)
got = update(list, 1, |x| x + 42)
want = [1, 44, 3]
got == want
@ -300,7 +300,7 @@ expect
expect
list : List U64
list = [1, 2, 3]
got = update(list, 5, \x -> x + 42)
got = update(list, 5, |x| x + 42)
got == list
# Update chain
@ -309,9 +309,9 @@ expect
list = [1, 2, 3]
got =
list
|> update(0, \x -> x + 10)
|> update(1, \x -> x + 20)
|> update(2, \x -> x + 30)
|> update(0, |x| x + 10)
|> update(1, |x| x + 20)
|> update(2, |x| x + 30)
want = [11, 22, 33]
got == want
@ -323,7 +323,7 @@ expect
## |> List.append(3)
## ```
append : List a, a -> List a
append = \list, element ->
append = |list, element|
list
|> List.reserve(1)
|> List.append_unsafe(element)
@ -338,7 +338,7 @@ append = \list, element ->
## |> List.append_if_ok(Err(3))
## ```
append_if_ok : List a, Result a * -> List a
append_if_ok = \list, result ->
append_if_ok = |list, result|
when result is
Ok(elem) -> append(list, elem)
Err(_) -> list
@ -369,7 +369,7 @@ prepend : List a, a -> List a
## |> List.prepend(Err(1))
## ```
prepend_if_ok : List a, Result a * -> List a
prepend_if_ok = \list, result ->
prepend_if_ok = |list, result|
when result is
Ok(elem) -> prepend(list, elem)
Err(_) -> list
@ -405,7 +405,7 @@ concat : List a, List a -> List a
## expect List.last([]) == Err(ListWasEmpty)
## ```
last : List a -> Result a [ListWasEmpty]
last = \list ->
last = |list|
when List.get(list, Num.sub_saturated(List.len(list), 1)) is
Ok(v) -> Ok(v)
Err(_) -> Err(ListWasEmpty)
@ -419,15 +419,15 @@ last = \list ->
## |> List.single
## ```
single : a -> List a
single = \x -> [x]
single = |x| [x]
## Returns a list with the given length, where every element is the given value.
repeat : a, U64 -> List a
repeat = \value, count ->
repeat = |value, count|
repeat_help(value, count, List.with_capacity(count))
repeat_help : a, U64, List a -> List a
repeat_help = \value, count, accum ->
repeat_help = |value, count, accum|
if count > 0 then
repeat_help(value, Num.sub_wrap(count, 1), List.append_unsafe(accum, value))
else
@ -438,11 +438,11 @@ repeat_help = \value, count, accum ->
## expect List.reverse([1, 2, 3]) == [3, 2, 1]
## ```
reverse : List a -> List a
reverse = \list ->
reverse = |list|
end = List.len(list) |> Num.sub_saturated(1)
reverse_help(List.clone(list), 0, end)
reverse_help = \list, left, right ->
reverse_help = |list, left, right|
if left < right then
reverse_help(List.swap(list, left, right), Num.add_wrap(left, 1), Num.sub_wrap(right, 1))
else
@ -458,15 +458,15 @@ clone : List a -> List a
## expect List.join([]) == []
## ```
join : List (List a) -> List a
join = \lists ->
join = |lists|
total_length =
List.walk(lists, 0, \state, list -> Num.add_wrap(state, List.len(list)))
List.walk(lists, 0, |state, list| Num.add_wrap(state, List.len(list)))
List.walk(lists, List.with_capacity(total_length), \state, list -> List.concat(state, list))
List.walk(lists, List.with_capacity(total_length), |state, list| List.concat(state, list))
contains : List a, a -> Bool where a implements Eq
contains = \list, needle ->
List.any(list, \x -> x == needle)
contains = |list, needle|
List.any(list, |x| x == needle)
## Build a value using each element in the list.
##
@ -501,12 +501,12 @@ contains = \list, needle ->
## Note that in other languages, `walk` is sometimes called `reduce`,
## `fold`, `fold_left`, or `foldl`.
walk : List elem, state, (state, elem -> state) -> state
walk = \list, init, func ->
walk = |list, init, func|
walk_help(list, init, func, 0, List.len(list))
## internal helper
walk_help : List elem, s, (s, elem -> s), U64, U64 -> s
walk_help = \list, state, f, index, length ->
walk_help = |list, state, f, index, length|
if index < length then
next_state = f(state, List.get_unsafe(list, index))
@ -516,12 +516,12 @@ walk_help = \list, state, f, index, length ->
## Like [walk], but at each step the function also receives the index of the current element.
walk_with_index : List elem, state, (state, elem, U64 -> state) -> state
walk_with_index = \list, init, func ->
walk_with_index = |list, init, func|
walk_with_index_help(list, init, func, 0, List.len(list))
## internal helper
walk_with_index_help : List elem, s, (s, elem, U64 -> s), U64, U64 -> s
walk_with_index_help = \list, state, f, index, length ->
walk_with_index_help = |list, state, f, index, length|
if index < length then
next_state = f(state, List.get_unsafe(list, index), index)
@ -531,14 +531,14 @@ walk_with_index_help = \list, state, f, index, length ->
## Like [walk_until], but at each step the function also receives the index of the current element.
walk_with_index_until : List elem, state, (state, elem, U64 -> [Continue state, Break state]) -> state
walk_with_index_until = \list, state, f ->
walk_with_index_until = |list, state, f|
when walk_with_index_until_help(list, state, f, 0, List.len(list)) is
Continue(new) -> new
Break(new) -> new
## internal helper
walk_with_index_until_help : List elem, s, (s, elem, U64 -> [Continue s, Break b]), U64, U64 -> [Continue s, Break b]
walk_with_index_until_help = \list, state, f, index, length ->
walk_with_index_until_help = |list, state, f, index, length|
if index < length then
when f(state, List.get_unsafe(list, index), index) is
Continue(next_state) ->
@ -551,12 +551,12 @@ walk_with_index_until_help = \list, state, f, index, length ->
## Note that in other languages, `walk_backwards` is sometimes called `reduce_right`,
## `fold`, `fold_right`, or `foldr`.
walk_backwards : List elem, state, (state, elem -> state) -> state
walk_backwards = \list, state, func ->
walk_backwards = |list, state, func|
walk_backwards_help(list, state, func, len(list))
## internal helper
walk_backwards_help : List elem, state, (state, elem -> state), U64 -> state
walk_backwards_help = \list, state, f, index_plus_one ->
walk_backwards_help = |list, state, f, index_plus_one|
if index_plus_one == 0 then
state
else
@ -577,47 +577,47 @@ walk_backwards_help = \list, state, f, index_plus_one ->
## As such, it is typically better for performance to use this over [List.walk]
## if returning `Break` earlier than the last element is expected to be common.
walk_until : List elem, state, (state, elem -> [Continue state, Break state]) -> state
walk_until = \list, initial, step ->
walk_until = |list, initial, step|
when List.iterate(list, initial, step) is
Continue(new) -> new
Break(new) -> new
## Same as [List.walk_until], but does it from the end of the list instead.
walk_backwards_until : List elem, state, (state, elem -> [Continue state, Break state]) -> state
walk_backwards_until = \list, initial, func ->
walk_backwards_until = |list, initial, func|
when List.iterate_backwards(list, initial, func) is
Continue(new) -> new
Break(new) -> new
## Walks to the end of the list from a specified starting index
walk_from : List elem, U64, state, (state, elem -> state) -> state
walk_from = \list, index, state, func ->
walk_from = |list, index, state, func|
step : _, _ -> [Continue _, Break []]
step = \current_state, element -> Continue(func(current_state, element))
step = |current_state, element| Continue(func(current_state, element))
when List.iter_help(list, state, step, index, List.len(list)) is
Continue(new) -> new
## A combination of [List.walk_from] and [List.walk_until]
walk_from_until : List elem, U64, state, (state, elem -> [Continue state, Break state]) -> state
walk_from_until = \list, index, state, func ->
walk_from_until = |list, index, state, func|
when List.iter_help(list, state, func, index, List.len(list)) is
Continue(new) -> new
Break(new) -> new
sum : List (Num a) -> Num a
sum = \list ->
sum = |list|
List.walk(list, 0, Num.add)
product : List (Num a) -> Num a
product = \list ->
product = |list|
List.walk(list, 1, Num.mul)
## Run the given predicate on each element of the list, returning `Bool.true` if
## any of the elements satisfy it.
any : List a, (a -> Bool) -> Bool
any = \list, predicate ->
looper = \{}, element ->
any = |list, predicate|
looper = |{}, element|
if predicate(element) then
Break({})
else
@ -630,8 +630,8 @@ any = \list, predicate ->
## Run the given predicate on each element of the list, returning `Bool.true` if
## all of the elements satisfy it.
all : List a, (a -> Bool) -> Bool
all = \list, predicate ->
looper = \{}, element ->
all = |list, predicate|
looper = |{}, element|
if predicate(element) then
Continue({})
else
@ -663,13 +663,13 @@ all = \list, predicate ->
## list unaltered.
##
keep_if : List a, (a -> Bool) -> List a
keep_if = \list, predicate ->
keep_if = |list, predicate|
length = List.len(list)
keep_if_help(list, predicate, 0, 0, length)
keep_if_help : List a, (a -> Bool), U64, U64, U64 -> List a
keep_if_help = \list, predicate, kept, index, length ->
keep_if_help = |list, predicate, kept, index, length|
if index < length then
if predicate(List.get_unsafe(list, index)) then
keep_if_help(List.swap(list, kept, index), predicate, Num.add_wrap(kept, 1), Num.add_wrap(index, 1), length)
@ -688,8 +688,8 @@ keep_if_help = \list, predicate, kept, index, length ->
## `List.drop_if` has the same performance characteristics as [List.keep_if].
## See its documentation for details on those characteristics!
drop_if : List a, (a -> Bool) -> List a
drop_if = \list, predicate ->
List.keep_if(list, \e -> Bool.not(predicate(e)))
drop_if = |list, predicate|
List.keep_if(list, |e| Bool.not(predicate(e)))
## Run the given function on each element of a list, and return the
## number of elements for which the function returned `Bool.true`.
@ -698,8 +698,8 @@ drop_if = \list, predicate ->
## expect List.count_if([1, 2, 3], (\num -> num > 1)) == 2
## ```
count_if : List a, (a -> Bool) -> U64
count_if = \list, predicate ->
walk_state = \state, elem ->
count_if = |list, predicate|
walk_state = |state, elem|
if predicate(elem) then
Num.add_wrap(state, 1)
else
@ -718,8 +718,8 @@ count_if = \list, predicate ->
## expect List.keep_oks(["", "a", "bc", "", "d", "ef", ""], fn) == ["a", "bc", "d", "ef"]
## ```
keep_oks : List before, (before -> Result after *) -> List after
keep_oks = \list, to_result ->
walker = \accum, element ->
keep_oks = |list, to_result|
walker = |accum, element|
when to_result(element) is
Ok(keep) -> List.append(accum, keep)
Err(_drop) -> accum
@ -736,8 +736,8 @@ keep_oks = \list, to_result ->
## List.keep_errs(["", "a", "bc", "", "d", "ef", ""], fn)
## ```
keep_errs : List before, (before -> Result * after) -> List after
keep_errs = \list, to_result ->
walker = \accum, element ->
keep_errs = |list, to_result|
walker = |accum, element|
when to_result(element) is
Ok(_drop) -> accum
Err(keep) -> List.append(accum, keep)
@ -752,14 +752,14 @@ keep_errs = \list, to_result ->
## expect List.map(["", "a", "bc"], Str.is_empty) == [Bool.true, Bool.false, Bool.false]
## ```
map : List a, (a -> b) -> List b
map = \list, mapper ->
map = |list, mapper|
# TODO: allow checking the refcounting and running the map inplace.
# Preferably allow it even if the types are different (must be same size with padding though).
length = List.len(list)
List.walk(
list,
List.with_capacity(length),
\state, elem ->
|state, elem|
List.append_unsafe(state, mapper(elem)),
)
@ -773,12 +773,12 @@ map = \list, mapper ->
## zipped = List.map2(["a", "b", "c"], [1, 2, 3], Pair)
## ```
map2 : List a, List b, (a, b -> c) -> List c
map2 = \list_a, list_b, mapper ->
map2 = |list_a, list_b, mapper|
length = Num.min(List.len(list_a), List.len(list_b))
map2_help(list_a, list_b, List.with_capacity(length), mapper, 0, length)
map2_help : List a, List b, List c, (a, b -> c), U64, U64 -> List c
map2_help = \list_a, list_b, out, mapper, index, length ->
map2_help = |list_a, list_b, out, mapper, index, length|
if index < length then
mapped = mapper(List.get_unsafe(list_a, index), List.get_unsafe(list_b, index))
@ -790,7 +790,7 @@ map2_help = \list_a, list_b, out, mapper, index, length ->
## and use that as the first element in the returned list.
## Repeat until a list runs out of elements.
map3 : List a, List b, List c, (a, b, c -> d) -> List d
map3 = \list_a, list_b, list_c, mapper ->
map3 = |list_a, list_b, list_c, mapper|
length = Num.min(
Num.min(List.len(list_a), List.len(list_b)),
List.len(list_c),
@ -798,7 +798,7 @@ map3 = \list_a, list_b, list_c, mapper ->
map3_help(list_a, list_b, list_c, List.with_capacity(length), mapper, 0, length)
map3_help : List a, List b, List c, List d, (a, b, c -> d), U64, U64 -> List d
map3_help = \list_a, list_b, list_c, out, mapper, index, length ->
map3_help = |list_a, list_b, list_c, out, mapper, index, length|
if index < length then
mapped = mapper(List.get_unsafe(list_a, index), List.get_unsafe(list_b, index), List.get_unsafe(list_c, index))
@ -810,7 +810,7 @@ map3_help = \list_a, list_b, list_c, out, mapper, index, length ->
## and use that as the first element in the returned list.
## Repeat until a list runs out of elements.
map4 : List a, List b, List c, List d, (a, b, c, d -> e) -> List e
map4 = \list_a, list_b, list_c, list_d, mapper ->
map4 = |list_a, list_b, list_c, list_d, mapper|
length = Num.min(
Num.min(List.len(list_a), List.len(list_b)),
Num.min(List.len(list_c), List.len(list_d)),
@ -818,7 +818,7 @@ map4 = \list_a, list_b, list_c, list_d, mapper ->
map4_help(list_a, list_b, list_c, list_d, List.with_capacity(length), mapper, 0, length)
map4_help : List a, List b, List c, List d, List e, (a, b, c, d -> e), U64, U64 -> List e
map4_help = \list_a, list_b, list_c, list_d, out, mapper, index, length ->
map4_help = |list_a, list_b, list_c, list_d, out, mapper, index, length|
if index < length then
mapped = mapper(List.get_unsafe(list_a, index), List.get_unsafe(list_b, index), List.get_unsafe(list_c, index), List.get_unsafe(list_d, index))
@ -832,7 +832,7 @@ map4_help = \list_a, list_b, list_c, list_d, out, mapper, index, length ->
## expect List.map_with_index([10, 20, 30], (\num, index -> num + index)) == [10, 21, 32]
## ```
map_with_index : List a, (a, U64 -> b) -> List b
map_with_index = \src, func ->
map_with_index = |src, func|
length = len(src)
dest = with_capacity(length)
@ -840,7 +840,7 @@ map_with_index = \src, func ->
# Internal helper
map_with_index_help : List a, List b, (a, U64 -> b), U64, U64 -> List b
map_with_index_help = \src, dest, func, index, length ->
map_with_index_help = |src, dest, func, index, length|
if index < length then
elem = get_unsafe(src, index)
mapped_elem = func(elem, index)
@ -875,30 +875,30 @@ map_with_index_help = \src, dest, func, index, length ->
## All of these options are compatible with the others. For example, you can use `At` or `After`
## with `start` regardless of what `end` and `step` are set to.
range : _
range = \{ start, end, step ?? 0 } ->
range = |{ start, end, step ?? 0 }|
{ calc_next, step_is_positive } =
if step == 0 then
when T(start, end) is
T(At(x), At(y)) | T(At(x), Before(y)) | T(After(x), At(y)) | T(After(x), Before(y)) ->
if x < y then
{
calc_next: \i -> Num.add_checked(i, 1),
calc_next: |i| Num.add_checked(i, 1),
step_is_positive: Bool.true,
}
else
{
calc_next: \i -> Num.sub_checked(i, 1),
calc_next: |i| Num.sub_checked(i, 1),
step_is_positive: Bool.false,
}
T(At(_), Length(_)) | T(After(_), Length(_)) ->
{
calc_next: \i -> Num.add_checked(i, 1),
calc_next: |i| Num.add_checked(i, 1),
step_is_positive: Bool.true,
}
else
{
calc_next: \i -> Num.add_checked(i, step),
calc_next: |i| Num.add_checked(i, step),
step_is_positive: step > 0,
}
@ -911,9 +911,9 @@ range = \{ start, end, step ?? 0 } ->
At(at) ->
is_valid =
if step_is_positive then
\i -> i <= at
|i| i <= at
else
\i -> i >= at
|i| i >= at
# TODO: switch to List.with_capacity
range_help([], inclusive_start, calc_next, is_valid)
@ -921,9 +921,9 @@ range = \{ start, end, step ?? 0 } ->
Before(before) ->
is_valid =
if step_is_positive then
\i -> i < before
|i| i < before
else
\i -> i > before
|i| i > before
# TODO: switch to List.with_capacity
range_help([], inclusive_start, calc_next, is_valid)
@ -931,7 +931,7 @@ range = \{ start, end, step ?? 0 } ->
Length(l) ->
range_length_help(List.with_capacity(l), inclusive_start, l, calc_next)
range_help = \accum, i, calc_next, is_valid ->
range_help = |accum, i, calc_next, is_valid|
when i is
Ok(val) ->
if is_valid(val) then
@ -945,7 +945,7 @@ range_help = \accum, i, calc_next, is_valid ->
# return the generated list.
accum
range_length_help = \accum, i, remaining, calc_next ->
range_length_help = |accum, i, remaining, calc_next|
if remaining == 0 then
accum
else
@ -1004,19 +1004,19 @@ sort_with : List a, (a, a -> [LT, EQ, GT]) -> List a
##
## To sort in descending order (highest to lowest), use [List.sort_desc] instead.
sort_asc : List (Num a) -> List (Num a)
sort_asc = \list -> List.sort_with(list, Num.compare)
sort_asc = |list| List.sort_with(list, Num.compare)
## Sorts a list of numbers in descending order (highest to lowest).
##
## To sort in ascending order (lowest to highest), use [List.sort_asc] instead.
sort_desc : List (Num a) -> List (Num a)
sort_desc = \list -> List.sort_with(list, \a, b -> Num.compare(b, a))
sort_desc = |list| List.sort_with(list, |a, b| Num.compare(b, a))
swap : List a, U64, U64 -> List a
## Returns the first element in the list, or `ListWasEmpty` if it was empty.
first : List a -> Result a [ListWasEmpty]
first = \list ->
first = |list|
when List.get(list, 0) is
Ok(v) -> Ok(v)
Err(_) -> Err(ListWasEmpty)
@ -1038,7 +1038,7 @@ first = \list ->
## To split the list into two lists, use `List.split_at`.
##
take_first : List elem, U64 -> List elem
take_first = \list, output_length ->
take_first = |list, output_length|
List.sublist(list, { start: 0, len: output_length })
## Returns the given number of elements from the end of the list.
@ -1058,19 +1058,19 @@ take_first = \list, output_length ->
## To split the list into two lists, use `List.split_at`.
##
take_last : List elem, U64 -> List elem
take_last = \list, output_length ->
take_last = |list, output_length|
List.sublist(list, { start: Num.sub_saturated(List.len(list), output_length), len: output_length })
## Drops n elements from the beginning of the list.
drop_first : List elem, U64 -> List elem
drop_first = \list, n ->
drop_first = |list, n|
remaining = Num.sub_saturated(List.len(list), n)
List.take_last(list, remaining)
## Drops n elements from the end of the list.
drop_last : List elem, U64 -> List elem
drop_last = \list, n ->
drop_last = |list, n|
remaining = Num.sub_saturated(List.len(list), n)
List.take_first(list, remaining)
@ -1083,7 +1083,7 @@ drop_last = \list, n ->
drop_at : List elem, U64 -> List elem
min : List (Num a) -> Result (Num a) [ListWasEmpty]
min = \list ->
min = |list|
when List.first(list) is
Ok(initial) ->
Ok(min_help(list, initial))
@ -1092,11 +1092,11 @@ min = \list ->
Err(ListWasEmpty)
min_help : List (Num a), Num a -> Num a
min_help = \list, initial ->
min_help = |list, initial|
List.walk(
list,
initial,
\best_so_far, current ->
|best_so_far, current|
if current < best_so_far then
current
else
@ -1104,7 +1104,7 @@ min_help = \list, initial ->
)
max : List (Num a) -> Result (Num a) [ListWasEmpty]
max = \list ->
max = |list|
when List.first(list) is
Ok(initial) ->
Ok(max_help(list, initial))
@ -1113,11 +1113,11 @@ max = \list ->
Err(ListWasEmpty)
max_help : List (Num a), Num a -> Num a
max_help = \list, initial ->
max_help = |list, initial|
List.walk(
list,
initial,
\best_so_far, current ->
|best_so_far, current|
if current > best_so_far then
current
else
@ -1129,14 +1129,14 @@ max_help = \list, initial ->
##
## You may know a similar function named `concat_map` in other languages.
join_map : List a, (a -> List b) -> List b
join_map = \list, mapper ->
List.walk(list, [], \state, elem -> List.concat(state, mapper(elem)))
join_map = |list, mapper|
List.walk(list, [], |state, elem| List.concat(state, mapper(elem)))
## Returns the first element of the list satisfying a predicate function.
## If no satisfying element is found, an `Err NotFound` is returned.
find_first : List elem, (elem -> Bool) -> Result elem [NotFound]
find_first = \list, pred ->
callback = \_, elem ->
find_first = |list, pred|
callback = |_, elem|
if pred(elem) then
Break(elem)
else
@ -1149,8 +1149,8 @@ find_first = \list, pred ->
## Returns the last element of the list satisfying a predicate function.
## If no satisfying element is found, an `Err NotFound` is returned.
find_last : List elem, (elem -> Bool) -> Result elem [NotFound]
find_last = \list, pred ->
callback = \_, elem ->
find_last = |list, pred|
callback = |_, elem|
if pred(elem) then
Break(elem)
else
@ -1164,11 +1164,11 @@ find_last = \list, pred ->
## satisfying a predicate function can be found.
## If no satisfying element is found, an `Err NotFound` is returned.
find_first_index : List elem, (elem -> Bool) -> Result U64 [NotFound]
find_first_index = \list, matcher ->
find_first_index = |list, matcher|
found_index = List.iterate(
list,
0,
\index, elem ->
|index, elem|
if matcher(elem) then
Break(index)
else
@ -1183,11 +1183,11 @@ find_first_index = \list, matcher ->
## satisfying a predicate function can be found.
## If no satisfying element is found, an `Err NotFound` is returned.
find_last_index : List elem, (elem -> Bool) -> Result U64 [NotFound]
find_last_index = \list, matches ->
find_last_index = |list, matches|
found_index = List.iterate_backwards(
list,
List.len(list),
\prev_index, elem ->
|prev_index, elem|
answer = Num.sub_wrap(prev_index, 1)
if matches(elem) then
@ -1216,7 +1216,7 @@ find_last_index = \list, matches ->
##
## Some languages have a function called **`slice`** which works similarly to this.
sublist : List elem, { start : U64, len : U64 } -> List elem
sublist = \list, config ->
sublist = |list, config|
sublist_lowlevel(list, config.start, config.len)
## low-level slicing operation that does no bounds checking
@ -1227,14 +1227,14 @@ sublist_lowlevel : List elem, U64, U64 -> List elem
## List.intersperse([1, 2, 3], 9) == [1, 9, 2, 9, 3]
## ```
intersperse : List elem, elem -> List elem
intersperse = \list, sep ->
intersperse = |list, sep|
capacity = 2 * List.len(list)
init = List.with_capacity(capacity)
new_list =
List.walk(
list,
init,
\acc, elem ->
|acc, elem|
acc
|> List.append_unsafe(elem)
|> List.append_unsafe(sep),
@ -1249,7 +1249,7 @@ intersperse = \list, sep ->
##
## If the first list is empty, this only returns `Bool.true` if the second list is empty.
starts_with : List elem, List elem -> Bool where elem implements Eq
starts_with = \list, prefix ->
starts_with = |list, prefix|
# TODO once we have seamless slices, verify that this wouldn't
# have better performance with a function like List.compare_sublists
prefix == List.sublist(list, { start: 0, len: List.len(prefix) })
@ -1261,7 +1261,7 @@ starts_with = \list, prefix ->
##
## If the first list is empty, this only returns `Bool.true` if the second list is empty.
ends_with : List elem, List elem -> Bool where elem implements Eq
ends_with = \list, suffix ->
ends_with = |list, suffix|
# TODO once we have seamless slices, verify that this wouldn't
# have better performance with a function like List.compare_sublists
length = List.len(suffix)
@ -1277,7 +1277,7 @@ ends_with = \list, suffix ->
## means if you give an index of 0, the `before` list will be empty and the
## `others` list will have the same elements as the original list.)
split_at : List elem, U64 -> { before : List elem, others : List elem }
split_at = \elements, user_split_index ->
split_at = |elements, user_split_index|
length = List.len(elements)
split_index = if length > user_split_index then user_split_index else length
before = List.sublist(elements, { start: 0, len: split_index })
@ -1291,8 +1291,8 @@ split_at = \elements, user_split_index ->
## List.split_on([1, 2, 3], 2) == [[1], [3]]
## ```
split_on : List a, a -> List (List a) where a implements Eq
split_on = \elements, delimiter ->
help = \remaining, chunks, current_chunk ->
split_on = |elements, delimiter|
help = |remaining, chunks, current_chunk|
when remaining is
[] -> List.append(chunks, current_chunk)
[x, .. as rest] if x == delimiter ->
@ -1308,8 +1308,8 @@ split_on = \elements, delimiter ->
## List.split_on_list([1, 2, 3], [1, 2]) == [[], [3]]
## ```
split_on_list : List a, List a -> List (List a) where a implements Eq
split_on_list = \elements, delimiter ->
help = \remaining, chunks, current_chunk ->
split_on_list = |elements, delimiter|
help = |remaining, chunks, current_chunk|
when remaining is
[] -> List.append(chunks, current_chunk)
[x, .. as rest] ->
@ -1329,8 +1329,8 @@ split_on_list = \elements, delimiter ->
## List.split_first([Foo, Z, Bar, Z, Baz], Z) == Ok({ before: [Foo], after: [Bar, Z, Baz] })
## ```
split_first : List elem, elem -> Result { before : List elem, after : List elem } [NotFound] where elem implements Eq
split_first = \list, delimiter ->
when List.find_first_index(list, \elem -> elem == delimiter) is
split_first = |list, delimiter|
when List.find_first_index(list, |elem| elem == delimiter) is
Ok(index) ->
before = List.sublist(list, { start: 0, len: index })
after = List.sublist(list, { start: Num.add_wrap(index, 1), len: Num.sub_wrap(List.len(list), index) |> Num.sub_wrap(1) })
@ -1345,8 +1345,8 @@ split_first = \list, delimiter ->
## List.split_last([Foo, Z, Bar, Z, Baz], Z) == Ok({ before: [Foo, Z, Bar], after: [Baz] })
## ```
split_last : List elem, elem -> Result { before : List elem, after : List elem } [NotFound] where elem implements Eq
split_last = \list, delimiter ->
when List.find_last_index(list, \elem -> elem == delimiter) is
split_last = |list, delimiter|
when List.find_last_index(list, |elem| elem == delimiter) is
Ok(index) ->
before = List.sublist(list, { start: 0, len: index })
after = List.sublist(list, { start: Num.add_wrap(index, 1), len: Num.sub_wrap(List.len(list), index) |> Num.sub_wrap(1) })
@ -1360,15 +1360,15 @@ split_last = \list, delimiter ->
## chunk size. If the provided list is empty or if the chunk size is 0 then the
## result is an empty list.
chunks_of : List a, U64 -> List (List a)
chunks_of = \list, chunk_size ->
if chunk_size == 0 || List.is_empty(list) then
chunks_of = |list, chunk_size|
if chunk_size == 0 or List.is_empty(list) then
[]
else
chunk_capacity = Num.div_ceil(List.len(list), chunk_size)
chunks_of_help(list, chunk_size, List.with_capacity(chunk_capacity))
chunks_of_help : List a, U64, List (List a) -> List (List a)
chunks_of_help = \list_rest, chunk_size, chunks ->
chunks_of_help = |list_rest, chunk_size, chunks|
if List.is_empty(list_rest) then
chunks
else
@ -1379,14 +1379,14 @@ chunks_of_help = \list_rest, chunk_size, chunks ->
## If that function ever returns `Err`, [map_try] immediately returns that `Err`.
## If it returns `Ok` for every element, [map_try] returns `Ok` with the transformed list.
map_try : List elem, (elem -> Result ok err) -> Result (List ok) err
map_try = \list, to_result ->
map_try = |list, to_result|
walk_try(
list,
[],
\state, elem ->
|state, elem|
Result.map_ok(
to_result(elem),
\ok ->
|ok|
List.append(state, ok),
),
)
@ -1403,12 +1403,12 @@ map_try = \list, to_result ->
## As such, it is typically better for performance to use this over [List.walk]
## if returning `Break` earlier than the last element is expected to be common.
walk_try : List elem, state, (state, elem -> Result state err) -> Result state err
walk_try = \list, init, func ->
walk_try = |list, init, func|
walk_try_help(list, init, func, 0, List.len(list))
## internal helper
walk_try_help : List elem, state, (state, elem -> Result state err), U64, U64 -> Result state err
walk_try_help = \list, state, f, index, length ->
walk_try_help = |list, state, f, index, length|
if index < length then
when f(state, List.get_unsafe(list, index)) is
Ok(next_state) -> walk_try_help(list, next_state, f, Num.add_wrap(index, 1), length)
@ -1418,12 +1418,12 @@ walk_try_help = \list, state, f, index, length ->
## Primitive for iterating over a List, being able to decide at every element whether to continue
iterate : List elem, s, (s, elem -> [Continue s, Break b]) -> [Continue s, Break b]
iterate = \list, init, func ->
iterate = |list, init, func|
iter_help(list, init, func, 0, List.len(list))
## internal helper
iter_help : List elem, s, (s, elem -> [Continue s, Break b]), U64, U64 -> [Continue s, Break b]
iter_help = \list, state, f, index, length ->
iter_help = |list, state, f, index, length|
if index < length then
when f(state, List.get_unsafe(list, index)) is
Continue(next_state) -> iter_help(list, next_state, f, Num.add_wrap(index, 1), length)
@ -1434,12 +1434,12 @@ iter_help = \list, state, f, index, length ->
## Primitive for iterating over a List from back to front, being able to decide at every
## element whether to continue
iterate_backwards : List elem, s, (s, elem -> [Continue s, Break b]) -> [Continue s, Break b]
iterate_backwards = \list, init, func ->
iterate_backwards = |list, init, func|
iter_backwards_help(list, init, func, List.len(list))
## internal helper
iter_backwards_help : List elem, s, (s, elem -> [Continue s, Break b]), U64 -> [Continue s, Break b]
iter_backwards_help = \list, state, f, prev_index ->
iter_backwards_help = |list, state, f, prev_index|
if prev_index > 0 then
index = Num.sub_wrap(prev_index, 1)
@ -1468,7 +1468,7 @@ expect (List.concat_utf8([1, 2, 3, 4], "🐦")) == [1, 2, 3, 4, 240, 159, 144, 1
##
## If the function might fail or you need to return early, use [for_each_try!].
for_each! : List a, (a => {}) => {}
for_each! = \list, func! ->
for_each! = |list, func!|
when list is
[] ->
{}
@ -1489,7 +1489,7 @@ for_each! = \list, func! ->
## )
## ```
for_each_try! : List a, (a => Result {} err) => Result {} err
for_each_try! = \list, func! ->
for_each_try! = |list, func!|
when list is
[] ->
Ok({})
@ -1513,7 +1513,7 @@ for_each_try! = \list, func! ->
##
## This is the same as [walk], except that the step function can have effects.
walk! : List elem, state, (state, elem => state) => state
walk! = \list, state, func! ->
walk! = |list, state, func!|
when list is
[] ->
state
@ -1540,7 +1540,7 @@ walk! = \list, state, func! ->
##
## This is the same as [walk_try], except that the step function can have effects.
walk_try! : List elem, state, (state, elem => Result state err) => Result state err
walk_try! = \list, state, func! ->
walk_try! = |list, state, func!|
when list is
[] ->
Ok(state)

View file

@ -532,7 +532,7 @@ pi = 3.14159265358979323846264338327950288419716939937510
## Circle constant (τ)
tau : Frac *
tau = 2 * pi
tau = 6.2831853071795864769252867665590057683943387987502
# ------- Functions
## Convert a number to a [Str].
@ -618,10 +618,10 @@ is_gte : Num a, Num a -> Bool
## If either argument is [*NaN*](Num.is_nan), returns `Bool.false` no matter what. (*NaN*
## is [defined to be unordered](https://en.wikipedia.org/wiki/NaN#Comparison_with_NaN).)
is_approx_eq : Frac a, Frac a, { rtol ?? Frac a, atol ?? Frac a } -> Bool
is_approx_eq = \x, y, { rtol ?? 0.00001, atol ?? 0.00000001 } ->
eq = x <= y && x >= y
is_approx_eq = |x, y, { rtol ?? 0.00001, atol ?? 0.00000001 }|
eq = x <= y and x >= y
meets_tolerance = Num.abs_diff(x, y) <= Num.max(atol, (rtol * Num.max(Num.abs(x), Num.abs(y))))
eq || meets_tolerance
eq or meets_tolerance
## Returns `Bool.true` if the number is `0`, and `Bool.false` otherwise.
is_zero : Num a -> Bool
@ -630,21 +630,21 @@ is_zero : Num a -> Bool
##
## Examples of even numbers: 0, 2, 4, 6, 8, -2, -4, -6, -8
is_even : Int a -> Bool
is_even = \x -> Num.is_multiple_of(x, 2)
is_even = |x| Num.is_multiple_of(x, 2)
## A number is odd if dividing it by 2 gives a remainder of 1.
##
## Examples of odd numbers: 1, 3, 5, 7, -1, -3, -5, -7
is_odd : Int a -> Bool
is_odd = \x -> Bool.not(Num.is_multiple_of(x, 2))
is_odd = |x| Bool.not(Num.is_multiple_of(x, 2))
## Positive numbers are greater than `0`.
is_positive : Num a -> Bool
is_positive = \x -> x > 0
is_positive = |x| x > 0
## Negative numbers are less than `0`.
is_negative : Num a -> Bool
is_negative = \x -> x < 0
is_negative = |x| x < 0
to_frac : Num * -> Frac *
@ -709,7 +709,7 @@ abs : Num a -> Num a
## *overflow*. For [F64] and [F32], overflow results in an answer of either
## ∞ or -∞. For all other number types, overflow results in a panic.
abs_diff : Num a, Num a -> Num a
abs_diff = \a, b ->
abs_diff = |a, b|
if a > b then
a - b
else
@ -812,7 +812,7 @@ mul : Num a, Num a -> Num a
## Num.min(3.0, -3.0)
## ```
min : Num a, Num a -> Num a
min = \a, b ->
min = |a, b|
if a < b then
a
else
@ -826,7 +826,7 @@ min = \a, b ->
## Num.max(3.0, -3.0)
## ```
max : Num a, Num a -> Num a
max = \a, b ->
max = |a, b|
if a > b then
a
else
@ -868,7 +868,7 @@ atan : Frac a -> Frac a
sqrt : Frac a -> Frac a
sqrt_checked : Frac a -> Result (Frac a) [SqrtOfNegative]
sqrt_checked = \x ->
sqrt_checked = |x|
if x < 0.0 then
Err(SqrtOfNegative)
else
@ -878,7 +878,7 @@ sqrt_checked = \x ->
log : Frac a -> Frac a
log_checked : Frac a -> Result (Frac a) [LogNeedsPositive]
log_checked = \x ->
log_checked = |x|
if x <= 0.0 then
Err(LogNeedsPositive)
else
@ -918,7 +918,7 @@ log_checked = \x ->
div : Frac a, Frac a -> Frac a
div_checked : Frac a, Frac a -> Result (Frac a) [DivByZero]
div_checked = \a, b ->
div_checked = |a, b|
if Num.is_zero(b) then
Err(DivByZero)
else
@ -927,7 +927,7 @@ div_checked = \a, b ->
div_ceil : Int a, Int a -> Int a
div_ceil_checked : Int a, Int a -> Result (Int a) [DivByZero]
div_ceil_checked = \a, b ->
div_ceil_checked = |a, b|
if Num.is_zero(b) then
Err(DivByZero)
else
@ -950,14 +950,14 @@ div_ceil_checked = \a, b ->
## Num.div_trunc(8, -3)
## ```
div_trunc : Int a, Int a -> Int a
div_trunc = \a, b ->
div_trunc = |a, b|
if Num.is_zero(b) then
crash("Integer division by 0!")
else
Num.div_trunc_unchecked(a, b)
div_trunc_checked : Int a, Int a -> Result (Int a) [DivByZero]
div_trunc_checked = \a, b ->
div_trunc_checked = |a, b|
if Num.is_zero(b) then
Err(DivByZero)
else
@ -979,14 +979,14 @@ div_trunc_unchecked : Int a, Int a -> Int a
## Num.rem(-8, -3)
## ```
rem : Int a, Int a -> Int a
rem = \a, b ->
rem = |a, b|
if Num.is_zero(b) then
crash("Integer division by 0!")
else
Num.rem_unchecked(a, b)
rem_checked : Int a, Int a -> Result (Int a) [DivByZero]
rem_checked = \a, b ->
rem_checked = |a, b|
if Num.is_zero(b) then
Err(DivByZero)
else
@ -1013,7 +1013,7 @@ bitwise_or : Int a, Int a -> Int a
## Returns the complement of x - the number you get by switching each 1 for a
## 0 and each 0 for a 1. This is the same as -x - 1.
bitwise_not : Int a -> Int a
bitwise_not = \n ->
bitwise_not = |n|
bitwise_xor(n, sub_wrap(0, 1))
## Bitwise left shift of a number by another
@ -1134,7 +1134,7 @@ add_saturated : Num a, Num a -> Num a
## This is the same as [Num.add] except if the operation overflows, instead of
## panicking or returning ∞ or -∞, it will return `Err Overflow`.
add_checked : Num a, Num a -> Result (Num a) [Overflow]
add_checked = \a, b ->
add_checked = |a, b|
result = add_checked_lowlevel(a, b)
if result.b then
@ -1160,7 +1160,7 @@ sub_saturated : Num a, Num a -> Num a
## This is the same as [Num.sub] except if the operation overflows, instead of
## panicking or returning ∞ or -∞, it will return `Err Overflow`.
sub_checked : Num a, Num a -> Result (Num a) [Overflow]
sub_checked = \a, b ->
sub_checked = |a, b|
result = sub_checked_lowlevel(a, b)
if result.b then
@ -1184,7 +1184,7 @@ mul_saturated : Num a, Num a -> Num a
## This is the same as [Num.mul] except if the operation overflows, instead of
## panicking or returning ∞ or -∞, it will return `Err Overflow`.
mul_checked : Num a, Num a -> Result (Num a) [Overflow]
mul_checked = \a, b ->
mul_checked = |a, b|
result = mul_checked_lowlevel(a, b)
if result.b then
@ -1464,7 +1464,7 @@ f64_from_parts : { sign : Bool, exponent : U16, fraction : U64 } -> F64
## expect Num.from_bool(Bool.false) == 0
## ```
from_bool : Bool -> Num *
from_bool = \bool ->
from_bool = |bool|
if bool then
1
else

View file

@ -23,7 +23,7 @@ Result ok err : [Ok ok, Err err]
## Result.is_ok(Ok(5))
## ```
is_ok : Result ok err -> Bool
is_ok = \result ->
is_ok = |result|
when result is
Ok(_) -> Bool.true
Err(_) -> Bool.false
@ -33,7 +33,7 @@ is_ok = \result ->
## Result.is_err(Err("uh oh"))
## ```
is_err : Result ok err -> Bool
is_err = \result ->
is_err = |result|
when result is
Ok(_) -> Bool.false
Err(_) -> Bool.true
@ -45,7 +45,7 @@ is_err = \result ->
## Result.with_default(Err("uh oh"), 42)
## ```
with_default : Result ok err, ok -> ok
with_default = \result, default ->
with_default = |result, default|
when result is
Ok(value) -> value
Err(_) -> default
@ -61,7 +61,7 @@ with_default = \result, default ->
## Functions like `map` are common in Roc; see for example [List.map],
## `Set.map`, and `Dict.map`.
map_ok : Result a err, (a -> b) -> Result b err
map_ok = \result, transform ->
map_ok = |result, transform|
when result is
Ok(v) -> Ok(transform(v))
Err(e) -> Err(e)
@ -74,14 +74,14 @@ map_ok = \result, transform ->
## Result.map_err(Ok(12), Str.is_empty)
## ```
map_err : Result ok a, (a -> b) -> Result ok b
map_err = \result, transform ->
map_err = |result, transform|
when result is
Ok(v) -> Ok(v)
Err(e) -> Err(transform(e))
## Maps both the `Ok` and `Err` values of a `Result` to new values.
map_both : Result ok1 err1, (ok1 -> ok2), (err1 -> err2) -> Result ok2 err2
map_both = \result, ok_transform, err_transform ->
map_both = |result, ok_transform, err_transform|
when result is
Ok(val) -> Ok(ok_transform(val))
Err(err) -> Err(err_transform(err))
@ -89,7 +89,7 @@ map_both = \result, ok_transform, err_transform ->
## Maps the `Ok` values of two `Result`s to a new value using a given transformation,
## or returns the first `Err` value encountered.
map2 : Result a err, Result b err, (a, b -> c) -> Result c err
map2 = \first_result, second_result, transform ->
map2 = |first_result, second_result, transform|
when (first_result, second_result) is
(Ok(first), Ok(second)) -> Ok(transform(first, second))
(Err(err), _) -> Err(err)
@ -103,7 +103,7 @@ map2 = \first_result, second_result, transform ->
## Result.try(Err("yipes!"), (\num -> if num < 0 then Err("negative!") else Ok(-num)))
## ```
try : Result a err, (a -> Result b err) -> Result b err
try = \result, transform ->
try = |result, transform|
when result is
Ok(v) -> transform(v)
Err(e) -> Err(e)
@ -116,7 +116,7 @@ try = \result, transform ->
## Result.on_err(Err("42"), (\error_num -> Str.to_u64(error_num)))
## ```
on_err : Result a err, (err -> Result a other_err) -> Result a other_err
on_err = \result, transform ->
on_err = |result, transform|
when result is
Ok(v) -> Ok(v)
Err(e) -> transform(e)
@ -132,7 +132,7 @@ on_err = \result, transform ->
## )
## ```
on_err! : Result a err, (err => Result a other_err) => Result a other_err
on_err! = \result, transform! ->
on_err! = |result, transform!|
when result is
Ok(v) -> Ok(v)
Err(e) -> transform!(e)

View file

@ -47,14 +47,14 @@ Set k := Dict.Dict k {} where k implements Hash & Eq
]
is_eq : Set k, Set k -> Bool
is_eq = \xs, ys ->
is_eq = |xs, ys|
if len(xs) != len(ys) then
Bool.false
else
walk_until(
xs,
Bool.true,
\_, elem ->
|_, elem|
if contains(ys, elem) then
Continue(Bool.true)
else
@ -62,12 +62,12 @@ is_eq = \xs, ys ->
)
hash_set : hasher, Set k -> hasher where hasher implements Hasher
hash_set = \hasher, @Set(inner) -> Hash.hash(hasher, inner)
hash_set = |hasher, @Set(inner)| Hash.hash(hasher, inner)
to_inspector_set : Set k -> Inspector f where k implements Inspect & Hash & Eq, f implements InspectFormatter
to_inspector_set = \set ->
to_inspector_set = |set|
Inspect.custom(
\fmt ->
|fmt|
Inspect.apply(Inspect.set(set, walk, Inspect.to_inspector), fmt),
)
@ -79,25 +79,25 @@ to_inspector_set = \set ->
## expect count_values == 0
## ```
empty : {} -> Set *
empty = \{} -> @Set(Dict.empty({}))
empty = |{}| @Set(Dict.empty({}))
## Return a set with space allocated for a number of entries. This
## may provide a performance optimization if you know how many entries will be
## inserted.
with_capacity : U64 -> Set *
with_capacity = \cap ->
with_capacity = |cap|
@Set(Dict.with_capacity(cap))
## Enlarge the set for at least capacity additional elements
reserve : Set k, U64 -> Set k
reserve = \@Set(dict), requested ->
reserve = |@Set(dict), requested|
@Set(Dict.reserve(dict, requested))
## Shrink the memory footprint of a set such that capacity is as small as possible.
## This function will require regenerating the metadata if the size changes.
## There will still be some overhead due to dictionary metadata always being a power of 2.
release_excess_capacity : Set k -> Set k
release_excess_capacity = \@Set(dict) ->
release_excess_capacity = |@Set(dict)|
@Set(Dict.release_excess_capacity(dict))
## Creates a new `Set` with a single value.
@ -108,7 +108,7 @@ release_excess_capacity = \@Set(dict) ->
## expect count_values == 1
## ```
single : k -> Set k
single = \key ->
single = |key|
Dict.single(key, {}) |> @Set
## Insert a value into a `Set`.
@ -124,7 +124,7 @@ single = \key ->
## expect count_values == 3
## ```
insert : Set k, k -> Set k
insert = \@Set(dict), key ->
insert = |@Set(dict), key|
Dict.insert(dict, key, {}) |> @Set
# Inserting a duplicate key has no effect.
@ -157,7 +157,7 @@ expect
## expect count_values == 3
## ```
len : Set * -> U64
len = \@Set(dict) ->
len = |@Set(dict)|
Dict.len(dict)
## Returns the max number of elements the set can hold before requiring a rehash.
@ -169,7 +169,7 @@ len = \@Set(dict) ->
## capacity_of_set = Set.capacity(food_set)
## ```
capacity : Set * -> U64
capacity = \@Set(dict) ->
capacity = |@Set(dict)|
Dict.capacity(dict)
## Check if the set is empty.
@ -179,7 +179,7 @@ capacity = \@Set(dict) ->
## Set.is_empty(Set.empty({}))
## ```
is_empty : Set * -> Bool
is_empty = \@Set(dict) ->
is_empty = |@Set(dict)|
Dict.is_empty(dict)
# Inserting a duplicate key has no effect on length.
@ -209,7 +209,7 @@ expect
## expect has20 == Bool.true
## ```
remove : Set k, k -> Set k
remove = \@Set(dict), key ->
remove = |@Set(dict), key|
Dict.remove(dict, key) |> @Set
## Test if a value is in the `Set`.
@ -228,7 +228,7 @@ remove = \@Set(dict), key ->
## expect has_banana == Bool.false
## ```
contains : Set k, k -> Bool
contains = \@Set(dict), key ->
contains = |@Set(dict), key|
Dict.contains(dict, key)
## Retrieve the values in a `Set` as a `List`.
@ -241,7 +241,7 @@ contains = \@Set(dict), key ->
## expect Set.to_list(numbers) == values
## ```
to_list : Set k -> List k
to_list = \@Set(dict) ->
to_list = |@Set(dict)|
Dict.keys(dict)
## Create a `Set` from a `List` of values.
@ -255,9 +255,9 @@ to_list = \@Set(dict) ->
## expect Set.from_list([Pear, Apple, Banana]) == values
## ```
from_list : List k -> Set k
from_list = \list ->
from_list = |list|
list
|> List.map(\k -> (k, {}))
|> List.map(|k| (k, {}))
|> Dict.from_list
|> @Set
@ -272,7 +272,7 @@ from_list = \list ->
## expect Set.union(set1, set2) == Set.from_list([Left, Right])
## ```
union : Set k, Set k -> Set k
union = \@Set(dict1), @Set(dict2) ->
union = |@Set(dict1), @Set(dict2)|
Dict.insert_all(dict1, dict2) |> @Set
## Combine two `Set`s by keeping the [intersection](https://en.wikipedia.org/wiki/Intersection_(set_theory))
@ -285,7 +285,7 @@ union = \@Set(dict1), @Set(dict2) ->
## expect Set.intersection(set1, set2) == Set.single(Left)
## ```
intersection : Set k, Set k -> Set k
intersection = \@Set(dict1), @Set(dict2) ->
intersection = |@Set(dict1), @Set(dict2)|
Dict.keep_shared(dict1, dict2) |> @Set
## Remove the values in the first `Set` that are also in the second `Set`
@ -299,7 +299,7 @@ intersection = \@Set(dict1), @Set(dict2) ->
## expect Set.difference(first, second) == Set.from_list([Up, Down])
## ```
difference : Set k, Set k -> Set k
difference = \@Set(dict1), @Set(dict2) ->
difference = |@Set(dict1), @Set(dict2)|
Dict.remove_all(dict1, dict2) |> @Set
## Iterate through the values of a given `Set` and build a value.
@ -322,20 +322,20 @@ difference = \@Set(dict1), @Set(dict2) ->
## expect result == 2
## ```
walk : Set k, state, (state, k -> state) -> state
walk = \@Set(dict), state, step ->
Dict.walk(dict, state, \s, k, _ -> step(s, k))
walk = |@Set(dict), state, step|
Dict.walk(dict, state, |s, k, _| step(s, k))
## Convert each value in the set to something new, by calling a conversion
## function on each of them which receives the old value. Then return a
## new set containing the converted values.
map : Set a, (a -> b) -> Set b
map = \set, transform ->
map = |set, transform|
init = with_capacity(capacity(set))
walk(
set,
init,
\answer, k ->
|answer, k|
insert(answer, transform(k)),
)
@ -345,13 +345,13 @@ map = \set, transform ->
##
## You may know a similar function named `concat_map` in other languages.
join_map : Set a, (a -> Set b) -> Set b
join_map = \set, transform ->
join_map = |set, transform|
init = with_capacity(capacity(set)) # Might be a pessimization
walk(
set,
init,
\answer, k ->
|answer, k|
union(answer, transform(k)),
)
@ -371,8 +371,8 @@ join_map = \set, transform ->
## expect result == FoundTheAnswer
## ```
walk_until : Set k, state, (state, k -> [Continue state, Break state]) -> state
walk_until = \@Set(dict), state, step ->
Dict.walk_until(dict, state, \s, k, _ -> step(s, k))
walk_until = |@Set(dict), state, step|
Dict.walk_until(dict, state, |s, k, _| step(s, k))
## Run the given function on each element in the `Set`, and return
## a `Set` with just the elements for which the function returned `Bool.true`.
@ -382,8 +382,8 @@ walk_until = \@Set(dict), state, step ->
## |> Bool.is_eq(Set.from_list([3,4,5]))
## ```
keep_if : Set k, (k -> Bool) -> Set k
keep_if = \@Set(dict), predicate ->
@Set(Dict.keep_if(dict, \(k, _v) -> predicate(k)))
keep_if = |@Set(dict), predicate|
@Set(Dict.keep_if(dict, |(k, _v)| predicate(k)))
## Run the given function on each element in the `Set`, and return
## a `Set` with just the elements for which the function returned `Bool.false`.
@ -393,8 +393,8 @@ keep_if = \@Set(dict), predicate ->
## |> Bool.is_eq(Set.from_list([1,2]))
## ```
drop_if : Set k, (k -> Bool) -> Set k
drop_if = \@Set(dict), predicate ->
@Set(Dict.drop_if(dict, \(k, _v) -> predicate(k)))
drop_if = |@Set(dict), predicate|
@Set(Dict.drop_if(dict, |(k, _v)| predicate(k)))
expect
first =
@ -498,10 +498,10 @@ expect
expect
Set.from_list([1, 2, 3, 4, 5])
|> Set.keep_if(\k -> k >= 3)
|> Set.keep_if(|k| k >= 3)
|> Bool.is_eq(Set.from_list([3, 4, 5]))
expect
Set.from_list([1, 2, 3, 4, 5])
|> Set.drop_if(\k -> k >= 3)
|> Set.drop_if(|k| k >= 3)
|> Bool.is_eq(Set.from_list([1, 2]))

View file

@ -328,7 +328,6 @@
## Currently, the only way to get seamless slices of strings is by calling certain `Str` functions which return them. In general, `Str` functions which accept a string and return a subset of that string tend to do this. [`Str.trim`](https://www.roc-lang.org/builtins/Str#trim) is another example of a function which returns a seamless slice.
module [
Utf8Problem,
Utf8ByteProblem,
concat,
is_empty,
join_with,
@ -337,6 +336,11 @@ module [
count_utf8_bytes,
to_utf8,
from_utf8,
from_utf16,
from_utf32,
from_utf8_lossy,
from_utf16_lossy,
from_utf32_lossy,
starts_with,
ends_with,
trim,
@ -369,6 +373,9 @@ module [
contains,
drop_prefix,
drop_suffix,
with_ascii_lowercased,
with_ascii_uppercased,
caseless_ascii_equals,
]
import Bool exposing [Bool]
@ -376,7 +383,7 @@ import Result exposing [Result]
import List
import Num exposing [Num, U8, U16, U32, U64, U128, I8, I16, I32, I64, I128, F32, F64, Dec]
Utf8ByteProblem : [
Utf8Problem : [
InvalidStartByte,
UnexpectedEndOfSequence,
ExpectedContinuation,
@ -385,8 +392,6 @@ Utf8ByteProblem : [
EncodesSurrogateHalf,
]
Utf8Problem : { byte_index : U64, problem : Utf8ByteProblem }
## Returns [Bool.true] if the string is empty, and [Bool.false] otherwise.
## ```roc
## expect Str.is_empty("hi!") == Bool.false
@ -538,8 +543,8 @@ to_utf8 : Str -> List U8
## expect Str.from_utf8([]) == Ok("")
## expect Str.from_utf8([255]) |> Result.is_err
## ```
from_utf8 : List U8 -> Result Str [BadUtf8 { problem : Utf8ByteProblem, index : U64 }]
from_utf8 = \bytes ->
from_utf8 : List U8 -> Result Str [BadUtf8 { problem : Utf8Problem, index : U64 }]
from_utf8 = |bytes|
result = from_utf8_lowlevel bytes
if result.c_is_ok then
@ -557,11 +562,242 @@ FromUtf8Result : {
a_byte_index : U64,
b_string : Str,
c_is_ok : Bool,
d_problem_code : Utf8ByteProblem,
d_problem_code : Utf8Problem,
}
from_utf8_lowlevel : List U8 -> FromUtf8Result
## Converts a [List] of [U8] UTF-8 [code units](https://unicode.org/glossary/#code_unit) to a string.
## Any grouping of invalid byte sequences are replaced with a single unicode replacement character '<27>'.
##
## An invalid byte sequence is defined as
## - a 2-byte-sequence starting byte, followed by less than 1 continuation byte
## - a 3-byte-sequence starting byte, followed by less than 2 continuation bytes
## - a 4-byte-sequence starting byte, followed by less than 3 continuation bytes
## - an invalid codepoint from the surrogate pair block
## - an invalid codepoint greater than 0x110000 encoded as a 4-byte sequence
## - any valid codepoint encoded as an incorrect sequence, for instance a codepoint that should be a 2-byte sequence encoded as a 3- or 4-byte sequence
##
## ```roc
## expect (Str.from_utf8_lossy [82, 111, 99, 240, 159, 144, 166]) == "Roc🐦"
## expect (Str.from_utf8_lossy [82, 255, 99]) == "R<>c"
## expect (Str.from_utf8_lossy [82, 0xED, 0xA0, 0xBD, 99]) == "R<>c"
## ```
from_utf8_lossy : List U8 -> Str
expect (Str.from_utf8_lossy [82, 111, 99, 240, 159, 144, 166]) == "Roc🐦"
expect (Str.from_utf8_lossy [82, 255, 99]) == "R<>c"
expect (Str.from_utf8_lossy [82, 0xED, 0xA0, 0xBD, 99]) == "R<>c"
## Converts a [List] of [U16] UTF-16 (little-endian) [code units](https://unicode.org/glossary/#code_unit) to a string.
##
## ```roc
## expect Str.from_utf16([82, 111, 99]) == Ok("Roc")
## expect Str.from_utf16([0xb9a, 0xbbf]) == Ok("சி")
## expect Str.from_utf16([0xd83d, 0xdc26]) == Ok("🐦")
## expect Str.from_utf16([]) == Ok("")
## # unpaired surrogates, first and second halves
## expect Str.from_utf16([82, 0xd83d, 99]) |> Result.isErr
## expect Str.from_utf16([82, 0xdc96, 99]) |> Result.isErr
## ```
from_utf16 : List U16 -> Result Str [BadUtf16 { problem : Utf8Problem, index : U64 }]
from_utf16 = |codeunits|
mk_err = |problem, index|
Err(BadUtf16({ problem, index }))
step = |state, unit|
c : U32
c = Num.int_cast(unit)
when state is
ExpectFirst(i, utf8) ->
if unit < 0xd800 then
when encode_utf8(utf8, c) is
Ok(utf8_next) -> ExpectFirst(i + 1, utf8_next)
Err(err) -> mk_err(err, i)
else
ExpectSecond(i, utf8, c)
ExpectSecond(i, utf8, first) ->
if unit < 0xdc00 then
mk_err(EncodesSurrogateHalf, i)
else
joined = ((first - 0xd800) * 0x400) + (c - 0xdc00) + 0x10000
when encode_utf8(utf8, joined) is
Ok(utf8_next) -> ExpectFirst(i + 2, utf8_next)
Err(err) -> mk_err(err, i)
Err(err) -> Err(err)
decode_res = List.walk(codeunits, ExpectFirst(0, []), step)
when decode_res is
ExpectFirst(_, utf8) ->
from_utf8(utf8)
|> Result.map_err(|BadUtf8(err)| BadUtf16(err))
ExpectSecond(i, _, _) ->
mk_err(EncodesSurrogateHalf, i)
Err(err) -> Err(err)
expect Str.from_utf16([82, 111, 99]) == Ok("Roc")
expect Str.from_utf16([0xb9a, 0xbbf]) == Ok("சி")
expect Str.from_utf16([0xd83d, 0xdc26]) == Ok("🐦")
expect Str.from_utf16([]) == Ok("")
# unpaired surrogates, first and second halves
expect Str.from_utf16([82, 0xd83d, 99]) == Err(BadUtf16({ index: 1, problem: EncodesSurrogateHalf }))
expect Str.from_utf16([82, 0xdc96, 99]) == Err(BadUtf16({ index: 1, problem: EncodesSurrogateHalf }))
## Converts a [List] of [U16] UTF-16 (little-endian) [code units](https://unicode.org/glossary/#code_unit) to a string.
## Any unpaired surrogate code unit is replaced with a single unicode replacement character '<27>'.
##
## ```roc
## expect Str.from_utf16_lossy([82, 111, 99, 0xd83d, 0xdc26]) == "Roc🐦"
## expect Str.from_utf16_lossy([82, 0xdc96, 99]) == "R<>c"
## ```
from_utf16_lossy : List U16 -> Str
from_utf16_lossy = |codeunits|
utf8_replacement = [0xef, 0xbf, 0xbd]
encode_lossy = |utf8, c|
when encode_utf8(utf8, c) is
Ok(utf8_next) -> utf8_next
Err(_) -> List.concat(utf8, utf8_replacement)
step = |state, unit|
c : U32
c = Num.int_cast(unit)
when state is
ExpectFirst(utf8) ->
if unit < 0xd800 then
ExpectFirst(encode_lossy(utf8, c))
else
ExpectSecond(utf8, c)
ExpectSecond(utf8, first) ->
if c < 0xd800 then
ExpectFirst(
List.concat(utf8, utf8_replacement)
|> encode_lossy(c),
)
else if c < 0xdc00 then
ExpectSecond(List.concat(utf8, utf8_replacement), c)
else
joined = ((first - 0xd800) * 0x400) + (c - 0xdc00) + 0x10000
ExpectFirst(encode_lossy(utf8, joined))
result = List.walk(codeunits, ExpectFirst([]), step)
when result is
ExpectFirst(utf8) -> from_utf8_lossy(utf8)
ExpectSecond(utf8, _) -> from_utf8_lossy(List.concat(utf8, utf8_replacement))
expect Str.from_utf16_lossy([82, 111, 99, 0xd83d, 0xdc26]) == "Roc🐦"
expect Str.from_utf16_lossy([82, 0xdc96, 99]) == "R<>c"
## Converts a [List] of [U32] UTF-32 [code units](https://unicode.org/glossary/#code_unit) to a string.
##
## ```roc
## expect Str.from_utf32([82, 111, 99]) == Ok("Roc")
## expect Str.from_utf32([0xb9a, 0xbbf]) == Ok("சி")
## expect Str.from_utf32([0x1f426]) == Ok("🐦")
## # unpaired surrogates, first and second halves
## expect Str.from_utf32([82, 0xd83d, 99]) |> Result.isErr
## expect Str.from_utf32([82, 0xdc96, 99]) |> Result.isErr
## # invalid codepoint
## expect Str.from_utf32([82, 0x110000, 99]) |> Result.isErr
## ```
from_utf32 : List U32 -> Result Str [BadUtf32 { problem : Utf8Problem, index : U64 }]
from_utf32 = |codepoints|
step = |state, c|
when state is
Ok({ i, utf8 }) ->
when encode_utf8(utf8, c) is
Ok(utf8_next) -> Ok({ i: i + 1, utf8: utf8_next })
Err(problem) -> Err(BadUtf32({ problem, index: i }))
Err(err) -> Err(err)
List.walk(codepoints, Ok({ i: 0, utf8: [] }), step)
|> Result.try(
|state|
when from_utf8(state.utf8) is
Ok(str) -> Ok(str)
Err(BadUtf8(err)) -> Err(BadUtf32(err)),
)
encode_utf8 : List U8, U32 -> Result (List U8) [EncodesSurrogateHalf, CodepointTooLarge]
encode_utf8 = |list, c|
if c < 0x80 then
Ok(List.append(list, Num.int_cast(c)))
else if c < 0x800 then
Ok(
List.concat(
list,
[
Num.int_cast(Num.bitwise_or(Num.shift_right_by(c, 6), 0b110_00000)),
Num.int_cast(Num.bitwise_or(Num.bitwise_and(c, 0b111111), 0b10_000000)),
],
),
)
else if c < 0x10000 then
if (c >= 0xd800) and (c < 0xe000) then
Err(EncodesSurrogateHalf)
else
Ok(
List.concat(
list,
[
Num.int_cast(Num.bitwise_or(Num.shift_right_by(c, 12), 0b1110_0000)),
Num.int_cast(Num.bitwise_or(Num.bitwise_and(Num.shift_right_by(c, 6), 0b111111), 0b10_000000)),
Num.int_cast(Num.bitwise_or(Num.bitwise_and(c, 0b111111), 0b10_000000)),
],
),
)
else if c < 0x110000 then
Ok(
List.concat(
list,
[
Num.int_cast(Num.bitwise_or(Num.shift_right_by(c, 18), 0b11110_000)),
Num.int_cast(Num.bitwise_or(Num.bitwise_and(Num.shift_right_by(c, 12), 0b111111), 0b10_000000)),
Num.int_cast(Num.bitwise_or(Num.bitwise_and(Num.shift_right_by(c, 6), 0b111111), 0b10_000000)),
Num.int_cast(Num.bitwise_or(Num.bitwise_and(c, 0b111111), 0b10_000000)),
],
),
)
else
Err(CodepointTooLarge)
expect Str.from_utf32([82, 111, 99]) == Ok("Roc")
expect Str.from_utf32([0xb9a, 0xbbf]) == Ok("சி")
expect Str.from_utf32([0x1f426]) == Ok("🐦")
expect Str.from_utf32([]) == Ok("")
# unpaired surrogates, first and second halves
expect Str.from_utf32([82, 0xd83d, 99]) |> Result.is_err
expect Str.from_utf32([82, 0xdc96, 99]) |> Result.is_err
# codepoint out of valid range
expect Str.from_utf32([82, 0x110000, 99]) |> Result.is_err
## Converts a [List] of [U32] UTF-32 [code units](https://unicode.org/glossary/#code_unit) to a string.
## Any invalid code points are replaced with a single unicode replacement character '<27>'.
## ```roc
## expect Str.from_utf32_lossy([82, 111, 99, 0x1f426]) == "Roc🐦"
## expect Str.from_utf32_lossy([82, 0x110000, 99]) == "R<>c"
## ```
from_utf32_lossy : List U32 -> Str
from_utf32_lossy = |codepoints|
step = |utf8, c|
when encode_utf8(utf8, c) is
Ok(utf8_next) -> utf8_next
# utf-8 encoded replacement character
Err(_) -> List.concat(utf8, [0xef, 0xbf, 0xbd])
List.walk(codepoints, [], step)
|> from_utf8_lossy()
expect Str.from_utf32_lossy([82, 111, 99, 0x1f426]) == "Roc🐦"
expect Str.from_utf32_lossy([82, 0x110000, 99]) == "R<>c"
## Check if the given [Str] starts with a value.
## ```roc
## expect Str.starts_with("ABC", "A") == Bool.true
@ -603,7 +839,7 @@ trim_end : Str -> Str
## expect Str.to_dec("not a number") == Err(InvalidNumStr)
## ```
to_dec : Str -> Result Dec [InvalidNumStr]
to_dec = \string -> str_to_num_help(string)
to_dec = |string| str_to_num_help(string)
## Encode a [Str] to a [F64]. A [F64] value is a 64-bit
## [floating-point number](https://en.wikipedia.org/wiki/IEEE_754) and can be
@ -613,7 +849,7 @@ to_dec = \string -> str_to_num_help(string)
## expect Str.to_f64("not a number") == Err(InvalidNumStr)
## ```
to_f64 : Str -> Result F64 [InvalidNumStr]
to_f64 = \string -> str_to_num_help(string)
to_f64 = |string| str_to_num_help(string)
## Encode a [Str] to a [F32].A [F32] value is a 32-bit
## [floating-point number](https://en.wikipedia.org/wiki/IEEE_754) and can be
@ -623,7 +859,7 @@ to_f64 = \string -> str_to_num_help(string)
## expect Str.to_f32("not a number") == Err(InvalidNumStr)
## ```
to_f32 : Str -> Result F32 [InvalidNumStr]
to_f32 = \string -> str_to_num_help(string)
to_f32 = |string| str_to_num_help(string)
## Encode a [Str] to an unsigned [U128] integer. A [U128] value can hold numbers
## from `0` to `340_282_366_920_938_463_463_374_607_431_768_211_455` (over
@ -635,7 +871,7 @@ to_f32 = \string -> str_to_num_help(string)
## expect Str.to_u128("not a number") == Err(InvalidNumStr)
## ```
to_u128 : Str -> Result U128 [InvalidNumStr]
to_u128 = \string -> str_to_num_help(string)
to_u128 = |string| str_to_num_help(string)
## Encode a [Str] to a signed [I128] integer. A [I128] value can hold numbers
## from `-170_141_183_460_469_231_731_687_303_715_884_105_728` to
@ -648,7 +884,7 @@ to_u128 = \string -> str_to_num_help(string)
## expect Str.to_i128("not a number") == Err(InvalidNumStr)
## ```
to_i128 : Str -> Result I128 [InvalidNumStr]
to_i128 = \string -> str_to_num_help(string)
to_i128 = |string| str_to_num_help(string)
## Encode a [Str] to an unsigned [U64] integer. A [U64] value can hold numbers
## from `0` to `18_446_744_073_709_551_615` (over 18 quintillion). It
@ -660,7 +896,7 @@ to_i128 = \string -> str_to_num_help(string)
## expect Str.to_u64("not a number") == Err(InvalidNumStr)
## ```
to_u64 : Str -> Result U64 [InvalidNumStr]
to_u64 = \string -> str_to_num_help(string)
to_u64 = |string| str_to_num_help(string)
## Encode a [Str] to a signed [I64] integer. A [I64] value can hold numbers
## from `-9_223_372_036_854_775_808` to `9_223_372_036_854_775_807`. It can be
@ -672,7 +908,7 @@ to_u64 = \string -> str_to_num_help(string)
## expect Str.to_i64("not a number") == Err(InvalidNumStr)
## ```
to_i64 : Str -> Result I64 [InvalidNumStr]
to_i64 = \string -> str_to_num_help(string)
to_i64 = |string| str_to_num_help(string)
## Encode a [Str] to an unsigned [U32] integer. A [U32] value can hold numbers
## from `0` to `4_294_967_295` (over 4 billion). It can be specified with
@ -684,7 +920,7 @@ to_i64 = \string -> str_to_num_help(string)
## expect Str.to_u32("not a number") == Err(InvalidNumStr)
## ```
to_u32 : Str -> Result U32 [InvalidNumStr]
to_u32 = \string -> str_to_num_help(string)
to_u32 = |string| str_to_num_help(string)
## Encode a [Str] to a signed [I32] integer. A [I32] value can hold numbers
## from `-2_147_483_648` to `2_147_483_647`. It can be
@ -696,7 +932,7 @@ to_u32 = \string -> str_to_num_help(string)
## expect Str.to_i32("not a number") == Err(InvalidNumStr)
## ```
to_i32 : Str -> Result I32 [InvalidNumStr]
to_i32 = \string -> str_to_num_help(string)
to_i32 = |string| str_to_num_help(string)
## Encode a [Str] to an unsigned [U16] integer. A [U16] value can hold numbers
## from `0` to `65_535`. It can be specified with a u16 suffix.
@ -707,7 +943,7 @@ to_i32 = \string -> str_to_num_help(string)
## expect Str.to_u16("not a number") == Err(InvalidNumStr)
## ```
to_u16 : Str -> Result U16 [InvalidNumStr]
to_u16 = \string -> str_to_num_help(string)
to_u16 = |string| str_to_num_help(string)
## Encode a [Str] to a signed [I16] integer. A [I16] value can hold numbers
## from `-32_768` to `32_767`. It can be
@ -719,7 +955,7 @@ to_u16 = \string -> str_to_num_help(string)
## expect Str.to_i16("not a number") == Err(InvalidNumStr)
## ```
to_i16 : Str -> Result I16 [InvalidNumStr]
to_i16 = \string -> str_to_num_help(string)
to_i16 = |string| str_to_num_help(string)
## Encode a [Str] to an unsigned [U8] integer. A [U8] value can hold numbers
## from `0` to `255`. It can be specified with a u8 suffix.
@ -730,7 +966,7 @@ to_i16 = \string -> str_to_num_help(string)
## expect Str.to_u8("1500") == Err(InvalidNumStr)
## ```
to_u8 : Str -> Result U8 [InvalidNumStr]
to_u8 = \string -> str_to_num_help(string)
to_u8 = |string| str_to_num_help(string)
## Encode a [Str] to a signed [I8] integer. A [I8] value can hold numbers
## from `-128` to `127`. It can be
@ -741,7 +977,7 @@ to_u8 = \string -> str_to_num_help(string)
## expect Str.to_i8("not a number") == Err(InvalidNumStr)
## ```
to_i8 : Str -> Result I8 [InvalidNumStr]
to_i8 = \string -> str_to_num_help(string)
to_i8 = |string| str_to_num_help(string)
## Get the byte at the given index, without performing a bounds check.
get_unsafe : Str, U64 -> U8
@ -763,7 +999,7 @@ substring_unsafe : Str, U64, U64 -> Str
## expect Str.replace_each("not here", "/", "_") == "not here"
## ```
replace_each : Str, Str, Str -> Str
replace_each = \haystack, needle, flower ->
replace_each = |haystack, needle, flower|
when split_first(haystack, needle) is
Ok({ before, after }) ->
# We found at least one needle, so start the buffer off with
@ -776,7 +1012,7 @@ replace_each = \haystack, needle, flower ->
Err(NotFound) -> haystack
replace_each_help : Str, Str, Str, Str -> Str
replace_each_help = \buf, haystack, needle, flower ->
replace_each_help = |buf, haystack, needle, flower|
when split_first(haystack, needle) is
Ok({ before, after }) ->
buf
@ -797,7 +1033,7 @@ expect Str.replace_each("abcdefg", "nothing", "_") == "abcdefg"
## expect Str.replace_first("no slashes here", "/", "_") == "no slashes here"
## ```
replace_first : Str, Str, Str -> Str
replace_first = \haystack, needle, flower ->
replace_first = |haystack, needle, flower|
when split_first(haystack, needle) is
Ok({ before, after }) ->
"${before}${flower}${after}"
@ -815,7 +1051,7 @@ expect Str.replace_first("abcdefg", "nothing", "_") == "abcdefg"
## expect Str.replace_last("no slashes here", "/", "_") == "no slashes here"
## ```
replace_last : Str, Str, Str -> Str
replace_last = \haystack, needle, flower ->
replace_last = |haystack, needle, flower|
when split_last(haystack, needle) is
Ok({ before, after }) ->
"${before}${flower}${after}"
@ -833,7 +1069,7 @@ expect Str.replace_last("abcdefg", "nothing", "_") == "abcdefg"
## expect Str.split_first("no slashes here", "/") == Err(NotFound)
## ```
split_first : Str, Str -> Result { before : Str, after : Str } [NotFound]
split_first = \haystack, needle ->
split_first = |haystack, needle|
when first_match(haystack, needle) is
Some(index) ->
remaining = Str.count_utf8_bytes(haystack) - Str.count_utf8_bytes(needle) - index
@ -862,7 +1098,7 @@ expect split_first("hullabaloo", "ab") == Ok({ before: "hull", after: "aloo" })
expect split_first("foo", "foo") == Ok({ before: "", after: "" })
first_match : Str, Str -> [Some U64, None]
first_match = \haystack, needle ->
first_match = |haystack, needle|
haystack_length = Str.count_utf8_bytes(haystack)
needle_length = Str.count_utf8_bytes(needle)
last_possible = Num.sub_saturated(haystack_length, needle_length)
@ -870,7 +1106,7 @@ first_match = \haystack, needle ->
first_match_help(haystack, needle, 0, last_possible)
first_match_help : Str, Str, U64, U64 -> [Some U64, None]
first_match_help = \haystack, needle, index, last_possible ->
first_match_help = |haystack, needle, index, last_possible|
if index <= last_possible then
if matches_at(haystack, index, needle) then
Some(index)
@ -887,7 +1123,7 @@ first_match_help = \haystack, needle, index, last_possible ->
## expect Str.split_last("no slashes here", "/") == Err(NotFound)
## ```
split_last : Str, Str -> Result { before : Str, after : Str } [NotFound]
split_last = \haystack, needle ->
split_last = |haystack, needle|
when last_match(haystack, needle) is
Some(index) ->
remaining = Str.count_utf8_bytes(haystack) - Str.count_utf8_bytes(needle) - index
@ -913,7 +1149,7 @@ expect Str.split_last("hullabaloo", "ab") == Ok({ before: "hull", after: "aloo"
expect Str.split_last("foo", "foo") == Ok({ before: "", after: "" })
last_match : Str, Str -> [Some U64, None]
last_match = \haystack, needle ->
last_match = |haystack, needle|
haystack_length = Str.count_utf8_bytes(haystack)
needle_length = Str.count_utf8_bytes(needle)
last_possible_index = Num.sub_saturated(haystack_length, needle_length)
@ -921,7 +1157,7 @@ last_match = \haystack, needle ->
last_match_help(haystack, needle, last_possible_index)
last_match_help : Str, Str, U64 -> [Some U64, None]
last_match_help = \haystack, needle, index ->
last_match_help = |haystack, needle, index|
if matches_at(haystack, index, needle) then
Some(index)
else
@ -932,10 +1168,10 @@ last_match_help = \haystack, needle, index ->
Err(_) ->
None
min = \x, y -> if x < y then x else y
min = |x, y| if x < y then x else y
matches_at : Str, U64, Str -> Bool
matches_at = \haystack, haystack_index, needle ->
matches_at = |haystack, haystack_index, needle|
haystack_length = Str.count_utf8_bytes(haystack)
needle_length = Str.count_utf8_bytes(needle)
end_index = min(Num.add_saturated(haystack_index, needle_length), haystack_length)
@ -951,7 +1187,7 @@ matches_at = \haystack, haystack_index, needle ->
},
)
matches_at_help = \state ->
matches_at_help = |state|
{ haystack, haystack_index, needle, needle_index, needle_length, end_index } = state
is_at_end_of_haystack = haystack_index >= end_index
@ -972,7 +1208,7 @@ matches_at_help = \state ->
},
)
does_this_match && does_rest_match
does_this_match and does_rest_match
## Walks over the `UTF-8` bytes of the given [Str] and calls a function to update
## state for each byte. The index for that byte in the string is provided
@ -983,11 +1219,11 @@ matches_at_help = \state ->
## expect Str.walk_utf8_with_index("ABC", [], f) == [65, 66, 67]
## ```
walk_utf8_with_index : Str, state, (state, U8, U64 -> state) -> state
walk_utf8_with_index = \string, state, step ->
walk_utf8_with_index = |string, state, step|
walk_utf8_with_index_help(string, state, step, 0, Str.count_utf8_bytes(string))
walk_utf8_with_index_help : Str, state, (state, U8, U64 -> state), U64, U64 -> state
walk_utf8_with_index_help = \string, state, step, index, length ->
walk_utf8_with_index_help = |string, state, step, index, length|
if index < length then
byte = Str.get_unsafe(string, index)
new_state = step(state, byte, index)
@ -1008,11 +1244,11 @@ walk_utf8_with_index_help = \string, state, step, index, length ->
## expect sum_of_utf8_bytes == 105
## ```
walk_utf8 : Str, state, (state, U8 -> state) -> state
walk_utf8 = \str, initial, step ->
walk_utf8 = |str, initial, step|
walk_utf8_help(str, initial, step, 0, Str.count_utf8_bytes(str))
walk_utf8_help : Str, state, (state, U8 -> state), U64, U64 -> state
walk_utf8_help = \str, state, step, index, length ->
walk_utf8_help = |str, state, step, index, length|
if index < length then
byte = Str.get_unsafe(str, index)
new_state = step(state, byte)
@ -1031,7 +1267,7 @@ release_excess_capacity : Str -> Str
str_to_num : Str -> { berrorcode : U8, aresult : Num * }
str_to_num_help : Str -> Result (Num a) [InvalidNumStr]
str_to_num_help = \string ->
str_to_num_help = |string|
result : { berrorcode : U8, aresult : Num a }
result = str_to_num(string)
@ -1045,7 +1281,7 @@ str_to_num_help = \string ->
## expect Str.with_prefix("Awesome", "Roc") == "RocAwesome"
## ```
with_prefix : Str, Str -> Str
with_prefix = \str, prefix -> Str.concat(prefix, str)
with_prefix = |str, prefix| Str.concat(prefix, str)
## Determines whether or not the first Str contains the second.
## ```roc
@ -1054,7 +1290,7 @@ with_prefix = \str, prefix -> Str.concat(prefix, str)
## expect Str.contains("anything", "")
## ```
contains : Str, Str -> Bool
contains = \haystack, needle ->
contains = |haystack, needle|
when first_match(haystack, needle) is
Some(_index) -> Bool.true
None -> Bool.false
@ -1067,7 +1303,7 @@ contains = \haystack, needle ->
## expect Str.drop_prefix("foobar", "foo") == "bar"
## ```
drop_prefix : Str, Str -> Str
drop_prefix = \haystack, prefix ->
drop_prefix = |haystack, prefix|
if Str.starts_with(haystack, prefix) then
start = Str.count_utf8_bytes(prefix)
len = Num.sub_wrap(Str.count_utf8_bytes(haystack), start)
@ -1084,7 +1320,7 @@ drop_prefix = \haystack, prefix ->
## expect Str.drop_suffix("barfoo", "foo") == "bar"
## ```
drop_suffix : Str, Str -> Str
drop_suffix = \haystack, suffix ->
drop_suffix = |haystack, suffix|
if Str.ends_with(haystack, suffix) then
start = 0
len = Num.sub_wrap(Str.count_utf8_bytes(haystack), Str.count_utf8_bytes(suffix))
@ -1092,3 +1328,93 @@ drop_suffix = \haystack, suffix ->
substring_unsafe(haystack, start, len)
else
haystack
## Returns a version of the string with all [ASCII characters](https://en.wikipedia.org/wiki/ASCII) lowercased.
## Non-ASCII characters are left unmodified. For example:
##
## ```roc
## expect Str.with_ascii_lowercased("CAFÉ") == "cafÉ"
## ```
##
## This function is useful for things like [command-line flags](https://en.wikipedia.org/wiki/Command-line_interface#Command-line_option)
## and [environment variable names](https://en.wikipedia.org/wiki/Environment_variable)
## where you know in advance that you're dealing with a string containing only ASCII characters.
## It has better performance than lowercasing operations which take Unicode into account.
##
## That said, strings received from user input can always contain
## non-ASCII Unicode characters, and lowercasing [Unicode](https://unicode.org) works
## differently in different languages. For example, the string `"I"` lowercases to `"i"`
## in English and to `"ı"` (a [dotless i](https://en.wikipedia.org/wiki/Dotless_I))
## in Turkish. These rules can also change in each [Unicode release](https://www.unicode.org/releases/),
## so we have separate [`unicode` package](https://github.com/roc-lang/unicode)
## for Unicode capitalization that can be upgraded independently from the language's builtins.
##
## To do a case-insensitive comparison of the ASCII characters in a string,
## you can use [Str.caseless_ascii_equals].
with_ascii_lowercased : Str -> Str
expect Str.with_ascii_lowercased("CAFÉ") == "cafÉ"
## Returns a version of the string with all [ASCII characters](https://en.wikipedia.org/wiki/ASCII) uppercased.
## Non-ASCII characters are left unmodified. For example:
##
## ```roc
## expect Str.with_ascii_uppercased("café") == "CAFé"
## ```
##
## This function is useful for things like
## [command-line flags](https://en.wikipedia.org/wiki/Command-line_interface#Command-line_option)
## and [environment variable names](https://en.wikipedia.org/wiki/Environment_variable)
## where you know in advance that you're dealing with a string containing only ASCII characters.
## It has better performance than lowercasing operations which take Unicode into account.
##
## That said, strings received from user input can always contain
## non-ASCII Unicode characters, and uppercasing [Unicode](https://unicode.org)
## works differently in different languages.
## For example, the string `"i"` uppercases to `"I"` in English and to `"İ"`
## (a [dotted I](https://en.wikipedia.org/wiki/%C4%B0)) in Turkish.
## These rules can also change in each Unicode release,
## so we have a separate [`unicode` package](https://github.com/roc-lang/unicode) for Unicode capitalization
## that can be upgraded independently from the language's builtins.
##
## To do a case-insensitive comparison of the ASCII characters in a string,
## you can use [Str.caseless_ascii_equals].
with_ascii_uppercased : Str -> Str
expect Str.with_ascii_uppercased("café") == "CAFé"
## Returns `True` if all the [ASCII characters](https://en.wikipedia.org/wiki/ASCII) in the string are the same
## when ignoring differences in capitalization.
## Non-ASCII characters must all be exactly the same,
## including capitalization. For example:
##
## ```roc
## expect Str.caseless_ascii_equals("café", "CAFé")
##
## expect !Str.caseless_ascii_equals("café", "CAFÉ")
## ```
##
## The first call returns `True` because all the ASCII characters are the same
## when ignoring differences in capitalization, and the only non-ASCII character
## (`é`) is the same in both strings. The second call returns `False`because
## `é` and `É` are not ASCII characters, and they are different.
##
## This function is useful for things like [command-line flags](https://en.wikipedia.org/wiki/Command-line_interface#Command-line_option)
## and [environment variable names](https://en.wikipedia.org/wiki/Environment_variable)
## where you know in advance that you're dealing with a string containing only ASCII characters.
## It has better performance than lowercasing operations which take Unicode into account.
##
## That said, strings received from user input can always contain
## non-ASCII Unicode characters, and lowercasing [Unicode](https://unicode.org) works
## differently in different languages. For example, the string `"I"` lowercases to `"i"`
## in English and to `"ı"` (a [dotless i](https://en.wikipedia.org/wiki/Dotless_I))
## in Turkish. These rules can also change in each [Unicode release](https://www.unicode.org/releases/),
## so we have separate [`unicode` package](https://github.com/roc-lang/unicode)
## for Unicode capitalization that can be upgraded independently from the language's builtins.
##
## To convert a string's ASCII characters to uppercase or lowercase, you can use [Str.with_ascii_uppercased]
## or [Str.with_ascii_lowercased].
caseless_ascii_equals : Str, Str -> Bool
expect Str.caseless_ascii_equals("café", "CAFé")
expect !Str.caseless_ascii_equals("café", "CAFÉ")

View file

@ -348,6 +348,7 @@ pub const STR_EQUAL: &str = "roc_builtins.str.equal";
pub const STR_SUBSTRING_UNSAFE: &str = "roc_builtins.str.substring_unsafe";
pub const STR_TO_UTF8: &str = "roc_builtins.str.to_utf8";
pub const STR_FROM_UTF8: &str = "roc_builtins.str.from_utf8";
pub const STR_FROM_UTF8_LOSSY: &str = "roc_builtins.str.from_utf8_lossy";
pub const STR_REPEAT: &str = "roc_builtins.str.repeat";
pub const STR_TRIM: &str = "roc_builtins.str.trim";
pub const STR_TRIM_START: &str = "roc_builtins.str.trim_start";
@ -358,6 +359,9 @@ pub const STR_CLONE_TO: &str = "roc_builtins.str.clone_to";
pub const STR_WITH_CAPACITY: &str = "roc_builtins.str.with_capacity";
pub const STR_ALLOCATION_PTR: &str = "roc_builtins.str.allocation_ptr";
pub const STR_RELEASE_EXCESS_CAPACITY: &str = "roc_builtins.str.release_excess_capacity";
pub const STR_WITH_ASCII_LOWERCASED: &str = "roc_builtins.str.with_ascii_lowercased";
pub const STR_WITH_ASCII_UPPERCASED: &str = "roc_builtins.str.with_ascii_uppercased";
pub const STR_CASELESS_ASCII_EQUALS: &str = "roc_builtins.str.caseless_ascii_equals";
pub const LIST_MAP: &str = "roc_builtins.list.map";
pub const LIST_MAP2: &str = "roc_builtins.list.map2";

View file

@ -8,6 +8,7 @@ license.workspace = true
version.workspace = true
[dependencies]
roc_can_solo.workspace = true
roc_collections.workspace = true
roc_error_macros.workspace = true
roc_exhaustive.workspace = true

View file

@ -119,6 +119,7 @@ map_symbol_to_lowlevel_and_arity! {
StrSplitOn; STR_SPLIT_ON; 2,
StrCountUtf8Bytes; STR_COUNT_UTF8_BYTES; 1,
StrFromUtf8; STR_FROM_UTF8_LOWLEVEL; 1,
StrFromUtf8Lossy; STR_FROM_UTF8_LOSSY; 1,
StrToUtf8; STR_TO_UTF8; 1,
StrRepeat; STR_REPEAT; 2,
StrTrim; STR_TRIM; 1,
@ -130,6 +131,9 @@ map_symbol_to_lowlevel_and_arity! {
StrToNum; STR_TO_NUM; 1,
StrWithCapacity; STR_WITH_CAPACITY; 1,
StrReleaseExcessCapacity; STR_RELEASE_EXCESS_CAPACITY; 1,
StrWithAsciiLowercased; STR_WITH_ASCII_LOWERCASED; 1,
StrWithAsciiUppercased; STR_WITH_ASCII_UPPERCASED; 1,
StrCaselessAsciiEquals; STR_CASELESS_ASCII_EQUALS; 2,
ListLenUsize; LIST_LEN_USIZE; 1,
ListLenU64; LIST_LEN_U64; 1,
@ -212,8 +216,6 @@ map_symbol_to_lowlevel_and_arity! {
Eq; BOOL_STRUCTURAL_EQ; 2,
NotEq; BOOL_STRUCTURAL_NOT_EQ; 2,
And; BOOL_AND; 2,
Or; BOOL_OR; 2,
Not; BOOL_NOT; 1,
BoxExpr; BOX_BOX_FUNCTION; 1,
UnboxExpr; BOX_UNBOX; 1,

View file

@ -915,7 +915,7 @@ pub struct DefTypes {
pub loc_symbols: Slice<(Symbol, Region)>,
}
#[derive(Debug, Clone, Copy)]
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct Generalizable(pub bool);
#[derive(Debug, Clone)]

File diff suppressed because it is too large Load diff

View file

@ -11,6 +11,7 @@ use roc_region::all::{LineInfo, Loc, Region};
use roc_types::subs::Variable;
/// The canonicalization environment for a particular module.
#[derive(Debug)]
pub struct Env<'a> {
/// The module's path. Opaques and unqualified references to identifiers
/// are assumed to be relative to this path.
@ -51,6 +52,36 @@ pub struct Env<'a> {
}
impl<'a> Env<'a> {
#[allow(clippy::too_many_arguments)]
pub fn from_solo_can(
arena: &'a Bump,
module_path: &'a Path,
home: ModuleId,
dep_idents: &'a IdentIdsByModule,
qualified_module_ids: &'a PackageModuleIds<'a>,
problems: Vec<Problem>,
opt_shorthand: Option<&'a str>,
src: &'a str,
line_info: &'a mut Option<LineInfo>,
) -> Self {
Env {
arena,
src,
home,
module_path,
dep_idents,
qualified_module_ids,
problems,
closures: MutMap::default(),
qualified_value_lookups: Default::default(),
tailcallable_symbol: None,
top_level_symbols: Default::default(),
home_params_record: None,
opt_shorthand,
line_info,
}
}
#[allow(clippy::too_many_arguments)]
pub fn new(
arena: &'a Bump,

View file

@ -1,9 +1,6 @@
use std::path::Path;
use crate::abilities::{AbilitiesStore, ImplKey, PendingAbilitiesStore, ResolvedImpl};
use crate::annotation::{canonicalize_annotation, AnnotationFor};
use crate::def::{canonicalize_defs, report_unused_imports, Def, DefKind};
use crate::desugar::desugar_record_destructures;
use crate::env::Env;
use crate::expr::{ClosureData, Declarations, ExpectLookup, Expr, Output, PendingDerives};
use crate::pattern::{
@ -16,8 +13,8 @@ use roc_collections::{MutMap, SendMap, VecMap, VecSet};
use roc_error_macros::internal_error;
use roc_module::ident::Ident;
use roc_module::ident::Lowercase;
use roc_module::symbol::{IdentId, IdentIds, IdentIdsByModule, ModuleId, PackageModuleIds, Symbol};
use roc_parse::ast::{Defs, TypeAnnotation};
use roc_module::symbol::{IdentId, ModuleId, Symbol};
use roc_parse::ast::{Collection, Defs, Pattern as ParsePattern, TypeAnnotation};
use roc_parse::header::HeaderType;
use roc_parse::pattern::PatternType;
use roc_problem::can::{Problem, RuntimeError};
@ -209,55 +206,19 @@ fn has_no_implementation(expr: &Expr) -> bool {
#[allow(clippy::too_many_arguments)]
pub fn canonicalize_module_defs<'a>(
arena: &'a Bump,
loc_defs: &'a mut Defs<'a>,
header_type: &'a roc_parse::header::HeaderType,
home: ModuleId,
module_path: &'a str,
src: &'a str,
qualified_module_ids: &'a PackageModuleIds<'a>,
exposed_ident_ids: IdentIds,
dep_idents: &'a IdentIdsByModule,
aliases: MutMap<Symbol, Alias>,
imported_abilities_state: PendingAbilitiesStore,
initial_scope: MutMap<Ident, (Symbol, Region)>,
exposed_symbols: VecSet<Symbol>,
symbols_from_requires: &[(Loc<Symbol>, Loc<TypeAnnotation<'a>>)],
var_store: &mut VarStore,
opt_shorthand: Option<&'a str>,
mut scope: Scope,
mut env: Env<'a>,
loc_defs: Defs<'a>,
module_params: Option<(Region, Collection<'a, Loc<ParsePattern<'a>>>)>,
) -> ModuleOutput {
let mut can_exposed_imports = MutMap::default();
let mut scope = Scope::new(
home,
qualified_module_ids
.get_name(home)
.expect("home module not found")
.as_inner()
.to_owned(),
exposed_ident_ids,
imported_abilities_state,
);
let mut env = Env::new(
arena,
src,
home,
arena.alloc(Path::new(module_path)),
dep_idents,
qualified_module_ids,
opt_shorthand,
);
for (name, alias) in aliases.into_iter() {
scope.add_alias(
name,
alias.region,
alias.type_variables,
alias.infer_ext_in_output_variables,
alias.typ,
alias.kind,
);
}
// Desugar operators (convert them to Apply calls, taking into account
// operator precedence and associativity rules), before doing other canonicalization.
//
@ -266,8 +227,6 @@ pub fn canonicalize_module_defs<'a>(
// operators, and then again on *their* nested operators, ultimately applying the
// rules multiple times unnecessarily.
crate::desugar::desugar_defs_node_values(&mut env, &mut scope, loc_defs);
let mut rigid_variables = RigidVariables::default();
// Initial scope values are treated like defs that appear before any others.
@ -319,51 +278,42 @@ pub fn canonicalize_module_defs<'a>(
let mut output = Output::default();
let module_params = header_type.get_params().as_ref().map(
|roc_parse::header::ModuleParams {
pattern,
before_arrow: _,
after_arrow: _,
}| {
let desugared_patterns =
desugar_record_destructures(&mut env, &mut scope, pattern.value);
let module_params = module_params.map(|(params_region, desugared_patterns)| {
let (destructs, _) = canonicalize_record_destructs(
&mut env,
var_store,
&mut scope,
&mut output,
PatternType::ModuleParams,
&desugared_patterns,
params_region,
PermitShadows(false),
);
let (destructs, _) = canonicalize_record_destructs(
&mut env,
var_store,
&mut scope,
&mut output,
PatternType::ModuleParams,
&desugared_patterns,
pattern.region,
PermitShadows(false),
);
let whole_symbol = scope.gen_unique_symbol();
env.top_level_symbols.insert(whole_symbol);
let whole_symbol = scope.gen_unique_symbol();
env.top_level_symbols.insert(whole_symbol);
let whole_var = var_store.fresh();
let whole_var = var_store.fresh();
env.home_params_record = Some((whole_symbol, whole_var));
env.home_params_record = Some((whole_symbol, whole_var));
ModuleParams {
region: pattern.region,
whole_var,
whole_symbol,
record_var: var_store.fresh(),
record_ext_var: var_store.fresh(),
destructs,
arity_by_name: Default::default(),
}
},
);
ModuleParams {
region: params_region,
whole_var,
whole_symbol,
record_var: var_store.fresh(),
record_ext_var: var_store.fresh(),
destructs,
arity_by_name: Default::default(),
}
});
let (defs, output, symbols_introduced, imports_introduced) = canonicalize_defs(
&mut env,
output,
var_store,
&mut scope,
loc_defs,
arena.alloc(loc_defs),
PatternType::TopLevelDef,
);

View file

@ -1,10 +1,11 @@
extern crate bumpalo;
use self::bumpalo::Bump;
use roc_can::desugar;
use roc_can::env::Env;
use roc_can::expr::{canonicalize_expr, Expr};
use roc_can::scope::Scope;
use roc_can_solo::env::SoloEnv;
use roc_can_solo::scope::SoloScope;
use roc_collections::all::MutMap;
use roc_module::symbol::{IdentIds, Interns, ModuleId, ModuleIds, PackageModuleIds, Symbol};
use roc_problem::can::Problem;
@ -65,7 +66,9 @@ pub fn can_expr_with(arena: &Bump, home: ModuleId, expr_str: &str) -> CanExprOut
// visited a BinOp node we'd recursively try to apply this to each of its nested
// operators, and then again on *their* nested operators, ultimately applying the
// rules multiple times unnecessarily.
let loc_expr = desugar::desugar_expr(&mut env, &mut scope, &loc_expr);
let mut solo_env = SoloEnv::new(arena, expr_str, Path::new("Test.roc"));
let mut solo_scope = SoloScope::new();
let loc_expr = roc_can_solo::desugar::desugar_expr(&mut solo_env, &mut solo_scope, &loc_expr);
scope.add_alias(
Symbol::NUM_INT,

View file

@ -972,10 +972,10 @@ mod test_can {
}
#[test]
fn try_desugar_double_question_suffix() {
fn try_desugar_pipe_suffix_pnc() {
let src = indoc!(
r#"
Str.to_u64 "123" ?? Num.max_u64
"123" |> Str.to_u64()?
"#
);
let arena = Bump::new();
@ -985,31 +985,13 @@ mod test_can {
// Assert that we desugar to:
//
// when Str.to_u64 "123"
// Ok success_BRANCH1_0_9 -> success_BRANCH1_0_9
// Err _ -> Num.max_u64
// Try(Str.to_u64 "123")
let (cond_expr, branches) = assert_when(&out.loc_expr.value);
let cond_expr = assert_try_expr(&out.loc_expr.value);
let cond_args = assert_func_call(cond_expr, "to_u64", CalledVia::Space, &out.interns);
assert_eq!(cond_args.len(), 1);
assert_str_value(&cond_args[0].1.value, "123");
assert_eq!(branches.len(), 2);
assert_eq!(branches[0].patterns.len(), 1);
assert_eq!(branches[1].patterns.len(), 1);
assert_pattern_tag_apply_with_ident(
&branches[0].patterns[0].pattern.value,
"Ok",
"success_BRANCH1_0_16",
&out.interns,
);
assert_var_usage(
&branches[0].value.value,
"success_BRANCH1_0_16",
&out.interns,
);
assert_pattern_tag_apply_with_underscore(&branches[1].patterns[0].pattern.value, "Err");
assert_var_usage(&branches[1].value.value, "max_u64", &out.interns);
}
#[test]
@ -1031,7 +1013,7 @@ mod test_can {
// when Foo 123 is
// Foo try -> try
let (cond_expr, branches) = assert_when(&out.loc_expr.value);
let (cond_expr, branches) = assert_when_expr(&out.loc_expr.value);
match cond_expr {
Expr::Tag {
name, arguments, ..
@ -1064,6 +1046,169 @@ mod test_can {
assert!(&branches[0].guard.is_none());
}
#[test]
fn desugar_double_question_binop() {
let src = indoc!(
r#"
Str.to_u64("123") ?? Num.max_u64
"#
);
let arena = Bump::new();
let out = can_expr_with(&arena, test_home(), src);
assert_eq!(out.problems, Vec::new());
// Assert that we desugar to:
//
// when Str.to_u64("123")
// Ok(#double_question_ok_0_17) -> Ok(#double_question_ok_0_17)
// Err(_) -> Num.max_u64
let (cond_expr, branches) = assert_when_expr(&out.loc_expr.value);
let cond_args = assert_func_call(cond_expr, "to_u64", CalledVia::Space, &out.interns);
assert_eq!(cond_args.len(), 1);
assert_str_value(&cond_args[0].1.value, "123");
assert_eq!(branches.len(), 2);
assert_eq!(branches[0].patterns.len(), 1);
assert_eq!(branches[1].patterns.len(), 1);
assert_pattern_tag_apply_with_ident(
&branches[0].patterns[0].pattern.value,
"Ok",
"#double_question_ok_0_17",
&out.interns,
);
assert_var_usage(
&branches[0].value.value,
"#double_question_ok_0_17",
&out.interns,
);
assert_pattern_tag_apply_with_underscore(&branches[1].patterns[0].pattern.value, "Err");
assert_var_usage(&branches[1].value.value, "max_u64", &out.interns);
}
#[test]
fn desugar_single_question_binop() {
let src = indoc!(
r#"
Str.to_u64("123") ? FailedToConvert
"#
);
let arena = Bump::new();
let out = can_expr_with(&arena, test_home(), src);
assert_eq!(out.problems, Vec::new());
// Assert that we desugar to:
//
// when Str.to_u64("123")
// Ok(#single_question_ok_0_17) -> #single_question_ok_0_17
// Err(#single_question_err_0_17) -> return Err(FailedToConvert(#single_question_err_0_17))
let (cond_expr, branches) = assert_when_expr(&out.loc_expr.value);
let cond_args = assert_func_call(cond_expr, "to_u64", CalledVia::Space, &out.interns);
assert_eq!(cond_args.len(), 1);
assert_str_value(&cond_args[0].1.value, "123");
assert_eq!(branches.len(), 2);
assert_eq!(branches[0].patterns.len(), 1);
assert_eq!(branches[1].patterns.len(), 1);
assert_pattern_tag_apply_with_ident(
&branches[0].patterns[0].pattern.value,
"Ok",
"#single_question_ok_0_17",
&out.interns,
);
assert_var_usage(
&branches[0].value.value,
"#single_question_ok_0_17",
&out.interns,
);
assert_pattern_tag_apply_with_ident(
&branches[1].patterns[0].pattern.value,
"Err",
"#single_question_err_0_17",
&out.interns,
);
let err_expr = assert_return_expr(&branches[1].value.value);
let mapped_err = assert_tag_application(err_expr, "Err");
assert_eq!(mapped_err.len(), 1);
let inner_err = assert_tag_application(&mapped_err[0].1.value, "FailedToConvert");
assert_eq!(inner_err.len(), 1);
assert_var_usage(
&inner_err[0].1.value,
"#single_question_err_0_17",
&out.interns,
);
}
#[test]
fn desugar_and_operator() {
let src = indoc!(
r#"
left = Bool.true
right = Bool.false
left and right
"#
);
let arena = Bump::new();
let out = can_expr_with(&arena, test_home(), src);
assert_eq!(out.problems, Vec::new());
// Assert that we desugar to:
//
// if left then right else Bool.false
let continuation1 = assert_let_expr(&out.loc_expr.value);
let continuation2 = assert_let_expr(&continuation1.value);
let (branches, final_else) = assert_if_expr(&continuation2.value);
assert_eq!(branches.len(), 1);
assert_var_usage(&branches[0].0.value, "left", &out.interns);
assert_var_usage(&branches[0].1.value, "right", &out.interns);
assert_var_usage(&final_else.value, "false", &out.interns);
}
#[test]
fn desugar_or_operator() {
let src = indoc!(
r#"
left = Bool.true
right = Bool.false
left or right
"#
);
let arena = Bump::new();
let out = can_expr_with(&arena, test_home(), src);
assert_eq!(out.problems, Vec::new());
// Assert that we desugar to:
//
// if left then Bool.true else right
let continuation1 = assert_let_expr(&out.loc_expr.value);
let continuation2 = assert_let_expr(&continuation1.value);
let (branches, final_else) = assert_if_expr(&continuation2.value);
assert_eq!(branches.len(), 1);
assert_var_usage(&branches[0].0.value, "left", &out.interns);
assert_var_usage(&branches[0].1.value, "true", &out.interns);
assert_var_usage(&final_else.value, "right", &out.interns);
}
fn assert_num_value(expr: &Expr, num: usize) {
match expr {
Expr::Num(_, num_str, _, _) => {
@ -1176,7 +1321,14 @@ mod test_can {
}
}
fn assert_when(expr: &Expr) -> (&Expr, &Vec<WhenBranch>) {
fn assert_let_expr(expr: &Expr) -> &Loc<Expr> {
match expr {
Expr::LetNonRec(_, continuation) | Expr::LetRec(_, continuation, _) => continuation,
_ => panic!("Expr was not a Let(Non)?Rec: {expr:?}",),
}
}
fn assert_when_expr(expr: &Expr) -> (&Expr, &Vec<WhenBranch>) {
match expr {
Expr::When {
loc_cond, branches, ..
@ -1185,6 +1337,18 @@ mod test_can {
}
}
#[allow(clippy::type_complexity)]
fn assert_if_expr(expr: &Expr) -> (&[(Loc<Expr>, Loc<Expr>)], &Loc<Expr>) {
match expr {
Expr::If {
branches,
final_else,
..
} => (&branches, &**final_else),
_ => panic!("Expr was not a When: {:?}", expr),
}
}
fn assert_try_expr(expr: &Expr) -> &Expr {
match expr {
Expr::Try { result_expr, .. } => &result_expr.value,
@ -1192,6 +1356,13 @@ mod test_can {
}
}
fn assert_return_expr(expr: &Expr) -> &Expr {
match expr {
Expr::Return { return_value, .. } => &return_value.value,
_ => panic!("Expr was not a Return: {:?}", expr),
}
}
// TAIL CALLS
fn get_closure(expr: &Expr, i: usize) -> roc_can::expr::Recursive {
match expr {

View file

@ -95,22 +95,6 @@ flags! {
/// Prints all type variables entered for fixpoint-fixing.
ROC_PRINT_FIXPOINT_FIXING
/// Verifies that after let-generalization of a def, any rigid variables in the type annotation
/// of the def are indeed generalized.
///
/// Note that rigids need not always be generalized in a def. For example, they may be
/// constrained by a type from a lower rank, as `b` is in the following def:
///
/// F a : { foo : a }
/// foo = \arg ->
/// x : F b
/// x = arg
/// x.foo
///
/// Instead, this flag is useful for checking that in general, introduction is correct, when
/// chainging how defs are constrained.
ROC_VERIFY_RIGID_LET_GENERALIZED
/// Verifies that an `occurs` check indeed only contains non-recursive types that need to be
/// fixed-up with one new recursion variable.
///

View file

@ -1206,7 +1206,6 @@ impl<'a> Nodify<'a> for TypeAnnotation<'a> {
before: first_node.before,
node: Node::CommaSequence {
allow_blank_lines: false,
allow_newlines: true,
indent_rest: false,
first: arena.alloc(first_node.node),
rest: rest_nodes.into_bump_slice(),
@ -1313,11 +1312,12 @@ impl<'a> Nodify<'a> for TypeAnnotation<'a> {
.to_node(arena, flags)
.add_parens(arena, Parens::NotNeeded);
let mut needs_indent = annot.needs_indent || !annot.after.is_empty();
let before = filter_newlines(arena, annot.after);
let mut needs_indent = annot.needs_indent || !before.is_empty();
items.push(Item {
comma_before: false,
before: annot.after,
before,
newline: false,
space: true,
node: Node::Literal(roc_parse::keyword::WHERE),
@ -1327,7 +1327,8 @@ impl<'a> Nodify<'a> for TypeAnnotation<'a> {
for (i, clause) in implements_clauses.iter().enumerate() {
let node = clause.value.to_node(arena, flags);
let before = merge_spaces_conservative(arena, last_after, node.before);
let before =
filter_newlines(arena, merge_spaces(arena, last_after, node.before));
last_after = node.after;
items.push(Item {
before,
@ -1346,8 +1347,7 @@ impl<'a> Nodify<'a> for TypeAnnotation<'a> {
first: arena.alloc(annot.node),
rest: arena.alloc_slice_copy(&items),
allow_blank_lines: false,
allow_newlines: false,
indent_rest: false,
indent_rest: true,
},
after: last_after,
needs_indent,
@ -1358,6 +1358,24 @@ impl<'a> Nodify<'a> for TypeAnnotation<'a> {
}
}
fn filter_newlines<'a, 'b: 'a>(
arena: &'a Bump,
items: &'b [CommentOrNewline<'b>],
) -> &'a [CommentOrNewline<'a>] {
let count = items.iter().filter(|i| i.is_newline()).count();
if count > 0 {
let mut new_items = Vec::with_capacity_in(items.len() - count, arena);
for item in items {
if !item.is_newline() {
new_items.push(*item);
}
}
arena.alloc_slice_copy(&new_items)
} else {
items
}
}
impl<'a> Nodify<'a> for &'a str {
fn to_node<'b>(&'a self, arena: &'b Bump, flags: MigrationFlags) -> NodeInfo<'b>
where
@ -1451,7 +1469,6 @@ impl<'a> Nodify<'a> for ImplementsClause<'a> {
first: arena.alloc(var.node),
rest: arena.alloc_slice_copy(&items),
allow_blank_lines: false,
allow_newlines: true,
indent_rest: false,
},
after: last_after,

View file

@ -1,20 +1,20 @@
use crate::annotation::{
ann_lift_spaces, ann_lift_spaces_after, is_collection_multiline, ty_is_outdentable,
Formattable, Newlines, Parens,
ann_lift_spaces_after, is_collection_multiline, Formattable, Newlines, Parens,
};
use crate::collection::{fmt_collection, Braces};
use crate::expr::{
expr_lift_and_lower, expr_lift_spaces, expr_lift_spaces_after, expr_lift_spaces_before,
fmt_str_literal, is_str_multiline, merge_spaces_conservative, sub_expr_requests_parens,
expr_lift_spaces, expr_lift_spaces_after, expr_lift_spaces_before, fmt_str_literal,
is_str_multiline, merge_spaces_conservative, sub_expr_requests_parens,
};
use crate::node::Nodify;
use crate::pattern::pattern_lift_spaces_before;
use crate::pattern::{pattern_lift_spaces, pattern_lift_spaces_before};
use crate::spaces::{
fmt_comments_only, fmt_default_newline, fmt_default_spaces, fmt_spaces, NewlineAt, INDENT,
};
use crate::Buf;
use bumpalo::Bump;
use roc_error_macros::internal_error;
use roc_parse::ast::Spaceable;
use roc_parse::ast::{
AbilityMember, Defs, Expr, ExtractSpaces, ImportAlias, ImportAsKeyword, ImportExposingKeyword,
ImportedModuleName, IngestedFileAnnotation, IngestedFileImport, ModuleImport,
@ -182,16 +182,29 @@ pub fn valdef_lift_spaces<'a, 'b: 'a>(
}
}
ValueDef::Body(pat, expr) => {
let pat_lifted = pattern_lift_spaces_before(arena, &pat.value);
let expr_lifted = expr_lift_spaces_after(Parens::NotNeeded, arena, &expr.value);
let pat_lifted = pattern_lift_spaces(arena, &pat.value);
Spaces {
before: pat_lifted.before,
item: ValueDef::Body(
arena.alloc(Loc::at(pat.region, pat_lifted.item)),
arena.alloc(Loc::at(expr.region, expr_lifted.item)),
),
after: expr_lifted.after,
// Don't format the `{} =` for defs with this pattern
if is_body_unit_assignment(&pat_lifted, &expr.extract_spaces()) {
let lifted = expr_lift_spaces(Parens::NotNeeded, arena, &expr.value);
Spaces {
before: lifted.before,
item: ValueDef::Stmt(arena.alloc(Loc::at_zero(lifted.item))),
after: lifted.after,
}
} else {
let lifted = expr_lift_spaces(Parens::NotNeeded, arena, &expr.value);
Spaces {
before: pat_lifted.before,
item: ValueDef::Body(
arena.alloc(Loc::at(
pat.region,
pat_lifted.item.maybe_after(arena, pat_lifted.after),
)),
expr,
),
after: lifted.after,
}
}
}
ValueDef::AnnotatedBody {
@ -312,11 +325,26 @@ pub fn valdef_lift_spaces_before<'a, 'b: 'a>(
}
}
ValueDef::Body(pat, expr) => {
let pat_lifted = pattern_lift_spaces_before(arena, &pat.value);
let pat_lifted = pattern_lift_spaces(arena, &pat.value);
SpacesBefore {
before: pat_lifted.before,
item: ValueDef::Body(arena.alloc(Loc::at(pat.region, pat_lifted.item)), expr),
// Don't format the `{} =` for defs with this pattern
if is_body_unit_assignment(&pat_lifted, &expr.extract_spaces()) {
let lifted = expr_lift_spaces_before(Parens::NotNeeded, arena, &expr.value);
SpacesBefore {
before: lifted.before,
item: ValueDef::Stmt(arena.alloc(Loc::at_zero(lifted.item))),
}
} else {
SpacesBefore {
before: pat_lifted.before,
item: ValueDef::Body(
arena.alloc(Loc::at(
pat.region,
pat_lifted.item.maybe_after(arena, pat_lifted.after),
)),
expr,
),
}
}
}
ValueDef::AnnotatedBody {
@ -396,6 +424,20 @@ pub fn valdef_lift_spaces_before<'a, 'b: 'a>(
}
}
fn is_body_unit_assignment(pat: &Spaces<'_, Pattern<'_>>, body: &Spaces<'_, Expr<'_>>) -> bool {
if let Pattern::RecordDestructure(collection) = pat.item {
collection.is_empty()
&& pat.before.iter().all(|s| s.is_newline())
&& pat.after.iter().all(|s| s.is_newline())
&& !matches!(body.item, Expr::Defs(..))
&& !matches!(body.item, Expr::Return(..))
&& !matches!(body.item, Expr::DbgStmt { .. })
&& !starts_with_expect_ident(&body.item)
} else {
false
}
}
impl<'a> Formattable for TypeDef<'a> {
fn is_multiline(&self) -> bool {
use roc_parse::ast::TypeDef::*;
@ -412,22 +454,15 @@ impl<'a> Formattable for TypeDef<'a> {
match self {
Alias { header, ann } => {
header.format(buf, indent);
buf.indent(indent);
buf.push_str(" :");
buf.spaces(1);
let ann = ann_lift_spaces(buf.text.bump(), &ann.value);
let inner_indent = if ty_is_outdentable(&ann.item) {
indent
} else {
indent + INDENT
};
fmt_comments_only(buf, ann.before.iter(), NewlineAt::Bottom, inner_indent);
ann.item.format(buf, inner_indent);
fmt_spaces(buf, ann.after.iter(), indent);
fmt_general_def(
header,
Parens::NotNeeded,
buf,
indent,
":",
&ann.value,
newlines,
);
}
Opaque {
header,
@ -797,7 +832,7 @@ impl<'a> Formattable for ValueDef<'a> {
);
}
Body(loc_pattern, loc_expr) => {
fmt_body(buf, true, &loc_pattern.value, &loc_expr.value, indent);
fmt_body(buf, &loc_pattern.value, &loc_expr.value, indent);
}
Dbg { condition, .. } => fmt_dbg_in_def(buf, condition, self.is_multiline(), indent),
Expect { condition, .. } => fmt_expect(buf, condition, self.is_multiline(), indent),
@ -826,7 +861,7 @@ impl<'a> Formattable for ValueDef<'a> {
fmt_annotated_body_comment(buf, indent, lines_between);
buf.newline();
fmt_body(buf, false, &body_pattern.value, &body_expr.value, indent);
fmt_body(buf, &body_pattern.value, &body_expr.value, indent);
}
ModuleImport(module_import) => module_import.format(buf, indent),
IngestedFileImport(ingested_file_import) => ingested_file_import.format(buf, indent),
@ -957,34 +992,7 @@ pub fn fmt_annotated_body_comment<'a>(
}
}
pub fn fmt_body<'a>(
buf: &mut Buf,
allow_simplify_empty_record_destructure: bool,
pattern: &'a Pattern<'a>,
body: &'a Expr<'a>,
indent: u16,
) {
let pattern_extracted = pattern.extract_spaces();
// Check if this is an assignment into the unit value
let is_unit_assignment = if let Pattern::RecordDestructure(collection) = pattern_extracted.item
{
allow_simplify_empty_record_destructure
&& collection.is_empty()
&& pattern_extracted.before.iter().all(|s| s.is_newline())
&& pattern_extracted.after.iter().all(|s| s.is_newline())
&& !matches!(body.extract_spaces().item, Expr::Defs(..))
&& !matches!(body.extract_spaces().item, Expr::Return(..))
&& !matches!(body.extract_spaces().item, Expr::DbgStmt { .. })
&& !starts_with_expect_ident(body)
} else {
false
};
// Don't format the `{} =` for defs with this pattern
if is_unit_assignment {
return body.format_with_options(buf, Parens::NotNeeded, Newlines::No, indent);
}
pub fn fmt_body<'a>(buf: &mut Buf, pattern: &'a Pattern<'a>, body: &'a Expr<'a>, indent: u16) {
pattern.format_with_options(buf, Parens::InApply, Newlines::No, indent);
if pattern.is_multiline() {
@ -996,7 +1004,12 @@ pub fn fmt_body<'a>(
let indent = buf.cur_line_indent();
buf.push_str("=");
let body = expr_lift_and_lower(Parens::NotNeeded, buf.text.bump(), body);
let body_lifted = expr_lift_spaces(Parens::NotNeeded, buf.text.bump(), body);
let after = body_lifted.after;
let body = body_lifted
.item
.maybe_before(buf.text.bump(), body_lifted.before);
if body.is_multiline() {
match body {
@ -1009,10 +1022,7 @@ pub fn fmt_body<'a>(
_ => false,
};
if is_unit_assignment {
fmt_comments_only(buf, spaces.iter(), NewlineAt::Bottom, indent);
sub_def.format_with_options(buf, Parens::NotNeeded, Newlines::Yes, indent);
} else if should_outdent {
if should_outdent {
buf.spaces(1);
sub_def.format_with_options(buf, Parens::NotNeeded, Newlines::Yes, indent);
} else {
@ -1057,7 +1067,7 @@ pub fn fmt_body<'a>(
buf.ensure_ends_with_newline();
body.format_with_options(buf, Parens::NotNeeded, Newlines::Yes, indent + INDENT);
}
Expr::Defs(..) | Expr::BinOps(_, _) => {
Expr::Defs(..) | Expr::BinOps(..) => {
// Binop chains always get a newline. Otherwise you can have things like:
//
// something = foo
@ -1092,6 +1102,8 @@ pub fn fmt_body<'a>(
buf.spaces(1);
body.format_with_options(buf, Parens::NotNeeded, Newlines::Yes, indent);
}
fmt_spaces(buf, after.iter(), indent);
}
fn starts_with_expect_ident(expr: &Expr<'_>) -> bool {
@ -1116,6 +1128,7 @@ pub fn starts_with_block_string_literal(expr: &Expr<'_>) -> bool {
}
Expr::Apply(inner, _, _) => starts_with_block_string_literal(&inner.value),
Expr::RecordAccess(inner, _) => starts_with_block_string_literal(inner),
Expr::TupleAccess(inner, _) => starts_with_block_string_literal(inner),
Expr::PncApply(inner, _) => starts_with_block_string_literal(&inner.value),
Expr::TrySuffix(inner) => starts_with_block_string_literal(inner),
_ => false,

View file

@ -1,6 +1,6 @@
use crate::annotation::{except_last, is_collection_multiline, Formattable, Newlines, Parens};
use crate::collection::{fmt_collection, Braces};
use crate::def::{fmt_defs, valdef_lift_spaces_before};
use crate::def::{fmt_defs, starts_with_block_string_literal, valdef_lift_spaces_before};
use crate::node::Prec;
use crate::pattern::{
fmt_pattern, pattern_lift_spaces, snakify_camel_ident, starts_with_inline_comment,
@ -233,16 +233,9 @@ fn format_expr_only(
Expr::If {
if_thens: branches,
final_else,
indented_else,
indented_else: _,
} => {
fmt_if(
buf,
branches,
final_else,
item.is_multiline(),
*indented_else,
indent,
);
fmt_if(buf, branches, final_else, item.is_multiline(), indent);
}
Expr::When(loc_condition, branches) => fmt_when(buf, loc_condition, branches, indent),
Expr::Tuple(items) => fmt_expr_collection(buf, indent, Braces::Round, *items, Newlines::No),
@ -264,7 +257,7 @@ fn format_expr_only(
let before_all_newlines = lifted.before.iter().all(|s| s.is_newline());
let needs_newline =
!before_all_newlines || term_starts_with_multiline_str(&lifted.item);
!before_all_newlines || starts_with_block_string_literal(&lifted.item);
let needs_parens = (needs_newline
&& matches!(unary_op.value, called_via::UnaryOp::Negate))
@ -367,14 +360,6 @@ fn format_expr_only(
}
}
fn term_starts_with_multiline_str(expr: &Expr<'_>) -> bool {
match expr {
Expr::Str(text) => is_str_multiline(text),
Expr::PncApply(inner, _) => term_starts_with_multiline_str(&inner.value),
_ => false,
}
}
fn prepare_expr_field_collection<'a>(
arena: &'a Bump,
items: Collection<'a, Loc<AssignedField<'a, Expr<'a>>>>,
@ -959,10 +944,11 @@ fn push_op(buf: &mut Buf, op: BinOp) {
called_via::BinOp::GreaterThan => buf.push('>'),
called_via::BinOp::LessThanOrEq => buf.push_str("<="),
called_via::BinOp::GreaterThanOrEq => buf.push_str(">="),
called_via::BinOp::And => buf.push_str("&&"),
called_via::BinOp::Or => buf.push_str("||"),
called_via::BinOp::Or => buf.push_str("or"),
called_via::BinOp::And => buf.push_str("and"),
called_via::BinOp::Pizza => buf.push_str("|>"),
called_via::BinOp::DoubleQuestion => buf.push_str("??"),
called_via::BinOp::SingleQuestion => buf.push_str("?"),
}
}
@ -1025,14 +1011,6 @@ pub fn fmt_str_literal(buf: &mut Buf, literal: StrLiteral, indent: u16) {
}
}
pub fn expr_lift_and_lower<'a, 'b: 'a>(
_parens: Parens,
arena: &'a Bump,
expr: &Expr<'b>,
) -> Expr<'a> {
lower(arena, expr_lift_spaces(Parens::NotNeeded, arena, expr))
}
pub fn expr_lift_spaces<'a, 'b: 'a>(
parens: Parens,
arena: &'a Bump,
@ -1495,7 +1473,6 @@ fn fmt_binops<'a>(
buf: &mut Buf,
lefts: &'a [(Loc<Expr<'a>>, Loc<BinOp>)],
loc_right_side: &'a Loc<Expr<'a>>,
indent: u16,
) {
let is_multiline = loc_right_side.value.is_multiline()
@ -1824,6 +1801,8 @@ fn guard_needs_parens(value: &Expr<'_>) -> bool {
Expr::ParensAround(expr) | Expr::SpaceBefore(expr, _) | Expr::SpaceAfter(expr, _) => {
guard_needs_parens(expr)
}
Expr::BinOps(_lefts, right) => guard_needs_parens(&right.value),
Expr::UnaryOp(inner, _) => guard_needs_parens(&inner.value),
Expr::Closure(_, body) => guard_needs_parens(&body.value),
Expr::Defs(_, final_expr) => guard_needs_parens(&final_expr.value),
_ => false,
@ -1941,8 +1920,6 @@ fn fmt_if<'a>(
branches: &'a [(Loc<Expr<'a>>, Loc<Expr<'a>>)],
final_else: &'a Loc<Expr<'a>>,
is_multiline: bool,
indented_else: bool,
indent: u16,
) {
// let is_multiline_then = loc_then.is_multiline();
@ -1986,35 +1963,26 @@ fn fmt_if<'a>(
}
buf.ensure_ends_with_whitespace();
if indented_else {
buf.indent(indent + INDENT);
buf.push_str("else");
buf.newline();
buf.newline();
} else if is_multiline {
buf.indent(indent);
buf.push_str("else");
buf.indent(indent);
buf.push_str("else");
if is_multiline {
buf.newline();
} else {
buf.indent(indent);
buf.push_str("else");
buf.spaces(1);
}
let indent = if indented_else { indent } else { return_indent };
final_else.format(buf, indent);
final_else.format(buf, return_indent);
}
fn fmt_closure<'a>(
buf: &mut Buf,
loc_patterns: &'a [Loc<Pattern<'a>>],
loc_ret: &'a Loc<Expr<'a>>,
indent: u16,
) {
use self::Expr::*;
buf.indent(indent);
buf.push('\\');
buf.push('|');
let arguments_are_multiline = loc_patterns
.iter()
@ -2062,12 +2030,10 @@ fn fmt_closure<'a>(
if arguments_are_multiline {
buf.ensure_ends_with_newline();
buf.indent(indent);
} else {
buf.spaces(1);
}
let arrow_line_indent = buf.cur_line_indent();
buf.push_str("->");
buf.push_str("|");
buf.spaces(1);
let is_multiline = loc_ret.value.is_multiline();
@ -2284,7 +2250,8 @@ pub fn sub_expr_requests_parens(expr: &Expr<'_>) -> bool {
| BinOp::And
| BinOp::Or
| BinOp::Pizza
| BinOp::DoubleQuestion => true,
| BinOp::DoubleQuestion
| BinOp::SingleQuestion => true,
})
}
Expr::If { .. } => true,

View file

@ -5,7 +5,7 @@ use crate::{
annotation::{Formattable, Newlines, Parens},
collection::Braces,
expr::merge_spaces_conservative,
spaces::{fmt_comments_only, fmt_spaces, fmt_spaces_no_blank_lines, NewlineAt, INDENT},
spaces::{fmt_spaces, fmt_spaces_no_blank_lines, INDENT},
Buf, MigrationFlags,
};
@ -87,7 +87,6 @@ pub enum Node<'a> {
},
CommaSequence {
allow_blank_lines: bool,
allow_newlines: bool,
indent_rest: bool,
first: &'a Node<'a>,
rest: &'a [Item<'a>],
@ -124,12 +123,8 @@ pub struct Item<'a> {
}
impl<'a> Item<'a> {
fn is_multiline(&self, allow_newlines: bool) -> bool {
let has_newlines = if allow_newlines {
!self.before.is_empty()
} else {
self.before.iter().any(|c| c.is_comment())
};
fn is_multiline(&self) -> bool {
let has_newlines = !self.before.is_empty();
self.newline || has_newlines || self.node.is_multiline()
}
}
@ -273,7 +268,6 @@ impl<'b> NodeInfo<'b> {
},
node: Node::CommaSequence {
allow_blank_lines: false,
allow_newlines: true,
indent_rest,
first: arena.alloc(first.node),
rest: rest.into_bump_slice(),
@ -361,11 +355,10 @@ impl<'a> Formattable for Node<'a> {
} => after.is_multiline() || items.iter().any(|item| item.is_multiline()),
Node::CommaSequence {
allow_blank_lines: _,
allow_newlines,
indent_rest: _,
first,
rest,
} => first.is_multiline() || rest.iter().any(|item| item.is_multiline(*allow_newlines)),
} => first.is_multiline() || rest.iter().any(|item| item.is_multiline()),
Node::Literal(_) => false,
Node::TypeAnnotation(type_annotation) => type_annotation.is_multiline(),
Node::Pattern(pat) => pat.is_multiline(),
@ -429,7 +422,6 @@ impl<'a> Formattable for Node<'a> {
}
Node::CommaSequence {
allow_blank_lines,
allow_newlines,
indent_rest,
first,
rest,
@ -448,10 +440,8 @@ impl<'a> Formattable for Node<'a> {
}
if *allow_blank_lines {
fmt_spaces(buf, item.before.iter(), indent);
} else if *allow_newlines {
fmt_spaces_no_blank_lines(buf, item.before.iter(), inner_indent);
} else {
fmt_comments_only(buf, item.before.iter(), NewlineAt::Bottom, inner_indent);
fmt_spaces_no_blank_lines(buf, item.before.iter(), inner_indent);
}
if item.newline {
buf.ensure_ends_with_newline();

View file

@ -294,7 +294,8 @@ fn fmt_pattern_only(
is_negative,
} => {
buf.indent(indent);
let needs_parens = parens == Parens::InClosurePattern;
let needs_parens = parens == Parens::InClosurePattern
|| (parens == Parens::InPncApplyFunc && *is_negative);
if needs_parens {
buf.push('(');
}
@ -317,7 +318,8 @@ fn fmt_pattern_only(
}
Pattern::FloatLiteral(string) => {
buf.indent(indent);
let needs_parens = parens == Parens::InClosurePattern;
let needs_parens = parens == Parens::InClosurePattern
|| (parens == Parens::InPncApplyFunc && string.starts_with('-'));
if needs_parens {
buf.push('(');
}

View file

@ -1268,20 +1268,6 @@ trait Backend<'a> {
internal_error!("bitwise xor on a non-integer")
}
}
LowLevel::And => {
if let LayoutRepr::Builtin(Builtin::Bool) = self.interner().get_repr(*ret_layout) {
self.build_int_bitwise_and(sym, &args[0], &args[1], IntWidth::U8)
} else {
internal_error!("bitwise and on a non-integer")
}
}
LowLevel::Or => {
if let LayoutRepr::Builtin(Builtin::Bool) = self.interner().get_repr(*ret_layout) {
self.build_int_bitwise_or(sym, &args[0], &args[1], IntWidth::U8)
} else {
internal_error!("bitwise or on a non-integer")
}
}
LowLevel::NumShiftLeftBy => {
if let LayoutRepr::Builtin(Builtin::Int(int_width)) =
self.interner().get_repr(*ret_layout)
@ -1677,6 +1663,13 @@ trait Backend<'a> {
ret_layout,
)
}
LowLevel::StrFromUtf8Lossy => self.build_fn_call(
sym,
bitcode::STR_FROM_UTF8_LOSSY.to_string(),
args,
arg_layouts,
ret_layout,
),
LowLevel::StrRepeat => self.build_fn_call(
sym,
bitcode::STR_REPEAT.to_string(),
@ -1726,6 +1719,27 @@ trait Backend<'a> {
arg_layouts,
ret_layout,
),
LowLevel::StrWithAsciiLowercased => self.build_fn_call(
sym,
bitcode::STR_WITH_ASCII_LOWERCASED.to_string(),
args,
arg_layouts,
ret_layout,
),
LowLevel::StrWithAsciiUppercased => self.build_fn_call(
sym,
bitcode::STR_WITH_ASCII_UPPERCASED.to_string(),
args,
arg_layouts,
ret_layout,
),
LowLevel::StrCaselessAsciiEquals => self.build_fn_call(
sym,
bitcode::STR_CASELESS_ASCII_EQUALS.to_string(),
args,
arg_layouts,
ret_layout,
),
LowLevel::StrToNum => {
let number_layout = match self.interner().get_repr(*ret_layout) {
LayoutRepr::Struct(field_layouts) => field_layouts[0], // TODO: why is it sometimes a struct?

View file

@ -7,7 +7,6 @@ use inkwell::values::{BasicValueEnum, FunctionValue, IntValue, PointerValue, Str
use inkwell::{AddressSpace, IntPredicate};
use morphic_lib::UpdateMode;
use roc_builtins::bitcode;
use roc_module::symbol::Symbol;
use roc_mono::layout::{
Builtin, InLayout, Layout, LayoutIds, LayoutInterner, LayoutRepr, STLayoutInterner,
};
@ -17,7 +16,6 @@ use super::build::{
create_entry_block_alloca, load_roc_value, store_roc_value, use_roc_value, BuilderExt,
};
use super::convert::zig_list_type;
use super::scope::Scope;
use super::struct_::struct_from_fields;
fn call_list_bitcode_fn_1<'ctx>(
@ -29,20 +27,6 @@ fn call_list_bitcode_fn_1<'ctx>(
call_list_bitcode_fn(env, &[list], other_arguments, BitcodeReturns::List, fn_name)
}
pub(crate) fn list_symbol_to_c_abi<'a, 'ctx>(
env: &Env<'a, 'ctx, '_>,
scope: &Scope<'a, 'ctx>,
symbol: Symbol,
) -> PointerValue<'ctx> {
let list_type = zig_list_type(env);
let list_alloca = create_entry_block_alloca(env, list_type, "list_alloca");
let list = scope.load_symbol(&symbol);
env.builder.new_build_store(list_alloca, list);
list_alloca
}
pub(crate) fn pass_update_mode<'ctx>(
env: &Env<'_, 'ctx, '_>,
update_mode: UpdateMode,

View file

@ -1,30 +1,68 @@
use crate::llvm::build::Env;
use inkwell::values::{BasicValueEnum, PointerValue};
use roc_builtins::bitcode;
use roc_mono::layout::{InLayout, Layout, LayoutRepr, STLayoutInterner};
use super::bitcode::{call_str_bitcode_fn, BitcodeReturns};
use super::build::load_roc_value;
use super::bitcode::{
call_str_bitcode_fn, call_void_bitcode_fn, pass_list_or_string_to_zig_32bit,
pass_list_to_zig_64bit, pass_list_to_zig_wasm, BitcodeReturns,
};
use super::build::{create_entry_block_alloca, load_roc_value, Env};
use bumpalo::collections::Vec;
pub static CHAR_LAYOUT: InLayout = Layout::U8;
pub(crate) fn decode_from_utf8_result<'a, 'ctx>(
pub(crate) fn call_str_from_utf_bitcode_fn<'a, 'ctx>(
env: &Env<'a, 'ctx, '_>,
layout_interner: &STLayoutInterner<'a>,
pointer: PointerValue<'ctx>,
args: &[BasicValueEnum<'ctx>],
result_struct_name: &str,
fn_name: &str,
) -> BasicValueEnum<'ctx> {
let result_type = env.module.get_struct_type(result_struct_name).unwrap();
let result_ptr = create_entry_block_alloca(env, result_type, "alloca_from_utf_result");
// FromUtf8Result, FromUtf16Result, FromUtf32Result all have the same layout of
// - index: u64
// - string: RocStr
// - is_ok: bool
// - problem_code: u8
let layout =
LayoutRepr::Struct(
env.arena
.alloc([Layout::U64, Layout::STR, Layout::BOOL, Layout::U8]),
);
let list = args[0];
let argn = &args[1..];
let mut args: Vec<BasicValueEnum<'ctx>> = Vec::with_capacity_in(args.len() + 2, env.arena);
args.push(result_ptr.into());
use roc_target::Architecture::*;
match env.target.architecture() {
Aarch32 | X86_32 => {
let (a, b) = pass_list_or_string_to_zig_32bit(env, list.into_struct_value());
args.push(a.into());
args.push(b.into());
}
Aarch64 | X86_64 => {
let list = pass_list_to_zig_64bit(env, list);
args.push(list.into());
}
Wasm32 => {
let list = pass_list_to_zig_wasm(env, list);
args.push(list.into());
}
};
args.extend(argn);
call_void_bitcode_fn(env, &args, fn_name);
load_roc_value(
env,
layout_interner,
layout,
pointer,
"load_decode_from_utf8_result",
result_ptr,
"load_from_utf_result",
)
}

View file

@ -37,9 +37,9 @@ use crate::llvm::{
build_list::{
list_append_unsafe, list_clone, list_concat, list_drop_at, list_get_unsafe, list_len_usize,
list_prepend, list_release_excess_capacity, list_replace_unsafe, list_reserve,
list_sort_with, list_sublist, list_swap, list_symbol_to_c_abi, list_with_capacity,
pass_update_mode,
list_sort_with, list_sublist, list_swap, list_with_capacity, pass_update_mode,
},
build_str::call_str_from_utf_bitcode_fn,
compare::{generic_eq, generic_neq},
convert::{
self, argument_type_from_layout, basic_type_from_layout, zig_num_parse_result_type,
@ -396,46 +396,15 @@ pub(crate) fn run_low_level<'a, 'ctx>(
)
}
StrFromUtf8 => {
let result_type = env.module.get_struct_type("str.FromUtf8Result").unwrap();
let result_ptr =
create_entry_block_alloca(env, result_type, "alloca_utf8_validate_bytes_result");
use roc_target::Architecture::*;
match env.target.architecture() {
Aarch32 | X86_32 => {
arguments!(list);
let (a, b) = pass_list_or_string_to_zig_32bit(env, list.into_struct_value());
call_void_bitcode_fn(
env,
&[
result_ptr.into(),
a.into(),
b.into(),
pass_update_mode(env, update_mode),
],
bitcode::STR_FROM_UTF8,
);
}
Aarch64 | X86_64 | Wasm32 => {
arguments!(_list);
// we use the symbol here instead
let list = args[0];
call_void_bitcode_fn(
env,
&[
result_ptr.into(),
list_symbol_to_c_abi(env, scope, list).into(),
pass_update_mode(env, update_mode),
],
bitcode::STR_FROM_UTF8,
);
}
}
crate::llvm::build_str::decode_from_utf8_result(env, layout_interner, result_ptr)
// Str.from_utf8_lowlevel : List U8 -> FromUtf8Result
arguments!(list);
call_str_from_utf_bitcode_fn(
env,
layout_interner,
&[list, pass_update_mode(env, update_mode)],
"str.FromUtf8Result",
bitcode::STR_FROM_UTF8,
)
}
StrToUtf8 => {
// Str.fromInt : Str -> List U8
@ -449,6 +418,16 @@ pub(crate) fn run_low_level<'a, 'ctx>(
bitcode::STR_TO_UTF8,
)
}
StrFromUtf8Lossy => {
arguments!(list);
call_list_bitcode_fn(
env,
&[list.into_struct_value()],
&[],
BitcodeReturns::Str,
bitcode::STR_FROM_UTF8_LOSSY,
)
}
StrRepeat => {
// Str.repeat : Str, U64 -> Str
arguments!(string, count);
@ -593,6 +572,7 @@ pub(crate) fn run_low_level<'a, 'ctx>(
bitcode::STR_WITH_CAPACITY,
)
}
ListLenU64 => {
// List.len : List * -> U64
arguments!(list);
@ -635,6 +615,39 @@ pub(crate) fn run_low_level<'a, 'ctx>(
list_element_layout!(layout_interner, result_layout),
)
}
StrWithAsciiLowercased => {
arguments!(string);
call_str_bitcode_fn(
env,
&[string],
&[],
BitcodeReturns::Str,
bitcode::STR_WITH_ASCII_LOWERCASED,
)
}
StrWithAsciiUppercased => {
arguments!(string);
call_str_bitcode_fn(
env,
&[string],
&[],
BitcodeReturns::Str,
bitcode::STR_WITH_ASCII_UPPERCASED,
)
}
StrCaselessAsciiEquals => {
arguments!(string1, string2);
call_str_bitcode_fn(
env,
&[string1, string2],
&[],
BitcodeReturns::Basic,
bitcode::STR_CASELESS_ASCII_EQUALS,
)
}
ListConcat => {
debug_assert_eq!(args.len(), 2);
@ -1284,30 +1297,6 @@ pub(crate) fn run_low_level<'a, 'ctx>(
rhs_layout,
)
}
And => {
// The (&&) operator
arguments!(lhs_arg, rhs_arg);
let bool_val = env.builder.new_build_and(
lhs_arg.into_int_value(),
rhs_arg.into_int_value(),
"bool_and",
);
BasicValueEnum::IntValue(bool_val)
}
Or => {
// The (||) operator
arguments!(lhs_arg, rhs_arg);
let bool_val = env.builder.new_build_or(
lhs_arg.into_int_value(),
rhs_arg.into_int_value(),
"bool_or",
);
BasicValueEnum::IntValue(bool_val)
}
Not => {
// The (!) operator
arguments!(arg);

View file

@ -245,6 +245,7 @@ impl<'a> LowLevelCall<'a> {
backend.code_builder.i32_const(UPDATE_MODE_IMMUTABLE);
backend.call_host_fn_after_loading_args(bitcode::STR_FROM_UTF8);
}
StrFromUtf8Lossy => self.load_args_and_call_zig(backend, bitcode::STR_FROM_UTF8_LOSSY),
StrTrimStart => self.load_args_and_call_zig(backend, bitcode::STR_TRIM_START),
StrTrimEnd => self.load_args_and_call_zig(backend, bitcode::STR_TRIM_END),
StrToUtf8 => self.load_args_and_call_zig(backend, bitcode::STR_TO_UTF8),
@ -258,6 +259,15 @@ impl<'a> LowLevelCall<'a> {
self.load_args_and_call_zig(backend, bitcode::STR_SUBSTRING_UNSAFE)
}
StrWithCapacity => self.load_args_and_call_zig(backend, bitcode::STR_WITH_CAPACITY),
StrWithAsciiLowercased => {
self.load_args_and_call_zig(backend, bitcode::STR_WITH_ASCII_LOWERCASED)
}
StrWithAsciiUppercased => {
self.load_args_and_call_zig(backend, bitcode::STR_WITH_ASCII_UPPERCASED)
}
StrCaselessAsciiEquals => {
self.load_args_and_call_zig(backend, bitcode::STR_CASELESS_ASCII_EQUALS)
}
// List
ListLenU64 => {
@ -2163,14 +2173,6 @@ impl<'a> LowLevelCall<'a> {
NumF64ToParts => self.load_args_and_call_zig(backend, bitcode::NUM_F64_TO_PARTS),
NumF32FromParts => self.load_args_and_call_zig(backend, bitcode::NUM_F32_FROM_PARTS),
NumF64FromParts => self.load_args_and_call_zig(backend, bitcode::NUM_F64_FROM_PARTS),
And => {
self.load_args(backend);
backend.code_builder.i32_and();
}
Or => {
self.load_args(backend);
backend.code_builder.i32_or();
}
Not => {
self.load_args(backend);
backend.code_builder.i32_eqz();

View file

@ -9,6 +9,7 @@ version.workspace = true
[dependencies]
roc_can.workspace = true
roc_can_solo.workspace = true
roc_collections.workspace = true
roc_load_internal.workspace = true
roc_module.workspace = true

View file

@ -3,11 +3,12 @@ extern crate bumpalo;
use self::bumpalo::Bump;
use roc_can::abilities::AbilitiesStore;
use roc_can::constraint::{Constraint, Constraints};
use roc_can::desugar;
use roc_can::env::Env;
use roc_can::expected::Expected;
use roc_can::expr::{canonicalize_expr, Expr, Output, PendingDerives};
use roc_can::scope::Scope;
use roc_can_solo::env::SoloEnv;
use roc_can_solo::scope::SoloScope;
use roc_collections::all::{ImMap, MutMap, SendSet};
use roc_constrain::expr::constrain_expr;
use roc_derive::SharedDerivedModule;
@ -162,24 +163,6 @@ pub fn can_expr_with<'a>(
// ensure the Test module is accessible in our tests
module_ids.get_or_insert(&PQModuleName::Unqualified("Test".into()));
let mut scope = Scope::new(
home,
"TestPath".into(),
IdentIds::default(),
Default::default(),
);
let dep_idents = IdentIds::exposed_builtins(0);
let mut env = Env::new(
arena,
expr_str,
home,
Path::new("Test.roc"),
&dep_idents,
&module_ids,
None,
);
// Desugar operators (convert them to Apply calls, taking into account
// operator precedence and associativity rules), before doing other canonicalization.
//
@ -187,7 +170,9 @@ pub fn can_expr_with<'a>(
// visited a BinOp node we'd recursively try to apply this to each of its nested
// operators, and then again on *their* nested operators, ultimately applying the
// rules multiple times unnecessarily.
let loc_expr = desugar::desugar_expr(&mut env, &mut scope, &loc_expr);
let mut solo_env = SoloEnv::new(arena, expr_str, Path::new("Test.roc"));
let mut solo_scope = SoloScope::new();
let loc_expr = roc_can_solo::desugar::desugar_expr(&mut solo_env, &mut solo_scope, &loc_expr);
let mut scope = Scope::new(
home,

View file

@ -1522,18 +1522,18 @@ mod test_reporting {
from_annotation_if,
indoc!(
r"
x : Num.Int *
x : Num.Int _
x = if Bool.true then 3.14 else 4
x
"
),
@r"
@r###"
TYPE MISMATCH in /code/proj/Main.roc
Something is off with the `then` branch of this `if` expression:
4 x : Num.Int *
4 x : Num.Int _
5 x = if Bool.true then 3.14 else 4
^^^^
@ -1547,14 +1547,14 @@ mod test_reporting {
Tip: You can convert between integers and fractions using functions
like `Num.to_frac` and `Num.round`.
"
"###
);
test_report!(
from_annotation_when,
indoc!(
r"
x : Num.Int *
x : Num.Int _
x =
when True is
_ -> 3.14
@ -1562,12 +1562,12 @@ mod test_reporting {
x
"
),
@r"
@r###"
TYPE MISMATCH in /code/proj/Main.roc
Something is off with the body of the `x` definition:
4 x : Num.Int *
4 x : Num.Int _
5 x =
6> when True is
7> _ -> 3.14
@ -1582,7 +1582,7 @@ mod test_reporting {
Tip: You can convert between integers and fractions using functions
like `Num.to_frac` and `Num.round`.
"
"###
);
test_report!(
@ -1910,7 +1910,7 @@ mod test_reporting {
from_annotation_complex_pattern,
indoc!(
r"
{ x } : { x : Num.Int * }
{ x } : { x : Num.Int _ }
{ x } = { x: 4.0 }
x
@ -1921,7 +1921,7 @@ mod test_reporting {
Something is off with the body of this definition:
4 { x } : { x : Num.Int * }
4 { x } : { x : Num.Int _ }
5 { x } = { x: 4.0 }
^^^^^^^^^^
@ -2047,18 +2047,18 @@ mod test_reporting {
missing_fields,
indoc!(
r"
x : { a : Num.Int *, b : Num.Frac *, c : Str }
x : { a : Num.Int _, b : Num.Frac _, c : Str }
x = { b: 4.0 }
x
"
),
@r"
@r###"
TYPE MISMATCH in /code/proj/Main.roc
Something is off with the body of the `x` definition:
4 x : { a : Num.Int *, b : Num.Frac *, c : Str }
4 x : { a : Num.Int _, b : Num.Frac _, c : Str }
5 x = { b: 4.0 }
^^^^^^^^^^
@ -2075,7 +2075,7 @@ mod test_reporting {
}
Tip: Looks like the c and a fields are missing.
"
"###
);
// this previously reported the message below, not sure which is better
@ -3448,7 +3448,7 @@ mod test_reporting {
x : AList Num.I64 Num.I64
x = ACons 0 (BCons 1 (ACons "foo" BNil ))
y : BList a a
y : BList _ _
y = BNil
{ x, y }
@ -4189,9 +4189,8 @@ mod test_reporting {
RBTree k v : [Node NodeColor k v (RBTree k v) (RBTree k v), Empty]
# Create an empty dictionary.
empty : RBTree k v
empty =
Empty
empty : {} -> RBTree k v
empty = \{} -> Empty
empty
"
@ -6036,7 +6035,7 @@ All branches in an `if` must have the same type!
double_binop,
indoc!(
r"
key >= 97 && <= 122
key >= 97 and <= 122
"
),
@r"
@ -6692,17 +6691,20 @@ All branches in an `if` must have the same type!
)
"
),
@r"
UNFINISHED FUNCTION in tmp/unfinished_closure_pattern_in_parens/Test.roc
@r###"
MISSING ARROW in tmp/unfinished_closure_pattern_in_parens/Test.roc
I was partway through parsing a function, but I got stuck here:
I am partway through parsing a function argument list, but I got stuck
here:
4 x = \( a
5 )
^
6
7
^
I just saw a pattern, so I was expecting to see a -> next.
"
I was expecting a -> next.
"###
);
test_report!(
@ -11215,10 +11217,10 @@ All branches in an `if` must have the same type!
import Decode exposing [decoder]
main =
my_decoder : Decoder (a -> a) fmt where fmt implements DecoderFormatting
my_decoder = decoder
my_decoder : Decoder (_ -> _) _
my_decoder = decoder
main =
my_decoder
"#
),
@ -11227,12 +11229,12 @@ All branches in an `if` must have the same type!
This expression has a type that does not implement the abilities it's expected to:
7 my_decoder = decoder
^^^^^^^
6 my_decoder = decoder
^^^^^^^
I can't generate an implementation of the `Decoding` ability for
a -> a
* -> *
Note: `Decoding` cannot be generated for functions.
"
@ -11248,10 +11250,10 @@ All branches in an `if` must have the same type!
A := {}
main =
my_decoder : Decoder {x : A} fmt where fmt implements DecoderFormatting
my_decoder = decoder
my_decoder : Decoder {x : A} _
my_decoder = decoder
main =
my_decoder
"#
),
@ -11260,8 +11262,8 @@ All branches in an `if` must have the same type!
This expression has a type that does not implement the abilities it's expected to:
9 my_decoder = decoder
^^^^^^^
8 my_decoder = decoder
^^^^^^^
I can't generate an implementation of the `Decoding` ability for
@ -11511,11 +11513,10 @@ All branches in an `if` must have the same type!
import Decode exposing [decoder]
main =
my_decoder : Decoder {x : Str, y ? Str} fmt where fmt implements DecoderFormatting
my_decoder = decoder
my_decoder : Decoder {x : Str, y ? Str} _
my_decoder = decoder
my_decoder
main = my_decoder
"#
),
@r"
@ -11523,8 +11524,8 @@ All branches in an `if` must have the same type!
This expression has a type that does not implement the abilities it's expected to:
7 my_decoder = decoder
^^^^^^^
6 my_decoder = decoder
^^^^^^^
I can't generate an implementation of the `Decoding` ability for
@ -14108,11 +14109,10 @@ All branches in an `if` must have the same type!
import Decode exposing [decoder]
main =
my_decoder : Decoder (U32, Str) fmt where fmt implements DecoderFormatting
my_decoder = decoder
my_decoder : Decoder (U32, Str) _
my_decoder = decoder
my_decoder
main = my_decoder
"#
)
);
@ -14125,11 +14125,10 @@ All branches in an `if` must have the same type!
import Decode exposing [decoder]
main =
my_decoder : Decoder (U32, {} -> {}) fmt where fmt implements DecoderFormatting
my_decoder = decoder
my_decoder : Decoder (U32, {} -> {}) _
my_decoder = decoder
my_decoder
main = my_decoder
"#
),
@r"
@ -14137,8 +14136,8 @@ All branches in an `if` must have the same type!
This expression has a type that does not implement the abilities it's expected to:
7 my_decoder = decoder
^^^^^^^
6 my_decoder = decoder
^^^^^^^
I can't generate an implementation of the `Decoding` ability for
@ -15994,4 +15993,66 @@ All branches in an `if` must have the same type!
Str -> {}
"#
);
test_report!(
invalid_generic_literal,
indoc!(
r#"
module [v]
v : *
v = 1
"#
),
@r###"
TYPE MISMATCH in /code/proj/Main.roc
Something is off with the body of the `v` definition:
3 v : *
4 v = 1
^
The body is a number of type:
Num *
But the type annotation on `v` says it should be:
*
Tip: The type annotation uses the type variable `*` to say that this
definition can produce any type of value. But in the body I see that
it will only produce a `Num` value of a single specific type. Maybe
change the type annotation to be more specific? Maybe change the code
to be more general?
"###
);
test_report!(
invalid_generic_literal_list,
indoc!(
r#"
module [v]
v : List *
v = []
"#
),
@r###"
TYPE VARIABLE IS NOT GENERIC in /code/proj/Main.roc
This type variable has a single type:
3 v : List *
^
Type variables tell me that they can be used with any type, but they
can only be used with functions. All other values have exactly one
type.
Hint: If you would like the type to be inferred for you, use an
underscore _ instead.
"###
);
}

View file

@ -10,6 +10,7 @@ version.workspace = true
[dependencies]
roc_builtins.workspace = true
roc_can.workspace = true
roc_can_solo.workspace = true
roc_work.workspace = true
roc_checkmate.workspace = true
roc_collections.workspace = true
@ -39,6 +40,8 @@ bumpalo.workspace = true
crossbeam.workspace = true
parking_lot.workspace = true
tempfile.workspace = true
base64-url.workspace = true
blake3.workspace = true
[dev-dependencies]
roc_test_utils_dir.workspace = true

View file

@ -15,11 +15,14 @@ use parking_lot::Mutex;
use roc_builtins::roc::module_source;
use roc_can::abilities::{AbilitiesStore, PendingAbilitiesStore, ResolvedImpl};
use roc_can::constraint::{Constraint as ConstraintSoa, Constraints, TypeOrVar};
use roc_can::env::Env;
use roc_can::expr::{Declarations, ExpectLookup, PendingDerives};
use roc_can::module::{
canonicalize_module_defs, ExposedByModule, ExposedForModule, ExposedModuleTypes, Module,
ModuleParams, ResolvedImplementations, TypeState,
};
use roc_can::scope::Scope;
use roc_can_solo::module::{solo_canonicalize_module_defs, SoloCanOutput};
use roc_collections::soa::slice_extend_new;
use roc_collections::{default_hasher, BumpMap, MutMap, MutSet, VecMap, VecSet};
use roc_constrain::module::constrain_module;
@ -204,9 +207,20 @@ fn start_phase<'a>(
root_type: state.root_type.clone(),
}
}
Phase::SoloCanonicalize => {
// canonicalize the file
let parsed = state.module_cache.parsed.get(&module_id).unwrap().clone();
BuildTask::SoloCanonicalize { parsed }
}
Phase::CanonicalizeAndConstrain => {
// canonicalize the file
let parsed = state.module_cache.parsed.remove(&module_id).unwrap();
let solo_can_output = state
.module_cache
.solo_canonicalized
.remove(&module_id)
.unwrap();
let deps_by_name = &parsed.deps_by_name;
let num_deps = deps_by_name.len();
@ -318,6 +332,7 @@ fn start_phase<'a>(
exposed_module_ids: state.exposed_modules,
exec_mode: state.exec_mode,
imported_module_params,
solo_can_output,
}
}
@ -577,6 +592,7 @@ enum Msg<'a> {
Many(Vec<Msg<'a>>),
Header(ModuleHeader<'a>),
Parsed(ParsedModule<'a>),
SoloCanonicalized(ModuleId, CanSolo<'a>),
CanonicalizedAndConstrained(CanAndCon),
SolvedTypes {
module_id: ModuleId,
@ -651,6 +667,9 @@ enum Msg<'a> {
IncorrectModuleName(FileError<'a, IncorrectModuleName<'a>>),
}
#[derive(Debug)]
struct CanSolo<'a>(SoloCanOutput<'a>);
#[derive(Debug)]
struct CanAndCon {
constrained_module: ConstrainedModule,
@ -890,6 +909,9 @@ enum BuildTask<'a> {
ident_ids_by_module: SharedIdentIdsByModule,
root_type: RootType,
},
SoloCanonicalize {
parsed: ParsedModule<'a>,
},
CanonicalizeAndConstrain {
parsed: ParsedModule<'a>,
qualified_module_ids: PackageModuleIds<'a>,
@ -901,6 +923,7 @@ enum BuildTask<'a> {
skip_constraint_gen: bool,
exec_mode: ExecutionMode,
imported_module_params: VecMap<ModuleId, ModuleParams>,
solo_can_output: SoloCanOutput<'a>,
},
Solve {
module: Module,
@ -2411,6 +2434,23 @@ fn update<'a>(
Ok(state)
}
SoloCanonicalized(module_id, CanSolo(solo_can_output)) => {
log!("solo canonicalized module {:?}", module_id);
state
.module_cache
.solo_canonicalized
.insert(module_id, solo_can_output);
let work = state
.dependencies
.notify(module_id, Phase::SoloCanonicalize);
start_tasks(arena, &mut state, work, injector, worker_wakers)?;
Ok(state)
}
CanonicalizedAndConstrained(CanAndCon {
constrained_module,
canonicalization_problems,
@ -2462,6 +2502,7 @@ fn update<'a>(
Ok(state)
}
SolvedTypes {
module_id,
ident_ids,
@ -5082,6 +5123,31 @@ fn build_platform_header<'a>(
build_header(info, parse_state, module_ids, module_timing)
}
#[allow(clippy::unnecessary_wraps)]
fn canonicalize_solo<'a>(arena: &'a Bump, parsed: ParsedModule<'a>) -> CanSolo<'a> {
let canonicalize_solo_start = Instant::now();
let ParsedModule {
module_path,
header_type,
src,
parsed_defs,
mut module_timing,
..
} = parsed;
let parsed_defs = arena.alloc(parsed_defs);
let solo_can_output =
solo_canonicalize_module_defs(arena, header_type, parsed_defs, module_path, src);
let canonicalize_solo_end = Instant::now();
module_timing.canonicalize_solo = canonicalize_solo_end.duration_since(canonicalize_solo_start);
CanSolo(solo_can_output)
}
#[allow(clippy::unnecessary_wraps)]
fn canonicalize_and_constrain<'a>(
arena: &'a Bump,
@ -5095,21 +5161,21 @@ fn canonicalize_and_constrain<'a>(
exposed_module_ids: &[ModuleId],
exec_mode: ExecutionMode,
imported_module_params: VecMap<ModuleId, ModuleParams>,
solo_can_output: SoloCanOutput<'a>,
) -> CanAndCon {
let canonicalize_start = Instant::now();
let ParsedModule {
module_id,
module_path,
src,
header_type,
exposed_ident_ids,
parsed_defs,
initial_scope,
available_modules,
mut module_timing,
symbols_from_requires,
opt_shorthand,
exposed_ident_ids,
..
} = parsed;
@ -5117,27 +5183,55 @@ fn canonicalize_and_constrain<'a>(
let _before = roc_types::types::get_type_clone_count();
let parsed_defs_for_docs = parsed_defs.clone();
let parsed_defs = arena.alloc(parsed_defs);
let mut var_store = VarStore::default();
let env = Env::from_solo_can(
arena,
&module_path,
module_id,
&dep_idents,
qualified_module_ids,
solo_can_output.problems,
opt_shorthand,
solo_can_output.src,
solo_can_output.lazy_line_info,
);
let mut scope = Scope::new(
module_id,
qualified_module_ids
.get_name(module_id)
.expect("home module not found")
.as_inner()
.to_owned(),
exposed_ident_ids,
imported_abilities_state,
);
for (name, alias) in aliases.into_iter() {
scope.add_alias(
name,
alias.region,
alias.type_variables,
alias.infer_ext_in_output_variables,
alias.typ,
alias.kind,
);
}
let mut module_output = canonicalize_module_defs(
arena,
parsed_defs,
&header_type,
module_id,
&*arena.alloc(module_path.to_string_lossy()),
src,
qualified_module_ids,
exposed_ident_ids,
&dep_idents,
aliases,
imported_abilities_state,
initial_scope,
exposed_symbols,
&symbols_from_requires,
&mut var_store,
opt_shorthand,
scope,
env,
solo_can_output.loc_defs,
solo_can_output.module_params,
);
let mut types = Types::new();
@ -6237,6 +6331,12 @@ fn run_task<'a>(
ident_ids_by_module,
root_type,
),
SoloCanonicalize { parsed } => {
let module_id = parsed.module_id;
let solo_can = canonicalize_solo(arena, parsed);
Ok(Msg::SoloCanonicalized(module_id, solo_can))
}
CanonicalizeAndConstrain {
parsed,
qualified_module_ids,
@ -6248,6 +6348,7 @@ fn run_task<'a>(
exposed_module_ids,
exec_mode,
imported_module_params,
solo_can_output,
} => {
let can_and_con = canonicalize_and_constrain(
arena,
@ -6261,6 +6362,7 @@ fn run_task<'a>(
exposed_module_ids,
exec_mode,
imported_module_params,
solo_can_output,
);
Ok(Msg::CanonicalizedAndConstrained(can_and_con))

View file

@ -189,7 +189,7 @@ pub struct MonomorphizedModule<'a> {
pub glue_layouts: GlueLayouts<'a>,
}
#[derive(Debug)]
#[derive(Debug, Clone)]
pub struct ParsedModule<'a> {
pub module_id: ModuleId,
pub module_path: PathBuf,
@ -237,11 +237,12 @@ pub struct ExposedToHost {
pub getters: Vec<Symbol>,
}
#[derive(Debug)]
#[derive(Debug, Clone)]
pub struct ModuleTiming {
pub read_roc_file: Duration,
pub parse_header: Duration,
pub parse_body: Duration,
pub canonicalize_solo: Duration,
pub canonicalize: Duration,
pub constrain: Duration,
pub solve: Duration,
@ -261,6 +262,7 @@ impl ModuleTiming {
read_roc_file: Duration::default(),
parse_header: Duration::default(),
parse_body: Duration::default(),
canonicalize_solo: Duration::default(),
canonicalize: Duration::default(),
constrain: Duration::default(),
solve: Duration::default(),
@ -281,6 +283,7 @@ impl ModuleTiming {
read_roc_file,
parse_header,
parse_body,
canonicalize_solo,
canonicalize,
constrain,
solve,
@ -297,6 +300,7 @@ impl ModuleTiming {
.checked_sub(*find_specializations)?
.checked_sub(*solve)?
.checked_sub(*constrain)?
.checked_sub(*canonicalize_solo)?
.checked_sub(*canonicalize)?
.checked_sub(*parse_body)?
.checked_sub(*parse_header)?

View file

@ -5,6 +5,7 @@ use crate::module::{
};
use roc_can::abilities::PendingAbilitiesStore;
use roc_can::module::ModuleParams;
use roc_can_solo::module::SoloCanOutput;
use roc_collections::{MutMap, MutSet, VecMap};
use roc_module::ident::ModuleName;
use roc_module::symbol::{ModuleId, PQModuleName, Symbol};
@ -26,6 +27,7 @@ pub(crate) struct ModuleCache<'a> {
pub(crate) parsed: MutMap<ModuleId, ParsedModule<'a>>,
pub(crate) aliases: MutMap<ModuleId, MutMap<Symbol, (bool, Alias)>>,
pub(crate) pending_abilities: MutMap<ModuleId, PendingAbilitiesStore>,
pub(crate) solo_canonicalized: MutMap<ModuleId, SoloCanOutput<'a>>,
pub(crate) constrained: MutMap<ModuleId, ConstrainedModule>,
pub(crate) module_params: MutMap<ModuleId, ModuleParams>,
pub(crate) typechecked: MutMap<ModuleId, TypeCheckedModule<'a>>,
@ -45,6 +47,8 @@ pub(crate) struct ModuleCache<'a> {
pub(crate) type_problems: MutMap<ModuleId, Vec<TypeError>>,
pub(crate) sources: MutMap<ModuleId, (PathBuf, &'a str)>,
#[allow(dead_code)]
pub(crate) content_hashes: MutMap<ModuleId, String>,
}
impl<'a> ModuleCache<'a> {
@ -65,6 +69,19 @@ impl<'a> ModuleCache<'a> {
pub fn has_errors(&self) -> bool {
self.has_can_errors() || self.has_type_errors()
}
#[allow(dead_code)]
pub fn add_module_content_hash(&mut self, module_id: ModuleId, contents: &str) -> String {
let hash = Self::hash_contents(contents);
self.content_hashes.insert(module_id, hash.clone());
hash
}
#[allow(dead_code)]
pub fn hash_contents(contents: &str) -> String {
base64_url::encode(blake3::hash(contents.as_bytes()).as_bytes())
}
}
impl Default for ModuleCache<'_> {
@ -101,6 +118,7 @@ impl Default for ModuleCache<'_> {
parsed: Default::default(),
aliases: Default::default(),
pending_abilities: Default::default(),
solo_canonicalized: Default::default(),
constrained: Default::default(),
module_params: Default::default(),
typechecked: Default::default(),
@ -116,6 +134,7 @@ impl Default for ModuleCache<'_> {
can_problems: Default::default(),
type_problems: Default::default(),
sources: Default::default(),
content_hashes: Default::default(),
}
}
}

View file

@ -24,7 +24,7 @@ succeed = \x -> Identity(x)
with_default = Res.with_default
yay : Res.Res {} err
yay : Res.Res {} _
yay =
ok = Ok("foo")

View file

@ -24,7 +24,7 @@ succeed = \x -> Identity(x)
with_default = Res.with_default
yay : Res.Res {} err
yay : Res.Res {} _
yay =
ok = Ok("foo")

View file

@ -271,11 +271,13 @@ fn load_fixture(
);
}
assert!(loaded_module
.type_problems
.remove(&home)
.unwrap_or_default()
.is_empty());
assert_eq!(
loaded_module
.type_problems
.remove(&home)
.unwrap_or_default(),
Vec::new()
);
let expected_name = loaded_module
.interns
@ -433,11 +435,13 @@ fn module_with_deps() {
loaded_module.can_problems.remove(&home).unwrap_or_default(),
Vec::new()
);
assert!(loaded_module
.type_problems
.remove(&home)
.unwrap_or_default()
.is_empty(),);
assert_eq!(
loaded_module
.type_problems
.remove(&home)
.unwrap_or_default(),
Vec::new()
);
let mut def_count = 0;
let declarations = loaded_module.declarations_by_id.remove(&home).unwrap();

View file

@ -105,7 +105,8 @@ pub fn remove_module_param_arguments(
| TypeError::ExpectedEffectful(_, _)
| TypeError::UnsuffixedEffectfulFunction(_, _)
| TypeError::SuffixedPureFunction(_, _)
| TypeError::InvalidTryTarget(_, _, _) => {}
| TypeError::InvalidTryTarget(_, _, _)
| TypeError::TypeIsNotGeneralized(..) => {}
}
}
}
@ -213,6 +214,7 @@ fn drop_last_argument(err_type: &mut ErrorType) {
| ErrorType::Alias(_, _, _, _)
| ErrorType::Range(_)
| ErrorType::Error
| ErrorType::EffectfulFunc => {}
| ErrorType::EffectfulFunc
| ErrorType::InferenceVar => {}
}
}

View file

@ -3,7 +3,7 @@ use self::BinOp::*;
use std::cmp::Ordering;
use std::fmt;
const PRECEDENCES: [(BinOp, u8); 17] = [
const PRECEDENCES: [(BinOp, u8); 18] = [
(Caret, 8),
(Star, 7),
(Slash, 7),
@ -12,6 +12,7 @@ const PRECEDENCES: [(BinOp, u8); 17] = [
(Plus, 5),
(Minus, 5),
(DoubleQuestion, 5),
(SingleQuestion, 5),
(Pizza, 4),
(Equals, 3),
(NotEquals, 3),
@ -23,7 +24,7 @@ const PRECEDENCES: [(BinOp, u8); 17] = [
(Or, 0),
];
const ASSOCIATIVITIES: [(BinOp, Associativity); 17] = [
const ASSOCIATIVITIES: [(BinOp, Associativity); 18] = [
(Caret, RightAssociative),
(Star, LeftAssociative),
(Slash, LeftAssociative),
@ -32,6 +33,7 @@ const ASSOCIATIVITIES: [(BinOp, Associativity); 17] = [
(Plus, LeftAssociative),
(Minus, LeftAssociative),
(DoubleQuestion, LeftAssociative),
(SingleQuestion, LeftAssociative),
(Pizza, LeftAssociative),
(Equals, NonAssociative),
(NotEquals, NonAssociative),
@ -43,7 +45,7 @@ const ASSOCIATIVITIES: [(BinOp, Associativity); 17] = [
(Or, RightAssociative),
];
const DISPLAY_STRINGS: [(BinOp, &str); 17] = [
const DISPLAY_STRINGS: [(BinOp, &str); 18] = [
(Caret, "^"),
(Star, "*"),
(Slash, "/"),
@ -52,6 +54,7 @@ const DISPLAY_STRINGS: [(BinOp, &str); 17] = [
(Plus, "+"),
(Minus, "-"),
(DoubleQuestion, "??"),
(SingleQuestion, "?"),
(Pizza, "|>"),
(Equals, "=="),
(NotEquals, "!="),
@ -59,8 +62,8 @@ const DISPLAY_STRINGS: [(BinOp, &str); 17] = [
(GreaterThan, ">"),
(LessThanOrEq, "<="),
(GreaterThanOrEq, ">="),
(And, "&&"),
(Or, "||"),
(And, "and"),
(Or, "or"),
];
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
@ -154,6 +157,7 @@ pub enum BinOp {
Plus,
Minus,
DoubleQuestion,
SingleQuestion,
Pizza,
Equals,
NotEquals,
@ -170,7 +174,8 @@ impl BinOp {
/// how wide this operator is when typed out
pub fn width(self) -> u16 {
match self {
Caret | Star | Slash | Percent | Plus | Minus | LessThan | GreaterThan => 1,
Caret | Star | Slash | Percent | Plus | Minus | LessThan | GreaterThan
| SingleQuestion => 1,
DoubleSlash | Equals | NotEquals | LessThanOrEq | GreaterThanOrEq | And | Or
| Pizza | DoubleQuestion => 2,
}
@ -194,7 +199,7 @@ pub enum Associativity {
/// right-associative operators:
///
/// exponentiation: ^
/// boolean: && ||
/// boolean: and or
/// application: <|
RightAssociative,
@ -206,13 +211,13 @@ pub enum Associativity {
impl BinOp {
pub fn associativity(self) -> Associativity {
const ASSOCIATIVITY_TABLE: [Associativity; 17] = generate_associativity_table();
const ASSOCIATIVITY_TABLE: [Associativity; 18] = generate_associativity_table();
ASSOCIATIVITY_TABLE[self as usize]
}
fn precedence(self) -> u8 {
const PRECEDENCE_TABLE: [u8; 17] = generate_precedence_table();
const PRECEDENCE_TABLE: [u8; 18] = generate_precedence_table();
PRECEDENCE_TABLE[self as usize]
}
@ -232,14 +237,14 @@ impl Ord for BinOp {
impl std::fmt::Display for BinOp {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
const DISPLAY_TABLE: [&str; 17] = generate_display_table();
const DISPLAY_TABLE: [&str; 18] = generate_display_table();
write!(f, "{}", DISPLAY_TABLE[*self as usize])
}
}
const fn generate_precedence_table() -> [u8; 17] {
let mut table = [0u8; 17];
const fn generate_precedence_table() -> [u8; 18] {
let mut table = [0u8; 18];
let mut i = 0;
while i < PRECEDENCES.len() {
@ -250,8 +255,8 @@ const fn generate_precedence_table() -> [u8; 17] {
table
}
const fn generate_associativity_table() -> [Associativity; 17] {
let mut table = [NonAssociative; 17];
const fn generate_associativity_table() -> [Associativity; 18] {
let mut table = [NonAssociative; 18];
let mut i = 0;
while i < ASSOCIATIVITIES.len() {
@ -262,8 +267,8 @@ const fn generate_associativity_table() -> [Associativity; 17] {
table
}
const fn generate_display_table() -> [&'static str; 17] {
let mut table = [""; 17];
const fn generate_display_table() -> [&'static str; 18] {
let mut table = [""; 18];
let mut i = 0;
while i < DISPLAY_STRINGS.len() {

View file

@ -14,6 +14,7 @@ pub enum LowLevel {
StrCountUtf8Bytes,
StrFromInt,
StrFromUtf8,
StrFromUtf8Lossy,
StrToUtf8,
StrRepeat,
StrFromFloat,
@ -26,6 +27,9 @@ pub enum LowLevel {
StrReserve,
StrWithCapacity,
StrReleaseExcessCapacity,
StrWithAsciiLowercased,
StrWithAsciiUppercased,
StrCaselessAsciiEquals,
ListLenUsize,
ListLenU64,
ListWithCapacity,
@ -109,8 +113,6 @@ pub enum LowLevel {
NumF64FromParts,
Eq,
NotEq,
And,
Or,
Not,
Hash,
PtrCast,
@ -256,6 +258,7 @@ map_symbol_to_lowlevel! {
StrSplitOn <= STR_SPLIT_ON;
StrCountUtf8Bytes <= STR_COUNT_UTF8_BYTES;
StrFromUtf8 <= STR_FROM_UTF8_LOWLEVEL;
StrFromUtf8Lossy <= STR_FROM_UTF8_LOSSY;
StrToUtf8 <= STR_TO_UTF8;
StrRepeat <= STR_REPEAT;
StrTrim <= STR_TRIM;
@ -267,6 +270,9 @@ map_symbol_to_lowlevel! {
StrToNum <= STR_TO_NUM;
StrWithCapacity <= STR_WITH_CAPACITY;
StrReleaseExcessCapacity <= STR_RELEASE_EXCESS_CAPACITY;
StrWithAsciiLowercased <= STR_WITH_ASCII_LOWERCASED;
StrWithAsciiUppercased <= STR_WITH_ASCII_UPPERCASED;
StrCaselessAsciiEquals <= STR_CASELESS_ASCII_EQUALS;
ListLenU64 <= LIST_LEN_U64;
ListLenUsize <= LIST_LEN_USIZE;
ListGetCapacity <= LIST_CAPACITY;
@ -343,8 +349,6 @@ map_symbol_to_lowlevel! {
NumF64FromParts <= NUM_F64_FROM_PARTS;
Eq <= BOOL_STRUCTURAL_EQ;
NotEq <= BOOL_STRUCTURAL_NOT_EQ;
And <= BOOL_AND;
Or <= BOOL_OR;
Not <= BOOL_NOT;
Unreachable <= LIST_UNREACHABLE;
DictPseudoSeed <= DICT_PSEUDO_SEED;

View file

@ -986,6 +986,10 @@ macro_rules! define_builtins {
self.to_zero_indexed() < $total
}
pub const fn first_after_builtins() -> Self {
ModuleId::from_zero_indexed($total)
}
$(
pub const $module_const: ModuleId = ModuleId::from_zero_indexed($module_id);
)+
@ -1355,16 +1359,14 @@ define_builtins! {
0 BOOL_BOOL: "Bool" exposed_type=true // the Bool.Bool type alias
1 BOOL_FALSE: "false"
2 BOOL_TRUE: "true"
3 BOOL_AND: "and"
4 BOOL_OR: "or"
5 BOOL_NOT: "not"
6 BOOL_XOR: "xor"
7 BOOL_NEQ: "is_not_eq"
8 BOOL_EQ: "Eq" exposed_type=true
9 BOOL_IS_EQ: "is_eq"
10 BOOL_IS_EQ_IMPL: "bool_is_eq"
unexposed 11 BOOL_STRUCTURAL_EQ: "structural_eq"
unexposed 12 BOOL_STRUCTURAL_NOT_EQ: "structural_not_eq"
3 BOOL_NOT: "not"
4 BOOL_XOR: "xor"
5 BOOL_NEQ: "is_not_eq"
6 BOOL_EQ: "Eq" exposed_type=true
7 BOOL_IS_EQ: "is_eq"
8 BOOL_IS_EQ_IMPL: "bool_is_eq"
unexposed 9 BOOL_STRUCTURAL_EQ: "structural_eq"
unexposed 10 BOOL_STRUCTURAL_NOT_EQ: "structural_not_eq"
}
5 STR: "Str" => {
0 STR_STR: "Str" exposed_apply_type=true // the Str.Str type alias
@ -1377,8 +1379,8 @@ define_builtins! {
7 STR_STARTS_WITH: "starts_with"
8 STR_ENDS_WITH: "ends_with"
9 STR_FROM_UTF8: "from_utf8"
10 STR_UT8_PROBLEM: "Utf8Problem" // the Utf8Problem type alias
11 STR_UT8_BYTE_PROBLEM: "Utf8ByteProblem" // the Utf8ByteProblem type alias
10 STR_FROM_UTF8_LOSSY: "from_utf8_lossy"
11 STR_UTF8_BYTE_PROBLEM: "Utf8Problem"
12 STR_TO_UTF8: "to_utf8"
13 STR_WALK_UTF8: "walk_utf8"
14 STR_ALIAS_ANALYSIS_STATIC: "#aliasAnalysisStatic" // string with the static lifetime
@ -1418,6 +1420,13 @@ define_builtins! {
48 STR_RELEASE_EXCESS_CAPACITY: "release_excess_capacity"
49 STR_DROP_PREFIX: "drop_prefix"
50 STR_DROP_SUFFIX: "drop_suffix"
51 STR_WITH_ASCII_LOWERCASED: "with_ascii_lowercased"
52 STR_WITH_ASCII_UPPERCASED: "with_ascii_uppercased"
53 STR_CASELESS_ASCII_EQUALS: "caseless_ascii_equals"
54 STR_FROM_UTF16: "from_utf16"
55 STR_FROM_UTF16_LOSSY: "from_utf16_lossy"
56 STR_FROM_UTF32: "from_utf32"
57 STR_FROM_UTF32_LOSSY: "from_utf32_lossy"
}
6 LIST: "List" => {
0 LIST_LIST: "List" exposed_apply_type=true // the List.List type alias

View file

@ -1549,6 +1549,9 @@ fn low_level_no_rc(lowlevel: &LowLevel) -> RC {
ListPrepend => RC::Rc,
StrJoinWith => RC::NoRc,
ListSortWith => RC::Rc,
StrWithAsciiLowercased => RC::Rc,
StrWithAsciiUppercased => RC::Rc,
StrCaselessAsciiEquals => RC::NoRc,
ListAppendUnsafe
| ListReserve
@ -1562,7 +1565,7 @@ fn low_level_no_rc(lowlevel: &LowLevel) -> RC {
Eq | NotEq => RC::NoRc,
And | Or | NumAdd | NumAddWrap | NumAddChecked | NumAddSaturated | NumSub | NumSubWrap
NumAdd | NumAddWrap | NumAddChecked | NumAddSaturated | NumSub | NumSubWrap
| NumSubChecked | NumSubSaturated | NumMul | NumMulWrap | NumMulSaturated
| NumMulChecked | NumGt | NumGte | NumLt | NumLte | NumCompare | NumDivFrac
| NumDivTruncUnchecked | NumDivCeilUnchecked | NumRemUnchecked | NumIsMultipleOf
@ -1603,6 +1606,7 @@ fn low_level_no_rc(lowlevel: &LowLevel) -> RC {
DictPseudoSeed => RC::NoRc,
StrStartsWith | StrEndsWith => RC::NoRc,
StrFromUtf8 => RC::Rc,
StrFromUtf8Lossy => RC::Rc,
StrToUtf8 => RC::Rc,
StrRepeat => RC::NoRc,
StrFromInt | StrFromFloat => RC::NoRc,

View file

@ -1258,10 +1258,13 @@ pub(crate) fn lowlevel_borrow_signature(op: LowLevel) -> &'static [Ownership] {
StrReleaseExcessCapacity => &[OWNED],
ListIncref => &[OWNED],
ListDecref => &[OWNED],
StrWithAsciiLowercased => &[OWNED],
StrWithAsciiUppercased => &[OWNED],
StrCaselessAsciiEquals => &[BORROWED, BORROWED],
Eq | NotEq => &[BORROWED, BORROWED],
And | Or | NumAdd | NumAddWrap | NumAddChecked | NumAddSaturated | NumSub | NumSubWrap
NumAdd | NumAddWrap | NumAddChecked | NumAddSaturated | NumSub | NumSubWrap
| NumSubChecked | NumSubSaturated | NumMul | NumMulWrap | NumMulSaturated
| NumMulChecked | NumGt | NumGte | NumLt | NumLte | NumCompare | NumDivFrac
| NumDivTruncUnchecked | NumDivCeilUnchecked | NumRemUnchecked | NumIsMultipleOf
@ -1302,6 +1305,7 @@ pub(crate) fn lowlevel_borrow_signature(op: LowLevel) -> &'static [Ownership] {
| NumF64FromParts => &[IRRELEVANT],
StrStartsWith | StrEndsWith => &[BORROWED, BORROWED],
StrFromUtf8 => &[OWNED],
StrFromUtf8Lossy => &[BORROWED],
StrToUtf8 => &[OWNED],
StrRepeat => &[BORROWED, IRRELEVANT],
StrFromInt | StrFromFloat => &[IRRELEVANT],

View file

@ -1241,7 +1241,13 @@ impl<'a> ReuseEnvironment<'a> {
Retrieve the layout of a symbol.
*/
fn get_symbol_layout(&self, symbol: Symbol) -> &LayoutOption<'a> {
self.symbol_layouts.get(&symbol).expect("Expected symbol to have a layout. It should have been inserted in the environment already.")
self.symbol_layouts
.get(&symbol)
.expect(
"Expected symbol to have a layout. \
It should have been inserted in the environment already. \
We are investigating this issue, follow github.com/roc-lang/roc/issues/7461 for updates.",
)
}
/**

View file

@ -49,7 +49,15 @@ pub fn apply_trmc<'a, 'i>(
let env = &mut env;
for proc in procs.values_mut() {
// TODO temporary workaround for #7531, remove this cloning and sorting once that is fixed
let clone_procs = procs.clone();
let mut procs_key_value_list = clone_procs.iter().collect::<std::vec::Vec<_>>();
procs_key_value_list.sort_by(|a, b| a.0 .0.cmp(&b.0 .0));
for (key, _) in procs_key_value_list {
let proc = procs.get_mut(key).unwrap();
use self::SelfRecursive::*;
if let SelfRecursive(id) = proc.is_self_recursive {
let trmc_candidate_symbols = trmc_candidates(env.interner, proc);

View file

@ -738,7 +738,7 @@ fn parse_stmt_operator_chain<'a>(
| Expr::Apply(
Loc {
region: _,
value: Expr::Tag(..)
value: Expr::Tag(_)
},
&[],
_
@ -1902,6 +1902,10 @@ fn parse_stmt_after_apply<'a>(
) -> ParseResult<'a, Stmt<'a>, EExpr<'a>> {
let before_op = state.clone();
match loc(operator()).parse(arena, state.clone(), call_min_indent) {
Err((MadeProgress, EExpr::BadOperator("|", _))) => {
let expr = parse_expr_final(expr_state, arena);
return Ok((MadeProgress, Stmt::Expr(expr), state));
}
Err((MadeProgress, f)) => Err((MadeProgress, f)),
Ok((_, loc_op, state)) => {
expr_state.consume_spaces(arena);
@ -1930,43 +1934,6 @@ fn parse_stmt_after_apply<'a>(
}
}
// #[allow(clippy::too_many_arguments)]
// fn parse_expr_after_apply<'a>(
// arena: &'a Bump,
// state: State<'a>,
// min_indent: u32,
// call_min_indent: u32,
// check_for_arrow: CheckForArrow,
// check_for_defs: bool,
// mut expr_state: ExprState<'a>,
// before_op: State<'a>,
// initial_state: State<'a>,
// ) -> Result<(Progress, Expr<'a>, State<'a>), (Progress, EExpr<'a>)> {
// match loc(bin_op(check_for_defs)).parse(arena, state.clone(), call_min_indent) {
// Err((MadeProgress, f)) => Err((MadeProgress, f)),
// Ok((_, loc_op, state)) => {
// expr_state.consume_spaces(arena);
// let initial_state = before_op;
// parse_expr_operator(
// arena,
// state,
// min_indent,
// call_min_indent,
// options,
// check_for_defs,
// expr_state,
// loc_op,
// initial_state,
// )
// }
// Err((NoProgress, _)) => {
// let expr = parse_expr_final(expr_state, arena);
// // roll back space parsing
// Ok((MadeProgress, expr, initial_state))
// }
// }
// }
#[allow(clippy::too_many_arguments)]
fn parse_apply_arg<'a>(
arena: &'a Bump,
@ -2336,47 +2303,41 @@ pub fn parse_top_level_defs<'a>(
fn closure_help<'a>(check_for_arrow: CheckForArrow) -> impl Parser<'a, Expr<'a>, EClosure<'a>> {
one_of!(
closure_new_syntax_help(),
closure_new_syntax_help(check_for_arrow),
closure_old_syntax_help(check_for_arrow),
)
}
fn closure_new_syntax_help<'a>() -> impl Parser<'a, Expr<'a>, EClosure<'a>> {
move |arena: &'a Bump, state: State<'a>, min_indent: u32| {
let parser = map_with_arena(
indented_seq_skip_first(
error_on_pizza(byte_indent(b'|', EClosure::Bar), EClosure::Start),
and(
sep_by1_e(
byte_indent(b',', EClosure::Comma),
space0_around_ee(
specialize_err(EClosure::Pattern, closure_param()),
EClosure::IndentArg,
EClosure::IndentArrow,
),
EClosure::Arg,
),
skip_first(
// Parse the -> which separates params from body
byte(b'|', EClosure::Bar),
// Parse the body
block(
CheckForArrow(false),
true,
EClosure::IndentBody,
EClosure::Body,
),
fn closure_new_syntax_help<'a>(
check_for_arrow: CheckForArrow,
) -> impl Parser<'a, Expr<'a>, EClosure<'a>> {
map_with_arena(
skip_first(
error_on_pizza(byte_indent(b'|', EClosure::Bar), EClosure::Start),
reset_min_indent(and(
sep_by1_e(
byte_indent(b',', EClosure::Comma),
space0_around_ee(
specialize_err(EClosure::Pattern, closure_param()),
EClosure::IndentArg,
EClosure::IndentArrow,
),
EClosure::Arg,
),
),
|arena: &'a Bump, (params, body)| {
let params: Vec<'a, Loc<Pattern<'a>>> = params;
let params: &'a [Loc<Pattern<'a>>] = params.into_bump_slice();
Expr::Closure(params, arena.alloc(body))
},
);
parser.parse(arena, state, min_indent)
}
skip_first(
// Parse the -> which separates params from body
byte(b'|', EClosure::Bar),
// Parse the body
block(check_for_arrow, true, EClosure::IndentBody, EClosure::Body),
),
)),
),
|arena: &'a Bump, (params, body)| {
let params: Vec<'a, Loc<Pattern<'a>>> = params;
let params: &'a [Loc<Pattern<'a>>] = params.into_bump_slice();
Expr::Closure(params, arena.alloc(body))
},
)
}
fn closure_old_syntax_help<'a>(
@ -2385,12 +2346,12 @@ fn closure_old_syntax_help<'a>(
// closure_help_help(options)
map_with_arena(
// After the first token, all other tokens must be indented past the start of the line
indented_seq_skip_first(
skip_first(
// All closures start with a '\' - e.g. (\x -> x + 1)
byte_indent(b'\\', EClosure::Start),
// Once we see the '\', we're committed to parsing this as a closure.
// It may turn out to be malformed, but it is definitely a closure.
and(
reset_min_indent(and(
// Parse the params
// Params are comma-separated
sep_by1_e(
@ -2408,7 +2369,7 @@ fn closure_old_syntax_help<'a>(
// Parse the body
block(check_for_arrow, true, EClosure::IndentBody, EClosure::Body),
),
),
)),
),
|arena: &'a Bump, (params, body)| {
let params: Vec<'a, Loc<Pattern<'a>>> = params;
@ -3073,6 +3034,11 @@ fn parse_stmt_seq<'a, E: SpaceProblem + 'a>(
break;
}
if new_state.bytes().starts_with(b"|") {
state = state_before_space;
break;
}
return Err((
MadeProgress,
wrap_error(arena.alloc(EExpr::BadExprEnd(state.pos())), state.pos()),
@ -3424,7 +3390,7 @@ fn starts_with_spaces_conservative(value: &Pattern<'_>) -> bool {
}
fn type_header_equivalent_to_pat<'a>(header: &TypeHeader<'a>, pat: &Pattern<'a>) -> bool {
match pat {
match pat.without_spaces() {
Pattern::Apply(func, args) => {
if !matches!(func.value, Pattern::Tag(tag) if header.name.value == tag) {
return false;
@ -3433,7 +3399,7 @@ fn type_header_equivalent_to_pat<'a>(header: &TypeHeader<'a>, pat: &Pattern<'a>)
return false;
}
for (arg, var) in (*args).iter().zip(header.vars) {
match (arg.value, var.value) {
match (arg.value.without_spaces(), var.value.without_spaces()) {
(Pattern::Identifier { ident: left }, TypeVar::Identifier(right)) => {
if left != right {
return false;
@ -3444,7 +3410,7 @@ fn type_header_equivalent_to_pat<'a>(header: &TypeHeader<'a>, pat: &Pattern<'a>)
}
true
}
Pattern::Tag(tag) => header.vars.is_empty() && header.name.value == *tag,
Pattern::Tag(tag) => header.vars.is_empty() && header.name.value == tag,
_ => false,
}
}
@ -4025,9 +3991,10 @@ enum OperatorOrDef {
}
fn bin_op<'a>(check_for_defs: bool) -> impl Parser<'a, BinOp, EExpr<'a>> {
move |_, state: State<'a>, min_indent| {
(move |arena: &'a Bump, state: State<'a>, min_indent| {
let start = state.pos();
let (_, op, state) = operator_help(EExpr::Start, EExpr::BadOperator, state, min_indent)?;
let (_, op, state) =
operator_help(arena, EExpr::Start, EExpr::BadOperator, state, min_indent)?;
let err_progress = if check_for_defs {
MadeProgress
} else {
@ -4043,16 +4010,20 @@ fn bin_op<'a>(check_for_defs: bool) -> impl Parser<'a, BinOp, EExpr<'a>> {
Err((err_progress, EExpr::BadOperator(":=", start)))
}
}
}
})
.trace("bin_op")
}
fn operator<'a>() -> impl Parser<'a, OperatorOrDef, EExpr<'a>> {
(move |_, state, min_indent| operator_help(EExpr::Start, EExpr::BadOperator, state, min_indent))
.trace("operator")
(move |arena: &'a Bump, state, min_indent| {
operator_help(arena, EExpr::Start, EExpr::BadOperator, state, min_indent)
})
.trace("operator")
}
#[inline(always)]
fn operator_help<'a, F, G, E>(
arena: &'a Bump,
to_expectation: F,
to_error: G,
mut state: State<'a>,
@ -4063,6 +4034,21 @@ where
G: Fn(&'a str, Position) -> E,
E: 'a,
{
let and_or = either(
parser::keyword(keyword::AND, EExpr::End),
parser::keyword(keyword::OR, EExpr::End),
);
match and_or.parse(arena, state.clone(), min_indent) {
Ok((MadeProgress, Either::First(_), state)) => {
return Ok((MadeProgress, OperatorOrDef::BinOp(BinOp::And), state))
}
Ok((MadeProgress, Either::Second(_), state)) => {
return Ok((MadeProgress, OperatorOrDef::BinOp(BinOp::Or), state))
}
_ => {}
}
let chomped = chomp_ops(state.bytes());
macro_rules! good {
@ -4097,6 +4083,7 @@ where
good!(OperatorOrDef::BinOp(BinOp::Minus), 1)
}
"?" => good!(OperatorOrDef::BinOp(BinOp::SingleQuestion), 1),
"*" => good!(OperatorOrDef::BinOp(BinOp::Star), 1),
"/" => good!(OperatorOrDef::BinOp(BinOp::Slash), 1),
"%" => good!(OperatorOrDef::BinOp(BinOp::Percent), 1),
@ -4118,6 +4105,7 @@ where
"<=" => good!(OperatorOrDef::BinOp(BinOp::LessThanOrEq), 2),
"&&" => good!(OperatorOrDef::BinOp(BinOp::And), 2),
"||" => good!(OperatorOrDef::BinOp(BinOp::Or), 2),
"|" => Err((NoProgress, to_error("|", state.pos()))),
"//" => good!(OperatorOrDef::BinOp(BinOp::DoubleSlash), 2),
"->" => {
// makes no progress, so it does not interfere with `_ if isGood -> ...`

View file

@ -927,7 +927,7 @@ impl<'a> HeaderType<'a> {
}
}
#[derive(Debug)]
#[derive(Debug, Clone)]
pub enum HeaderType<'a> {
App {
provides: &'a [Loc<ExposedName<'a>>],

View file

@ -10,6 +10,8 @@ pub const IMPORT: &str = "import";
pub const EXPECT: &str = "expect";
pub const RETURN: &str = "return";
pub const CRASH: &str = "crash";
pub const AND: &str = "and";
pub const OR: &str = "or";
// These keywords are valid in imports
pub const EXPOSING: &str = "exposing";
@ -21,8 +23,8 @@ pub const WHERE: &str = "where";
// These keywords are valid in headers
pub const PLATFORM: &str = "platform";
pub const KEYWORDS: [&str; 11] = [
IF, THEN, ELSE, WHEN, AS, IS, DBG, IMPORT, EXPECT, RETURN, CRASH,
pub const KEYWORDS: [&str; 13] = [
IF, THEN, ELSE, WHEN, AS, IS, DBG, IMPORT, EXPECT, RETURN, CRASH, AND, OR,
];
pub fn is_allowed_identifier(mut ident: &str) -> bool {

View file

@ -778,11 +778,11 @@ impl<'a> Normalize<'a> for Expr<'a> {
Expr::If {
if_thens,
final_else,
indented_else,
indented_else: _,
} => Expr::If {
if_thens: if_thens.normalize(arena),
final_else: arena.alloc(final_else.normalize(arena)),
indented_else,
indented_else: false,
},
Expr::When(a, b) => Expr::When(arena.alloc(a.normalize(arena)), b.normalize(arena)),
Expr::ParensAround(a) => {

View file

@ -15,15 +15,12 @@ use bumpalo::Bump;
use roc_can::abilities::{AbilitiesStore, MemberSpecializationInfo};
use roc_can::constraint::Constraint::{self, *};
use roc_can::constraint::{
Cycle, FxCallConstraint, FxSuffixConstraint, FxSuffixKind, LetConstraint, OpportunisticResolve,
TryTargetConstraint,
Cycle, FxCallConstraint, FxSuffixConstraint, FxSuffixKind, Generalizable, LetConstraint,
OpportunisticResolve, TryTargetConstraint,
};
use roc_can::expected::{Expected, PExpected};
use roc_can::module::ModuleParams;
use roc_collections::{VecMap, VecSet};
use roc_debug_flags::dbg_do;
#[cfg(debug_assertions)]
use roc_debug_flags::ROC_VERIFY_RIGID_LET_GENERALIZED;
use roc_error_macros::internal_error;
use roc_module::ident::IdentSuffix;
use roc_module::symbol::{ModuleId, Symbol};
@ -32,8 +29,8 @@ use roc_region::all::{Loc, Region};
use roc_solve_problem::TypeError;
use roc_solve_schema::UnificationMode;
use roc_types::subs::{
self, Content, FlatType, GetSubsSlice, Mark, OptVariable, Rank, Subs, TagExt, UlsOfVar,
Variable,
self, Content, ErrorTypeContext, FlatType, GetSubsSlice, Mark, OptVariable, Rank, Subs, TagExt,
UlsOfVar, Variable,
};
use roc_types::types::{Category, Polarity, Reason, RecordField, Type, TypeExtension, Types, Uls};
use roc_unify::unify::{
@ -356,29 +353,13 @@ fn solve(
generalize(env, young_mark, visit_mark, rank.next());
debug_assert!(env.pools.get(rank.next()).is_empty(), "variables left over in let-binding scope, but they should all be in a lower scope or generalized now");
// check that things went well
dbg_do!(ROC_VERIFY_RIGID_LET_GENERALIZED, {
let rigid_vars = &env.constraints[let_con.rigid_vars];
// NOTE the `subs.redundant` check does not come from elm.
// It's unclear whether this is a bug with our implementation
// (something is redundant that shouldn't be)
// or that it just never came up in elm.
let mut it = rigid_vars
.iter()
.filter(|loc_var| {
let var = loc_var.value;
!env.subs.redundant(var) && env.subs.get_rank(var) != Rank::GENERALIZED
})
.peekable();
if it.peek().is_some() {
let failing: Vec<_> = it.collect();
println!("Rigids {:?}", &rigid_vars);
println!("Failing {failing:?}");
debug_assert!(false);
}
});
let named_variables = &env.constraints[let_con.rigid_vars];
check_named_variables_are_generalized(
env,
problems,
named_variables,
let_con.generalizable,
);
let mut new_scope = scope.clone();
for (symbol, loc_var) in local_def_vars.iter() {
@ -1636,6 +1617,30 @@ fn solve(
state
}
fn check_named_variables_are_generalized(
env: &mut InferenceEnv<'_>,
problems: &mut Vec<TypeError>,
named_variables: &[Loc<Variable>],
generalizable: Generalizable,
) {
for loc_var in named_variables {
let is_generalized = env.subs.get_rank(loc_var.value) == Rank::GENERALIZED;
if !is_generalized {
// TODO: should be OF_PATTERN if on the LHS of a function, otherwise OF_VALUE.
let polarity = Polarity::OF_VALUE;
let ctx = ErrorTypeContext::NON_GENERALIZED_AS_INFERRED;
let error_type = env
.subs
.var_to_error_type_contextual(loc_var.value, ctx, polarity);
problems.push(TypeError::TypeIsNotGeneralized(
loc_var.region,
error_type,
generalizable,
));
}
}
}
fn solve_suffix_fx(
env: &mut InferenceEnv<'_>,
problems: &mut Vec<TypeError>,

View file

@ -165,7 +165,7 @@ mod solve_expr {
Str.from_utf8
"
),
"List U8 -> Result Str [BadUtf8 { index : U64, problem : Utf8ByteProblem }]",
"List U8 -> Result Str [BadUtf8 { index : U64, problem : Utf8Problem }]",
);
}
@ -2510,13 +2510,15 @@ mod solve_expr {
infer_eq_without_problem(
indoc!(
r"
empty : [Cons a (ConsList a), Nil] as ConsList a
empty = Nil
ConsList a : [Cons a (ConsList a), Nil]
empty
"
empty : ConsList _
empty = Nil
empty
"
),
"ConsList a",
"ConsList *",
);
}
@ -3742,7 +3744,7 @@ mod solve_expr {
indoc!(
r"
\rec ->
{ x, y } : { x : I64, y ? Bool }*
{ x, y } : { x : I64, y ? Bool }_
{ x, y ? Bool.false } = rec
{ x, y }
@ -3824,6 +3826,42 @@ mod solve_expr {
);
}
#[test]
fn str_with_ascii_lowercased() {
infer_eq_without_problem(
indoc!(
r"
Str.with_ascii_lowercased
"
),
"Str -> Str",
);
}
#[test]
fn str_with_ascii_uppercased() {
infer_eq_without_problem(
indoc!(
r"
Str.with_ascii_uppercased
"
),
"Str -> Str",
);
}
#[test]
fn str_caseless_ascii_equals() {
infer_eq_without_problem(
indoc!(
r"
Str.caseless_ascii_equals
"
),
"Str, Str -> Bool",
);
}
#[test]
fn list_take_first() {
infer_eq_without_problem(
@ -3909,26 +3947,6 @@ mod solve_expr {
);
}
#[test]
fn double_named_rigids() {
infer_eq_without_problem(
indoc!(
r#"
app "test" provides [main] to "./platform"
main : List x
main =
empty : List x
empty = []
empty
"#
),
"List x",
);
}
#[test]
fn double_tag_application() {
infer_eq_without_problem(

View file

@ -1,7 +1,7 @@
//! Provides types to describe problems that can occur during solving.
use std::{path::PathBuf, str::Utf8Error};
use roc_can::constraint::{ExpectEffectfulReason, FxSuffixKind};
use roc_can::constraint::{ExpectEffectfulReason, FxSuffixKind, Generalizable};
use roc_can::expr::TryKind;
use roc_can::{
constraint::FxCallKind,
@ -50,6 +50,7 @@ pub enum TypeError {
UnsuffixedEffectfulFunction(Region, FxSuffixKind),
SuffixedPureFunction(Region, FxSuffixKind),
InvalidTryTarget(Region, ErrorType, TryKind),
TypeIsNotGeneralized(Region, ErrorType, Generalizable),
}
impl TypeError {
@ -80,6 +81,7 @@ impl TypeError {
TypeError::UnsuffixedEffectfulFunction(_, _) => Warning,
TypeError::SuffixedPureFunction(_, _) => Warning,
TypeError::InvalidTryTarget(_, _, _) => RuntimeError,
TypeError::TypeIsNotGeneralized(..) => RuntimeError,
}
}
@ -101,7 +103,8 @@ impl TypeError {
| TypeError::ExpectedEffectful(region, _)
| TypeError::UnsuffixedEffectfulFunction(region, _)
| TypeError::SuffixedPureFunction(region, _)
| TypeError::InvalidTryTarget(region, _, _) => Some(*region),
| TypeError::InvalidTryTarget(region, _, _)
| TypeError::TypeIsNotGeneralized(region, _, _) => Some(*region),
TypeError::UnfulfilledAbility(ab, ..) => ab.region(),
TypeError::Exhaustive(e) => Some(e.region()),
TypeError::CircularDef(c) => c.first().map(|ce| ce.symbol_region),

View file

@ -2095,13 +2095,13 @@ mod eq {
LyingEq := U8 implements [Eq {is_eq}]
is_eq = \@LyingEq m, @LyingEq n -> m != n
is_eq = \@LyingEq(m), @LyingEq(n) -> m != n
main =
a = @LyingEq 10
b = @LyingEq 5
c = @LyingEq 5
if Bool.is_eq a b && !(Bool.is_eq b c) then
a = @LyingEq(10)
b = @LyingEq(5)
c = @LyingEq(5)
if Bool.is_eq(a, b) and !(Bool.is_eq(b, c)) then
"okay"
else
"fail"

View file

@ -121,7 +121,7 @@ fn bool_logic() {
bool2 = Bool.false
bool3 = !bool1
(bool1 && bool2) || bool2 && bool3
(bool1 and bool2) or bool2 and bool3
"#
),
false,
@ -132,19 +132,19 @@ fn bool_logic() {
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))]
fn and_bool() {
assert_evals_to!("Bool.true && Bool.true", true, bool);
assert_evals_to!("Bool.true && Bool.false", false, bool);
assert_evals_to!("Bool.false && Bool.true", false, bool);
assert_evals_to!("Bool.false && Bool.false", false, bool);
assert_evals_to!("Bool.true and Bool.true", true, bool);
assert_evals_to!("Bool.true and Bool.false", false, bool);
assert_evals_to!("Bool.false and Bool.true", false, bool);
assert_evals_to!("Bool.false and Bool.false", false, bool);
}
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))]
fn or_bool() {
assert_evals_to!("Bool.true || Bool.true", true, bool);
assert_evals_to!("Bool.true || Bool.false", true, bool);
assert_evals_to!("Bool.false || Bool.true", true, bool);
assert_evals_to!("Bool.false || Bool.false", false, bool);
assert_evals_to!("Bool.true or Bool.true", true, bool);
assert_evals_to!("Bool.true or Bool.false", true, bool);
assert_evals_to!("Bool.false or Bool.true", true, bool);
assert_evals_to!("Bool.false or Bool.false", false, bool);
}
#[test]
@ -544,7 +544,7 @@ fn eq_different_rosetrees() {
cd = c2 == d2
ab && cd
ab and cd
"#
),
true,

View file

@ -662,7 +662,7 @@ fn linked_list_len_1() {
LinkedList a : [Nil, Cons a (LinkedList a)]
one : LinkedList (Int *)
one : LinkedList (Int _)
one = Cons 1 Nil
length : LinkedList a -> Int *
@ -690,7 +690,7 @@ fn linked_list_len_twice_1() {
LinkedList a : [Nil, Cons a (LinkedList a)]
one : LinkedList (Int *)
one : LinkedList (Int _)
one = Cons 1 Nil
length : LinkedList a -> Int *
@ -718,7 +718,7 @@ fn linked_list_len_3() {
LinkedList a : [Nil, Cons a (LinkedList a)]
three : LinkedList (Int *)
three : LinkedList (Int _)
three = Cons 3 (Cons 2 (Cons 1 Nil))
length : LinkedList a -> Int *
@ -747,7 +747,7 @@ fn linked_list_sum_num_a() {
LinkedList a : [Nil, Cons a (LinkedList a)]
three : LinkedList (Int *)
three : LinkedList (Int _)
three = Cons 3 (Cons 2 (Cons 1 Nil))
@ -776,7 +776,7 @@ fn linked_list_sum_int() {
LinkedList a : [Nil, Cons a (LinkedList a)]
zero : LinkedList (Int *)
zero : LinkedList (Int _)
zero = Nil
sum : LinkedList (Int a) -> Int a
@ -804,7 +804,7 @@ fn linked_list_map() {
LinkedList a : [Nil, Cons a (LinkedList a)]
three : LinkedList (Int *)
three : LinkedList (Int _)
three = Cons 3 (Cons 2 (Cons 1 Nil))
sum : LinkedList (Num a) -> Num a
@ -836,7 +836,7 @@ fn when_nested_maybe() {
r"
Maybe a : [Nothing, Just a]
x : Maybe (Maybe (Int a))
x : Maybe (Maybe (Int _))
x = Just (Just 41)
when x is
@ -853,7 +853,7 @@ fn when_nested_maybe() {
r"
Maybe a : [Nothing, Just a]
x : Maybe (Maybe (Int *))
x : Maybe (Maybe (Int _))
x = Just Nothing
when x is
@ -871,7 +871,7 @@ fn when_nested_maybe() {
r"
Maybe a : [Nothing, Just a]
x : Maybe (Maybe (Int *))
x : Maybe (Maybe (Int _))
x = Nothing
when x is
@ -1402,7 +1402,7 @@ fn recursive_function_with_rigid() {
else
1 + foo { count: state.count - 1, x: state.x }
main : Int *
main : Int _
main =
foo { count: 3, x: {} }
"#
@ -1517,7 +1517,7 @@ fn rbtree_balance_3() {
balance = \key, left ->
Node key left Empty
main : RedBlackTree (Int *)
main : RedBlackTree (Int _)
main =
balance 0 Empty
"#
@ -1696,7 +1696,7 @@ fn nested_pattern_match_two_ways() {
_ -> 3
_ -> 3
main : Int *
main : Int _
main =
when balance Nil is
_ -> 3
@ -1719,7 +1719,7 @@ fn nested_pattern_match_two_ways() {
Cons 1 (Cons 1 _) -> 3
_ -> 3
main : Int *
main : Int _
main =
when balance Nil is
_ -> 3
@ -1751,7 +1751,7 @@ fn linked_list_guarded_double_pattern_match() {
_ -> 3
_ -> 3
main : Int *
main : Int _
main =
when balance Nil is
_ -> 3
@ -1778,7 +1778,7 @@ fn linked_list_double_pattern_match() {
Cons _ (Cons x _) -> x
_ -> 0
main : Int *
main : Int _
main =
foo (Cons 1 (Cons 32 Nil))
"#
@ -1886,7 +1886,7 @@ fn wildcard_rigid() {
@Effect inner
main : MyTask {} (Frac *)
main : MyTask {} (Frac _)
main = always {}
"#
),

View file

@ -135,7 +135,7 @@ fn err_type_var_annotation() {
assert_evals_to!(
indoc!(
r"
ok : Result I64 *
ok : Result I64 _
ok = Ok 3
Result.map_ok ok (\x -> x + 1)

File diff suppressed because it is too large Load diff

View file

@ -158,19 +158,19 @@ fn even_odd() {
assert_evals_to!(
indoc!(
r"
even = \n ->
even = |n|
when n is
0 -> Bool.true
1 -> Bool.false
_ -> odd (n - 1)
_ -> odd(n - 1)
odd = \n ->
odd = |n|
when n is
0 -> Bool.false
1 -> Bool.true
_ -> even (n - 1)
_ -> even(n - 1)
odd 5 && even 42
odd(5) and even(42)
"
),
true,
@ -1075,7 +1075,7 @@ fn applied_tag_function_result() {
assert_evals_to!(
indoc!(
r#"
x : List (Result Str *)
x : List (Result Str _)
x = List.map ["a", "b"] Ok
List.keep_oks x (\y -> y)
@ -2315,12 +2315,12 @@ fn recursive_tag_id_in_allocation_eq() {
]
x : Value
x = G 42
x = G(42)
y : Value
y = H 42
y = H(42)
main = (x == x) && (x != y) && (y == y)
main = x == x and x != y and y == y
"#
),
true,

View file

@ -22,7 +22,6 @@ pub mod gen_str;
pub mod gen_tags;
pub mod gen_tuples;
mod helpers;
pub mod wasm_str;
#[cfg(feature = "gen-wasm")]
pub mod wasm_linking;

View file

@ -66,7 +66,7 @@ fn build_app_mono<'a>(
let or1_expr = Expr::Call(Call {
call_type: CallType::LowLevel {
op: LowLevel::Or,
op: LowLevel::NumBitwiseOr,
update_mode: UpdateModeId::BACKEND_DUMMY,
},
arguments: arena.alloc([js_call_result, host_call_result]),
@ -74,7 +74,7 @@ fn build_app_mono<'a>(
let or2_expr = Expr::Call(Call {
call_type: CallType::LowLevel {
op: LowLevel::Or,
op: LowLevel::NumBitwiseOr,
update_mode: UpdateModeId::BACKEND_DUMMY,
},
arguments: arena.alloc([or1, bitflag]),

File diff suppressed because it is too large Load diff

View file

@ -1,6 +1,6 @@
procedure Bool.11 (#Attr.2, #Attr.3):
let Bool.23 : Int1 = lowlevel Eq #Attr.2 #Attr.3;
ret Bool.23;
procedure Bool.9 (#Attr.2, #Attr.3):
let Bool.21 : Int1 = lowlevel Eq #Attr.2 #Attr.3;
ret Bool.21;
procedure List.116 (List.563, List.564, List.565):
let List.693 : U64 = 0i64;
@ -51,7 +51,7 @@ procedure List.72 (#Attr.2, #Attr.3, #Attr.4):
let List.681 : List U8 = lowlevel ListSublist #Attr.2 #Attr.3 #Attr.4;
ret List.681;
procedure List.80 (#Derived_gen.0, #Derived_gen.1, #Derived_gen.2, #Derived_gen.3, #Derived_gen.4):
procedure List.80 (Bool.22, Bool.23, Bool.24, Bool.25, Bool.26):
joinpoint List.695 List.566 List.567 List.568 List.569 List.570:
let List.697 : Int1 = CallByName Num.22 List.569 List.570;
if List.697 then
@ -75,8 +75,8 @@ procedure List.80 (#Derived_gen.0, #Derived_gen.1, #Derived_gen.2, #Derived_gen.
let List.696 : [C U64, C U64] = TagId(1) List.567;
ret List.696;
in
inc #Derived_gen.0;
jump List.695 #Derived_gen.0 #Derived_gen.1 #Derived_gen.2 #Derived_gen.3 #Derived_gen.4;
inc Bool.22;
jump List.695 Bool.22 Bool.23 Bool.24 Bool.25 Bool.26;
procedure Num.22 (#Attr.2, #Attr.3):
let Num.286 : Int1 = lowlevel NumLt #Attr.2 #Attr.3;
@ -95,7 +95,7 @@ procedure Test.1 (Test.2):
let Test.14 : {} = Struct {};
let Test.3 : U64 = CallByName List.26 Test.2 Test.13 Test.14;
let Test.12 : U64 = 0i64;
let Test.10 : Int1 = CallByName Bool.11 Test.3 Test.12;
let Test.10 : Int1 = CallByName Bool.9 Test.3 Test.12;
if Test.10 then
ret Test.2;
else

View file

@ -8,7 +8,7 @@ procedure Test.4 (Test.27):
let Test.38 : I64 = CallByName Test.5 Test.27 Test.39 Test.40;
ret Test.38;
procedure Test.5 (#Derived_gen.0, #Derived_gen.1, #Derived_gen.2):
procedure Test.5 (Bool.21, Bool.22, Bool.23):
joinpoint Test.41 Test.29 Test.30 Test.31:
let Test.51 : U8 = 0i64;
let Test.52 : U8 = GetTagId Test.29;
@ -16,22 +16,22 @@ procedure Test.5 (#Derived_gen.0, #Derived_gen.1, #Derived_gen.2):
if Test.53 then
let Test.32 : [<rnu><null>, C *self *self] = UnionAtIndex (Id 0) (Index 0) Test.29;
let Test.33 : [<rnu><null>, C *self *self] = UnionAtIndex (Id 0) (Index 1) Test.29;
joinpoint #Derived_gen.3 #Derived_gen.6:
let #Derived_gen.7 : [<rnu>C [<rnu><null>, C *self *self] *self, <null>] = lowlevel PtrCast #Derived_gen.6;
let Test.43 : [<rnu>C [<rnu><null>, C *self *self] *self, <null>] = Reuse #Derived_gen.7 UpdateModeId { id: 1 } TagId(1) Test.33 Test.30;
joinpoint Bool.24 Bool.27:
let Bool.28 : [<rnu>C [<rnu><null>, C *self *self] *self, <null>] = lowlevel PtrCast Bool.27;
let Test.43 : [<rnu>C [<rnu><null>, C *self *self] *self, <null>] = Reuse Bool.28 UpdateModeId { id: 1 } TagId(1) Test.33 Test.30;
let Test.45 : I64 = 1i64;
let Test.44 : I64 = CallByName Num.19 Test.31 Test.45;
jump Test.41 Test.32 Test.43 Test.44;
in
let #Derived_gen.4 : Int1 = lowlevel RefCountIsUnique Test.29;
if #Derived_gen.4 then
jump #Derived_gen.3 Test.29;
let Bool.25 : Int1 = lowlevel RefCountIsUnique Test.29;
if Bool.25 then
jump Bool.24 Test.29;
else
inc Test.32;
inc Test.33;
decref Test.29;
let #Derived_gen.8 : [<rnu><null>, C *self *self] = NullPointer;
jump #Derived_gen.3 #Derived_gen.8;
let Bool.29 : [<rnu><null>, C *self *self] = NullPointer;
jump Bool.24 Bool.29;
else
let Test.48 : U8 = 1i64;
let Test.49 : U8 = GetTagId Test.30;
@ -39,8 +39,8 @@ procedure Test.5 (#Derived_gen.0, #Derived_gen.1, #Derived_gen.2):
if Test.50 then
let Test.35 : [<rnu><null>, C *self *self] = UnionAtIndex (Id 1) (Index 0) Test.30;
let Test.36 : [<rnu>C [<rnu><null>, C *self *self] *self, <null>] = UnionAtIndex (Id 1) (Index 1) Test.30;
let #Derived_gen.5 : Int1 = lowlevel RefCountIsUnique Test.30;
if #Derived_gen.5 then
let Bool.26 : Int1 = lowlevel RefCountIsUnique Test.30;
if Bool.26 then
free Test.30;
jump Test.41 Test.35 Test.36 Test.31;
else
@ -51,7 +51,7 @@ procedure Test.5 (#Derived_gen.0, #Derived_gen.1, #Derived_gen.2):
else
ret Test.31;
in
jump Test.41 #Derived_gen.0 #Derived_gen.1 #Derived_gen.2;
jump Test.41 Bool.21 Bool.22 Bool.23;
procedure Test.0 ():
let Test.64 : [<rnu><null>, C *self *self] = TagId(1) ;

View file

@ -1,4 +1,4 @@
procedure List.101 (#Derived_gen.0, #Derived_gen.1, #Derived_gen.2, #Derived_gen.3, #Derived_gen.4):
procedure List.101 (Bool.21, Bool.22, Bool.23, Bool.24, Bool.25):
joinpoint List.681 List.175 List.176 List.177 List.178 List.179:
let List.683 : Int1 = CallByName Num.22 List.178 List.179;
if List.683 then
@ -11,8 +11,8 @@ procedure List.101 (#Derived_gen.0, #Derived_gen.1, #Derived_gen.2, #Derived_gen
dec List.175;
ret List.176;
in
inc #Derived_gen.0;
jump List.681 #Derived_gen.0 #Derived_gen.1 #Derived_gen.2 #Derived_gen.3 #Derived_gen.4;
inc Bool.21;
jump List.681 Bool.21 Bool.22 Bool.23 Bool.24 Bool.25;
procedure List.18 (List.172, List.173, List.174):
let List.679 : U64 = 0i64;

View file

@ -1,4 +1,4 @@
procedure List.101 (#Derived_gen.0, #Derived_gen.1, #Derived_gen.2, #Derived_gen.3, #Derived_gen.4):
procedure List.101 (Bool.21, Bool.22, Bool.23, Bool.24, Bool.25):
joinpoint List.681 List.175 List.176 List.177 List.178 List.179:
let List.683 : Int1 = CallByName Num.22 List.178 List.179;
if List.683 then
@ -11,8 +11,8 @@ procedure List.101 (#Derived_gen.0, #Derived_gen.1, #Derived_gen.2, #Derived_gen
dec List.175;
ret List.176;
in
inc #Derived_gen.0;
jump List.681 #Derived_gen.0 #Derived_gen.1 #Derived_gen.2 #Derived_gen.3 #Derived_gen.4;
inc Bool.21;
jump List.681 Bool.21 Bool.22 Bool.23 Bool.24 Bool.25;
procedure List.18 (List.172, List.173, List.174):
let List.679 : U64 = 0i64;

View file

@ -1,4 +1,4 @@
procedure List.101 (#Derived_gen.8, #Derived_gen.9, #Derived_gen.10, #Derived_gen.11, #Derived_gen.12):
procedure List.101 (Bool.34, Bool.35, Bool.36, Bool.37, Bool.38):
joinpoint List.678 List.175 List.176 List.177 List.178 List.179:
let List.680 : Int1 = CallByName Num.22 List.178 List.179;
if List.680 then
@ -11,8 +11,8 @@ procedure List.101 (#Derived_gen.8, #Derived_gen.9, #Derived_gen.10, #Derived_ge
dec List.175;
ret List.176;
in
inc #Derived_gen.8;
jump List.678 #Derived_gen.8 #Derived_gen.9 #Derived_gen.10 #Derived_gen.11 #Derived_gen.12;
inc Bool.34;
jump List.678 Bool.34 Bool.35 Bool.36 Bool.37 Bool.38;
procedure List.18 (List.172, List.173, List.174):
let List.676 : U64 = 0i64;
@ -38,8 +38,8 @@ procedure Num.51 (#Attr.2, #Attr.3):
procedure Test.10 (Test.69, #Attr.12):
let Test.72 : {} = UnionAtIndex (Id 0) (Index 0) #Attr.12;
let #Derived_gen.18 : Int1 = lowlevel RefCountIsUnique #Attr.12;
if #Derived_gen.18 then
let Bool.39 : Int1 = lowlevel RefCountIsUnique #Attr.12;
if Bool.39 then
free #Attr.12;
ret Test.72;
else
@ -53,7 +53,7 @@ procedure Test.10 (Test.69, #Attr.12):
procedure Test.14 (Test.45, #Attr.12):
let Test.55 : {{}, []} = UnionAtIndex (Id 1) (Index 1) #Attr.12;
let Test.54 : [<r>C {}, C *self {{}, []}] = UnionAtIndex (Id 1) (Index 0) #Attr.12;
joinpoint #Derived_gen.19:
joinpoint Bool.40:
let Test.50 : {} = Struct {};
let Test.51 : U8 = GetTagId Test.54;
joinpoint Test.52 Test.15:
@ -80,14 +80,14 @@ procedure Test.14 (Test.45, #Attr.12):
jump Test.52 Test.53;
in
let #Derived_gen.20 : Int1 = lowlevel RefCountIsUnique #Attr.12;
if #Derived_gen.20 then
let Bool.41 : Int1 = lowlevel RefCountIsUnique #Attr.12;
if Bool.41 then
free #Attr.12;
jump #Derived_gen.19;
jump Bool.40;
else
inc Test.54;
decref #Attr.12;
jump #Derived_gen.19;
jump Bool.40;
procedure Test.20 (Test.21, Test.18):
let Test.23 : [C {}, C []] = CallByName Test.32 Test.21 Test.18;

View file

@ -1,6 +1,6 @@
procedure Bool.1 ():
let Bool.23 : Int1 = false;
ret Bool.23;
let Bool.21 : Int1 = false;
ret Bool.21;
procedure Test.2 (Test.6):
let Test.22 : U8 = 1i64;
@ -8,7 +8,7 @@ procedure Test.2 (Test.6):
let Test.24 : Int1 = lowlevel Eq Test.22 Test.23;
if Test.24 then
let Test.7 : [<r>C List *self, C *self] = UnionAtIndex (Id 1) (Index 0) Test.6;
joinpoint #Derived_gen.1:
joinpoint Bool.23:
let Test.8 : Str = CallByName Test.2 Test.7;
let Test.18 : Int1 = CallByName Bool.1;
if Test.18 then
@ -18,29 +18,29 @@ procedure Test.2 (Test.6):
let Test.17 : Str = "foo";
ret Test.17;
in
let #Derived_gen.2 : Int1 = lowlevel RefCountIsUnique Test.6;
if #Derived_gen.2 then
let Bool.24 : Int1 = lowlevel RefCountIsUnique Test.6;
if Bool.24 then
free Test.6;
jump #Derived_gen.1;
jump Bool.23;
else
inc Test.7;
decref Test.6;
jump #Derived_gen.1;
jump Bool.23;
else
let Test.9 : List [<r>C List [<r>C List *self, C *self], C [<r>C List *self, C *self]] = UnionAtIndex (Id 0) (Index 0) Test.6;
joinpoint #Derived_gen.3:
joinpoint Bool.25:
dec Test.9;
let Test.21 : Str = "ValueNotExposed { module_name: ModuleName(IdentStr { string: \"Result\" }), ident: Ident(IdentStr { string: \"withDefault\" }), region: @662-680, exposed_values: ['is_err', 'on_err', 'map_ok', 'map_err', 'with_default', 'try', 'is_ok', 'map_both', 'map2', 'on_err!'] }";
Crash Test.21
in
let #Derived_gen.4 : Int1 = lowlevel RefCountIsUnique Test.6;
if #Derived_gen.4 then
let Bool.26 : Int1 = lowlevel RefCountIsUnique Test.6;
if Bool.26 then
free Test.6;
jump #Derived_gen.3;
jump Bool.25;
else
inc Test.9;
decref Test.6;
jump #Derived_gen.3;
jump Bool.25;
procedure Test.0 ():
let Test.25 : List [<r>C List [<r>C List *self, C *self], C [<r>C List *self, C *self]] = Array [];

View file

@ -1,8 +1,8 @@
procedure Bool.2 ():
let Bool.23 : Int1 = true;
ret Bool.23;
let Bool.21 : Int1 = true;
ret Bool.21;
procedure List.101 (#Derived_gen.5, #Derived_gen.6, #Derived_gen.7, #Derived_gen.8, #Derived_gen.9):
procedure List.101 (Bool.29, Bool.30, Bool.31, Bool.32, Bool.33):
joinpoint List.678 List.175 List.176 List.177 List.178 List.179:
let List.680 : Int1 = CallByName Num.22 List.178 List.179;
if List.680 then
@ -15,8 +15,8 @@ procedure List.101 (#Derived_gen.5, #Derived_gen.6, #Derived_gen.7, #Derived_gen
dec List.175;
ret List.176;
in
inc #Derived_gen.5;
jump List.678 #Derived_gen.5 #Derived_gen.6 #Derived_gen.7 #Derived_gen.8 #Derived_gen.9;
inc Bool.29;
jump List.678 Bool.29 Bool.30 Bool.31 Bool.32 Bool.33;
procedure List.18 (List.172, List.173, List.174):
let List.676 : U64 = 0i64;
@ -41,17 +41,17 @@ procedure Num.51 (#Attr.2, #Attr.3):
ret Num.283;
procedure Str.3 (#Attr.2, #Attr.3):
let Str.248 : Str = lowlevel StrConcat #Attr.2 #Attr.3;
ret Str.248;
let Str.387 : Str = lowlevel StrConcat #Attr.2 #Attr.3;
ret Str.387;
procedure Test.1 (Test.5):
ret Test.5;
procedure Test.11 (#Derived_gen.10, #Derived_gen.11):
procedure Test.11 (Bool.27, Bool.28):
joinpoint Test.27 Test.12 #Attr.12:
let Test.34 : Int1 = UnionAtIndex (Id 2) (Index 1) #Attr.12;
let Test.33 : [<rnw><null>, C *self Int1, C *self Int1] = UnionAtIndex (Id 2) (Index 0) #Attr.12;
joinpoint #Derived_gen.14:
joinpoint Bool.36:
joinpoint Test.31 Test.29:
let Test.30 : U8 = GetTagId Test.33;
switch Test.30:
@ -78,16 +78,16 @@ procedure Test.11 (#Derived_gen.10, #Derived_gen.11):
jump Test.31 Test.32;
in
let #Derived_gen.15 : Int1 = lowlevel RefCountIsUnique #Attr.12;
if #Derived_gen.15 then
let Bool.37 : Int1 = lowlevel RefCountIsUnique #Attr.12;
if Bool.37 then
free #Attr.12;
jump #Derived_gen.14;
jump Bool.36;
else
inc Test.33;
decref #Attr.12;
jump #Derived_gen.14;
jump Bool.36;
in
jump Test.27 #Derived_gen.10 #Derived_gen.11;
jump Test.27 Bool.27 Bool.28;
procedure Test.2 (Test.13):
ret Test.13;
@ -118,7 +118,7 @@ procedure Test.6 (Test.7, Test.8, Test.5):
procedure Test.9 (Test.10, #Attr.12):
let Test.43 : Int1 = UnionAtIndex (Id 1) (Index 1) #Attr.12;
let Test.42 : [<rnw><null>, C *self Int1, C *self Int1] = UnionAtIndex (Id 1) (Index 0) #Attr.12;
joinpoint #Derived_gen.12:
joinpoint Bool.34:
let Test.39 : U8 = GetTagId Test.42;
joinpoint Test.40 Test.38:
switch Test.43:
@ -146,14 +146,14 @@ procedure Test.9 (Test.10, #Attr.12):
jump Test.40 Test.41;
in
let #Derived_gen.13 : Int1 = lowlevel RefCountIsUnique #Attr.12;
if #Derived_gen.13 then
let Bool.35 : Int1 = lowlevel RefCountIsUnique #Attr.12;
if Bool.35 then
free #Attr.12;
jump #Derived_gen.12;
jump Bool.34;
else
inc Test.42;
decref #Attr.12;
jump #Derived_gen.12;
jump Bool.34;
procedure Test.0 ():
let Test.45 : Int1 = false;

View file

@ -43,14 +43,14 @@ procedure Num.96 (#Attr.2):
ret Num.284;
procedure Str.3 (#Attr.2, #Attr.3):
let Str.246 : Str = lowlevel StrConcat #Attr.2 #Attr.3;
ret Str.246;
let Str.385 : Str = lowlevel StrConcat #Attr.2 #Attr.3;
ret Str.385;
procedure Test.0 ():
let Test.5 : I64 = 1i64;
let Test.2 : I64 = 2i64;
let Test.3 : Str = CallByName Inspect.33 Test.2;
dbg Test.3;
dec Test.3;
let Test.4 : I64 = CallByName Num.19 Test.5 Test.2;
ret Test.4;
let Test.4 : I64 = 1i64;
let Test.1 : I64 = 2i64;
let Test.2 : Str = CallByName Inspect.33 Test.1;
dbg Test.2;
dec Test.2;
let Test.3 : I64 = CallByName Num.19 Test.4 Test.1;
ret Test.3;

View file

@ -1,17 +1,17 @@
procedure Bool.11 (#Attr.2, #Attr.3):
let Bool.25 : Int1 = lowlevel Eq #Attr.2 #Attr.3;
ret Bool.25;
procedure Bool.11 (#Attr.2, #Attr.3):
let Bool.26 : Int1 = lowlevel Eq #Attr.2 #Attr.3;
ret Bool.26;
procedure Bool.1 ():
let Bool.22 : Int1 = false;
ret Bool.22;
procedure Bool.2 ():
let Bool.23 : Int1 = true;
let Bool.21 : Int1 = true;
ret Bool.21;
procedure Bool.9 (#Attr.2, #Attr.3):
let Bool.23 : Int1 = lowlevel Eq #Attr.2 #Attr.3;
ret Bool.23;
procedure Bool.3 (#Attr.2, #Attr.3):
let Bool.24 : Int1 = lowlevel And #Attr.2 #Attr.3;
procedure Bool.9 (#Attr.2, #Attr.3):
let Bool.24 : Int1 = lowlevel Eq #Attr.2 #Attr.3;
ret Bool.24;
procedure Inspect.245 (Inspect.246, Inspect.244):
@ -104,181 +104,184 @@ procedure Num.77 (#Attr.2, #Attr.3):
ret Num.293;
procedure Str.20 (#Attr.2):
let Str.313 : Str = lowlevel StrWithCapacity #Attr.2;
ret Str.313;
let Str.453 : Str = lowlevel StrWithCapacity #Attr.2;
ret Str.453;
procedure Str.3 (#Attr.2, #Attr.3):
let Str.246 : Str = lowlevel StrConcat #Attr.2 #Attr.3;
ret Str.246;
let Str.385 : Str = lowlevel StrConcat #Attr.2 #Attr.3;
ret Str.385;
procedure Str.35 (#Attr.2, #Attr.3):
let Str.303 : U8 = lowlevel StrGetUnsafe #Attr.2 #Attr.3;
ret Str.303;
let Str.443 : U8 = lowlevel StrGetUnsafe #Attr.2 #Attr.3;
ret Str.443;
procedure Str.36 (#Attr.2):
let Str.266 : U64 = lowlevel StrCountUtf8Bytes #Attr.2;
ret Str.266;
let Str.405 : U64 = lowlevel StrCountUtf8Bytes #Attr.2;
ret Str.405;
procedure Str.37 (#Attr.2, #Attr.3, #Attr.4):
let Str.264 : Str = lowlevel StrSubstringUnsafe #Attr.2 #Attr.3 #Attr.4;
ret Str.264;
let Str.403 : Str = lowlevel StrSubstringUnsafe #Attr.2 #Attr.3 #Attr.4;
ret Str.403;
procedure Str.38 (Str.112, Str.113):
let Str.260 : [C , C U64] = CallByName Str.57 Str.112 Str.113;
let Str.273 : U8 = 1i64;
let Str.274 : U8 = GetTagId Str.260;
let Str.275 : Int1 = lowlevel Eq Str.273 Str.274;
if Str.275 then
let Str.114 : U64 = UnionAtIndex (Id 1) (Index 0) Str.260;
let Str.269 : U64 = CallByName Str.36 Str.112;
let Str.270 : U64 = CallByName Str.36 Str.113;
let Str.268 : U64 = CallByName Num.20 Str.269 Str.270;
let Str.115 : U64 = CallByName Num.20 Str.268 Str.114;
let Str.267 : U64 = 0i64;
inc Str.112;
let Str.116 : Str = CallByName Str.37 Str.112 Str.267 Str.114;
let Str.265 : U64 = CallByName Str.36 Str.113;
let Str.263 : U64 = CallByName Num.51 Str.114 Str.265;
let Str.117 : Str = CallByName Str.37 Str.112 Str.263 Str.115;
let Str.262 : {Str, Str} = Struct {Str.117, Str.116};
let Str.261 : [C {}, C {Str, Str}] = TagId(1) Str.262;
ret Str.261;
procedure Str.38 (Str.213, Str.214):
let Str.399 : [C , C U64] = CallByName Str.65 Str.213 Str.214;
let Str.412 : U8 = 1i64;
let Str.413 : U8 = GetTagId Str.399;
let Str.414 : Int1 = lowlevel Eq Str.412 Str.413;
if Str.414 then
let Str.215 : U64 = UnionAtIndex (Id 1) (Index 0) Str.399;
let Str.408 : U64 = CallByName Str.36 Str.213;
let Str.409 : U64 = CallByName Str.36 Str.214;
let Str.407 : U64 = CallByName Num.20 Str.408 Str.409;
let Str.216 : U64 = CallByName Num.20 Str.407 Str.215;
let Str.406 : U64 = 0i64;
inc Str.213;
let Str.217 : Str = CallByName Str.37 Str.213 Str.406 Str.215;
let Str.404 : U64 = CallByName Str.36 Str.214;
let Str.402 : U64 = CallByName Num.51 Str.215 Str.404;
let Str.218 : Str = CallByName Str.37 Str.213 Str.402 Str.216;
let Str.401 : {Str, Str} = Struct {Str.218, Str.217};
let Str.400 : [C {}, C {Str, Str}] = TagId(1) Str.401;
ret Str.400;
else
dec Str.112;
let Str.272 : {} = Struct {};
let Str.271 : [C {}, C {Str, Str}] = TagId(0) Str.272;
ret Str.271;
dec Str.213;
let Str.411 : {} = Struct {};
let Str.410 : [C {}, C {Str, Str}] = TagId(0) Str.411;
ret Str.410;
procedure Str.45 (Str.91, Str.92, Str.93):
inc Str.91;
let Str.341 : [C {}, C {Str, Str}] = CallByName Str.38 Str.91 Str.92;
let Str.349 : U8 = 1i64;
let Str.350 : U8 = GetTagId Str.341;
let Str.351 : Int1 = lowlevel Eq Str.349 Str.350;
if Str.351 then
let Str.348 : {Str, Str} = UnionAtIndex (Id 1) (Index 0) Str.341;
let Str.95 : Str = StructAtIndex 0 Str.348;
let Str.94 : Str = StructAtIndex 1 Str.348;
let Str.346 : U64 = CallByName Str.36 Str.91;
dec Str.91;
let Str.345 : Str = CallByName Str.20 Str.346;
let Str.344 : Str = CallByName Str.3 Str.345 Str.94;
dec Str.94;
let Str.343 : Str = CallByName Str.3 Str.344 Str.93;
let Str.342 : Str = CallByName Str.56 Str.343 Str.95 Str.92 Str.93;
ret Str.342;
procedure Str.45 (Str.192, Str.193, Str.194):
inc Str.192;
let Str.481 : [C {}, C {Str, Str}] = CallByName Str.38 Str.192 Str.193;
let Str.489 : U8 = 1i64;
let Str.490 : U8 = GetTagId Str.481;
let Str.491 : Int1 = lowlevel Eq Str.489 Str.490;
if Str.491 then
let Str.488 : {Str, Str} = UnionAtIndex (Id 1) (Index 0) Str.481;
let Str.196 : Str = StructAtIndex 0 Str.488;
let Str.195 : Str = StructAtIndex 1 Str.488;
let Str.486 : U64 = CallByName Str.36 Str.192;
dec Str.192;
let Str.485 : Str = CallByName Str.20 Str.486;
let Str.484 : Str = CallByName Str.3 Str.485 Str.195;
dec Str.195;
let Str.483 : Str = CallByName Str.3 Str.484 Str.194;
let Str.482 : Str = CallByName Str.64 Str.483 Str.196 Str.193 Str.194;
ret Str.482;
else
dec Str.341;
ret Str.91;
dec Str.481;
ret Str.192;
procedure Str.56 (#Derived_gen.5, #Derived_gen.6, #Derived_gen.7, #Derived_gen.8):
joinpoint Str.250 Str.96 Str.97 Str.98 Str.99:
inc Str.97;
let Str.251 : [C {}, C {Str, Str}] = CallByName Str.38 Str.97 Str.98;
let Str.257 : U8 = 1i64;
let Str.258 : U8 = GetTagId Str.251;
let Str.259 : Int1 = lowlevel Eq Str.257 Str.258;
if Str.259 then
dec Str.97;
let Str.256 : {Str, Str} = UnionAtIndex (Id 1) (Index 0) Str.251;
let Str.101 : Str = StructAtIndex 0 Str.256;
let Str.100 : Str = StructAtIndex 1 Str.256;
let Str.254 : Str = CallByName Str.3 Str.96 Str.100;
dec Str.100;
let Str.253 : Str = CallByName Str.3 Str.254 Str.99;
jump Str.250 Str.253 Str.101 Str.98 Str.99;
procedure Str.64 (#Derived_gen.0, #Derived_gen.1, #Derived_gen.2, #Derived_gen.3):
joinpoint Str.389 Str.197 Str.198 Str.199 Str.200:
inc Str.198;
let Str.390 : [C {}, C {Str, Str}] = CallByName Str.38 Str.198 Str.199;
let Str.396 : U8 = 1i64;
let Str.397 : U8 = GetTagId Str.390;
let Str.398 : Int1 = lowlevel Eq Str.396 Str.397;
if Str.398 then
dec Str.198;
let Str.395 : {Str, Str} = UnionAtIndex (Id 1) (Index 0) Str.390;
let Str.202 : Str = StructAtIndex 0 Str.395;
let Str.201 : Str = StructAtIndex 1 Str.395;
let Str.393 : Str = CallByName Str.3 Str.197 Str.201;
dec Str.201;
let Str.392 : Str = CallByName Str.3 Str.393 Str.200;
jump Str.389 Str.392 Str.202 Str.199 Str.200;
else
dec Str.98;
dec Str.251;
dec Str.99;
let Str.255 : Str = CallByName Str.3 Str.96 Str.97;
dec Str.97;
ret Str.255;
dec Str.199;
dec Str.390;
dec Str.200;
let Str.394 : Str = CallByName Str.3 Str.197 Str.198;
dec Str.198;
ret Str.394;
in
inc #Derived_gen.7;
inc #Derived_gen.8;
jump Str.250 #Derived_gen.5 #Derived_gen.6 #Derived_gen.7 #Derived_gen.8;
procedure Str.57 (Str.121, Str.122):
let Str.123 : U64 = CallByName Str.36 Str.121;
let Str.124 : U64 = CallByName Str.36 Str.122;
let Str.125 : U64 = CallByName Num.77 Str.123 Str.124;
let Str.277 : U64 = 0i64;
let Str.276 : [C , C U64] = CallByName Str.58 Str.121 Str.122 Str.277 Str.125;
ret Str.276;
procedure Str.58 (#Derived_gen.1, #Derived_gen.2, #Derived_gen.3, #Derived_gen.4):
joinpoint Str.278 Str.126 Str.127 Str.128 Str.129:
let Str.280 : Int1 = CallByName Num.23 Str.128 Str.129;
if Str.280 then
let Str.284 : Int1 = CallByName Str.62 Str.126 Str.128 Str.127;
if Str.284 then
dec Str.127;
dec Str.126;
let Str.285 : [C , C U64] = TagId(1) Str.128;
ret Str.285;
else
let Str.283 : U64 = 1i64;
let Str.282 : U64 = CallByName Num.51 Str.128 Str.283;
jump Str.278 Str.126 Str.127 Str.282 Str.129;
else
dec Str.127;
dec Str.126;
let Str.279 : [C , C U64] = TagId(0) ;
ret Str.279;
in
inc #Derived_gen.1;
inc #Derived_gen.3;
inc #Derived_gen.2;
jump Str.278 #Derived_gen.1 #Derived_gen.2 #Derived_gen.3 #Derived_gen.4;
jump Str.389 #Derived_gen.0 #Derived_gen.1 #Derived_gen.2 #Derived_gen.3;
procedure Str.61 (Str.152, Str.153):
let Str.308 : Int1 = CallByName Num.22 Str.152 Str.153;
if Str.308 then
ret Str.152;
procedure Str.65 (Str.222, Str.223):
let Str.224 : U64 = CallByName Str.36 Str.222;
let Str.225 : U64 = CallByName Str.36 Str.223;
let Str.226 : U64 = CallByName Num.77 Str.224 Str.225;
let Str.416 : U64 = 0i64;
let Str.415 : [C , C U64] = CallByName Str.66 Str.222 Str.223 Str.416 Str.226;
ret Str.415;
procedure Str.66 (#Derived_gen.4, #Derived_gen.5, #Derived_gen.6, #Derived_gen.7):
joinpoint Str.417 Str.227 Str.228 Str.229 Str.230:
let Str.419 : Int1 = CallByName Num.23 Str.229 Str.230;
if Str.419 then
let Str.423 : Int1 = CallByName Str.70 Str.227 Str.229 Str.228;
if Str.423 then
dec Str.227;
dec Str.228;
let Str.424 : [C , C U64] = TagId(1) Str.229;
ret Str.424;
else
let Str.422 : U64 = 1i64;
let Str.421 : U64 = CallByName Num.51 Str.229 Str.422;
jump Str.417 Str.227 Str.228 Str.421 Str.230;
else
dec Str.227;
dec Str.228;
let Str.418 : [C , C U64] = TagId(0) ;
ret Str.418;
in
inc #Derived_gen.5;
inc #Derived_gen.4;
jump Str.417 #Derived_gen.4 #Derived_gen.5 #Derived_gen.6 #Derived_gen.7;
procedure Str.69 (Str.253, Str.254):
let Str.448 : Int1 = CallByName Num.22 Str.253 Str.254;
if Str.448 then
ret Str.253;
else
ret Str.153;
ret Str.254;
procedure Str.62 (Str.154, Str.155, Str.156):
let Str.157 : U64 = CallByName Str.36 Str.154;
let Str.158 : U64 = CallByName Str.36 Str.156;
let Str.306 : U64 = CallByName Num.53 Str.155 Str.158;
let Str.159 : U64 = CallByName Str.61 Str.306 Str.157;
let Str.305 : U64 = 0i64;
inc Str.156;
inc Str.154;
let Str.287 : {U64, Str, U64, Str, U64, U64} = Struct {Str.159, Str.154, Str.155, Str.156, Str.305, Str.158};
let Str.286 : Int1 = CallByName Str.63 Str.287;
ret Str.286;
procedure Str.70 (Str.255, Str.256, Str.257):
let Str.258 : U64 = CallByName Str.36 Str.255;
let Str.259 : U64 = CallByName Str.36 Str.257;
let Str.446 : U64 = CallByName Num.53 Str.256 Str.259;
let Str.260 : U64 = CallByName Str.69 Str.446 Str.258;
let Str.445 : U64 = 0i64;
inc Str.255;
inc Str.257;
let Str.426 : {U64, Str, U64, Str, U64, U64} = Struct {Str.260, Str.255, Str.256, Str.257, Str.445, Str.259};
let Str.425 : Int1 = CallByName Str.71 Str.426;
ret Str.425;
procedure Str.63 (Str.160):
let Str.166 : U64 = StructAtIndex 0 Str.160;
let Str.161 : Str = StructAtIndex 1 Str.160;
let Str.162 : U64 = StructAtIndex 2 Str.160;
let Str.163 : Str = StructAtIndex 3 Str.160;
let Str.164 : U64 = StructAtIndex 4 Str.160;
let Str.165 : U64 = StructAtIndex 5 Str.160;
let Str.167 : Int1 = CallByName Num.25 Str.162 Str.166;
if Str.167 then
dec Str.163;
dec Str.161;
let Str.168 : Int1 = CallByName Bool.11 Str.164 Str.165;
ret Str.168;
procedure Str.71 (Str.261):
let Str.267 : U64 = StructAtIndex 0 Str.261;
let Str.262 : Str = StructAtIndex 1 Str.261;
let Str.263 : U64 = StructAtIndex 2 Str.261;
let Str.264 : Str = StructAtIndex 3 Str.261;
let Str.265 : U64 = StructAtIndex 4 Str.261;
let Str.266 : U64 = StructAtIndex 5 Str.261;
let Str.268 : Int1 = CallByName Num.25 Str.263 Str.267;
if Str.268 then
dec Str.262;
dec Str.264;
let Str.269 : Int1 = CallByName Bool.9 Str.265 Str.266;
ret Str.269;
else
let Str.301 : U8 = CallByName Str.35 Str.161 Str.162;
let Str.302 : U8 = CallByName Str.35 Str.163 Str.164;
let Str.169 : Int1 = CallByName Bool.11 Str.301 Str.302;
let Str.291 : U64 = StructAtIndex 0 Str.160;
let Str.292 : Str = StructAtIndex 1 Str.160;
let Str.294 : Str = StructAtIndex 3 Str.160;
let Str.296 : U64 = StructAtIndex 5 Str.160;
let Str.300 : U64 = 1i64;
let Str.298 : U64 = CallByName Num.51 Str.164 Str.300;
let Str.299 : U64 = 1i64;
let Str.297 : U64 = CallByName Num.51 Str.162 Str.299;
let Str.290 : {U64, Str, U64, Str, U64, U64} = Struct {Str.291, Str.292, Str.297, Str.294, Str.298, Str.296};
let Str.170 : Int1 = CallByName Str.63 Str.290;
let Str.289 : Int1 = CallByName Bool.3 Str.169 Str.170;
ret Str.289;
let Str.441 : U8 = CallByName Str.35 Str.262 Str.263;
let Str.442 : U8 = CallByName Str.35 Str.264 Str.265;
let Str.270 : Int1 = CallByName Bool.9 Str.441 Str.442;
let Str.431 : U64 = StructAtIndex 0 Str.261;
let Str.432 : Str = StructAtIndex 1 Str.261;
let Str.434 : Str = StructAtIndex 3 Str.261;
let Str.436 : U64 = StructAtIndex 5 Str.261;
let Str.440 : U64 = 1i64;
let Str.438 : U64 = CallByName Num.51 Str.265 Str.440;
let Str.439 : U64 = 1i64;
let Str.437 : U64 = CallByName Num.51 Str.263 Str.439;
let Str.430 : {U64, Str, U64, Str, U64, U64} = Struct {Str.431, Str.432, Str.437, Str.434, Str.438, Str.436};
let Str.271 : Int1 = CallByName Str.71 Str.430;
if Str.270 then
ret Str.271;
else
let Str.428 : Int1 = CallByName Bool.1;
ret Str.428;
procedure Test.1 ():
let Test.4 : Str = "";

View file

@ -1,13 +1,13 @@
procedure Bool.11 (#Attr.2, #Attr.3):
let Bool.24 : Int1 = lowlevel Eq #Attr.2 #Attr.3;
ret Bool.24;
procedure Bool.1 ():
let Bool.21 : Int1 = false;
ret Bool.21;
procedure Bool.11 (#Attr.2, #Attr.3):
let Bool.25 : Int1 = lowlevel Eq #Attr.2 #Attr.3;
ret Bool.25;
procedure Bool.9 (#Attr.2, #Attr.3):
let Bool.22 : Int1 = lowlevel Eq #Attr.2 #Attr.3;
ret Bool.22;
procedure Bool.3 (#Attr.2, #Attr.3):
let Bool.23 : Int1 = lowlevel And #Attr.2 #Attr.3;
procedure Bool.9 (#Attr.2, #Attr.3):
let Bool.23 : Int1 = lowlevel Eq #Attr.2 #Attr.3;
ret Bool.23;
procedure Inspect.245 (Inspect.246, Inspect.244):
@ -100,192 +100,195 @@ procedure Num.77 (#Attr.2, #Attr.3):
ret Num.293;
procedure Str.20 (#Attr.2):
let Str.315 : Str = lowlevel StrWithCapacity #Attr.2;
ret Str.315;
let Str.455 : Str = lowlevel StrWithCapacity #Attr.2;
ret Str.455;
procedure Str.3 (#Attr.2, #Attr.3):
let Str.248 : Str = lowlevel StrConcat #Attr.2 #Attr.3;
ret Str.248;
let Str.387 : Str = lowlevel StrConcat #Attr.2 #Attr.3;
ret Str.387;
procedure Str.35 (#Attr.2, #Attr.3):
let Str.305 : U8 = lowlevel StrGetUnsafe #Attr.2 #Attr.3;
ret Str.305;
let Str.445 : U8 = lowlevel StrGetUnsafe #Attr.2 #Attr.3;
ret Str.445;
procedure Str.36 (#Attr.2):
let Str.268 : U64 = lowlevel StrCountUtf8Bytes #Attr.2;
ret Str.268;
let Str.407 : U64 = lowlevel StrCountUtf8Bytes #Attr.2;
ret Str.407;
procedure Str.37 (#Attr.2, #Attr.3, #Attr.4):
let Str.266 : Str = lowlevel StrSubstringUnsafe #Attr.2 #Attr.3 #Attr.4;
ret Str.266;
let Str.405 : Str = lowlevel StrSubstringUnsafe #Attr.2 #Attr.3 #Attr.4;
ret Str.405;
procedure Str.38 (Str.112, Str.113):
let Str.262 : [C , C U64] = CallByName Str.57 Str.112 Str.113;
let Str.275 : U8 = 1i64;
let Str.276 : U8 = GetTagId Str.262;
let Str.277 : Int1 = lowlevel Eq Str.275 Str.276;
if Str.277 then
let Str.114 : U64 = UnionAtIndex (Id 1) (Index 0) Str.262;
let Str.271 : U64 = CallByName Str.36 Str.112;
let Str.272 : U64 = CallByName Str.36 Str.113;
let Str.270 : U64 = CallByName Num.20 Str.271 Str.272;
let Str.115 : U64 = CallByName Num.20 Str.270 Str.114;
let Str.269 : U64 = 0i64;
inc Str.112;
let Str.116 : Str = CallByName Str.37 Str.112 Str.269 Str.114;
let Str.267 : U64 = CallByName Str.36 Str.113;
let Str.265 : U64 = CallByName Num.51 Str.114 Str.267;
let Str.117 : Str = CallByName Str.37 Str.112 Str.265 Str.115;
let Str.264 : {Str, Str} = Struct {Str.117, Str.116};
let Str.263 : [C {}, C {Str, Str}] = TagId(1) Str.264;
ret Str.263;
procedure Str.38 (Str.213, Str.214):
let Str.401 : [C , C U64] = CallByName Str.65 Str.213 Str.214;
let Str.414 : U8 = 1i64;
let Str.415 : U8 = GetTagId Str.401;
let Str.416 : Int1 = lowlevel Eq Str.414 Str.415;
if Str.416 then
let Str.215 : U64 = UnionAtIndex (Id 1) (Index 0) Str.401;
let Str.410 : U64 = CallByName Str.36 Str.213;
let Str.411 : U64 = CallByName Str.36 Str.214;
let Str.409 : U64 = CallByName Num.20 Str.410 Str.411;
let Str.216 : U64 = CallByName Num.20 Str.409 Str.215;
let Str.408 : U64 = 0i64;
inc Str.213;
let Str.217 : Str = CallByName Str.37 Str.213 Str.408 Str.215;
let Str.406 : U64 = CallByName Str.36 Str.214;
let Str.404 : U64 = CallByName Num.51 Str.215 Str.406;
let Str.218 : Str = CallByName Str.37 Str.213 Str.404 Str.216;
let Str.403 : {Str, Str} = Struct {Str.218, Str.217};
let Str.402 : [C {}, C {Str, Str}] = TagId(1) Str.403;
ret Str.402;
else
dec Str.112;
let Str.274 : {} = Struct {};
let Str.273 : [C {}, C {Str, Str}] = TagId(0) Str.274;
ret Str.273;
dec Str.213;
let Str.413 : {} = Struct {};
let Str.412 : [C {}, C {Str, Str}] = TagId(0) Str.413;
ret Str.412;
procedure Str.45 (Str.91, Str.92, Str.93):
inc Str.91;
let Str.343 : [C {}, C {Str, Str}] = CallByName Str.38 Str.91 Str.92;
let Str.351 : U8 = 1i64;
let Str.352 : U8 = GetTagId Str.343;
let Str.353 : Int1 = lowlevel Eq Str.351 Str.352;
if Str.353 then
let Str.350 : {Str, Str} = UnionAtIndex (Id 1) (Index 0) Str.343;
let Str.95 : Str = StructAtIndex 0 Str.350;
let Str.94 : Str = StructAtIndex 1 Str.350;
let Str.348 : U64 = CallByName Str.36 Str.91;
dec Str.91;
let Str.347 : Str = CallByName Str.20 Str.348;
let Str.346 : Str = CallByName Str.3 Str.347 Str.94;
dec Str.94;
let Str.345 : Str = CallByName Str.3 Str.346 Str.93;
let Str.344 : Str = CallByName Str.56 Str.345 Str.95 Str.92 Str.93;
ret Str.344;
procedure Str.45 (Str.192, Str.193, Str.194):
inc Str.192;
let Str.483 : [C {}, C {Str, Str}] = CallByName Str.38 Str.192 Str.193;
let Str.491 : U8 = 1i64;
let Str.492 : U8 = GetTagId Str.483;
let Str.493 : Int1 = lowlevel Eq Str.491 Str.492;
if Str.493 then
let Str.490 : {Str, Str} = UnionAtIndex (Id 1) (Index 0) Str.483;
let Str.196 : Str = StructAtIndex 0 Str.490;
let Str.195 : Str = StructAtIndex 1 Str.490;
let Str.488 : U64 = CallByName Str.36 Str.192;
dec Str.192;
let Str.487 : Str = CallByName Str.20 Str.488;
let Str.486 : Str = CallByName Str.3 Str.487 Str.195;
dec Str.195;
let Str.485 : Str = CallByName Str.3 Str.486 Str.194;
let Str.484 : Str = CallByName Str.64 Str.485 Str.196 Str.193 Str.194;
ret Str.484;
else
dec Str.343;
ret Str.91;
dec Str.483;
ret Str.192;
procedure Str.56 (#Derived_gen.5, #Derived_gen.6, #Derived_gen.7, #Derived_gen.8):
joinpoint Str.252 Str.96 Str.97 Str.98 Str.99:
inc Str.97;
let Str.253 : [C {}, C {Str, Str}] = CallByName Str.38 Str.97 Str.98;
let Str.259 : U8 = 1i64;
let Str.260 : U8 = GetTagId Str.253;
let Str.261 : Int1 = lowlevel Eq Str.259 Str.260;
if Str.261 then
dec Str.97;
let Str.258 : {Str, Str} = UnionAtIndex (Id 1) (Index 0) Str.253;
let Str.101 : Str = StructAtIndex 0 Str.258;
let Str.100 : Str = StructAtIndex 1 Str.258;
let Str.256 : Str = CallByName Str.3 Str.96 Str.100;
dec Str.100;
let Str.255 : Str = CallByName Str.3 Str.256 Str.99;
jump Str.252 Str.255 Str.101 Str.98 Str.99;
procedure Str.64 (Bool.24, Bool.25, Bool.26, Bool.27):
joinpoint Str.391 Str.197 Str.198 Str.199 Str.200:
inc Str.198;
let Str.392 : [C {}, C {Str, Str}] = CallByName Str.38 Str.198 Str.199;
let Str.398 : U8 = 1i64;
let Str.399 : U8 = GetTagId Str.392;
let Str.400 : Int1 = lowlevel Eq Str.398 Str.399;
if Str.400 then
dec Str.198;
let Str.397 : {Str, Str} = UnionAtIndex (Id 1) (Index 0) Str.392;
let Str.202 : Str = StructAtIndex 0 Str.397;
let Str.201 : Str = StructAtIndex 1 Str.397;
let Str.395 : Str = CallByName Str.3 Str.197 Str.201;
dec Str.201;
let Str.394 : Str = CallByName Str.3 Str.395 Str.200;
jump Str.391 Str.394 Str.202 Str.199 Str.200;
else
dec Str.98;
dec Str.99;
dec Str.253;
let Str.257 : Str = CallByName Str.3 Str.96 Str.97;
dec Str.97;
ret Str.257;
dec Str.199;
dec Str.392;
dec Str.200;
let Str.396 : Str = CallByName Str.3 Str.197 Str.198;
dec Str.198;
ret Str.396;
in
inc #Derived_gen.7;
inc #Derived_gen.8;
jump Str.252 #Derived_gen.5 #Derived_gen.6 #Derived_gen.7 #Derived_gen.8;
inc Bool.26;
inc Bool.27;
jump Str.391 Bool.24 Bool.25 Bool.26 Bool.27;
procedure Str.57 (Str.121, Str.122):
let Str.123 : U64 = CallByName Str.36 Str.121;
let Str.124 : U64 = CallByName Str.36 Str.122;
let Str.125 : U64 = CallByName Num.77 Str.123 Str.124;
let Str.279 : U64 = 0i64;
let Str.278 : [C , C U64] = CallByName Str.58 Str.121 Str.122 Str.279 Str.125;
ret Str.278;
procedure Str.65 (Str.222, Str.223):
let Str.224 : U64 = CallByName Str.36 Str.222;
let Str.225 : U64 = CallByName Str.36 Str.223;
let Str.226 : U64 = CallByName Num.77 Str.224 Str.225;
let Str.418 : U64 = 0i64;
let Str.417 : [C , C U64] = CallByName Str.66 Str.222 Str.223 Str.418 Str.226;
ret Str.417;
procedure Str.58 (#Derived_gen.1, #Derived_gen.2, #Derived_gen.3, #Derived_gen.4):
joinpoint Str.280 Str.126 Str.127 Str.128 Str.129:
let Str.282 : Int1 = CallByName Num.23 Str.128 Str.129;
if Str.282 then
let Str.286 : Int1 = CallByName Str.62 Str.126 Str.128 Str.127;
if Str.286 then
dec Str.127;
dec Str.126;
let Str.287 : [C , C U64] = TagId(1) Str.128;
ret Str.287;
procedure Str.66 (Bool.28, Bool.29, Bool.30, Bool.31):
joinpoint Str.419 Str.227 Str.228 Str.229 Str.230:
let Str.421 : Int1 = CallByName Num.23 Str.229 Str.230;
if Str.421 then
let Str.425 : Int1 = CallByName Str.70 Str.227 Str.229 Str.228;
if Str.425 then
dec Str.227;
dec Str.228;
let Str.426 : [C , C U64] = TagId(1) Str.229;
ret Str.426;
else
let Str.285 : U64 = 1i64;
let Str.284 : U64 = CallByName Num.51 Str.128 Str.285;
jump Str.280 Str.126 Str.127 Str.284 Str.129;
let Str.424 : U64 = 1i64;
let Str.423 : U64 = CallByName Num.51 Str.229 Str.424;
jump Str.419 Str.227 Str.228 Str.423 Str.230;
else
dec Str.127;
dec Str.126;
let Str.281 : [C , C U64] = TagId(0) ;
ret Str.281;
dec Str.227;
dec Str.228;
let Str.420 : [C , C U64] = TagId(0) ;
ret Str.420;
in
inc #Derived_gen.1;
inc #Derived_gen.2;
jump Str.280 #Derived_gen.1 #Derived_gen.2 #Derived_gen.3 #Derived_gen.4;
inc Bool.29;
inc Bool.28;
jump Str.419 Bool.28 Bool.29 Bool.30 Bool.31;
procedure Str.61 (Str.152, Str.153):
let Str.310 : Int1 = CallByName Num.22 Str.152 Str.153;
if Str.310 then
ret Str.152;
procedure Str.69 (Str.253, Str.254):
let Str.450 : Int1 = CallByName Num.22 Str.253 Str.254;
if Str.450 then
ret Str.253;
else
ret Str.153;
ret Str.254;
procedure Str.62 (Str.154, Str.155, Str.156):
let Str.157 : U64 = CallByName Str.36 Str.154;
let Str.158 : U64 = CallByName Str.36 Str.156;
let Str.308 : U64 = CallByName Num.53 Str.155 Str.158;
let Str.159 : U64 = CallByName Str.61 Str.308 Str.157;
let Str.307 : U64 = 0i64;
inc Str.156;
inc Str.154;
let Str.289 : {U64, Str, U64, Str, U64, U64} = Struct {Str.159, Str.154, Str.155, Str.156, Str.307, Str.158};
let Str.288 : Int1 = CallByName Str.63 Str.289;
ret Str.288;
procedure Str.70 (Str.255, Str.256, Str.257):
let Str.258 : U64 = CallByName Str.36 Str.255;
let Str.259 : U64 = CallByName Str.36 Str.257;
let Str.448 : U64 = CallByName Num.53 Str.256 Str.259;
let Str.260 : U64 = CallByName Str.69 Str.448 Str.258;
let Str.447 : U64 = 0i64;
inc Str.255;
inc Str.257;
let Str.428 : {U64, Str, U64, Str, U64, U64} = Struct {Str.260, Str.255, Str.256, Str.257, Str.447, Str.259};
let Str.427 : Int1 = CallByName Str.71 Str.428;
ret Str.427;
procedure Str.63 (Str.160):
let Str.166 : U64 = StructAtIndex 0 Str.160;
let Str.161 : Str = StructAtIndex 1 Str.160;
let Str.162 : U64 = StructAtIndex 2 Str.160;
let Str.163 : Str = StructAtIndex 3 Str.160;
let Str.164 : U64 = StructAtIndex 4 Str.160;
let Str.165 : U64 = StructAtIndex 5 Str.160;
let Str.167 : Int1 = CallByName Num.25 Str.162 Str.166;
if Str.167 then
dec Str.163;
dec Str.161;
let Str.168 : Int1 = CallByName Bool.11 Str.164 Str.165;
ret Str.168;
procedure Str.71 (Str.261):
let Str.267 : U64 = StructAtIndex 0 Str.261;
let Str.262 : Str = StructAtIndex 1 Str.261;
let Str.263 : U64 = StructAtIndex 2 Str.261;
let Str.264 : Str = StructAtIndex 3 Str.261;
let Str.265 : U64 = StructAtIndex 4 Str.261;
let Str.266 : U64 = StructAtIndex 5 Str.261;
let Str.268 : Int1 = CallByName Num.25 Str.263 Str.267;
if Str.268 then
dec Str.262;
dec Str.264;
let Str.269 : Int1 = CallByName Bool.9 Str.265 Str.266;
ret Str.269;
else
let Str.303 : U8 = CallByName Str.35 Str.161 Str.162;
let Str.304 : U8 = CallByName Str.35 Str.163 Str.164;
let Str.169 : Int1 = CallByName Bool.11 Str.303 Str.304;
let Str.293 : U64 = StructAtIndex 0 Str.160;
let Str.294 : Str = StructAtIndex 1 Str.160;
let Str.296 : Str = StructAtIndex 3 Str.160;
let Str.298 : U64 = StructAtIndex 5 Str.160;
let Str.302 : U64 = 1i64;
let Str.300 : U64 = CallByName Num.51 Str.164 Str.302;
let Str.301 : U64 = 1i64;
let Str.299 : U64 = CallByName Num.51 Str.162 Str.301;
let Str.292 : {U64, Str, U64, Str, U64, U64} = Struct {Str.293, Str.294, Str.299, Str.296, Str.300, Str.298};
let Str.170 : Int1 = CallByName Str.63 Str.292;
let Str.291 : Int1 = CallByName Bool.3 Str.169 Str.170;
ret Str.291;
let Str.443 : U8 = CallByName Str.35 Str.262 Str.263;
let Str.444 : U8 = CallByName Str.35 Str.264 Str.265;
let Str.270 : Int1 = CallByName Bool.9 Str.443 Str.444;
let Str.433 : U64 = StructAtIndex 0 Str.261;
let Str.434 : Str = StructAtIndex 1 Str.261;
let Str.436 : Str = StructAtIndex 3 Str.261;
let Str.438 : U64 = StructAtIndex 5 Str.261;
let Str.442 : U64 = 1i64;
let Str.440 : U64 = CallByName Num.51 Str.265 Str.442;
let Str.441 : U64 = 1i64;
let Str.439 : U64 = CallByName Num.51 Str.263 Str.441;
let Str.432 : {U64, Str, U64, Str, U64, U64} = Struct {Str.433, Str.434, Str.439, Str.436, Str.440, Str.438};
let Str.271 : Int1 = CallByName Str.71 Str.432;
if Str.270 then
ret Str.271;
else
let Str.430 : Int1 = CallByName Bool.1;
ret Str.430;
procedure Test.0 ():
let Test.5 : Str = "Hello ";
let Test.2 : Str = "world";
inc Test.2;
let Test.3 : Str = CallByName Inspect.33 Test.2;
dbg Test.3;
dec Test.3;
let Test.8 : Str = "!";
let Test.6 : Str = CallByName Str.3 Test.2 Test.8;
dec Test.8;
let Test.4 : Str = CallByName Str.3 Test.5 Test.6;
dec Test.6;
ret Test.4;
let Test.4 : Str = "Hello ";
let Test.1 : Str = "world";
inc Test.1;
let Test.2 : Str = CallByName Inspect.33 Test.1;
dbg Test.2;
dec Test.2;
let Test.7 : Str = "!";
let Test.5 : Str = CallByName Str.3 Test.1 Test.7;
dec Test.7;
let Test.3 : Str = CallByName Str.3 Test.4 Test.5;
dec Test.5;
ret Test.3;

View file

@ -39,18 +39,18 @@ procedure Num.96 (#Attr.2):
ret Num.283;
procedure Str.3 (#Attr.2, #Attr.3):
let Str.246 : Str = lowlevel StrConcat #Attr.2 #Attr.3;
ret Str.246;
let Str.385 : Str = lowlevel StrConcat #Attr.2 #Attr.3;
ret Str.385;
procedure Test.0 ():
let Test.6 : I64 = 1i64;
let Test.7 : Str = CallByName Inspect.33 Test.6;
dbg Test.7;
dec Test.7;
let Test.8 : Str = CallByName Inspect.33 Test.6;
dbg Test.8;
dec Test.8;
let Test.9 : Str = CallByName Inspect.33 Test.6;
dbg Test.9;
dec Test.9;
ret Test.6;
let Test.3 : I64 = 1i64;
let Test.4 : Str = CallByName Inspect.33 Test.3;
dbg Test.4;
dec Test.4;
let Test.5 : Str = CallByName Inspect.33 Test.3;
dbg Test.5;
dec Test.5;
let Test.6 : Str = CallByName Inspect.33 Test.3;
dbg Test.6;
dec Test.6;
ret Test.3;

View file

@ -1,13 +1,13 @@
procedure Bool.11 (#Attr.2, #Attr.3):
let Bool.24 : Int1 = lowlevel Eq #Attr.2 #Attr.3;
ret Bool.24;
procedure Bool.1 ():
let Bool.21 : Int1 = false;
ret Bool.21;
procedure Bool.11 (#Attr.2, #Attr.3):
let Bool.25 : Int1 = lowlevel Eq #Attr.2 #Attr.3;
ret Bool.25;
procedure Bool.9 (#Attr.2, #Attr.3):
let Bool.22 : Int1 = lowlevel Eq #Attr.2 #Attr.3;
ret Bool.22;
procedure Bool.3 (#Attr.2, #Attr.3):
let Bool.23 : Int1 = lowlevel And #Attr.2 #Attr.3;
procedure Bool.9 (#Attr.2, #Attr.3):
let Bool.23 : Int1 = lowlevel Eq #Attr.2 #Attr.3;
ret Bool.23;
procedure Inspect.245 (Inspect.246, Inspect.244):
@ -100,181 +100,184 @@ procedure Num.77 (#Attr.2, #Attr.3):
ret Num.293;
procedure Str.20 (#Attr.2):
let Str.313 : Str = lowlevel StrWithCapacity #Attr.2;
ret Str.313;
let Str.453 : Str = lowlevel StrWithCapacity #Attr.2;
ret Str.453;
procedure Str.3 (#Attr.2, #Attr.3):
let Str.246 : Str = lowlevel StrConcat #Attr.2 #Attr.3;
ret Str.246;
let Str.385 : Str = lowlevel StrConcat #Attr.2 #Attr.3;
ret Str.385;
procedure Str.35 (#Attr.2, #Attr.3):
let Str.303 : U8 = lowlevel StrGetUnsafe #Attr.2 #Attr.3;
ret Str.303;
let Str.443 : U8 = lowlevel StrGetUnsafe #Attr.2 #Attr.3;
ret Str.443;
procedure Str.36 (#Attr.2):
let Str.266 : U64 = lowlevel StrCountUtf8Bytes #Attr.2;
ret Str.266;
let Str.405 : U64 = lowlevel StrCountUtf8Bytes #Attr.2;
ret Str.405;
procedure Str.37 (#Attr.2, #Attr.3, #Attr.4):
let Str.264 : Str = lowlevel StrSubstringUnsafe #Attr.2 #Attr.3 #Attr.4;
ret Str.264;
let Str.403 : Str = lowlevel StrSubstringUnsafe #Attr.2 #Attr.3 #Attr.4;
ret Str.403;
procedure Str.38 (Str.112, Str.113):
let Str.260 : [C , C U64] = CallByName Str.57 Str.112 Str.113;
let Str.273 : U8 = 1i64;
let Str.274 : U8 = GetTagId Str.260;
let Str.275 : Int1 = lowlevel Eq Str.273 Str.274;
if Str.275 then
let Str.114 : U64 = UnionAtIndex (Id 1) (Index 0) Str.260;
let Str.269 : U64 = CallByName Str.36 Str.112;
let Str.270 : U64 = CallByName Str.36 Str.113;
let Str.268 : U64 = CallByName Num.20 Str.269 Str.270;
let Str.115 : U64 = CallByName Num.20 Str.268 Str.114;
let Str.267 : U64 = 0i64;
inc Str.112;
let Str.116 : Str = CallByName Str.37 Str.112 Str.267 Str.114;
let Str.265 : U64 = CallByName Str.36 Str.113;
let Str.263 : U64 = CallByName Num.51 Str.114 Str.265;
let Str.117 : Str = CallByName Str.37 Str.112 Str.263 Str.115;
let Str.262 : {Str, Str} = Struct {Str.117, Str.116};
let Str.261 : [C {}, C {Str, Str}] = TagId(1) Str.262;
ret Str.261;
procedure Str.38 (Str.213, Str.214):
let Str.399 : [C , C U64] = CallByName Str.65 Str.213 Str.214;
let Str.412 : U8 = 1i64;
let Str.413 : U8 = GetTagId Str.399;
let Str.414 : Int1 = lowlevel Eq Str.412 Str.413;
if Str.414 then
let Str.215 : U64 = UnionAtIndex (Id 1) (Index 0) Str.399;
let Str.408 : U64 = CallByName Str.36 Str.213;
let Str.409 : U64 = CallByName Str.36 Str.214;
let Str.407 : U64 = CallByName Num.20 Str.408 Str.409;
let Str.216 : U64 = CallByName Num.20 Str.407 Str.215;
let Str.406 : U64 = 0i64;
inc Str.213;
let Str.217 : Str = CallByName Str.37 Str.213 Str.406 Str.215;
let Str.404 : U64 = CallByName Str.36 Str.214;
let Str.402 : U64 = CallByName Num.51 Str.215 Str.404;
let Str.218 : Str = CallByName Str.37 Str.213 Str.402 Str.216;
let Str.401 : {Str, Str} = Struct {Str.218, Str.217};
let Str.400 : [C {}, C {Str, Str}] = TagId(1) Str.401;
ret Str.400;
else
dec Str.112;
let Str.272 : {} = Struct {};
let Str.271 : [C {}, C {Str, Str}] = TagId(0) Str.272;
ret Str.271;
dec Str.213;
let Str.411 : {} = Struct {};
let Str.410 : [C {}, C {Str, Str}] = TagId(0) Str.411;
ret Str.410;
procedure Str.45 (Str.91, Str.92, Str.93):
inc Str.91;
let Str.341 : [C {}, C {Str, Str}] = CallByName Str.38 Str.91 Str.92;
let Str.349 : U8 = 1i64;
let Str.350 : U8 = GetTagId Str.341;
let Str.351 : Int1 = lowlevel Eq Str.349 Str.350;
if Str.351 then
let Str.348 : {Str, Str} = UnionAtIndex (Id 1) (Index 0) Str.341;
let Str.95 : Str = StructAtIndex 0 Str.348;
let Str.94 : Str = StructAtIndex 1 Str.348;
let Str.346 : U64 = CallByName Str.36 Str.91;
dec Str.91;
let Str.345 : Str = CallByName Str.20 Str.346;
let Str.344 : Str = CallByName Str.3 Str.345 Str.94;
dec Str.94;
let Str.343 : Str = CallByName Str.3 Str.344 Str.93;
let Str.342 : Str = CallByName Str.56 Str.343 Str.95 Str.92 Str.93;
ret Str.342;
procedure Str.45 (Str.192, Str.193, Str.194):
inc Str.192;
let Str.481 : [C {}, C {Str, Str}] = CallByName Str.38 Str.192 Str.193;
let Str.489 : U8 = 1i64;
let Str.490 : U8 = GetTagId Str.481;
let Str.491 : Int1 = lowlevel Eq Str.489 Str.490;
if Str.491 then
let Str.488 : {Str, Str} = UnionAtIndex (Id 1) (Index 0) Str.481;
let Str.196 : Str = StructAtIndex 0 Str.488;
let Str.195 : Str = StructAtIndex 1 Str.488;
let Str.486 : U64 = CallByName Str.36 Str.192;
dec Str.192;
let Str.485 : Str = CallByName Str.20 Str.486;
let Str.484 : Str = CallByName Str.3 Str.485 Str.195;
dec Str.195;
let Str.483 : Str = CallByName Str.3 Str.484 Str.194;
let Str.482 : Str = CallByName Str.64 Str.483 Str.196 Str.193 Str.194;
ret Str.482;
else
dec Str.341;
ret Str.91;
dec Str.481;
ret Str.192;
procedure Str.56 (#Derived_gen.5, #Derived_gen.6, #Derived_gen.7, #Derived_gen.8):
joinpoint Str.250 Str.96 Str.97 Str.98 Str.99:
inc Str.97;
let Str.251 : [C {}, C {Str, Str}] = CallByName Str.38 Str.97 Str.98;
let Str.257 : U8 = 1i64;
let Str.258 : U8 = GetTagId Str.251;
let Str.259 : Int1 = lowlevel Eq Str.257 Str.258;
if Str.259 then
dec Str.97;
let Str.256 : {Str, Str} = UnionAtIndex (Id 1) (Index 0) Str.251;
let Str.101 : Str = StructAtIndex 0 Str.256;
let Str.100 : Str = StructAtIndex 1 Str.256;
let Str.254 : Str = CallByName Str.3 Str.96 Str.100;
dec Str.100;
let Str.253 : Str = CallByName Str.3 Str.254 Str.99;
jump Str.250 Str.253 Str.101 Str.98 Str.99;
procedure Str.64 (Bool.24, Bool.25, Bool.26, Bool.27):
joinpoint Str.389 Str.197 Str.198 Str.199 Str.200:
inc Str.198;
let Str.390 : [C {}, C {Str, Str}] = CallByName Str.38 Str.198 Str.199;
let Str.396 : U8 = 1i64;
let Str.397 : U8 = GetTagId Str.390;
let Str.398 : Int1 = lowlevel Eq Str.396 Str.397;
if Str.398 then
dec Str.198;
let Str.395 : {Str, Str} = UnionAtIndex (Id 1) (Index 0) Str.390;
let Str.202 : Str = StructAtIndex 0 Str.395;
let Str.201 : Str = StructAtIndex 1 Str.395;
let Str.393 : Str = CallByName Str.3 Str.197 Str.201;
dec Str.201;
let Str.392 : Str = CallByName Str.3 Str.393 Str.200;
jump Str.389 Str.392 Str.202 Str.199 Str.200;
else
dec Str.98;
dec Str.251;
dec Str.99;
let Str.255 : Str = CallByName Str.3 Str.96 Str.97;
dec Str.97;
ret Str.255;
dec Str.199;
dec Str.390;
dec Str.200;
let Str.394 : Str = CallByName Str.3 Str.197 Str.198;
dec Str.198;
ret Str.394;
in
inc #Derived_gen.7;
inc #Derived_gen.8;
jump Str.250 #Derived_gen.5 #Derived_gen.6 #Derived_gen.7 #Derived_gen.8;
inc Bool.26;
inc Bool.27;
jump Str.389 Bool.24 Bool.25 Bool.26 Bool.27;
procedure Str.57 (Str.121, Str.122):
let Str.123 : U64 = CallByName Str.36 Str.121;
let Str.124 : U64 = CallByName Str.36 Str.122;
let Str.125 : U64 = CallByName Num.77 Str.123 Str.124;
let Str.277 : U64 = 0i64;
let Str.276 : [C , C U64] = CallByName Str.58 Str.121 Str.122 Str.277 Str.125;
ret Str.276;
procedure Str.65 (Str.222, Str.223):
let Str.224 : U64 = CallByName Str.36 Str.222;
let Str.225 : U64 = CallByName Str.36 Str.223;
let Str.226 : U64 = CallByName Num.77 Str.224 Str.225;
let Str.416 : U64 = 0i64;
let Str.415 : [C , C U64] = CallByName Str.66 Str.222 Str.223 Str.416 Str.226;
ret Str.415;
procedure Str.58 (#Derived_gen.1, #Derived_gen.2, #Derived_gen.3, #Derived_gen.4):
joinpoint Str.278 Str.126 Str.127 Str.128 Str.129:
let Str.280 : Int1 = CallByName Num.23 Str.128 Str.129;
if Str.280 then
let Str.284 : Int1 = CallByName Str.62 Str.126 Str.128 Str.127;
if Str.284 then
dec Str.127;
dec Str.126;
let Str.285 : [C , C U64] = TagId(1) Str.128;
ret Str.285;
procedure Str.66 (Bool.28, Bool.29, Bool.30, Bool.31):
joinpoint Str.417 Str.227 Str.228 Str.229 Str.230:
let Str.419 : Int1 = CallByName Num.23 Str.229 Str.230;
if Str.419 then
let Str.423 : Int1 = CallByName Str.70 Str.227 Str.229 Str.228;
if Str.423 then
dec Str.227;
dec Str.228;
let Str.424 : [C , C U64] = TagId(1) Str.229;
ret Str.424;
else
let Str.283 : U64 = 1i64;
let Str.282 : U64 = CallByName Num.51 Str.128 Str.283;
jump Str.278 Str.126 Str.127 Str.282 Str.129;
let Str.422 : U64 = 1i64;
let Str.421 : U64 = CallByName Num.51 Str.229 Str.422;
jump Str.417 Str.227 Str.228 Str.421 Str.230;
else
dec Str.127;
dec Str.126;
let Str.279 : [C , C U64] = TagId(0) ;
ret Str.279;
dec Str.227;
dec Str.228;
let Str.418 : [C , C U64] = TagId(0) ;
ret Str.418;
in
inc #Derived_gen.1;
inc #Derived_gen.2;
jump Str.278 #Derived_gen.1 #Derived_gen.2 #Derived_gen.3 #Derived_gen.4;
inc Bool.29;
inc Bool.28;
jump Str.417 Bool.28 Bool.29 Bool.30 Bool.31;
procedure Str.61 (Str.152, Str.153):
let Str.308 : Int1 = CallByName Num.22 Str.152 Str.153;
if Str.308 then
ret Str.152;
procedure Str.69 (Str.253, Str.254):
let Str.448 : Int1 = CallByName Num.22 Str.253 Str.254;
if Str.448 then
ret Str.253;
else
ret Str.153;
ret Str.254;
procedure Str.62 (Str.154, Str.155, Str.156):
let Str.157 : U64 = CallByName Str.36 Str.154;
let Str.158 : U64 = CallByName Str.36 Str.156;
let Str.306 : U64 = CallByName Num.53 Str.155 Str.158;
let Str.159 : U64 = CallByName Str.61 Str.306 Str.157;
let Str.305 : U64 = 0i64;
inc Str.156;
inc Str.154;
let Str.287 : {U64, Str, U64, Str, U64, U64} = Struct {Str.159, Str.154, Str.155, Str.156, Str.305, Str.158};
let Str.286 : Int1 = CallByName Str.63 Str.287;
ret Str.286;
procedure Str.70 (Str.255, Str.256, Str.257):
let Str.258 : U64 = CallByName Str.36 Str.255;
let Str.259 : U64 = CallByName Str.36 Str.257;
let Str.446 : U64 = CallByName Num.53 Str.256 Str.259;
let Str.260 : U64 = CallByName Str.69 Str.446 Str.258;
let Str.445 : U64 = 0i64;
inc Str.255;
inc Str.257;
let Str.426 : {U64, Str, U64, Str, U64, U64} = Struct {Str.260, Str.255, Str.256, Str.257, Str.445, Str.259};
let Str.425 : Int1 = CallByName Str.71 Str.426;
ret Str.425;
procedure Str.63 (Str.160):
let Str.166 : U64 = StructAtIndex 0 Str.160;
let Str.161 : Str = StructAtIndex 1 Str.160;
let Str.162 : U64 = StructAtIndex 2 Str.160;
let Str.163 : Str = StructAtIndex 3 Str.160;
let Str.164 : U64 = StructAtIndex 4 Str.160;
let Str.165 : U64 = StructAtIndex 5 Str.160;
let Str.167 : Int1 = CallByName Num.25 Str.162 Str.166;
if Str.167 then
dec Str.163;
dec Str.161;
let Str.168 : Int1 = CallByName Bool.11 Str.164 Str.165;
ret Str.168;
procedure Str.71 (Str.261):
let Str.267 : U64 = StructAtIndex 0 Str.261;
let Str.262 : Str = StructAtIndex 1 Str.261;
let Str.263 : U64 = StructAtIndex 2 Str.261;
let Str.264 : Str = StructAtIndex 3 Str.261;
let Str.265 : U64 = StructAtIndex 4 Str.261;
let Str.266 : U64 = StructAtIndex 5 Str.261;
let Str.268 : Int1 = CallByName Num.25 Str.263 Str.267;
if Str.268 then
dec Str.262;
dec Str.264;
let Str.269 : Int1 = CallByName Bool.9 Str.265 Str.266;
ret Str.269;
else
let Str.301 : U8 = CallByName Str.35 Str.161 Str.162;
let Str.302 : U8 = CallByName Str.35 Str.163 Str.164;
let Str.169 : Int1 = CallByName Bool.11 Str.301 Str.302;
let Str.291 : U64 = StructAtIndex 0 Str.160;
let Str.292 : Str = StructAtIndex 1 Str.160;
let Str.294 : Str = StructAtIndex 3 Str.160;
let Str.296 : U64 = StructAtIndex 5 Str.160;
let Str.300 : U64 = 1i64;
let Str.298 : U64 = CallByName Num.51 Str.164 Str.300;
let Str.299 : U64 = 1i64;
let Str.297 : U64 = CallByName Num.51 Str.162 Str.299;
let Str.290 : {U64, Str, U64, Str, U64, U64} = Struct {Str.291, Str.292, Str.297, Str.294, Str.298, Str.296};
let Str.170 : Int1 = CallByName Str.63 Str.290;
let Str.289 : Int1 = CallByName Bool.3 Str.169 Str.170;
ret Str.289;
let Str.441 : U8 = CallByName Str.35 Str.262 Str.263;
let Str.442 : U8 = CallByName Str.35 Str.264 Str.265;
let Str.270 : Int1 = CallByName Bool.9 Str.441 Str.442;
let Str.431 : U64 = StructAtIndex 0 Str.261;
let Str.432 : Str = StructAtIndex 1 Str.261;
let Str.434 : Str = StructAtIndex 3 Str.261;
let Str.436 : U64 = StructAtIndex 5 Str.261;
let Str.440 : U64 = 1i64;
let Str.438 : U64 = CallByName Num.51 Str.265 Str.440;
let Str.439 : U64 = 1i64;
let Str.437 : U64 = CallByName Num.51 Str.263 Str.439;
let Str.430 : {U64, Str, U64, Str, U64, U64} = Struct {Str.431, Str.432, Str.437, Str.434, Str.438, Str.436};
let Str.271 : Int1 = CallByName Str.71 Str.430;
if Str.270 then
ret Str.271;
else
let Str.428 : Int1 = CallByName Bool.1;
ret Str.428;
procedure Test.0 ():
let Test.3 : Str = "";

View file

@ -1,6 +1,6 @@
procedure Bool.11 (#Attr.2, #Attr.3):
let Bool.24 : Int1 = lowlevel Eq #Attr.2 #Attr.3;
ret Bool.24;
procedure Bool.9 (#Attr.2, #Attr.3):
let Bool.22 : Int1 = lowlevel Eq #Attr.2 #Attr.3;
ret Bool.22;
procedure Num.19 (#Attr.2, #Attr.3):
let Num.285 : I64 = lowlevel NumAdd #Attr.2 #Attr.3;
@ -11,8 +11,8 @@ procedure Num.96 (#Attr.2):
ret Num.284;
procedure Str.3 (#Attr.2, #Attr.3):
let Str.247 : Str = lowlevel StrConcat #Attr.2 #Attr.3;
ret Str.247;
let Str.386 : Str = lowlevel StrConcat #Attr.2 #Attr.3;
ret Str.386;
procedure Test.1 (Test.2):
let Test.3 : Str = CallByName Num.96 Test.2;
@ -25,7 +25,7 @@ procedure Test.1 (Test.2):
ret Test.8;
in
let Test.22 : I64 = 1i64;
let Test.20 : Int1 = CallByName Bool.11 Test.2 Test.22;
let Test.20 : Int1 = CallByName Bool.9 Test.2 Test.22;
if Test.20 then
dec Test.3;
let Test.21 : Str = "early 1";
@ -38,7 +38,7 @@ procedure Test.1 (Test.2):
jump Test.12 Test.11;
in
let Test.17 : I64 = 2i64;
let Test.15 : Int1 = CallByName Bool.11 Test.2 Test.17;
let Test.15 : Int1 = CallByName Bool.9 Test.2 Test.17;
if Test.15 then
dec Test.3;
dec Test.5;

View file

@ -9,8 +9,8 @@ procedure Dict.1 (Dict.732):
procedure Dict.4 (Dict.738):
let Dict.163 : List {[], []} = StructAtIndex 1 Dict.738;
let #Derived_gen.0 : List {U32, U32} = StructAtIndex 0 Dict.738;
dec #Derived_gen.0;
let Bool.21 : List {U32, U32} = StructAtIndex 0 Dict.738;
dec Bool.21;
let Dict.739 : U64 = CallByName List.6 Dict.163;
dec Dict.163;
ret Dict.739;

View file

@ -1,6 +1,6 @@
procedure Bool.2 ():
let Bool.23 : Int1 = true;
ret Bool.23;
let Bool.21 : Int1 = true;
ret Bool.21;
procedure Test.2 (Test.5):
let Test.6 : Int1 = CallByName Bool.2;

View file

@ -1,6 +1,6 @@
procedure Bool.2 ():
let Bool.23 : Int1 = true;
ret Bool.23;
let Bool.21 : Int1 = true;
ret Bool.21;
procedure Test.2 (Test.5):
let Test.6 : Int1 = CallByName Bool.2;

View file

@ -1,6 +1,6 @@
procedure Bool.1 ():
let Bool.23 : Int1 = false;
ret Bool.23;
let Bool.21 : Int1 = false;
ret Bool.21;
procedure List.2 (List.120, List.121):
let List.681 : U64 = CallByName List.6 List.120;

View file

@ -67,7 +67,7 @@ procedure Encode.26 (Encode.107, Encode.108):
let Encode.110 : List U8 = CallByName Encode.24 Encode.111 Encode.112 Encode.108;
ret Encode.110;
procedure List.101 (#Derived_gen.26, #Derived_gen.27, #Derived_gen.28, #Derived_gen.29, #Derived_gen.30):
procedure List.101 (#Derived_gen.35, #Derived_gen.36, #Derived_gen.37, #Derived_gen.38, #Derived_gen.39):
joinpoint List.678 List.175 List.176 List.177 List.178 List.179:
let List.680 : Int1 = CallByName Num.22 List.178 List.179;
if List.680 then
@ -81,10 +81,10 @@ procedure List.101 (#Derived_gen.26, #Derived_gen.27, #Derived_gen.28, #Derived_
dec List.175;
ret List.176;
in
inc #Derived_gen.26;
jump List.678 #Derived_gen.26 #Derived_gen.27 #Derived_gen.28 #Derived_gen.29 #Derived_gen.30;
inc #Derived_gen.35;
jump List.678 #Derived_gen.35 #Derived_gen.36 #Derived_gen.37 #Derived_gen.38 #Derived_gen.39;
procedure List.101 (#Derived_gen.31, #Derived_gen.32, #Derived_gen.33, #Derived_gen.34, #Derived_gen.35):
procedure List.101 (#Derived_gen.40, #Derived_gen.41, #Derived_gen.42, #Derived_gen.43, #Derived_gen.44):
joinpoint List.704 List.175 List.176 List.177 List.178 List.179:
let List.706 : Int1 = CallByName Num.22 List.178 List.179;
if List.706 then
@ -98,8 +98,8 @@ procedure List.101 (#Derived_gen.31, #Derived_gen.32, #Derived_gen.33, #Derived_
dec List.175;
ret List.176;
in
inc #Derived_gen.31;
jump List.704 #Derived_gen.31 #Derived_gen.32 #Derived_gen.33 #Derived_gen.34 #Derived_gen.35;
inc #Derived_gen.40;
jump List.704 #Derived_gen.40 #Derived_gen.41 #Derived_gen.42 #Derived_gen.43 #Derived_gen.44;
procedure List.18 (List.172, List.173, List.174):
let List.676 : U64 = 0i64;
@ -164,32 +164,32 @@ procedure Num.96 (#Attr.2):
ret Num.287;
procedure Str.12 (#Attr.2):
let Str.259 : List U8 = lowlevel StrToUtf8 #Attr.2;
ret Str.259;
let Str.398 : List U8 = lowlevel StrToUtf8 #Attr.2;
ret Str.398;
procedure Str.36 (#Attr.2):
let Str.260 : U64 = lowlevel StrCountUtf8Bytes #Attr.2;
ret Str.260;
let Str.399 : U64 = lowlevel StrCountUtf8Bytes #Attr.2;
ret Str.399;
procedure Str.43 (#Attr.2):
let Str.254 : {U64, Str, Int1, U8} = lowlevel StrFromUtf8 #Attr.2;
ret Str.254;
let Str.393 : {U64, Str, Int1, U8} = lowlevel StrFromUtf8 #Attr.2;
ret Str.393;
procedure Str.9 (Str.73):
let Str.74 : {U64, Str, Int1, U8} = CallByName Str.43 Str.73;
let Str.251 : Int1 = StructAtIndex 2 Str.74;
if Str.251 then
let Str.253 : Str = StructAtIndex 1 Str.74;
let Str.252 : [C {U64, U8}, C Str] = TagId(1) Str.253;
ret Str.252;
procedure Str.9 (Str.79):
let Str.80 : {U64, Str, Int1, U8} = CallByName Str.43 Str.79;
let Str.390 : Int1 = StructAtIndex 2 Str.80;
if Str.390 then
let Str.392 : Str = StructAtIndex 1 Str.80;
let Str.391 : [C {U64, U8}, C Str] = TagId(1) Str.392;
ret Str.391;
else
let Str.249 : U64 = StructAtIndex 0 Str.74;
let Str.250 : U8 = StructAtIndex 3 Str.74;
let #Derived_gen.45 : Str = StructAtIndex 1 Str.74;
let Str.388 : U64 = StructAtIndex 0 Str.80;
let Str.389 : U8 = StructAtIndex 3 Str.80;
let #Derived_gen.45 : Str = StructAtIndex 1 Str.80;
dec #Derived_gen.45;
let Str.248 : {U64, U8} = Struct {Str.249, Str.250};
let Str.246 : [C {U64, U8}, C Str] = TagId(0) Str.248;
ret Str.246;
let Str.387 : {U64, U8} = Struct {Str.388, Str.389};
let Str.385 : [C {U64, U8}, C Str] = TagId(0) Str.387;
ret Str.385;
procedure Test.20 (Test.56):
let Test.325 : Str = CallByName Encode.23 Test.56;

View file

@ -39,7 +39,7 @@ procedure Encode.26 (Encode.107, Encode.108):
let Encode.110 : List U8 = CallByName Encode.24 Encode.111 Encode.112 Encode.108;
ret Encode.110;
procedure List.101 (#Derived_gen.16, #Derived_gen.17, #Derived_gen.18, #Derived_gen.19, #Derived_gen.20):
procedure List.101 (#Derived_gen.19, #Derived_gen.20, #Derived_gen.21, #Derived_gen.22, #Derived_gen.23):
joinpoint List.678 List.175 List.176 List.177 List.178 List.179:
let List.680 : Int1 = CallByName Num.22 List.178 List.179;
if List.680 then
@ -53,8 +53,8 @@ procedure List.101 (#Derived_gen.16, #Derived_gen.17, #Derived_gen.18, #Derived_
dec List.175;
ret List.176;
in
inc #Derived_gen.16;
jump List.678 #Derived_gen.16 #Derived_gen.17 #Derived_gen.18 #Derived_gen.19 #Derived_gen.20;
inc #Derived_gen.19;
jump List.678 #Derived_gen.19 #Derived_gen.20 #Derived_gen.21 #Derived_gen.22 #Derived_gen.23;
procedure List.18 (List.172, List.173, List.174):
let List.676 : U64 = 0i64;
@ -105,32 +105,32 @@ procedure Num.96 (#Attr.2):
ret Num.283;
procedure Str.12 (#Attr.2):
let Str.256 : List U8 = lowlevel StrToUtf8 #Attr.2;
ret Str.256;
let Str.395 : List U8 = lowlevel StrToUtf8 #Attr.2;
ret Str.395;
procedure Str.36 (#Attr.2):
let Str.257 : U64 = lowlevel StrCountUtf8Bytes #Attr.2;
ret Str.257;
let Str.396 : U64 = lowlevel StrCountUtf8Bytes #Attr.2;
ret Str.396;
procedure Str.43 (#Attr.2):
let Str.254 : {U64, Str, Int1, U8} = lowlevel StrFromUtf8 #Attr.2;
ret Str.254;
let Str.393 : {U64, Str, Int1, U8} = lowlevel StrFromUtf8 #Attr.2;
ret Str.393;
procedure Str.9 (Str.73):
let Str.74 : {U64, Str, Int1, U8} = CallByName Str.43 Str.73;
let Str.251 : Int1 = StructAtIndex 2 Str.74;
if Str.251 then
let Str.253 : Str = StructAtIndex 1 Str.74;
let Str.252 : [C {U64, U8}, C Str] = TagId(1) Str.253;
ret Str.252;
procedure Str.9 (Str.79):
let Str.80 : {U64, Str, Int1, U8} = CallByName Str.43 Str.79;
let Str.390 : Int1 = StructAtIndex 2 Str.80;
if Str.390 then
let Str.392 : Str = StructAtIndex 1 Str.80;
let Str.391 : [C {U64, U8}, C Str] = TagId(1) Str.392;
ret Str.391;
else
let Str.249 : U64 = StructAtIndex 0 Str.74;
let Str.250 : U8 = StructAtIndex 3 Str.74;
let #Derived_gen.24 : Str = StructAtIndex 1 Str.74;
let Str.388 : U64 = StructAtIndex 0 Str.80;
let Str.389 : U8 = StructAtIndex 3 Str.80;
let #Derived_gen.24 : Str = StructAtIndex 1 Str.80;
dec #Derived_gen.24;
let Str.248 : {U64, U8} = Struct {Str.249, Str.250};
let Str.246 : [C {U64, U8}, C Str] = TagId(0) Str.248;
ret Str.246;
let Str.387 : {U64, U8} = Struct {Str.388, Str.389};
let Str.385 : [C {U64, U8}, C Str] = TagId(0) Str.387;
ret Str.385;
procedure Test.20 (Test.56):
let Test.292 : Str = CallByName Encode.23 Test.56;

View file

@ -46,7 +46,7 @@ procedure Encode.26 (Encode.107, Encode.108):
let Encode.110 : List U8 = CallByName Encode.24 Encode.111 Encode.112 Encode.108;
ret Encode.110;
procedure List.101 (#Derived_gen.20, #Derived_gen.21, #Derived_gen.22, #Derived_gen.23, #Derived_gen.24):
procedure List.101 (#Derived_gen.23, #Derived_gen.24, #Derived_gen.25, #Derived_gen.26, #Derived_gen.27):
joinpoint List.678 List.175 List.176 List.177 List.178 List.179:
let List.680 : Int1 = CallByName Num.22 List.178 List.179;
if List.680 then
@ -60,8 +60,8 @@ procedure List.101 (#Derived_gen.20, #Derived_gen.21, #Derived_gen.22, #Derived_
dec List.175;
ret List.176;
in
inc #Derived_gen.20;
jump List.678 #Derived_gen.20 #Derived_gen.21 #Derived_gen.22 #Derived_gen.23 #Derived_gen.24;
inc #Derived_gen.23;
jump List.678 #Derived_gen.23 #Derived_gen.24 #Derived_gen.25 #Derived_gen.26 #Derived_gen.27;
procedure List.18 (List.172, List.173, List.174):
let List.676 : U64 = 0i64;
@ -112,32 +112,32 @@ procedure Num.96 (#Attr.2):
ret Num.283;
procedure Str.12 (#Attr.2):
let Str.256 : List U8 = lowlevel StrToUtf8 #Attr.2;
ret Str.256;
let Str.395 : List U8 = lowlevel StrToUtf8 #Attr.2;
ret Str.395;
procedure Str.36 (#Attr.2):
let Str.257 : U64 = lowlevel StrCountUtf8Bytes #Attr.2;
ret Str.257;
let Str.396 : U64 = lowlevel StrCountUtf8Bytes #Attr.2;
ret Str.396;
procedure Str.43 (#Attr.2):
let Str.254 : {U64, Str, Int1, U8} = lowlevel StrFromUtf8 #Attr.2;
ret Str.254;
let Str.393 : {U64, Str, Int1, U8} = lowlevel StrFromUtf8 #Attr.2;
ret Str.393;
procedure Str.9 (Str.73):
let Str.74 : {U64, Str, Int1, U8} = CallByName Str.43 Str.73;
let Str.251 : Int1 = StructAtIndex 2 Str.74;
if Str.251 then
let Str.253 : Str = StructAtIndex 1 Str.74;
let Str.252 : [C {U64, U8}, C Str] = TagId(1) Str.253;
ret Str.252;
procedure Str.9 (Str.79):
let Str.80 : {U64, Str, Int1, U8} = CallByName Str.43 Str.79;
let Str.390 : Int1 = StructAtIndex 2 Str.80;
if Str.390 then
let Str.392 : Str = StructAtIndex 1 Str.80;
let Str.391 : [C {U64, U8}, C Str] = TagId(1) Str.392;
ret Str.391;
else
let Str.249 : U64 = StructAtIndex 0 Str.74;
let Str.250 : U8 = StructAtIndex 3 Str.74;
let #Derived_gen.28 : Str = StructAtIndex 1 Str.74;
let Str.388 : U64 = StructAtIndex 0 Str.80;
let Str.389 : U8 = StructAtIndex 3 Str.80;
let #Derived_gen.28 : Str = StructAtIndex 1 Str.80;
dec #Derived_gen.28;
let Str.248 : {U64, U8} = Struct {Str.249, Str.250};
let Str.246 : [C {U64, U8}, C Str] = TagId(0) Str.248;
ret Str.246;
let Str.387 : {U64, U8} = Struct {Str.388, Str.389};
let Str.385 : [C {U64, U8}, C Str] = TagId(0) Str.387;
ret Str.385;
procedure Test.20 (Test.56):
let Test.296 : Str = CallByName Encode.23 Test.56;

View file

@ -38,32 +38,32 @@ procedure Num.96 (#Attr.2):
ret Num.283;
procedure Str.12 (#Attr.2):
let Str.256 : List U8 = lowlevel StrToUtf8 #Attr.2;
ret Str.256;
let Str.395 : List U8 = lowlevel StrToUtf8 #Attr.2;
ret Str.395;
procedure Str.36 (#Attr.2):
let Str.257 : U64 = lowlevel StrCountUtf8Bytes #Attr.2;
ret Str.257;
let Str.396 : U64 = lowlevel StrCountUtf8Bytes #Attr.2;
ret Str.396;
procedure Str.43 (#Attr.2):
let Str.254 : {U64, Str, Int1, U8} = lowlevel StrFromUtf8 #Attr.2;
ret Str.254;
let Str.393 : {U64, Str, Int1, U8} = lowlevel StrFromUtf8 #Attr.2;
ret Str.393;
procedure Str.9 (Str.73):
let Str.74 : {U64, Str, Int1, U8} = CallByName Str.43 Str.73;
let Str.251 : Int1 = StructAtIndex 2 Str.74;
if Str.251 then
let Str.253 : Str = StructAtIndex 1 Str.74;
let Str.252 : [C {U64, U8}, C Str] = TagId(1) Str.253;
ret Str.252;
procedure Str.9 (Str.79):
let Str.80 : {U64, Str, Int1, U8} = CallByName Str.43 Str.79;
let Str.390 : Int1 = StructAtIndex 2 Str.80;
if Str.390 then
let Str.392 : Str = StructAtIndex 1 Str.80;
let Str.391 : [C {U64, U8}, C Str] = TagId(1) Str.392;
ret Str.391;
else
let Str.249 : U64 = StructAtIndex 0 Str.74;
let Str.250 : U8 = StructAtIndex 3 Str.74;
let #Derived_gen.3 : Str = StructAtIndex 1 Str.74;
let Str.388 : U64 = StructAtIndex 0 Str.80;
let Str.389 : U8 = StructAtIndex 3 Str.80;
let #Derived_gen.3 : Str = StructAtIndex 1 Str.80;
dec #Derived_gen.3;
let Str.248 : {U64, U8} = Struct {Str.249, Str.250};
let Str.246 : [C {U64, U8}, C Str] = TagId(0) Str.248;
ret Str.246;
let Str.387 : {U64, U8} = Struct {Str.388, Str.389};
let Str.385 : [C {U64, U8}, C Str] = TagId(0) Str.387;
ret Str.385;
procedure Test.20 (Test.56):
let Test.259 : Str = CallByName Encode.23 Test.56;

View file

@ -40,7 +40,7 @@ procedure Encode.26 (Encode.107, Encode.108):
let Encode.110 : List U8 = CallByName Encode.24 Encode.111 Encode.112 Encode.108;
ret Encode.110;
procedure List.101 (#Derived_gen.10, #Derived_gen.11, #Derived_gen.12, #Derived_gen.13, #Derived_gen.14):
procedure List.101 (#Derived_gen.22, #Derived_gen.23, #Derived_gen.24, #Derived_gen.25, #Derived_gen.26):
joinpoint List.678 List.175 List.176 List.177 List.178 List.179:
let List.680 : Int1 = CallByName Num.22 List.178 List.179;
if List.680 then
@ -54,8 +54,8 @@ procedure List.101 (#Derived_gen.10, #Derived_gen.11, #Derived_gen.12, #Derived_
dec List.175;
ret List.176;
in
inc #Derived_gen.10;
jump List.678 #Derived_gen.10 #Derived_gen.11 #Derived_gen.12 #Derived_gen.13 #Derived_gen.14;
inc #Derived_gen.22;
jump List.678 #Derived_gen.22 #Derived_gen.23 #Derived_gen.24 #Derived_gen.25 #Derived_gen.26;
procedure List.13 (#Attr.2, #Attr.3):
let List.701 : List Str = lowlevel ListPrepend #Attr.2 #Attr.3;
@ -110,32 +110,32 @@ procedure Num.96 (#Attr.2):
ret Num.283;
procedure Str.12 (#Attr.2):
let Str.256 : List U8 = lowlevel StrToUtf8 #Attr.2;
ret Str.256;
let Str.395 : List U8 = lowlevel StrToUtf8 #Attr.2;
ret Str.395;
procedure Str.36 (#Attr.2):
let Str.257 : U64 = lowlevel StrCountUtf8Bytes #Attr.2;
ret Str.257;
let Str.396 : U64 = lowlevel StrCountUtf8Bytes #Attr.2;
ret Str.396;
procedure Str.43 (#Attr.2):
let Str.254 : {U64, Str, Int1, U8} = lowlevel StrFromUtf8 #Attr.2;
ret Str.254;
let Str.393 : {U64, Str, Int1, U8} = lowlevel StrFromUtf8 #Attr.2;
ret Str.393;
procedure Str.9 (Str.73):
let Str.74 : {U64, Str, Int1, U8} = CallByName Str.43 Str.73;
let Str.251 : Int1 = StructAtIndex 2 Str.74;
if Str.251 then
let Str.253 : Str = StructAtIndex 1 Str.74;
let Str.252 : [C {U64, U8}, C Str] = TagId(1) Str.253;
ret Str.252;
procedure Str.9 (Str.79):
let Str.80 : {U64, Str, Int1, U8} = CallByName Str.43 Str.79;
let Str.390 : Int1 = StructAtIndex 2 Str.80;
if Str.390 then
let Str.392 : Str = StructAtIndex 1 Str.80;
let Str.391 : [C {U64, U8}, C Str] = TagId(1) Str.392;
ret Str.391;
else
let Str.249 : U64 = StructAtIndex 0 Str.74;
let Str.250 : U8 = StructAtIndex 3 Str.74;
let #Derived_gen.27 : Str = StructAtIndex 1 Str.74;
let Str.388 : U64 = StructAtIndex 0 Str.80;
let Str.389 : U8 = StructAtIndex 3 Str.80;
let #Derived_gen.27 : Str = StructAtIndex 1 Str.80;
dec #Derived_gen.27;
let Str.248 : {U64, U8} = Struct {Str.249, Str.250};
let Str.246 : [C {U64, U8}, C Str] = TagId(0) Str.248;
ret Str.246;
let Str.387 : {U64, U8} = Struct {Str.388, Str.389};
let Str.385 : [C {U64, U8}, C Str] = TagId(0) Str.387;
ret Str.385;
procedure Test.20 (Test.56):
let Test.297 : Str = CallByName Encode.23 Test.56;

View file

@ -43,7 +43,7 @@ procedure Encode.26 (Encode.107, Encode.108):
let Encode.110 : List U8 = CallByName Encode.24 Encode.111 Encode.112 Encode.108;
ret Encode.110;
procedure List.101 (#Derived_gen.20, #Derived_gen.21, #Derived_gen.22, #Derived_gen.23, #Derived_gen.24):
procedure List.101 (#Derived_gen.23, #Derived_gen.24, #Derived_gen.25, #Derived_gen.26, #Derived_gen.27):
joinpoint List.678 List.175 List.176 List.177 List.178 List.179:
let List.680 : Int1 = CallByName Num.22 List.178 List.179;
if List.680 then
@ -57,8 +57,8 @@ procedure List.101 (#Derived_gen.20, #Derived_gen.21, #Derived_gen.22, #Derived_
dec List.175;
ret List.176;
in
inc #Derived_gen.20;
jump List.678 #Derived_gen.20 #Derived_gen.21 #Derived_gen.22 #Derived_gen.23 #Derived_gen.24;
inc #Derived_gen.23;
jump List.678 #Derived_gen.23 #Derived_gen.24 #Derived_gen.25 #Derived_gen.26 #Derived_gen.27;
procedure List.13 (#Attr.2, #Attr.3):
let List.701 : List Str = lowlevel ListPrepend #Attr.2 #Attr.3;
@ -113,32 +113,32 @@ procedure Num.96 (#Attr.2):
ret Num.283;
procedure Str.12 (#Attr.2):
let Str.256 : List U8 = lowlevel StrToUtf8 #Attr.2;
ret Str.256;
let Str.395 : List U8 = lowlevel StrToUtf8 #Attr.2;
ret Str.395;
procedure Str.36 (#Attr.2):
let Str.257 : U64 = lowlevel StrCountUtf8Bytes #Attr.2;
ret Str.257;
let Str.396 : U64 = lowlevel StrCountUtf8Bytes #Attr.2;
ret Str.396;
procedure Str.43 (#Attr.2):
let Str.254 : {U64, Str, Int1, U8} = lowlevel StrFromUtf8 #Attr.2;
ret Str.254;
let Str.393 : {U64, Str, Int1, U8} = lowlevel StrFromUtf8 #Attr.2;
ret Str.393;
procedure Str.9 (Str.73):
let Str.74 : {U64, Str, Int1, U8} = CallByName Str.43 Str.73;
let Str.251 : Int1 = StructAtIndex 2 Str.74;
if Str.251 then
let Str.253 : Str = StructAtIndex 1 Str.74;
let Str.252 : [C {U64, U8}, C Str] = TagId(1) Str.253;
ret Str.252;
procedure Str.9 (Str.79):
let Str.80 : {U64, Str, Int1, U8} = CallByName Str.43 Str.79;
let Str.390 : Int1 = StructAtIndex 2 Str.80;
if Str.390 then
let Str.392 : Str = StructAtIndex 1 Str.80;
let Str.391 : [C {U64, U8}, C Str] = TagId(1) Str.392;
ret Str.391;
else
let Str.249 : U64 = StructAtIndex 0 Str.74;
let Str.250 : U8 = StructAtIndex 3 Str.74;
let #Derived_gen.28 : Str = StructAtIndex 1 Str.74;
let Str.388 : U64 = StructAtIndex 0 Str.80;
let Str.389 : U8 = StructAtIndex 3 Str.80;
let #Derived_gen.28 : Str = StructAtIndex 1 Str.80;
dec #Derived_gen.28;
let Str.248 : {U64, U8} = Struct {Str.249, Str.250};
let Str.246 : [C {U64, U8}, C Str] = TagId(0) Str.248;
ret Str.246;
let Str.387 : {U64, U8} = Struct {Str.388, Str.389};
let Str.385 : [C {U64, U8}, C Str] = TagId(0) Str.387;
ret Str.385;
procedure Test.20 (Test.56):
let Test.301 : Str = CallByName Encode.23 Test.56;

View file

@ -6,7 +6,7 @@ procedure Num.21 (#Attr.2, #Attr.3):
let Num.283 : I64 = lowlevel NumMul #Attr.2 #Attr.3;
ret Num.283;
procedure Test.1 (#Derived_gen.0, #Derived_gen.1):
procedure Test.1 (Bool.21, Bool.22):
joinpoint Test.7 Test.2 Test.3:
let Test.13 : I64 = 0i64;
let Test.14 : Int1 = lowlevel Eq Test.13 Test.2;
@ -18,7 +18,7 @@ procedure Test.1 (#Derived_gen.0, #Derived_gen.1):
let Test.11 : I64 = CallByName Num.21 Test.2 Test.3;
jump Test.7 Test.10 Test.11;
in
jump Test.7 #Derived_gen.0 #Derived_gen.1;
jump Test.7 Bool.21 Bool.22;
procedure Test.0 ():
let Test.5 : I64 = 10i64;

View file

@ -1,6 +1,6 @@
procedure Bool.1 ():
let Bool.23 : Int1 = false;
ret Bool.23;
let Bool.21 : Int1 = false;
ret Bool.21;
procedure Test.1 (Test.2):
let Test.5 : I64 = 2i64;

View file

@ -1,6 +1,6 @@
procedure Bool.11 (#Attr.2, #Attr.3):
let Bool.23 : Int1 = lowlevel Eq #Attr.2 #Attr.3;
ret Bool.23;
procedure Bool.9 (#Attr.2, #Attr.3):
let Bool.21 : Int1 = lowlevel Eq #Attr.2 #Attr.3;
ret Bool.21;
procedure Test.1 (Test.3):
let Test.6 : I64 = 10i64;
@ -13,7 +13,7 @@ procedure Test.1 (Test.3):
ret Test.8;
in
let Test.12 : I64 = 5i64;
let Test.11 : Int1 = CallByName Bool.11 Test.6 Test.12;
let Test.11 : Int1 = CallByName Bool.9 Test.6 Test.12;
jump Test.10 Test.11;
procedure Test.0 ():

View file

@ -1,10 +1,10 @@
procedure Bool.1 ():
let Bool.23 : Int1 = false;
ret Bool.23;
let Bool.21 : Int1 = false;
ret Bool.21;
procedure Bool.2 ():
let Bool.24 : Int1 = true;
ret Bool.24;
let Bool.22 : Int1 = true;
ret Bool.22;
procedure Test.0 ():
let Test.4 : Int1 = CallByName Bool.2;

View file

@ -1,6 +1,6 @@
procedure Bool.1 ():
let Bool.23 : Int1 = false;
ret Bool.23;
let Bool.21 : Int1 = false;
ret Bool.21;
procedure Num.19 (#Attr.2, #Attr.3):
let Num.283 : I64 = lowlevel NumAdd #Attr.2 #Attr.3;
@ -9,7 +9,7 @@ procedure Num.19 (#Attr.2, #Attr.3):
procedure Test.3 (Test.4):
ret Test.4;
procedure Test.0 (#Derived_gen.0):
procedure Test.0 (Bool.22):
joinpoint Test.5 Test.1:
joinpoint Test.10 Test.2:
let Test.8 : I64 = 1i64;
@ -31,4 +31,4 @@ procedure Test.0 (#Derived_gen.0):
let Test.9 : Int1 = true;
jump Test.10 Test.9;
in
jump Test.5 #Derived_gen.0;
jump Test.5 Bool.22;

View file

@ -34,7 +34,7 @@ procedure Test.8 (Test.9):
let Test.23 : I64 = CallByName Num.19 Test.9 Test.24;
ret Test.23;
procedure Test.0 (#Derived_gen.0):
procedure Test.0 (Bool.21):
joinpoint Test.11 Test.1:
let Test.25 : I64 = 1i64;
let Test.13 : I64 = CallByName Num.19 Test.1 Test.25;
@ -57,4 +57,4 @@ procedure Test.0 (#Derived_gen.0):
ret Test.12;
in
jump Test.11 #Derived_gen.0;
jump Test.11 Bool.21;

View file

@ -17,7 +17,7 @@ procedure Test.4 (Test.5, #Attr.12):
let Test.16 : I64 = CallByName Num.19 Test.5 Test.17;
ret Test.16;
procedure Test.0 (#Derived_gen.0):
procedure Test.0 (Bool.21):
joinpoint Test.7 Test.1:
let Test.21 : I64 = 1i64;
let Test.9 : I64 = CallByName Num.19 Test.1 Test.21;
@ -33,4 +33,4 @@ procedure Test.0 (#Derived_gen.0):
ret Test.8;
in
jump Test.7 #Derived_gen.0;
jump Test.7 Bool.21;

File diff suppressed because it is too large Load diff

View file

@ -14,12 +14,12 @@ procedure #Derived.4 (#Derived.5, #Derived.1):
ret #Derived_gen.3;
procedure Bool.1 ():
let Bool.24 : Int1 = false;
ret Bool.24;
let Bool.22 : Int1 = false;
ret Bool.22;
procedure Bool.2 ():
let Bool.23 : Int1 = true;
ret Bool.23;
let Bool.21 : Int1 = true;
ret Bool.21;
procedure Inspect.156 (Inspect.157, #Attr.12):
let Inspect.155 : {} = StructAtIndex 2 #Attr.12;
@ -120,7 +120,7 @@ procedure Inspect.63 (Inspect.295, Inspect.291):
procedure Inspect.64 (Inspect.297):
ret Inspect.297;
procedure List.101 (#Derived_gen.11, #Derived_gen.12, #Derived_gen.13, #Derived_gen.14, #Derived_gen.15):
procedure List.101 (#Derived_gen.10, #Derived_gen.11, #Derived_gen.12, #Derived_gen.13, #Derived_gen.14):
joinpoint List.678 List.175 List.176 List.177 List.178 List.179:
let List.680 : Int1 = CallByName Num.22 List.178 List.179;
if List.680 then
@ -133,8 +133,8 @@ procedure List.101 (#Derived_gen.11, #Derived_gen.12, #Derived_gen.13, #Derived_
dec List.175;
ret List.176;
in
inc #Derived_gen.11;
jump List.678 #Derived_gen.11 #Derived_gen.12 #Derived_gen.13 #Derived_gen.14 #Derived_gen.15;
inc #Derived_gen.10;
jump List.678 #Derived_gen.10 #Derived_gen.11 #Derived_gen.12 #Derived_gen.13 #Derived_gen.14;
procedure List.18 (List.172, List.173, List.174):
let List.676 : U64 = 0i64;
@ -163,8 +163,8 @@ procedure Num.96 (#Attr.2):
ret Num.283;
procedure Str.3 (#Attr.2, #Attr.3):
let Str.246 : Str = lowlevel StrConcat #Attr.2 #Attr.3;
ret Str.246;
let Str.385 : Str = lowlevel StrConcat #Attr.2 #Attr.3;
ret Str.385;
procedure Test.0 ():
let Test.2 : List I64 = Array [1i64, 2i64, 3i64];

View file

@ -27,23 +27,19 @@ procedure #Derived.6 (#Derived.7, #Derived.5):
ret #Derived_gen.13;
procedure Bool.1 ():
let Bool.26 : Int1 = false;
ret Bool.26;
procedure Bool.11 (#Attr.2, #Attr.3):
let Bool.28 : Int1 = lowlevel Eq #Attr.2 #Attr.3;
ret Bool.28;
procedure Bool.11 (#Attr.2, #Attr.3):
let Bool.29 : Int1 = lowlevel Eq #Attr.2 #Attr.3;
ret Bool.29;
procedure Bool.2 ():
let Bool.25 : Int1 = true;
let Bool.25 : Int1 = false;
ret Bool.25;
procedure Bool.3 (#Attr.2, #Attr.3):
let Bool.27 : Int1 = lowlevel And #Attr.2 #Attr.3;
procedure Bool.2 ():
let Bool.23 : Int1 = true;
ret Bool.23;
procedure Bool.9 (#Attr.2, #Attr.3):
let Bool.26 : Int1 = lowlevel Eq #Attr.2 #Attr.3;
ret Bool.26;
procedure Bool.9 (#Attr.2, #Attr.3):
let Bool.27 : Int1 = lowlevel Eq #Attr.2 #Attr.3;
ret Bool.27;
procedure Inspect.225 (Inspect.226, Inspect.224):
@ -235,7 +231,7 @@ procedure Inspect.63 (Inspect.295, Inspect.291):
procedure Inspect.64 (Inspect.297):
ret Inspect.297;
procedure List.101 (#Derived_gen.20, #Derived_gen.21, #Derived_gen.22, #Derived_gen.23, #Derived_gen.24):
procedure List.101 (#Derived_gen.33, #Derived_gen.34, #Derived_gen.35, #Derived_gen.36, #Derived_gen.37):
joinpoint List.678 List.175 List.176 List.177 List.178 List.179:
let List.680 : Int1 = CallByName Num.22 List.178 List.179;
if List.680 then
@ -249,10 +245,10 @@ procedure List.101 (#Derived_gen.20, #Derived_gen.21, #Derived_gen.22, #Derived_
dec List.175;
ret List.176;
in
inc #Derived_gen.20;
jump List.678 #Derived_gen.20 #Derived_gen.21 #Derived_gen.22 #Derived_gen.23 #Derived_gen.24;
inc #Derived_gen.33;
jump List.678 #Derived_gen.33 #Derived_gen.34 #Derived_gen.35 #Derived_gen.36 #Derived_gen.37;
procedure List.101 (#Derived_gen.39, #Derived_gen.40, #Derived_gen.41, #Derived_gen.42, #Derived_gen.43):
procedure List.101 (#Derived_gen.38, #Derived_gen.39, #Derived_gen.40, #Derived_gen.41, #Derived_gen.42):
joinpoint List.690 List.175 List.176 List.177 List.178 List.179:
let List.692 : Int1 = CallByName Num.22 List.178 List.179;
if List.692 then
@ -266,8 +262,8 @@ procedure List.101 (#Derived_gen.39, #Derived_gen.40, #Derived_gen.41, #Derived_
dec List.175;
ret List.176;
in
inc #Derived_gen.39;
jump List.690 #Derived_gen.39 #Derived_gen.40 #Derived_gen.41 #Derived_gen.42 #Derived_gen.43;
inc #Derived_gen.38;
jump List.690 #Derived_gen.38 #Derived_gen.39 #Derived_gen.40 #Derived_gen.41 #Derived_gen.42;
procedure List.18 (List.172, List.173, List.174):
let List.676 : U64 = 0i64;
@ -326,181 +322,184 @@ procedure Num.77 (#Attr.2, #Attr.3):
ret Num.295;
procedure Str.20 (#Attr.2):
let Str.314 : Str = lowlevel StrWithCapacity #Attr.2;
ret Str.314;
let Str.454 : Str = lowlevel StrWithCapacity #Attr.2;
ret Str.454;
procedure Str.3 (#Attr.2, #Attr.3):
let Str.247 : Str = lowlevel StrConcat #Attr.2 #Attr.3;
ret Str.247;
let Str.386 : Str = lowlevel StrConcat #Attr.2 #Attr.3;
ret Str.386;
procedure Str.35 (#Attr.2, #Attr.3):
let Str.304 : U8 = lowlevel StrGetUnsafe #Attr.2 #Attr.3;
ret Str.304;
let Str.444 : U8 = lowlevel StrGetUnsafe #Attr.2 #Attr.3;
ret Str.444;
procedure Str.36 (#Attr.2):
let Str.267 : U64 = lowlevel StrCountUtf8Bytes #Attr.2;
ret Str.267;
let Str.406 : U64 = lowlevel StrCountUtf8Bytes #Attr.2;
ret Str.406;
procedure Str.37 (#Attr.2, #Attr.3, #Attr.4):
let Str.265 : Str = lowlevel StrSubstringUnsafe #Attr.2 #Attr.3 #Attr.4;
ret Str.265;
let Str.404 : Str = lowlevel StrSubstringUnsafe #Attr.2 #Attr.3 #Attr.4;
ret Str.404;
procedure Str.38 (Str.112, Str.113):
let Str.261 : [C , C U64] = CallByName Str.57 Str.112 Str.113;
let Str.274 : U8 = 1i64;
let Str.275 : U8 = GetTagId Str.261;
let Str.276 : Int1 = lowlevel Eq Str.274 Str.275;
if Str.276 then
let Str.114 : U64 = UnionAtIndex (Id 1) (Index 0) Str.261;
let Str.270 : U64 = CallByName Str.36 Str.112;
let Str.271 : U64 = CallByName Str.36 Str.113;
let Str.269 : U64 = CallByName Num.20 Str.270 Str.271;
let Str.115 : U64 = CallByName Num.20 Str.269 Str.114;
let Str.268 : U64 = 0i64;
inc Str.112;
let Str.116 : Str = CallByName Str.37 Str.112 Str.268 Str.114;
let Str.266 : U64 = CallByName Str.36 Str.113;
let Str.264 : U64 = CallByName Num.51 Str.114 Str.266;
let Str.117 : Str = CallByName Str.37 Str.112 Str.264 Str.115;
let Str.263 : {Str, Str} = Struct {Str.117, Str.116};
let Str.262 : [C {}, C {Str, Str}] = TagId(1) Str.263;
ret Str.262;
procedure Str.38 (Str.213, Str.214):
let Str.400 : [C , C U64] = CallByName Str.65 Str.213 Str.214;
let Str.413 : U8 = 1i64;
let Str.414 : U8 = GetTagId Str.400;
let Str.415 : Int1 = lowlevel Eq Str.413 Str.414;
if Str.415 then
let Str.215 : U64 = UnionAtIndex (Id 1) (Index 0) Str.400;
let Str.409 : U64 = CallByName Str.36 Str.213;
let Str.410 : U64 = CallByName Str.36 Str.214;
let Str.408 : U64 = CallByName Num.20 Str.409 Str.410;
let Str.216 : U64 = CallByName Num.20 Str.408 Str.215;
let Str.407 : U64 = 0i64;
inc Str.213;
let Str.217 : Str = CallByName Str.37 Str.213 Str.407 Str.215;
let Str.405 : U64 = CallByName Str.36 Str.214;
let Str.403 : U64 = CallByName Num.51 Str.215 Str.405;
let Str.218 : Str = CallByName Str.37 Str.213 Str.403 Str.216;
let Str.402 : {Str, Str} = Struct {Str.218, Str.217};
let Str.401 : [C {}, C {Str, Str}] = TagId(1) Str.402;
ret Str.401;
else
dec Str.112;
let Str.273 : {} = Struct {};
let Str.272 : [C {}, C {Str, Str}] = TagId(0) Str.273;
ret Str.272;
dec Str.213;
let Str.412 : {} = Struct {};
let Str.411 : [C {}, C {Str, Str}] = TagId(0) Str.412;
ret Str.411;
procedure Str.45 (Str.91, Str.92, Str.93):
inc Str.91;
let Str.342 : [C {}, C {Str, Str}] = CallByName Str.38 Str.91 Str.92;
let Str.350 : U8 = 1i64;
let Str.351 : U8 = GetTagId Str.342;
let Str.352 : Int1 = lowlevel Eq Str.350 Str.351;
if Str.352 then
let Str.349 : {Str, Str} = UnionAtIndex (Id 1) (Index 0) Str.342;
let Str.95 : Str = StructAtIndex 0 Str.349;
let Str.94 : Str = StructAtIndex 1 Str.349;
let Str.347 : U64 = CallByName Str.36 Str.91;
dec Str.91;
let Str.346 : Str = CallByName Str.20 Str.347;
let Str.345 : Str = CallByName Str.3 Str.346 Str.94;
dec Str.94;
let Str.344 : Str = CallByName Str.3 Str.345 Str.93;
let Str.343 : Str = CallByName Str.56 Str.344 Str.95 Str.92 Str.93;
ret Str.343;
procedure Str.45 (Str.192, Str.193, Str.194):
inc Str.192;
let Str.482 : [C {}, C {Str, Str}] = CallByName Str.38 Str.192 Str.193;
let Str.490 : U8 = 1i64;
let Str.491 : U8 = GetTagId Str.482;
let Str.492 : Int1 = lowlevel Eq Str.490 Str.491;
if Str.492 then
let Str.489 : {Str, Str} = UnionAtIndex (Id 1) (Index 0) Str.482;
let Str.196 : Str = StructAtIndex 0 Str.489;
let Str.195 : Str = StructAtIndex 1 Str.489;
let Str.487 : U64 = CallByName Str.36 Str.192;
dec Str.192;
let Str.486 : Str = CallByName Str.20 Str.487;
let Str.485 : Str = CallByName Str.3 Str.486 Str.195;
dec Str.195;
let Str.484 : Str = CallByName Str.3 Str.485 Str.194;
let Str.483 : Str = CallByName Str.64 Str.484 Str.196 Str.193 Str.194;
ret Str.483;
else
dec Str.342;
ret Str.91;
dec Str.482;
ret Str.192;
procedure Str.56 (#Derived_gen.27, #Derived_gen.28, #Derived_gen.29, #Derived_gen.30):
joinpoint Str.251 Str.96 Str.97 Str.98 Str.99:
inc Str.97;
let Str.252 : [C {}, C {Str, Str}] = CallByName Str.38 Str.97 Str.98;
let Str.258 : U8 = 1i64;
let Str.259 : U8 = GetTagId Str.252;
let Str.260 : Int1 = lowlevel Eq Str.258 Str.259;
if Str.260 then
dec Str.97;
let Str.257 : {Str, Str} = UnionAtIndex (Id 1) (Index 0) Str.252;
let Str.101 : Str = StructAtIndex 0 Str.257;
let Str.100 : Str = StructAtIndex 1 Str.257;
let Str.255 : Str = CallByName Str.3 Str.96 Str.100;
dec Str.100;
let Str.254 : Str = CallByName Str.3 Str.255 Str.99;
jump Str.251 Str.254 Str.101 Str.98 Str.99;
procedure Str.64 (#Derived_gen.24, #Derived_gen.25, #Derived_gen.26, #Derived_gen.27):
joinpoint Str.390 Str.197 Str.198 Str.199 Str.200:
inc Str.198;
let Str.391 : [C {}, C {Str, Str}] = CallByName Str.38 Str.198 Str.199;
let Str.397 : U8 = 1i64;
let Str.398 : U8 = GetTagId Str.391;
let Str.399 : Int1 = lowlevel Eq Str.397 Str.398;
if Str.399 then
dec Str.198;
let Str.396 : {Str, Str} = UnionAtIndex (Id 1) (Index 0) Str.391;
let Str.202 : Str = StructAtIndex 0 Str.396;
let Str.201 : Str = StructAtIndex 1 Str.396;
let Str.394 : Str = CallByName Str.3 Str.197 Str.201;
dec Str.201;
let Str.393 : Str = CallByName Str.3 Str.394 Str.200;
jump Str.390 Str.393 Str.202 Str.199 Str.200;
else
dec Str.98;
dec Str.252;
dec Str.99;
let Str.256 : Str = CallByName Str.3 Str.96 Str.97;
dec Str.97;
ret Str.256;
dec Str.199;
dec Str.391;
dec Str.200;
let Str.395 : Str = CallByName Str.3 Str.197 Str.198;
dec Str.198;
ret Str.395;
in
inc #Derived_gen.27;
inc #Derived_gen.26;
jump Str.390 #Derived_gen.24 #Derived_gen.25 #Derived_gen.26 #Derived_gen.27;
procedure Str.65 (Str.222, Str.223):
let Str.224 : U64 = CallByName Str.36 Str.222;
let Str.225 : U64 = CallByName Str.36 Str.223;
let Str.226 : U64 = CallByName Num.77 Str.224 Str.225;
let Str.417 : U64 = 0i64;
let Str.416 : [C , C U64] = CallByName Str.66 Str.222 Str.223 Str.417 Str.226;
ret Str.416;
procedure Str.66 (#Derived_gen.28, #Derived_gen.29, #Derived_gen.30, #Derived_gen.31):
joinpoint Str.418 Str.227 Str.228 Str.229 Str.230:
let Str.420 : Int1 = CallByName Num.23 Str.229 Str.230;
if Str.420 then
let Str.424 : Int1 = CallByName Str.70 Str.227 Str.229 Str.228;
if Str.424 then
dec Str.227;
dec Str.228;
let Str.425 : [C , C U64] = TagId(1) Str.229;
ret Str.425;
else
let Str.423 : U64 = 1i64;
let Str.422 : U64 = CallByName Num.51 Str.229 Str.423;
jump Str.418 Str.227 Str.228 Str.422 Str.230;
else
dec Str.227;
dec Str.228;
let Str.419 : [C , C U64] = TagId(0) ;
ret Str.419;
in
inc #Derived_gen.29;
inc #Derived_gen.30;
jump Str.251 #Derived_gen.27 #Derived_gen.28 #Derived_gen.29 #Derived_gen.30;
inc #Derived_gen.28;
jump Str.418 #Derived_gen.28 #Derived_gen.29 #Derived_gen.30 #Derived_gen.31;
procedure Str.57 (Str.121, Str.122):
let Str.123 : U64 = CallByName Str.36 Str.121;
let Str.124 : U64 = CallByName Str.36 Str.122;
let Str.125 : U64 = CallByName Num.77 Str.123 Str.124;
let Str.278 : U64 = 0i64;
let Str.277 : [C , C U64] = CallByName Str.58 Str.121 Str.122 Str.278 Str.125;
ret Str.277;
procedure Str.69 (Str.253, Str.254):
let Str.449 : Int1 = CallByName Num.22 Str.253 Str.254;
if Str.449 then
ret Str.253;
else
ret Str.254;
procedure Str.58 (#Derived_gen.33, #Derived_gen.34, #Derived_gen.35, #Derived_gen.36):
joinpoint Str.279 Str.126 Str.127 Str.128 Str.129:
let Str.281 : Int1 = CallByName Num.23 Str.128 Str.129;
if Str.281 then
let Str.285 : Int1 = CallByName Str.62 Str.126 Str.128 Str.127;
if Str.285 then
dec Str.127;
dec Str.126;
let Str.286 : [C , C U64] = TagId(1) Str.128;
ret Str.286;
else
let Str.284 : U64 = 1i64;
let Str.283 : U64 = CallByName Num.51 Str.128 Str.284;
jump Str.279 Str.126 Str.127 Str.283 Str.129;
procedure Str.70 (Str.255, Str.256, Str.257):
let Str.258 : U64 = CallByName Str.36 Str.255;
let Str.259 : U64 = CallByName Str.36 Str.257;
let Str.447 : U64 = CallByName Num.53 Str.256 Str.259;
let Str.260 : U64 = CallByName Str.69 Str.447 Str.258;
let Str.446 : U64 = 0i64;
inc Str.255;
inc Str.257;
let Str.427 : {U64, Str, U64, Str, U64, U64} = Struct {Str.260, Str.255, Str.256, Str.257, Str.446, Str.259};
let Str.426 : Int1 = CallByName Str.71 Str.427;
ret Str.426;
procedure Str.71 (Str.261):
let Str.267 : U64 = StructAtIndex 0 Str.261;
let Str.262 : Str = StructAtIndex 1 Str.261;
let Str.263 : U64 = StructAtIndex 2 Str.261;
let Str.264 : Str = StructAtIndex 3 Str.261;
let Str.265 : U64 = StructAtIndex 4 Str.261;
let Str.266 : U64 = StructAtIndex 5 Str.261;
let Str.268 : Int1 = CallByName Num.25 Str.263 Str.267;
if Str.268 then
dec Str.262;
dec Str.264;
let Str.269 : Int1 = CallByName Bool.9 Str.265 Str.266;
ret Str.269;
else
let Str.442 : U8 = CallByName Str.35 Str.262 Str.263;
let Str.443 : U8 = CallByName Str.35 Str.264 Str.265;
let Str.270 : Int1 = CallByName Bool.9 Str.442 Str.443;
let Str.432 : U64 = StructAtIndex 0 Str.261;
let Str.433 : Str = StructAtIndex 1 Str.261;
let Str.435 : Str = StructAtIndex 3 Str.261;
let Str.437 : U64 = StructAtIndex 5 Str.261;
let Str.441 : U64 = 1i64;
let Str.439 : U64 = CallByName Num.51 Str.265 Str.441;
let Str.440 : U64 = 1i64;
let Str.438 : U64 = CallByName Num.51 Str.263 Str.440;
let Str.431 : {U64, Str, U64, Str, U64, U64} = Struct {Str.432, Str.433, Str.438, Str.435, Str.439, Str.437};
let Str.271 : Int1 = CallByName Str.71 Str.431;
if Str.270 then
ret Str.271;
else
dec Str.127;
dec Str.126;
let Str.280 : [C , C U64] = TagId(0) ;
ret Str.280;
in
inc #Derived_gen.34;
inc #Derived_gen.33;
jump Str.279 #Derived_gen.33 #Derived_gen.34 #Derived_gen.35 #Derived_gen.36;
procedure Str.61 (Str.152, Str.153):
let Str.309 : Int1 = CallByName Num.22 Str.152 Str.153;
if Str.309 then
ret Str.152;
else
ret Str.153;
procedure Str.62 (Str.154, Str.155, Str.156):
let Str.157 : U64 = CallByName Str.36 Str.154;
let Str.158 : U64 = CallByName Str.36 Str.156;
let Str.307 : U64 = CallByName Num.53 Str.155 Str.158;
let Str.159 : U64 = CallByName Str.61 Str.307 Str.157;
let Str.306 : U64 = 0i64;
inc Str.156;
inc Str.154;
let Str.288 : {U64, Str, U64, Str, U64, U64} = Struct {Str.159, Str.154, Str.155, Str.156, Str.306, Str.158};
let Str.287 : Int1 = CallByName Str.63 Str.288;
ret Str.287;
procedure Str.63 (Str.160):
let Str.166 : U64 = StructAtIndex 0 Str.160;
let Str.161 : Str = StructAtIndex 1 Str.160;
let Str.162 : U64 = StructAtIndex 2 Str.160;
let Str.163 : Str = StructAtIndex 3 Str.160;
let Str.164 : U64 = StructAtIndex 4 Str.160;
let Str.165 : U64 = StructAtIndex 5 Str.160;
let Str.167 : Int1 = CallByName Num.25 Str.162 Str.166;
if Str.167 then
dec Str.163;
dec Str.161;
let Str.168 : Int1 = CallByName Bool.11 Str.164 Str.165;
ret Str.168;
else
let Str.302 : U8 = CallByName Str.35 Str.161 Str.162;
let Str.303 : U8 = CallByName Str.35 Str.163 Str.164;
let Str.169 : Int1 = CallByName Bool.11 Str.302 Str.303;
let Str.292 : U64 = StructAtIndex 0 Str.160;
let Str.293 : Str = StructAtIndex 1 Str.160;
let Str.295 : Str = StructAtIndex 3 Str.160;
let Str.297 : U64 = StructAtIndex 5 Str.160;
let Str.301 : U64 = 1i64;
let Str.299 : U64 = CallByName Num.51 Str.164 Str.301;
let Str.300 : U64 = 1i64;
let Str.298 : U64 = CallByName Num.51 Str.162 Str.300;
let Str.291 : {U64, Str, U64, Str, U64, U64} = Struct {Str.292, Str.293, Str.298, Str.295, Str.299, Str.297};
let Str.170 : Int1 = CallByName Str.63 Str.291;
let Str.290 : Int1 = CallByName Bool.3 Str.169 Str.170;
ret Str.290;
let Str.429 : Int1 = CallByName Bool.1;
ret Str.429;
procedure Test.0 ():
let Test.4 : Str = "bar";

Some files were not shown because too many files have changed in this diff Show more