mirror of
https://github.com/roc-lang/roc.git
synced 2025-08-04 12:18:19 +00:00
Merge branch 'main' into fromutf-roc
This commit is contained in:
commit
17624a9d2c
161 changed files with 2323 additions and 1168 deletions
|
@ -1,9 +1,9 @@
|
|||
// For format details, see https://aka.ms/devcontainer.json. For config options, see the
|
||||
// README at: https://github.com/devcontainers/templates/tree/main/src/rust
|
||||
{
|
||||
"name": "Rust",
|
||||
"name": "roc-devcontainer",
|
||||
// Or use a Dockerfile or Docker Compose file. More info: https://containers.dev/guide/dockerfile
|
||||
"image": "mcr.microsoft.com/devcontainers/rust:1-1-bullseye",
|
||||
"image": "roclang/devcontainer:latest",
|
||||
|
||||
// Use 'mounts' to make the cargo cache persistent in a Docker Volume.
|
||||
// "mounts": [
|
||||
|
@ -15,20 +15,20 @@
|
|||
// ]
|
||||
|
||||
// Features to add to the dev container. More info: https://containers.dev/features.
|
||||
"features": {
|
||||
"ghcr.io/devcontainers-contrib/features/zig:1": {
|
||||
"version": "0.13.0"
|
||||
},
|
||||
"ghcr.io/devcontainers-community/features/llvm:3": {
|
||||
"version": 18
|
||||
}
|
||||
},
|
||||
// "features": {
|
||||
// "ghcr.io/devcontainers-contrib/features/zig:1": {
|
||||
// "version": "0.13.0"
|
||||
// },
|
||||
// "ghcr.io/devcontainers-community/features/llvm:3": {
|
||||
// "version": 18
|
||||
// }
|
||||
// },
|
||||
// Use 'forwardPorts' to make a list of ports inside the container available locally.
|
||||
// "forwardPorts": [],
|
||||
|
||||
// Use 'postCreateCommand' to run commands after the container is created.
|
||||
// "postCreateCommand": "rustc --version",
|
||||
"onCreateCommand": "sudo apt-get update && sudo apt-get install -y pkg-config libz-dev libzstd-dev valgrind"
|
||||
// "onCreateCommand": "sudo apt-get update && sudo apt-get install -y pkg-config libz-dev libzstd-dev valgrind"
|
||||
|
||||
// Configure tool-specific properties.
|
||||
// "customizations": {},
|
||||
|
|
4
.github/workflows/macos_x86_64.yml
vendored
4
.github/workflows/macos_x86_64.yml
vendored
|
@ -31,6 +31,4 @@ jobs:
|
|||
run: cargo nextest-gen-llvm --release --no-fail-fast --locked -E "package(test_gen) - test(gen_str::str_append_scalar)"
|
||||
|
||||
- name: regular rust tests
|
||||
run: cargo test --locked --release -- --skip opaque_wrap_function --skip gen_list::bool_list_literal --skip platform_switching_swift --skip swift_ui --skip gen_tags::phantom_polymorphic_record && sccache --show-stats
|
||||
# swift tests are skipped because of "Could not find or use auto-linked library 'swiftCompatibilityConcurrency'" on macos x86_64 CI machine
|
||||
# this issue may be caused by using older versions of XCode
|
||||
run: cargo test --locked --release -- --skip opaque_wrap_function --skip gen_list::bool_list_literal --skip gen_tags::phantom_polymorphic_record && sccache --show-stats
|
||||
|
|
4
.github/workflows/nightly_macos_x86_64.yml
vendored
4
.github/workflows/nightly_macos_x86_64.yml
vendored
|
@ -27,9 +27,7 @@ jobs:
|
|||
run: ./ci/write_version.sh
|
||||
|
||||
- name: execute rust tests
|
||||
run: cargo test --release --locked -- --skip opaque_wrap_function --skip gen_list::bool_list_literal --skip platform_switching_swift --skip swift_ui --skip gen_tags::phantom_polymorphic_record
|
||||
# swift tests are skipped because of "Could not find or use auto-linked library 'swiftCompatibilityConcurrency'" on macos x86_64 CI machine
|
||||
# this issue may be caused by using older versions of XCode
|
||||
run: cargo test --release --locked -- --skip opaque_wrap_function --skip gen_list::bool_list_literal --skip gen_tags::phantom_polymorphic_record
|
||||
|
||||
- name: build release
|
||||
run: RUSTFLAGS="-C target-cpu=x86-64" cargo build --profile=release-with-lto --locked --bin roc --bin roc_language_server
|
||||
|
|
|
@ -32,7 +32,7 @@ cheapest_open = \cost_fn, model ->
|
|||
)
|
||||
|> Quicksort.sort_by(.cost)
|
||||
|> List.first
|
||||
|> Result.map(.position)
|
||||
|> Result.map_ok(.position)
|
||||
|> Result.map_err(\_ -> {})
|
||||
|
||||
reconstruct_path : Dict position position, position -> List position where position implements Hash & Eq
|
||||
|
|
|
@ -12,7 +12,7 @@ main! = \{} ->
|
|||
closure1 : {} -> Result {} []
|
||||
closure1 = \_ ->
|
||||
Ok(foo(to_unit_borrowed, "a long string such that it's malloced"))
|
||||
|> Result.map(\_ -> {})
|
||||
|> Result.map_ok(\_ -> {})
|
||||
|
||||
to_unit_borrowed = \x -> Str.count_utf8_bytes(x)
|
||||
|
||||
|
@ -25,8 +25,8 @@ closure2 = \_ ->
|
|||
x = "a long string such that it's malloced"
|
||||
|
||||
Ok({})
|
||||
|> Result.map(\_ -> x)
|
||||
|> Result.map(to_unit)
|
||||
|> Result.map_ok(\_ -> x)
|
||||
|> Result.map_ok(to_unit)
|
||||
|
||||
to_unit = \_ -> {}
|
||||
|
||||
|
@ -37,7 +37,7 @@ closure3 = \_ ->
|
|||
x = "a long string such that it's malloced"
|
||||
|
||||
Ok({})
|
||||
|> Result.try(\_ -> Ok(x) |> Result.map(\_ -> {}))
|
||||
|> Result.try(\_ -> Ok(x) |> Result.map_ok(\_ -> {}))
|
||||
|
||||
# # ---
|
||||
closure4 : {} -> Result {} []
|
||||
|
@ -47,4 +47,4 @@ closure4 = \_ ->
|
|||
|
||||
Ok({})
|
||||
|> Result.try(\_ -> Ok(x))
|
||||
|> Result.map(\_ -> {})
|
||||
|> Result.map_ok(\_ -> {})
|
||||
|
|
|
@ -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
|
||||
|
@ -115,7 +115,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.
|
||||
|
|
|
@ -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(result, mapper), rest }
|
||||
map_result = |{ result, rest }, mapper| { result: Result.map_ok(result, mapper), rest }
|
||||
|
|
|
@ -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,13 +158,13 @@ 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)
|
||||
|
@ -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,10 +905,10 @@ 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
|
||||
|
@ -920,34 +920,34 @@ calc_shifts_for_size_helper = \shifts, 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
|
||||
|
@ -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,7 +1265,7 @@ expect
|
|||
List.walk(
|
||||
bad_keys,
|
||||
Bool.true,
|
||||
\acc, k ->
|
||||
|acc, k|
|
||||
acc && Dict.contains(dict, k),
|
||||
)
|
||||
|
||||
|
@ -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({})
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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,7 +1360,7 @@ 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 ->
|
||||
chunks_of = |list, chunk_size|
|
||||
if chunk_size == 0 || List.is_empty(list) then
|
||||
[]
|
||||
else
|
||||
|
@ -1368,7 +1368,7 @@ chunks_of = \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 ->
|
||||
Result.map(
|
||||
|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)
|
||||
|
|
|
@ -618,7 +618,7 @@ 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 } ->
|
||||
is_approx_eq = |x, y, { rtol ?? 0.00001, atol ?? 0.00000001 }|
|
||||
eq = x <= y && x >= y
|
||||
meets_tolerance = Num.abs_diff(x, y) <= Num.max(atol, (rtol * Num.max(Num.abs(x), Num.abs(y))))
|
||||
eq || meets_tolerance
|
||||
|
@ -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
|
||||
|
|
|
@ -2,7 +2,7 @@ module [
|
|||
Result,
|
||||
is_ok,
|
||||
is_err,
|
||||
map,
|
||||
map_ok,
|
||||
map_err,
|
||||
map_both,
|
||||
map2,
|
||||
|
@ -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
|
||||
|
@ -54,14 +54,14 @@ with_default = \result, default ->
|
|||
## function on it. Then returns a new `Ok` holding the transformed value. If the
|
||||
## result is `Err`, this has no effect. Use [map_err] to transform an `Err`.
|
||||
## ```roc
|
||||
## Result.map(Ok(12), Num.neg)
|
||||
## Result.map(Err("yipes!"), Num.neg)
|
||||
## Result.map_ok(Ok(12), Num.neg)
|
||||
## Result.map_ok(Err("yipes!"), Num.neg)
|
||||
## ```
|
||||
##
|
||||
## Functions like `map` are common in Roc; see for example [List.map],
|
||||
## `Set.map`, and `Dict.map`.
|
||||
map : Result a err, (a -> b) -> Result b err
|
||||
map = \result, transform ->
|
||||
map_ok : Result a err, (a -> b) -> Result b err
|
||||
map_ok = |result, transform|
|
||||
when result is
|
||||
Ok(v) -> Ok(transform(v))
|
||||
Err(e) -> Err(e)
|
||||
|
@ -74,14 +74,14 @@ map = \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)
|
||||
|
|
|
@ -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]))
|
||||
|
|
|
@ -541,7 +541,7 @@ to_utf8 : Str -> List U8
|
|||
## expect Str.from_utf8([255]) |> Result.is_err
|
||||
## ```
|
||||
from_utf8 : List U8 -> Result Str [BadUtf8 { problem : Utf8Problem, index : U64 }]
|
||||
from_utf8 = \bytes ->
|
||||
from_utf8 = |bytes|
|
||||
result = from_utf8_lowlevel bytes
|
||||
|
||||
if result.c_is_ok then
|
||||
|
@ -598,11 +598,11 @@ expect (Str.from_utf8_lossy [82, 0xED, 0xA0, 0xBD, 99]) == "R<>c"
|
|||
## 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 ->
|
||||
from_utf16 = |codeunits|
|
||||
mk_err = |problem, index|
|
||||
Err(BadUtf16({ problem, index }))
|
||||
|
||||
step = \state, unit ->
|
||||
step = |state, unit|
|
||||
c : U32
|
||||
c = Num.int_cast(unit)
|
||||
when state is
|
||||
|
@ -630,7 +630,7 @@ from_utf16 = \codeunits ->
|
|||
when decode_res is
|
||||
ExpectFirst(_, utf8) ->
|
||||
from_utf8(utf8)
|
||||
|> Result.map_err(\BadUtf8(err) -> BadUtf16(err))
|
||||
|> Result.map_err(|BadUtf8(err)| BadUtf16(err))
|
||||
|
||||
ExpectSecond(i, _, _) ->
|
||||
mk_err(EncodesSurrogateHalf, i)
|
||||
|
@ -653,14 +653,14 @@ expect Str.from_utf16([82, 0xdc96, 99]) == Err(BadUtf16({ index: 1, problem: Enc
|
|||
## expect Str.from_utf16_lossy([82, 0xdc96, 99]) == "R<>c"
|
||||
## ```
|
||||
from_utf16_lossy : List U16 -> Str
|
||||
from_utf16_lossy = \codeunits ->
|
||||
from_utf16_lossy = |codeunits|
|
||||
utf8_replacement = [0xef, 0xbf, 0xbd]
|
||||
encode_lossy = \utf8, c ->
|
||||
encode_lossy = |utf8, c|
|
||||
when encode_utf8(utf8, c) is
|
||||
Ok(utf8_next) -> utf8_next
|
||||
Err(_) -> List.concat(utf8, utf8_replacement)
|
||||
|
||||
step = \state, unit ->
|
||||
step = |state, unit|
|
||||
c : U32
|
||||
c = Num.int_cast(unit)
|
||||
when state is
|
||||
|
@ -704,8 +704,8 @@ expect Str.from_utf16_lossy([82, 0xdc96, 99]) == "R<>c"
|
|||
## ```
|
||||
|
||||
from_utf32 : List U32 -> Result Str [BadUtf32 { problem : Utf8Problem, index : U64 }]
|
||||
from_utf32 = \codepoints ->
|
||||
step = \state, c ->
|
||||
from_utf32 = |codepoints|
|
||||
step = |state, c|
|
||||
when state is
|
||||
Ok({ i, utf8 }) ->
|
||||
when encode_utf8(utf8, c) is
|
||||
|
@ -716,14 +716,14 @@ from_utf32 = \codepoints ->
|
|||
|
||||
List.walk(codepoints, Ok({ i: 0, utf8: [] }), step)
|
||||
|> Result.try(
|
||||
\state ->
|
||||
|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 ->
|
||||
encode_utf8 = |list, c|
|
||||
if c < 0x80 then
|
||||
Ok(List.append(list, Num.int_cast(c)))
|
||||
else if c < 0x800 then
|
||||
|
@ -782,8 +782,8 @@ expect Str.from_utf32([82, 0x110000, 99]) |> Result.is_err
|
|||
## expect Str.from_utf32_lossy([82, 0x110000, 99]) == "R<>c"
|
||||
## ```
|
||||
from_utf32_lossy : List U32 -> Str
|
||||
from_utf32_lossy = \codepoints ->
|
||||
step = \utf8, c ->
|
||||
from_utf32_lossy = |codepoints|
|
||||
step = |utf8, c|
|
||||
when encode_utf8(utf8, c) is
|
||||
Ok(utf8_next) -> utf8_next
|
||||
# utf-8 encoded replacement character
|
||||
|
@ -836,7 +836,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
|
||||
|
@ -846,7 +846,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
|
||||
|
@ -856,7 +856,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
|
||||
|
@ -868,7 +868,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
|
||||
|
@ -881,7 +881,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
|
||||
|
@ -893,7 +893,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
|
||||
|
@ -905,7 +905,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
|
||||
|
@ -917,7 +917,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
|
||||
|
@ -929,7 +929,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.
|
||||
|
@ -940,7 +940,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
|
||||
|
@ -952,7 +952,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.
|
||||
|
@ -963,7 +963,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
|
||||
|
@ -974,7 +974,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
|
||||
|
@ -996,7 +996,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
|
||||
|
@ -1009,7 +1009,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
|
||||
|
@ -1030,7 +1030,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}"
|
||||
|
@ -1048,7 +1048,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}"
|
||||
|
@ -1066,7 +1066,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
|
||||
|
@ -1095,7 +1095,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)
|
||||
|
@ -1103,7 +1103,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)
|
||||
|
@ -1120,7 +1120,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
|
||||
|
@ -1146,7 +1146,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)
|
||||
|
@ -1154,7 +1154,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
|
||||
|
@ -1165,10 +1165,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)
|
||||
|
@ -1184,7 +1184,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
|
||||
|
||||
|
@ -1216,11 +1216,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)
|
||||
|
@ -1241,11 +1241,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)
|
||||
|
@ -1264,7 +1264,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)
|
||||
|
||||
|
@ -1278,7 +1278,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
|
||||
|
@ -1287,7 +1287,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
|
||||
|
@ -1300,7 +1300,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)
|
||||
|
@ -1317,7 +1317,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))
|
||||
|
|
|
@ -5,7 +5,7 @@ use roc_collections::{ImMap, MutSet, SendMap, VecMap, VecSet};
|
|||
use roc_module::ident::{Ident, IdentSuffix, Lowercase, TagName};
|
||||
use roc_module::symbol::Symbol;
|
||||
use roc_parse::ast::{
|
||||
AssignedField, ExtractSpaces, FunctionArrow, Pattern, Tag, TypeAnnotation, TypeHeader,
|
||||
AssignedField, ExtractSpaces, FunctionArrow, Tag, TypeAnnotation, TypeHeader, TypeVar,
|
||||
};
|
||||
use roc_problem::can::{Problem, ShadowKind};
|
||||
use roc_region::all::{Loc, Region};
|
||||
|
@ -754,9 +754,7 @@ fn can_annotation_help(
|
|||
|
||||
for loc_var in *loc_vars {
|
||||
let var = match loc_var.value {
|
||||
Pattern::Identifier { ident: name, .. }
|
||||
if name.chars().next().unwrap().is_lowercase() =>
|
||||
{
|
||||
TypeVar::Identifier(name) if name.chars().next().unwrap().is_lowercase() => {
|
||||
name
|
||||
}
|
||||
_ => unreachable!("I thought this was validated during parsing"),
|
||||
|
|
|
@ -2858,7 +2858,7 @@ fn to_pending_alias_or_opaque<'a>(
|
|||
env: &mut Env<'a>,
|
||||
scope: &mut Scope,
|
||||
name: &'a Loc<&'a str>,
|
||||
vars: &'a [Loc<ast::Pattern<'a>>],
|
||||
vars: &'a [Loc<ast::TypeVar<'a>>],
|
||||
ann: &'a Loc<ast::TypeAnnotation<'a>>,
|
||||
opt_derived: Option<&'a ast::ImplementsAbilities<'a>>,
|
||||
kind: AliasKind,
|
||||
|
@ -2870,10 +2870,9 @@ fn to_pending_alias_or_opaque<'a>(
|
|||
let mut can_rigids: Vec<Loc<Lowercase>> = Vec::with_capacity(vars.len());
|
||||
|
||||
for loc_var in vars.iter() {
|
||||
match loc_var.value {
|
||||
ast::Pattern::Identifier { ident: name, .. }
|
||||
if name.chars().next().unwrap().is_lowercase() =>
|
||||
{
|
||||
match loc_var.value.extract_spaces().item {
|
||||
ast::TypeVar::Identifier(name) => {
|
||||
debug_assert!(name.chars().next().unwrap().is_lowercase());
|
||||
let lowercase = Lowercase::from(name);
|
||||
can_rigids.push(Loc {
|
||||
value: lowercase,
|
||||
|
@ -2881,7 +2880,6 @@ fn to_pending_alias_or_opaque<'a>(
|
|||
});
|
||||
}
|
||||
_ => {
|
||||
// any other pattern in this position is a syntax error.
|
||||
let problem = Problem::InvalidAliasRigid {
|
||||
alias_name: symbol,
|
||||
region: loc_var.region,
|
||||
|
|
|
@ -4,7 +4,7 @@ use crate::env::Env;
|
|||
use crate::scope::Scope;
|
||||
use bumpalo::collections::Vec;
|
||||
use roc_error_macros::internal_error;
|
||||
use roc_module::called_via::BinOp::{DoubleQuestion, Pizza};
|
||||
use roc_module::called_via::BinOp::{DoubleQuestion, Pizza, SingleQuestion};
|
||||
use roc_module::called_via::{BinOp, CalledVia};
|
||||
use roc_module::ident::ModuleName;
|
||||
use roc_parse::ast::Expr::{self, *};
|
||||
|
@ -213,62 +213,132 @@ fn new_op_call_expr<'a>(
|
|||
let left = desugar_expr(env, scope, left);
|
||||
let right = desugar_expr(env, scope, right);
|
||||
|
||||
let mut branches = Vec::with_capacity_in(2, env.arena);
|
||||
let mut branch_1_patts = Vec::with_capacity_in(1, env.arena);
|
||||
let mut branch_1_patts_args = Vec::with_capacity_in(1, env.arena);
|
||||
let success_var = env.arena.alloc_str(
|
||||
format!(
|
||||
"success_BRANCH1_{}_{}",
|
||||
left.region.start().offset,
|
||||
left.region.end().offset
|
||||
)
|
||||
.as_str(),
|
||||
);
|
||||
branch_1_patts_args.push(Loc::at(
|
||||
left.region,
|
||||
Pattern::Identifier { ident: success_var },
|
||||
let ok_var = env.arena.alloc_str(&format!(
|
||||
"#double_question_ok_{}_{}",
|
||||
left.region.start().offset,
|
||||
left.region.end().offset
|
||||
));
|
||||
let branch_1_tag: &Loc<Pattern<'a>> =
|
||||
env.arena.alloc(Loc::at(left.region, Pattern::Tag("Ok")));
|
||||
branch_1_patts.push(Loc::at(
|
||||
|
||||
let ok_branch_pattern_args = env
|
||||
.arena
|
||||
.alloc([Loc::at(left.region, Pattern::Identifier { ident: ok_var })]);
|
||||
let ok_branch_patterns = env.arena.alloc([Loc::at(
|
||||
left.region,
|
||||
Pattern::PncApply(
|
||||
branch_1_tag,
|
||||
Collection::with_items(branch_1_patts_args.into_bump_slice()),
|
||||
env.arena.alloc(Loc::at(left.region, Pattern::Tag("Ok"))),
|
||||
Collection::with_items(ok_branch_pattern_args),
|
||||
),
|
||||
));
|
||||
let branch_one: &WhenBranch<'_> = env.arena.alloc(WhenBranch {
|
||||
patterns: branch_1_patts.into_bump_slice(),
|
||||
)]);
|
||||
let ok_branch = &*env.arena.alloc(WhenBranch {
|
||||
patterns: ok_branch_patterns,
|
||||
value: Loc::at(
|
||||
left.region,
|
||||
Expr::Var {
|
||||
module_name: "",
|
||||
ident: success_var,
|
||||
ident: ok_var,
|
||||
},
|
||||
),
|
||||
guard: None,
|
||||
});
|
||||
branches.push(branch_one);
|
||||
let mut branch_2_patts = Vec::with_capacity_in(1, env.arena);
|
||||
let mut branch_2_patts_args = Vec::with_capacity_in(1, env.arena);
|
||||
branch_2_patts_args.push(Loc::at(right.region, Pattern::Underscore("")));
|
||||
let branch_2_tag: &Loc<Pattern<'a>> =
|
||||
env.arena.alloc(Loc::at(left.region, Pattern::Tag("Err")));
|
||||
branch_2_patts.push(Loc::at(
|
||||
|
||||
let err_branch_pattern_args = env
|
||||
.arena
|
||||
.alloc([(Loc::at(right.region, Pattern::Underscore("")))]);
|
||||
let err_branch_patterns = env.arena.alloc([Loc::at(
|
||||
right.region,
|
||||
Pattern::PncApply(
|
||||
branch_2_tag,
|
||||
Collection::with_items(branch_2_patts_args.into_bump_slice()),
|
||||
env.arena.alloc(Loc::at(left.region, Pattern::Tag("Err"))),
|
||||
Collection::with_items(err_branch_pattern_args),
|
||||
),
|
||||
));
|
||||
let branch_two: &WhenBranch<'_> = env.arena.alloc(WhenBranch {
|
||||
patterns: branch_2_patts.into_bump_slice(),
|
||||
)]);
|
||||
let err_branch = &*env.arena.alloc(WhenBranch {
|
||||
patterns: err_branch_patterns,
|
||||
value: *right,
|
||||
guard: None,
|
||||
});
|
||||
branches.push(branch_two);
|
||||
|
||||
When(left, branches.into_bump_slice())
|
||||
When(left, &*env.arena.alloc([ok_branch, err_branch]))
|
||||
}
|
||||
SingleQuestion => {
|
||||
let left = desugar_expr(env, scope, left);
|
||||
let right = desugar_expr(env, scope, right);
|
||||
|
||||
let ok_var = env.arena.alloc_str(&format!(
|
||||
"#single_question_ok_{}_{}",
|
||||
left.region.start().offset,
|
||||
left.region.end().offset
|
||||
));
|
||||
|
||||
let ok_branch_pattern_args = env
|
||||
.arena
|
||||
.alloc([Loc::at(left.region, Pattern::Identifier { ident: ok_var })]);
|
||||
let ok_branch_patterns = env.arena.alloc([Loc::at(
|
||||
left.region,
|
||||
Pattern::PncApply(
|
||||
env.arena.alloc(Loc::at(left.region, Pattern::Tag("Ok"))),
|
||||
Collection::with_items(ok_branch_pattern_args),
|
||||
),
|
||||
)]);
|
||||
let ok_branch = &*env.arena.alloc(WhenBranch {
|
||||
patterns: ok_branch_patterns,
|
||||
value: Loc::at(
|
||||
left.region,
|
||||
Expr::Var {
|
||||
module_name: "",
|
||||
ident: ok_var,
|
||||
},
|
||||
),
|
||||
guard: None,
|
||||
});
|
||||
|
||||
let err_var = env.arena.alloc_str(&format!(
|
||||
"#single_question_err_{}_{}",
|
||||
left.region.start().offset,
|
||||
left.region.end().offset
|
||||
));
|
||||
|
||||
let err_branch_pattern_args = env
|
||||
.arena
|
||||
.alloc([(Loc::at(right.region, Pattern::Identifier { ident: err_var }))]);
|
||||
let err_branch_patterns = env.arena.alloc([Loc::at(
|
||||
right.region,
|
||||
Pattern::PncApply(
|
||||
env.arena.alloc(Loc::at(left.region, Pattern::Tag("Err"))),
|
||||
Collection::with_items(err_branch_pattern_args),
|
||||
),
|
||||
)]);
|
||||
let map_err_expr = &*env.arena.alloc(Loc::at(
|
||||
right.region,
|
||||
Expr::PncApply(
|
||||
right,
|
||||
Collection::with_items(&*env.arena.alloc([&*env.arena.alloc(Loc::at(
|
||||
left.region,
|
||||
Expr::Var {
|
||||
module_name: "",
|
||||
ident: err_var,
|
||||
},
|
||||
))])),
|
||||
),
|
||||
));
|
||||
let err_branch = &*env.arena.alloc(WhenBranch {
|
||||
patterns: err_branch_patterns,
|
||||
value: Loc::at(
|
||||
region,
|
||||
Expr::Return(
|
||||
env.arena.alloc(Loc::at(
|
||||
region,
|
||||
Expr::PncApply(
|
||||
env.arena.alloc(Loc::at(region, Expr::Tag("Err"))),
|
||||
Collection::with_items(&*env.arena.alloc([map_err_expr])),
|
||||
),
|
||||
)),
|
||||
None,
|
||||
),
|
||||
),
|
||||
guard: None,
|
||||
});
|
||||
|
||||
When(left, &*env.arena.alloc([ok_branch, err_branch]))
|
||||
}
|
||||
binop => {
|
||||
let left = desugar_expr(env, scope, left);
|
||||
|
@ -1357,6 +1427,7 @@ fn desugar_pattern<'a>(env: &mut Env<'a>, scope: &mut Scope, pattern: Pattern<'a
|
|||
| ListRest(_)
|
||||
| Malformed(_)
|
||||
| MalformedIdent(_, _)
|
||||
| MalformedExpr(_)
|
||||
| QualifiedIdentifier { .. } => pattern,
|
||||
|
||||
Apply(tag, arg_patterns) => {
|
||||
|
@ -1574,6 +1645,7 @@ fn binop_to_function(binop: BinOp) -> (&'static str, &'static str) {
|
|||
Or => (ModuleName::BOOL, "or"),
|
||||
Pizza => unreachable!("Cannot desugar the |> operator"),
|
||||
DoubleQuestion => unreachable!("Cannot desugar the ?? operator"),
|
||||
SingleQuestion => unreachable!("Cannot desugar the ? operator"),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -817,7 +817,7 @@ pub fn canonicalize_pattern<'a>(
|
|||
}
|
||||
}
|
||||
|
||||
Malformed(_str) => {
|
||||
Malformed(_) | MalformedExpr(_) => {
|
||||
let problem = MalformedPatternProblem::Unknown;
|
||||
malformed_pattern(env, problem, region)
|
||||
}
|
||||
|
|
|
@ -284,7 +284,7 @@ mod test_can {
|
|||
let src = indoc!(
|
||||
r"
|
||||
f : Num.Int * -> Num.Int *
|
||||
f = \ a -> a
|
||||
f = | a| a
|
||||
|
||||
f
|
||||
"
|
||||
|
@ -300,7 +300,7 @@ mod test_can {
|
|||
let src = indoc!(
|
||||
r"
|
||||
f : Num.Int * -> Num.Int * # comment
|
||||
f = \ a -> a
|
||||
f = | a| a
|
||||
|
||||
f
|
||||
"
|
||||
|
@ -316,7 +316,7 @@ mod test_can {
|
|||
let src = indoc!(
|
||||
r"
|
||||
f : Num.Int * -> Num.Int *
|
||||
g = \ a -> a
|
||||
g = | a| a
|
||||
|
||||
g
|
||||
"
|
||||
|
@ -344,7 +344,7 @@ mod test_can {
|
|||
let src = indoc!(
|
||||
r"
|
||||
f : Num.Int * -> Num.Int * # comment
|
||||
g = \ a -> a
|
||||
g = | a| a
|
||||
|
||||
g
|
||||
"
|
||||
|
@ -373,7 +373,7 @@ mod test_can {
|
|||
r"
|
||||
f : Num.Int * -> Num.Int *
|
||||
|
||||
f = \ a -> a
|
||||
f = | a| a
|
||||
|
||||
f 42
|
||||
"
|
||||
|
@ -390,7 +390,7 @@ mod test_can {
|
|||
r"
|
||||
f : Num.Int * -> Num.Int *
|
||||
# comment
|
||||
f = \ a -> a
|
||||
f = | a| a
|
||||
|
||||
f 42
|
||||
"
|
||||
|
@ -677,8 +677,8 @@ mod test_can {
|
|||
fn record_builder_desugar() {
|
||||
let src = indoc!(
|
||||
r#"
|
||||
map2 = \a, b, combine -> combine a b
|
||||
double = \n -> n * 2
|
||||
map2 = |a, b, combine| combine a b
|
||||
double = |n| n * 2
|
||||
|
||||
c = 3
|
||||
|
||||
|
@ -972,10 +972,10 @@ mod test_can {
|
|||
}
|
||||
|
||||
#[test]
|
||||
fn try_desugar_double_question_suffix() {
|
||||
fn try_desugar_double_question_binop() {
|
||||
let src = indoc!(
|
||||
r#"
|
||||
Str.to_u64 "123" ?? Num.max_u64
|
||||
Str.to_u64("123") ?? Num.max_u64
|
||||
"#
|
||||
);
|
||||
let arena = Bump::new();
|
||||
|
@ -985,33 +985,95 @@ 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
|
||||
// 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(&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",
|
||||
"#double_question_ok_0_17",
|
||||
&out.interns,
|
||||
);
|
||||
assert_var_usage(
|
||||
&branches[0].value.value,
|
||||
"success_BRANCH1_0_16",
|
||||
"#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 try_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(&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 try_desugar_works_elsewhere() {
|
||||
let src = indoc!(
|
||||
|
@ -1192,6 +1254,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 {
|
||||
|
@ -1241,20 +1310,20 @@ mod test_can {
|
|||
fn recognize_tail_calls() {
|
||||
let src = indoc!(
|
||||
r"
|
||||
g = \x ->
|
||||
g = |x|
|
||||
when x is
|
||||
0 -> 0
|
||||
_ -> g (x - 1)
|
||||
|
||||
# use parens to force the ordering!
|
||||
(
|
||||
h = \x ->
|
||||
h = |x|
|
||||
when x is
|
||||
0 -> 0
|
||||
_ -> g (x - 1)
|
||||
|
||||
(
|
||||
p = \x ->
|
||||
p = |x|
|
||||
when x is
|
||||
0 -> 0
|
||||
1 -> g (x - 1)
|
||||
|
@ -1342,7 +1411,7 @@ mod test_can {
|
|||
fn when_tail_call() {
|
||||
let src = indoc!(
|
||||
r"
|
||||
g = \x ->
|
||||
g = |x|
|
||||
when x is
|
||||
0 -> 0
|
||||
_ -> g (x + 1)
|
||||
|
@ -1364,7 +1433,7 @@ mod test_can {
|
|||
fn immediate_tail_call() {
|
||||
let src = indoc!(
|
||||
r"
|
||||
f = \x -> f x
|
||||
f = |x| f x
|
||||
|
||||
f 0
|
||||
"
|
||||
|
@ -1385,7 +1454,7 @@ mod test_can {
|
|||
fn when_condition_is_no_tail_call() {
|
||||
let src = indoc!(
|
||||
r"
|
||||
q = \x ->
|
||||
q = |x|
|
||||
when q x is
|
||||
_ -> 0
|
||||
|
||||
|
@ -1406,12 +1475,12 @@ mod test_can {
|
|||
fn good_mutual_recursion() {
|
||||
let src = indoc!(
|
||||
r"
|
||||
q = \x ->
|
||||
q = |x|
|
||||
when x is
|
||||
0 -> 0
|
||||
_ -> p (x - 1)
|
||||
|
||||
p = \x ->
|
||||
p = |x|
|
||||
when x is
|
||||
0 -> 0
|
||||
_ -> q (x - 1)
|
||||
|
@ -1437,7 +1506,7 @@ mod test_can {
|
|||
fn valid_self_recursion() {
|
||||
let src = indoc!(
|
||||
r"
|
||||
boom = \_ -> boom {}
|
||||
boom = |_| boom {}
|
||||
|
||||
boom
|
||||
"
|
||||
|
@ -1548,7 +1617,7 @@ mod test_can {
|
|||
r"
|
||||
fallbackZ = 3
|
||||
|
||||
fn = \{ x, y, z ? fallbackZ } ->
|
||||
fn = |{ x, y, z ? fallbackZ }|
|
||||
{ x, y, z }
|
||||
|
||||
fn { x: 0, y: 1 }
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
use crate::{
|
||||
collection::{fmt_collection, Braces},
|
||||
expr::merge_spaces_conservative,
|
||||
expr::{expr_lift_spaces, expr_lift_spaces_after, expr_prec, merge_spaces_conservative},
|
||||
node::{
|
||||
parens_around_node, DelimitedItem, Item, Node, NodeInfo, NodeSequenceBuilder, Nodify, Prec,
|
||||
Sp,
|
||||
},
|
||||
pattern::{pattern_lift_spaces_after, snakify_camel_ident},
|
||||
pattern::snakify_camel_ident,
|
||||
spaces::{fmt_comments_only, fmt_spaces, NewlineAt, INDENT},
|
||||
Buf, MigrationFlags,
|
||||
};
|
||||
|
@ -13,7 +13,6 @@ use bumpalo::{
|
|||
collections::{String, Vec},
|
||||
Bump,
|
||||
};
|
||||
use roc_parse::{ast::Spaced, ident::UppercaseIdent};
|
||||
use roc_parse::{
|
||||
ast::{
|
||||
AbilityImpls, AssignedField, Collection, CommentOrNewline, Expr, ExtractSpaces,
|
||||
|
@ -22,6 +21,10 @@ use roc_parse::{
|
|||
},
|
||||
expr::merge_spaces,
|
||||
};
|
||||
use roc_parse::{
|
||||
ast::{Spaced, TypeVar},
|
||||
ident::UppercaseIdent,
|
||||
};
|
||||
use roc_region::all::Loc;
|
||||
|
||||
/// Does an AST node need parens around it?
|
||||
|
@ -966,7 +969,7 @@ pub fn type_head_lift_spaces_after<'a, 'b: 'a>(
|
|||
) -> SpacesAfter<'a, TypeHeader<'a>> {
|
||||
let new_vars = arena.alloc_slice_copy(header.vars);
|
||||
let after = if let Some(last) = new_vars.last_mut() {
|
||||
let lifted = pattern_lift_spaces_after(arena, &last.value);
|
||||
let lifted = type_var_lift_spaces_after(arena, last.value);
|
||||
last.value = lifted.item;
|
||||
lifted.after
|
||||
} else {
|
||||
|
@ -981,6 +984,135 @@ pub fn type_head_lift_spaces_after<'a, 'b: 'a>(
|
|||
}
|
||||
}
|
||||
|
||||
fn type_var_lift_spaces_after<'a, 'b: 'a>(
|
||||
arena: &'a Bump,
|
||||
var: TypeVar<'b>,
|
||||
) -> SpacesAfter<'a, TypeVar<'a>> {
|
||||
match var {
|
||||
item @ TypeVar::Identifier(_) => SpacesAfter { item, after: &[] },
|
||||
TypeVar::Malformed(expr) => {
|
||||
let lifted = expr_lift_spaces_after(Parens::NotNeeded, arena, expr);
|
||||
SpacesAfter {
|
||||
item: TypeVar::Malformed(arena.alloc(lifted.item)),
|
||||
after: lifted.after,
|
||||
}
|
||||
}
|
||||
TypeVar::SpaceBefore(inner, spaces) => {
|
||||
let lifted = type_var_lift_spaces_after(arena, *inner);
|
||||
SpacesAfter {
|
||||
item: TypeVar::SpaceBefore(arena.alloc(lifted.item), spaces),
|
||||
after: lifted.after,
|
||||
}
|
||||
}
|
||||
TypeVar::SpaceAfter(inner, spaces) => {
|
||||
let mut lifted = type_var_lift_spaces_after(arena, *inner);
|
||||
lifted.after = merge_spaces_conservative(arena, lifted.after, spaces);
|
||||
lifted
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Formattable for TypeHeader<'a> {
|
||||
fn is_multiline(&self) -> bool {
|
||||
self.vars.iter().any(|v| v.is_multiline())
|
||||
}
|
||||
|
||||
fn format_with_options(
|
||||
&self,
|
||||
buf: &mut Buf,
|
||||
_parens: Parens,
|
||||
_newlines: Newlines,
|
||||
indent: u16,
|
||||
) {
|
||||
let node = self.to_node(buf.text.bump(), buf.flags());
|
||||
node.format(buf, indent);
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Formattable for TypeVar<'a> {
|
||||
fn is_multiline(&self) -> bool {
|
||||
match self {
|
||||
TypeVar::Identifier(_) => false,
|
||||
TypeVar::Malformed(expr) => expr.is_multiline(),
|
||||
TypeVar::SpaceBefore(inner, spaces) | TypeVar::SpaceAfter(inner, spaces) => {
|
||||
inner.is_multiline() || !spaces.is_empty()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn format_with_options(
|
||||
&self,
|
||||
buf: &mut Buf,
|
||||
_parens: Parens,
|
||||
_newlines: Newlines,
|
||||
indent: u16,
|
||||
) {
|
||||
let node = self.to_node(buf.text.bump(), buf.flags());
|
||||
node.format(buf, indent);
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Nodify<'a> for TypeHeader<'a> {
|
||||
fn to_node<'b>(&'a self, arena: &'b Bump, flags: MigrationFlags) -> NodeInfo<'b>
|
||||
where
|
||||
'a: 'b,
|
||||
{
|
||||
NodeInfo::apply(
|
||||
arena,
|
||||
NodeInfo::item(Node::Literal(self.name.value)),
|
||||
self.vars.iter().map(|v| v.value.to_node(arena, flags)),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Nodify<'a> for TypeVar<'a> {
|
||||
fn to_node<'b>(&'a self, arena: &'b Bump, flags: MigrationFlags) -> NodeInfo<'b>
|
||||
where
|
||||
'a: 'b,
|
||||
{
|
||||
match self {
|
||||
TypeVar::SpaceBefore(inner, spaces) => {
|
||||
let mut inner = inner.to_node(arena, flags);
|
||||
inner.before = merge_spaces_conservative(arena, spaces, inner.before);
|
||||
inner
|
||||
}
|
||||
TypeVar::SpaceAfter(inner, spaces) => {
|
||||
let mut inner = inner.to_node(arena, flags);
|
||||
inner.after = merge_spaces_conservative(arena, inner.after, spaces);
|
||||
inner
|
||||
}
|
||||
TypeVar::Identifier(text) => {
|
||||
let var_name = if flags.snakify {
|
||||
let mut buf = Buf::new_in(arena, flags);
|
||||
buf.indent(0); // Take out of beginning of line
|
||||
snakify_camel_ident(&mut buf, text);
|
||||
let s: &str = arena.alloc_str(buf.as_str());
|
||||
s
|
||||
} else {
|
||||
text
|
||||
};
|
||||
let item = NodeInfo::item(Node::Literal(var_name));
|
||||
|
||||
if *text == "implements" {
|
||||
parens_around_node(arena, item, false)
|
||||
} else {
|
||||
item
|
||||
}
|
||||
}
|
||||
TypeVar::Malformed(expr) => {
|
||||
let lifted = expr_lift_spaces(Parens::InApply, arena, expr);
|
||||
NodeInfo {
|
||||
before: lifted.before,
|
||||
node: Node::Expr(lifted.item),
|
||||
after: lifted.after,
|
||||
needs_indent: true,
|
||||
prec: expr_prec(**expr),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Nodify<'a> for TypeAnnotation<'a> {
|
||||
fn to_node<'b>(&'a self, arena: &'b Bump, flags: MigrationFlags) -> NodeInfo<'b>
|
||||
where
|
||||
|
|
|
@ -7,20 +7,19 @@ 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,
|
||||
};
|
||||
use crate::node::{NodeInfo, Nodify};
|
||||
use crate::pattern::{pattern_apply_to_node, pattern_fmt_apply};
|
||||
use crate::pattern::{pattern_lift_spaces, pattern_lift_spaces_before};
|
||||
use crate::node::Nodify;
|
||||
use crate::pattern::pattern_lift_spaces_before;
|
||||
use crate::spaces::{
|
||||
fmt_comments_only, fmt_default_newline, fmt_default_spaces, fmt_spaces, NewlineAt, INDENT,
|
||||
};
|
||||
use crate::{Buf, MigrationFlags};
|
||||
use crate::Buf;
|
||||
use bumpalo::Bump;
|
||||
use roc_error_macros::internal_error;
|
||||
use roc_parse::ast::{
|
||||
AbilityMember, Defs, Expr, ExtractSpaces, ImportAlias, ImportAsKeyword, ImportExposingKeyword,
|
||||
ImportedModuleName, IngestedFileAnnotation, IngestedFileImport, ModuleImport,
|
||||
ModuleImportParams, Pattern, Spaces, SpacesBefore, StrLiteral, TypeAnnotation, TypeDef,
|
||||
TypeHeader, ValueDef,
|
||||
ValueDef,
|
||||
};
|
||||
use roc_parse::expr::merge_spaces;
|
||||
use roc_parse::header::Keyword;
|
||||
|
@ -474,8 +473,8 @@ impl<'a> Formattable for TypeDef<'a> {
|
|||
loc_implements,
|
||||
members,
|
||||
} => {
|
||||
let header_lifted = type_head_lift_spaces(buf.text.bump(), *header);
|
||||
header_lifted.item.format(buf, indent);
|
||||
let header_lifted = header.to_node(buf.text.bump(), buf.flags());
|
||||
header_lifted.node.format(buf, indent);
|
||||
let implements = loc_implements.value.extract_spaces();
|
||||
let before_implements = merge_spaces_conservative(
|
||||
buf.text.bump(),
|
||||
|
@ -532,57 +531,6 @@ impl<'a> Formattable for TypeDef<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'a> Formattable for TypeHeader<'a> {
|
||||
fn is_multiline(&self) -> bool {
|
||||
self.vars.iter().any(|v| v.is_multiline())
|
||||
}
|
||||
|
||||
fn format_with_options(
|
||||
&self,
|
||||
buf: &mut Buf,
|
||||
_parens: Parens,
|
||||
_newlines: Newlines,
|
||||
indent: u16,
|
||||
) {
|
||||
let old_flags = buf.flags;
|
||||
buf.flags = MigrationFlags {
|
||||
parens_and_commas: false,
|
||||
..old_flags
|
||||
};
|
||||
pattern_fmt_apply(
|
||||
buf,
|
||||
Pattern::Tag(self.name.value),
|
||||
self.vars,
|
||||
Parens::NotNeeded,
|
||||
indent,
|
||||
self.vars.iter().any(|v| v.is_multiline()),
|
||||
false,
|
||||
None,
|
||||
);
|
||||
buf.flags = old_flags;
|
||||
}
|
||||
}
|
||||
|
||||
fn type_head_lift_spaces<'a, 'b: 'a>(
|
||||
arena: &'a Bump,
|
||||
head: TypeHeader<'b>,
|
||||
) -> Spaces<'a, Pattern<'a>> {
|
||||
let pat = Pattern::Apply(
|
||||
arena.alloc(Loc::at(head.name.region, Pattern::Tag(head.name.value))),
|
||||
head.vars,
|
||||
);
|
||||
|
||||
pattern_lift_spaces(arena, &pat)
|
||||
}
|
||||
|
||||
impl<'a> Nodify<'a> for TypeHeader<'a> {
|
||||
fn to_node<'b>(&'a self, arena: &'b Bump, _flags: MigrationFlags) -> NodeInfo<'b>
|
||||
where
|
||||
'a: 'b,
|
||||
{
|
||||
pattern_apply_to_node(arena, Pattern::Tag(self.name.value), self.vars)
|
||||
}
|
||||
}
|
||||
impl<'a> Formattable for ModuleImport<'a> {
|
||||
fn is_multiline(&self) -> bool {
|
||||
let Self {
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
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::node::Prec;
|
||||
use crate::pattern::{
|
||||
fmt_pattern, pattern_lift_spaces, snakify_camel_ident, starts_with_inline_comment,
|
||||
};
|
||||
|
@ -962,6 +963,7 @@ fn push_op(buf: &mut Buf, op: BinOp) {
|
|||
called_via::BinOp::Or => buf.push_str("||"),
|
||||
called_via::BinOp::Pizza => buf.push_str("|>"),
|
||||
called_via::BinOp::DoubleQuestion => buf.push_str("??"),
|
||||
called_via::BinOp::SingleQuestion => buf.push_str("?"),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1390,11 +1392,7 @@ pub fn expr_lift_spaces<'a, 'b: 'a>(
|
|||
before: &[],
|
||||
item: *expr,
|
||||
after: &[],
|
||||
}, // _ => Spaces {
|
||||
// before: &[],
|
||||
// item: *expr,
|
||||
// after: &[],
|
||||
// },
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1410,6 +1408,54 @@ pub fn expr_lift_spaces_before<'a, 'b: 'a>(
|
|||
}
|
||||
}
|
||||
|
||||
pub fn expr_prec(expr: Expr<'_>) -> Prec {
|
||||
match expr {
|
||||
Expr::Float(_)
|
||||
| Expr::Num(_)
|
||||
| Expr::NonBase10Int { .. }
|
||||
| Expr::Str(_)
|
||||
| Expr::SingleQuote(_)
|
||||
| Expr::AccessorFunction(_)
|
||||
| Expr::RecordUpdater(_)
|
||||
| Expr::Var { .. }
|
||||
| Expr::Underscore(_)
|
||||
| Expr::Crash
|
||||
| Expr::Tag(_)
|
||||
| Expr::OpaqueRef(_)
|
||||
| Expr::Dbg
|
||||
| Expr::Try
|
||||
| Expr::MalformedIdent(_, _)
|
||||
| Expr::EmptyRecordBuilder(_)
|
||||
| Expr::SingleFieldRecordBuilder(_)
|
||||
| Expr::RecordAccess(_, _)
|
||||
| Expr::TupleAccess(_, _)
|
||||
| Expr::TrySuffix { .. }
|
||||
| Expr::List(_)
|
||||
| Expr::RecordUpdate { .. }
|
||||
| Expr::Record(_)
|
||||
| Expr::Tuple(_)
|
||||
| Expr::RecordBuilder { .. }
|
||||
| Expr::LowLevelTry(_, _)
|
||||
| Expr::LowLevelDbg(_, _, _)
|
||||
| Expr::PncApply(_, _)
|
||||
| Expr::OptionalFieldInRecordBuilder(_, _) => Prec::Term,
|
||||
|
||||
Expr::Closure(_, _)
|
||||
| Expr::Defs(_, _)
|
||||
| Expr::DbgStmt { .. }
|
||||
| Expr::Apply(_, _, _)
|
||||
| Expr::BinOps(_, _)
|
||||
| Expr::UnaryOp(_, _)
|
||||
| Expr::If { .. }
|
||||
| Expr::When(_, _)
|
||||
| Expr::Return(_, _)
|
||||
| Expr::SpaceBefore(_, _)
|
||||
| Expr::SpaceAfter(_, _)
|
||||
| Expr::ParensAround(_)
|
||||
| Expr::PrecedenceConflict(_) => Prec::Apply,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn expr_lift_spaces_after<'a, 'b: 'a>(
|
||||
parens: Parens,
|
||||
arena: &'a Bump,
|
||||
|
@ -1969,7 +2015,7 @@ fn fmt_closure<'a>(
|
|||
use self::Expr::*;
|
||||
|
||||
buf.indent(indent);
|
||||
buf.push('\\');
|
||||
buf.push('|');
|
||||
|
||||
let arguments_are_multiline = loc_patterns
|
||||
.iter()
|
||||
|
@ -2017,12 +2063,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();
|
||||
|
@ -2239,7 +2283,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,
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
use bumpalo::{collections::Vec, Bump};
|
||||
use roc_parse::ast::{CommentOrNewline, Pattern, TypeAnnotation};
|
||||
use roc_parse::ast::{CommentOrNewline, Expr, Pattern, TypeAnnotation};
|
||||
|
||||
use crate::{
|
||||
annotation::{Formattable, Newlines, Parens},
|
||||
|
@ -96,6 +96,7 @@ pub enum Node<'a> {
|
|||
// Temporary! TODO: translate these into proper Node elements
|
||||
TypeAnnotation(TypeAnnotation<'a>),
|
||||
Pattern(Pattern<'a>),
|
||||
Expr(Expr<'a>),
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
|
@ -321,6 +322,24 @@ fn fmt_sp(buf: &mut Buf, sp: Sp<'_>, indent: u16) {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'a> Formattable for NodeInfo<'a> {
|
||||
fn is_multiline(&self) -> bool {
|
||||
!self.before.is_empty() || self.node.is_multiline() || !self.after.is_empty()
|
||||
}
|
||||
|
||||
fn format_with_options(
|
||||
&self,
|
||||
buf: &mut Buf,
|
||||
_parens: Parens,
|
||||
_newlines: Newlines,
|
||||
indent: u16,
|
||||
) {
|
||||
fmt_spaces(buf, self.before.iter(), indent);
|
||||
self.node.format(buf, indent);
|
||||
fmt_spaces(buf, self.after.iter(), indent);
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Formattable for Node<'a> {
|
||||
fn is_multiline(&self) -> bool {
|
||||
match self {
|
||||
|
@ -350,6 +369,7 @@ impl<'a> Formattable for Node<'a> {
|
|||
Node::Literal(_) => false,
|
||||
Node::TypeAnnotation(type_annotation) => type_annotation.is_multiline(),
|
||||
Node::Pattern(pat) => pat.is_multiline(),
|
||||
Node::Expr(expr) => expr.is_multiline(),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -452,6 +472,9 @@ impl<'a> Formattable for Node<'a> {
|
|||
Node::Pattern(pat) => {
|
||||
pat.format_with_options(buf, parens, newlines, indent);
|
||||
}
|
||||
Node::Expr(expr) => {
|
||||
expr.format_with_options(buf, parens, newlines, indent);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -91,6 +91,7 @@ impl<'a> Formattable for Pattern<'a> {
|
|||
| Pattern::Underscore(_)
|
||||
| Pattern::Malformed(_)
|
||||
| Pattern::MalformedIdent(_, _)
|
||||
| Pattern::MalformedExpr(_)
|
||||
| Pattern::QualifiedIdentifier { .. } => false,
|
||||
|
||||
Pattern::Tuple(patterns) | Pattern::List(patterns) => {
|
||||
|
@ -447,6 +448,10 @@ fn fmt_pattern_only(
|
|||
buf.indent(indent);
|
||||
buf.push_str(string);
|
||||
}
|
||||
Pattern::MalformedExpr(expr) => {
|
||||
buf.indent(indent);
|
||||
expr.format(buf, indent);
|
||||
}
|
||||
Pattern::QualifiedIdentifier { module_name, ident } => {
|
||||
buf.indent(indent);
|
||||
if !module_name.is_empty() {
|
||||
|
@ -538,20 +543,6 @@ pub fn pattern_fmt_apply(
|
|||
let mut before = merge_spaces(buf.text.bump(), last_after, arg.before);
|
||||
|
||||
if !before.is_empty() {
|
||||
if starts_with_block_str(&arg.item) {
|
||||
// Ick!
|
||||
// The block string will keep "generating" newlines when formatted (it wants to start on its own line),
|
||||
// so we strip one out here.
|
||||
//
|
||||
// Note that this doesn't affect Expr's because those have explicit parens, and we can control
|
||||
// whether spaces cross that boundary.
|
||||
let chop_off = before
|
||||
.iter()
|
||||
.rev()
|
||||
.take_while(|&&s| matches!(s, CommentOrNewline::Newline))
|
||||
.count();
|
||||
before = &before[..before.len() - chop_off];
|
||||
}
|
||||
handle_multiline_str_spaces(&arg.item, &mut before);
|
||||
|
||||
if !is_multiline {
|
||||
|
@ -674,7 +665,9 @@ fn pattern_prec(pat: Pattern<'_>) -> Prec {
|
|||
| Pattern::PncApply(_, _) => Prec::Term,
|
||||
Pattern::Apply(_, _) | Pattern::As(_, _) => Prec::Apply,
|
||||
Pattern::SpaceBefore(inner, _) | Pattern::SpaceAfter(inner, _) => pattern_prec(*inner),
|
||||
Pattern::Malformed(_) | Pattern::MalformedIdent(..) => Prec::Term,
|
||||
Pattern::Malformed(_) | Pattern::MalformedIdent(..) | Pattern::MalformedExpr(_) => {
|
||||
Prec::Term
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -4,7 +4,7 @@ use roc_can::scope::Scope;
|
|||
use roc_collections::VecSet;
|
||||
use roc_module::ident::ModuleName;
|
||||
use roc_module::symbol::{IdentIds, ModuleId, ModuleIds, Symbol};
|
||||
use roc_parse::ast::{self, ExtractSpaces, TypeHeader};
|
||||
use roc_parse::ast::{self, ExtractSpaces, TypeHeader, TypeVar};
|
||||
use roc_parse::ast::{AssignedField, FunctionArrow};
|
||||
use roc_parse::ast::{CommentOrNewline, TypeDef, ValueDef};
|
||||
|
||||
|
@ -312,7 +312,7 @@ fn generate_entry_docs(
|
|||
let mut type_vars = Vec::new();
|
||||
|
||||
for var in vars.iter() {
|
||||
if let Pattern::Identifier { ident: ident_name } = var.value {
|
||||
if let TypeVar::Identifier(ident_name) = var.value {
|
||||
type_vars.push(ident_name.to_string());
|
||||
}
|
||||
}
|
||||
|
@ -346,7 +346,7 @@ fn generate_entry_docs(
|
|||
let mut type_vars = Vec::new();
|
||||
|
||||
for var in vars.iter() {
|
||||
if let Pattern::Identifier { ident: ident_name } = var.value {
|
||||
if let TypeVar::Identifier(ident_name) = var.value {
|
||||
type_vars.push(ident_name.to_string());
|
||||
}
|
||||
}
|
||||
|
@ -370,7 +370,7 @@ fn generate_entry_docs(
|
|||
let mut type_vars = Vec::new();
|
||||
|
||||
for var in vars.iter() {
|
||||
if let Pattern::Identifier { ident: ident_name } = var.value {
|
||||
if let TypeVar::Identifier(ident_name) = var.value {
|
||||
type_vars.push(ident_name.to_string());
|
||||
}
|
||||
}
|
||||
|
@ -631,7 +631,7 @@ fn type_to_docs(in_func_type_ann: bool, type_annotation: ast::TypeAnnotation) ->
|
|||
.vars
|
||||
.iter()
|
||||
.filter_map(|loc_pattern| match loc_pattern.value {
|
||||
ast::Pattern::Identifier { ident } => Some(ident.to_string()),
|
||||
ast::TypeVar::Identifier(ident) => Some(ident.to_string()),
|
||||
_ => None,
|
||||
})
|
||||
.collect(),
|
||||
|
|
|
@ -37,7 +37,7 @@ cheapest_open = \cost_function, model ->
|
|||
Ok(smallest_so_far)
|
||||
|
||||
Set.walk(model.open_set, Err(KeyNotFound), folder)
|
||||
|> Result.map(\x -> x.position)
|
||||
|> Result.map_ok(\x -> x.position)
|
||||
|
||||
reconstruct_path : Map position position, position -> List position
|
||||
reconstruct_path = \came_from, goal ->
|
||||
|
|
|
@ -37,7 +37,7 @@ cheapest_open = \cost_function, model ->
|
|||
Ok(smallest_so_far)
|
||||
|
||||
Set.walk(model.open_set, Err(KeyNotFound), folder)
|
||||
|> Result.map(\x -> x.position)
|
||||
|> Result.map_ok(\x -> x.position)
|
||||
|
||||
reconstruct_path : Dict position position, position -> List position where position implements Hash & Eq
|
||||
reconstruct_path = \came_from, goal ->
|
||||
|
|
|
@ -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, "!="),
|
||||
|
@ -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,
|
||||
}
|
||||
|
@ -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() {
|
||||
|
|
|
@ -1525,7 +1525,7 @@ define_builtins! {
|
|||
0 RESULT_RESULT: "Result" exposed_type=true // the Result.Result type alias
|
||||
1 RESULT_IS_ERR: "is_err"
|
||||
2 RESULT_ON_ERR: "on_err"
|
||||
3 RESULT_MAP: "map"
|
||||
3 RESULT_MAP_OK: "map_ok"
|
||||
4 RESULT_MAP_ERR: "map_err"
|
||||
5 RESULT_WITH_DEFAULT: "with_default"
|
||||
6 RESULT_TRY: "try"
|
||||
|
|
|
@ -633,7 +633,7 @@ pub struct PrecedenceConflict<'a> {
|
|||
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||
pub struct TypeHeader<'a> {
|
||||
pub name: Loc<&'a str>,
|
||||
pub vars: &'a [Loc<Pattern<'a>>],
|
||||
pub vars: &'a [Loc<TypeVar<'a>>],
|
||||
}
|
||||
|
||||
impl<'a> TypeHeader<'a> {
|
||||
|
@ -646,6 +646,17 @@ impl<'a> TypeHeader<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||
pub enum TypeVar<'a> {
|
||||
Identifier(&'a str),
|
||||
SpaceBefore(&'a TypeVar<'a>, &'a [CommentOrNewline<'a>]),
|
||||
SpaceAfter(&'a TypeVar<'a>, &'a [CommentOrNewline<'a>]),
|
||||
|
||||
// These are syntactically parsed as exprs first, so if there's anything else here,
|
||||
// we consider it malformed but preserve it for error reporting and more resilient parsing.
|
||||
Malformed(&'a Expr<'a>),
|
||||
}
|
||||
|
||||
/// The `implements` keyword associated with ability definitions.
|
||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||
pub enum Implements<'a> {
|
||||
|
@ -1642,6 +1653,7 @@ pub enum Pattern<'a> {
|
|||
// Malformed
|
||||
Malformed(&'a str),
|
||||
MalformedIdent(&'a str, crate::ident::BadIdent),
|
||||
MalformedExpr(&'a Expr<'a>),
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
|
||||
|
@ -1868,6 +1880,11 @@ impl<'a> Pattern<'a> {
|
|||
false
|
||||
}
|
||||
}
|
||||
|
||||
MalformedExpr(_expr_x) => {
|
||||
// conservatively assume all malformed expr patterns are not-equivalient
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2256,6 +2273,7 @@ impl_extract_spaces!(AssignedField<T>);
|
|||
impl_extract_spaces!(TypeAnnotation);
|
||||
impl_extract_spaces!(ImplementsAbility);
|
||||
impl_extract_spaces!(Implements);
|
||||
impl_extract_spaces!(TypeVar);
|
||||
|
||||
impl<'a, T: Copy> ExtractSpaces<'a> for Spaced<'a, T> {
|
||||
type Item = T;
|
||||
|
@ -2560,6 +2578,7 @@ impl<'a> Malformed for Pattern<'a> {
|
|||
|
||||
Malformed(_) |
|
||||
MalformedIdent(_, _) |
|
||||
MalformedExpr(_) |
|
||||
QualifiedIdentifier { .. } => true,
|
||||
}
|
||||
}
|
||||
|
@ -2730,6 +2749,16 @@ impl<'a> Malformed for TypeHeader<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'a> Malformed for TypeVar<'a> {
|
||||
fn is_malformed(&self) -> bool {
|
||||
match self {
|
||||
TypeVar::Identifier(_) => false,
|
||||
TypeVar::Malformed(_) => true,
|
||||
TypeVar::SpaceBefore(var, _) | TypeVar::SpaceAfter(var, _) => var.is_malformed(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Malformed for Tag<'a> {
|
||||
fn is_malformed(&self) -> bool {
|
||||
match self {
|
||||
|
|
|
@ -2,7 +2,8 @@ use crate::ast::{
|
|||
AssignedField, Collection, CommentOrNewline, Defs, Expr, ExtractSpaces, Implements,
|
||||
ImplementsAbilities, ImportAlias, ImportAsKeyword, ImportExposingKeyword, ImportedModuleName,
|
||||
IngestedFileAnnotation, IngestedFileImport, ModuleImport, ModuleImportParams, Pattern,
|
||||
Spaceable, Spaced, Spaces, SpacesBefore, TypeAnnotation, TypeDef, TypeHeader, ValueDef,
|
||||
Spaceable, Spaced, Spaces, SpacesBefore, TypeAnnotation, TypeDef, TypeHeader, TypeVar,
|
||||
ValueDef,
|
||||
};
|
||||
use crate::blankspace::{
|
||||
loc_space0_e, require_newline_or_eof, space0_after_e, space0_around_ee, space0_before_e,
|
||||
|
@ -17,8 +18,8 @@ use crate::parser::{
|
|||
collection_trailing_sep_e, either, increment_min_indent, indented_seq_skip_first, loc, map,
|
||||
map_with_arena, optional, reset_min_indent, sep_by1, sep_by1_e, set_min_indent, skip_first,
|
||||
skip_second, specialize_err, specialize_err_ref, then, two_bytes, zero_or_more, EClosure,
|
||||
EExpect, EExpr, EIf, EImport, EImportParams, EInParens, EList, ENumber, EPattern, ERecord,
|
||||
EReturn, EString, EType, EWhen, Either, ParseResult, Parser, SpaceProblem,
|
||||
EExpect, EExpr, EIf, EImport, EImportParams, EInParens, EList, ENumber, ERecord, EReturn,
|
||||
EString, EType, EWhen, Either, ParseResult, Parser, SpaceProblem,
|
||||
};
|
||||
use crate::pattern::closure_param;
|
||||
use crate::state::State;
|
||||
|
@ -751,7 +752,7 @@ fn parse_stmt_operator_chain<'a>(
|
|||
// try an operator
|
||||
return parse_stmt_after_apply(
|
||||
arena,
|
||||
state,
|
||||
state.clone(),
|
||||
min_indent,
|
||||
call_min_indent,
|
||||
expr_state,
|
||||
|
@ -1244,20 +1245,10 @@ fn parse_stmt_alias_or_opaque<'a>(
|
|||
let mut type_arguments = Vec::with_capacity_in(arguments.len(), arena);
|
||||
|
||||
for argument in arguments {
|
||||
match expr_to_pattern_help(arena, &argument.value) {
|
||||
Ok(good) => {
|
||||
type_arguments.push(Loc::at(argument.region, good));
|
||||
}
|
||||
Err(()) => {
|
||||
return Err((
|
||||
MadeProgress,
|
||||
EExpr::Pattern(
|
||||
arena.alloc(EPattern::NotAPattern(state.pos())),
|
||||
state.pos(),
|
||||
),
|
||||
));
|
||||
}
|
||||
}
|
||||
type_arguments.push(Loc::at(
|
||||
argument.region,
|
||||
expr_to_type_var(arena, &argument.value),
|
||||
));
|
||||
}
|
||||
|
||||
match kind.value {
|
||||
|
@ -1344,6 +1335,29 @@ fn parse_stmt_alias_or_opaque<'a>(
|
|||
Ok((MadeProgress, res, state))
|
||||
}
|
||||
|
||||
fn expr_to_type_var<'a>(arena: &'a Bump, expr: &'a Expr<'a>) -> TypeVar<'a> {
|
||||
let expr = expr.extract_spaces();
|
||||
|
||||
let mut ty = match expr.item {
|
||||
Expr::Var {
|
||||
module_name: "",
|
||||
ident,
|
||||
} => TypeVar::Identifier(ident),
|
||||
_ => TypeVar::Malformed(arena.alloc(expr.item)),
|
||||
};
|
||||
|
||||
// Now we re-add the spaces
|
||||
|
||||
if !expr.before.is_empty() {
|
||||
ty = TypeVar::SpaceBefore(arena.alloc(ty), expr.before);
|
||||
}
|
||||
if !expr.after.is_empty() {
|
||||
ty = TypeVar::SpaceAfter(arena.alloc(ty), expr.after);
|
||||
}
|
||||
|
||||
ty
|
||||
}
|
||||
|
||||
mod ability {
|
||||
use parser::absolute_indented_seq;
|
||||
|
||||
|
@ -1464,7 +1478,7 @@ mod ability {
|
|||
fn finish_parsing_ability_def_help<'a>(
|
||||
call_min_indent: u32,
|
||||
name: Loc<&'a str>,
|
||||
args: &'a [Loc<Pattern<'a>>],
|
||||
args: &'a [Loc<TypeVar<'a>>],
|
||||
loc_implements: Loc<Implements<'a>>,
|
||||
arena: &'a Bump,
|
||||
state: State<'a>,
|
||||
|
@ -1888,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);
|
||||
|
@ -2028,16 +2046,10 @@ fn parse_ability_def<'a>(
|
|||
|
||||
let mut arguments = Vec::with_capacity_in(expr_state.arguments.len(), arena);
|
||||
for argument in expr_state.arguments {
|
||||
match expr_to_pattern_help(arena, &argument.value) {
|
||||
Ok(good) => {
|
||||
arguments.push(Loc::at(argument.region, good));
|
||||
}
|
||||
Err(_) => {
|
||||
let start = argument.region.start();
|
||||
let err = &*arena.alloc(EPattern::Start(start));
|
||||
return Err((MadeProgress, EExpr::Pattern(err, argument.region.start())));
|
||||
}
|
||||
}
|
||||
arguments.push(Loc::at(
|
||||
argument.region,
|
||||
expr_to_type_var(arena, &argument.value),
|
||||
));
|
||||
}
|
||||
|
||||
// Attach any spaces to the `implements` keyword
|
||||
|
@ -2327,6 +2339,50 @@ pub fn parse_top_level_defs<'a>(
|
|||
// PARSER HELPERS
|
||||
|
||||
fn closure_help<'a>(check_for_arrow: CheckForArrow) -> impl Parser<'a, Expr<'a>, EClosure<'a>> {
|
||||
one_of!(
|
||||
closure_new_syntax_help(),
|
||||
closure_old_syntax_help(check_for_arrow),
|
||||
)
|
||||
}
|
||||
|
||||
fn closure_new_syntax_help<'a>() -> impl Parser<'a, Expr<'a>, EClosure<'a>> {
|
||||
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,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
|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>(
|
||||
check_for_arrow: CheckForArrow,
|
||||
) -> impl Parser<'a, Expr<'a>, EClosure<'a>> {
|
||||
// closure_help_help(options)
|
||||
map_with_arena(
|
||||
// After the first token, all other tokens must be indented past the start of the line
|
||||
|
@ -2363,6 +2419,19 @@ fn closure_help<'a>(check_for_arrow: CheckForArrow) -> impl Parser<'a, Expr<'a>,
|
|||
)
|
||||
}
|
||||
|
||||
fn error_on_pizza<'a, T, E: 'a>(
|
||||
p: impl Parser<'a, T, E>,
|
||||
f: impl Fn(Position) -> E,
|
||||
) -> impl Parser<'a, T, E> {
|
||||
move |arena: &'a Bump, state: State<'a>, min_indent: u32| {
|
||||
if state.bytes().starts_with(b"|>") || state.bytes().starts_with(b"||") {
|
||||
Err((NoProgress, f(state.pos())))
|
||||
} else {
|
||||
p.parse(arena, state, min_indent)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mod when {
|
||||
use parser::indented_seq_skip_first;
|
||||
|
||||
|
@ -3005,6 +3074,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()),
|
||||
|
@ -3209,7 +3283,7 @@ fn stmts_to_defs<'a>(
|
|||
if (spaces_middle.len() <= 1
|
||||
&& !ends_with_spaces_conservative(&ann_type.value)
|
||||
&& !starts_with_spaces_conservative(&loc_pattern.value))
|
||||
|| header_to_pat(arena, header).equivalent(&loc_pattern.value)
|
||||
|| type_header_equivalent_to_pat(&header, &loc_pattern.value)
|
||||
{
|
||||
// This is a case like
|
||||
// UserId x : [UserId Int]
|
||||
|
@ -3351,18 +3425,33 @@ fn starts_with_spaces_conservative(value: &Pattern<'_>) -> bool {
|
|||
Pattern::RequiredField(_, _) | Pattern::OptionalField(_, _) => false,
|
||||
Pattern::SpaceBefore(_, _) => true,
|
||||
Pattern::SpaceAfter(inner, _) => starts_with_spaces_conservative(inner),
|
||||
Pattern::Malformed(_) | Pattern::MalformedIdent(_, _) => true,
|
||||
Pattern::Malformed(_) | Pattern::MalformedIdent(_, _) | Pattern::MalformedExpr(_) => true,
|
||||
}
|
||||
}
|
||||
|
||||
fn header_to_pat<'a>(arena: &'a Bump, header: TypeHeader<'a>) -> Pattern<'a> {
|
||||
if header.vars.is_empty() {
|
||||
Pattern::Tag(header.name.value)
|
||||
} else {
|
||||
Pattern::Apply(
|
||||
arena.alloc(Loc::at(header.name.region, Pattern::Tag(header.name.value))),
|
||||
header.vars,
|
||||
)
|
||||
fn type_header_equivalent_to_pat<'a>(header: &TypeHeader<'a>, pat: &Pattern<'a>) -> bool {
|
||||
match pat {
|
||||
Pattern::Apply(func, args) => {
|
||||
if !matches!(func.value, Pattern::Tag(tag) if header.name.value == tag) {
|
||||
return false;
|
||||
}
|
||||
if args.len() != header.vars.len() {
|
||||
return false;
|
||||
}
|
||||
for (arg, var) in (*args).iter().zip(header.vars) {
|
||||
match (arg.value, var.value) {
|
||||
(Pattern::Identifier { ident: left }, TypeVar::Identifier(right)) => {
|
||||
if left != right {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
_ => return false,
|
||||
}
|
||||
}
|
||||
true
|
||||
}
|
||||
Pattern::Tag(tag) => header.vars.is_empty() && header.name.value == *tag,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -3375,7 +3464,7 @@ fn ends_with_spaces_conservative(ty: &TypeAnnotation<'_>) -> bool {
|
|||
TypeAnnotation::As(_, _, type_header) => type_header
|
||||
.vars
|
||||
.last()
|
||||
.map_or(false, |v| pat_ends_with_spaces_conservative(&v.value)),
|
||||
.map_or(false, |v| type_var_ends_with_spaces_conservative(&v.value)),
|
||||
TypeAnnotation::Record { fields: _, ext }
|
||||
| TypeAnnotation::Tuple { elems: _, ext }
|
||||
| TypeAnnotation::TagUnion { ext, tags: _ } => {
|
||||
|
@ -3396,32 +3485,15 @@ fn ends_with_spaces_conservative(ty: &TypeAnnotation<'_>) -> bool {
|
|||
}
|
||||
}
|
||||
|
||||
fn pat_ends_with_spaces_conservative(pat: &Pattern<'_>) -> bool {
|
||||
match pat {
|
||||
Pattern::Identifier { .. }
|
||||
| Pattern::QualifiedIdentifier { .. }
|
||||
| Pattern::Tag(_)
|
||||
| Pattern::NumLiteral(_)
|
||||
| Pattern::FloatLiteral(_)
|
||||
| Pattern::StrLiteral(_)
|
||||
| Pattern::Underscore(_)
|
||||
| Pattern::SingleQuote(_)
|
||||
| Pattern::Tuple(_)
|
||||
| Pattern::List(_)
|
||||
| Pattern::NonBase10Literal { .. }
|
||||
| Pattern::ListRest(_)
|
||||
| Pattern::As(_, _)
|
||||
| Pattern::OpaqueRef(_)
|
||||
| Pattern::PncApply(_, _) => false,
|
||||
Pattern::Apply(_, args) => args
|
||||
.last()
|
||||
.map_or(false, |a| pat_ends_with_spaces_conservative(&a.value)),
|
||||
Pattern::RecordDestructure(_) => false,
|
||||
Pattern::RequiredField(_, _) => unreachable!(),
|
||||
Pattern::OptionalField(_, _) => unreachable!(),
|
||||
Pattern::SpaceBefore(inner, _) => pat_ends_with_spaces_conservative(inner),
|
||||
Pattern::SpaceAfter(_, _) => true,
|
||||
Pattern::Malformed(_) | Pattern::MalformedIdent(_, _) => false,
|
||||
fn type_var_ends_with_spaces_conservative(value: &TypeVar<'_>) -> bool {
|
||||
match value {
|
||||
TypeVar::Identifier(_) => false,
|
||||
TypeVar::Malformed(_) => {
|
||||
// conservativly assume it might end in a space
|
||||
true
|
||||
}
|
||||
TypeVar::SpaceBefore(inner, _sp) => type_var_ends_with_spaces_conservative(inner),
|
||||
TypeVar::SpaceAfter(_inner, _sp) => true,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -3435,7 +3507,7 @@ pub fn join_alias_to_body<'a>(
|
|||
body_expr: &'a Loc<Expr<'a>>,
|
||||
) -> ValueDef<'a> {
|
||||
let loc_name = arena.alloc(header.name.map(|x| Pattern::Tag(x)));
|
||||
let ann_pattern = Pattern::Apply(loc_name, header.vars);
|
||||
let ann_pattern = Pattern::Apply(loc_name, type_vars_to_patterns(arena, header.vars));
|
||||
|
||||
let vars_region = Region::across_all(header.vars.iter().map(|v| &v.region));
|
||||
let region_ann_pattern = Region::span_across(&loc_name.region, &vars_region);
|
||||
|
@ -3450,6 +3522,35 @@ pub fn join_alias_to_body<'a>(
|
|||
}
|
||||
}
|
||||
|
||||
fn type_vars_to_patterns<'a>(
|
||||
arena: &'a Bump,
|
||||
vars: &'a [Loc<TypeVar<'a>>],
|
||||
) -> &'a [Loc<Pattern<'a>>] {
|
||||
let mut result = Vec::with_capacity_in(vars.len(), arena);
|
||||
for var in vars {
|
||||
let pat = type_var_to_pat(arena, &var.value);
|
||||
result.push(Loc::at(var.region, pat));
|
||||
}
|
||||
|
||||
result.into_bump_slice()
|
||||
}
|
||||
|
||||
fn type_var_to_pat<'a>(arena: &'a Bump, var: &TypeVar<'a>) -> Pattern<'a> {
|
||||
match var {
|
||||
TypeVar::Identifier(ident) => Pattern::Identifier { ident },
|
||||
TypeVar::Malformed(expr) => match expr_to_pattern_help(arena, expr) {
|
||||
Ok(pat) => pat,
|
||||
Err(()) => Pattern::MalformedExpr(expr),
|
||||
},
|
||||
TypeVar::SpaceBefore(inner, sp) => {
|
||||
Pattern::SpaceBefore(arena.alloc(type_var_to_pat(arena, inner)), sp)
|
||||
}
|
||||
TypeVar::SpaceAfter(inner, sp) => {
|
||||
Pattern::SpaceAfter(arena.alloc(type_var_to_pat(arena, inner)), sp)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// This is a helper function for parsing function args.
|
||||
/// The rules for (-) are special-cased, and they come up in function args.
|
||||
///
|
||||
|
@ -4002,6 +4103,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),
|
||||
|
|
|
@ -3,7 +3,7 @@ use bumpalo::Bump;
|
|||
use roc_module::called_via::{BinOp, UnaryOp};
|
||||
use roc_region::all::{Loc, Position, Region};
|
||||
|
||||
use crate::ast::ImplementsAbilities;
|
||||
use crate::ast::{ImplementsAbilities, TypeVar};
|
||||
use crate::{
|
||||
ast::{
|
||||
AbilityImpls, AbilityMember, AssignedField, Collection, Defs, Expr, FullAst, Header,
|
||||
|
@ -390,6 +390,18 @@ impl<'a> Normalize<'a> for TypeDef<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'a> Normalize<'a> for TypeVar<'a> {
|
||||
fn normalize(&self, arena: &'a Bump) -> Self {
|
||||
match self {
|
||||
TypeVar::Identifier(ident) => TypeVar::Identifier(ident),
|
||||
TypeVar::Malformed(expr) => TypeVar::Malformed(expr.normalize(arena)),
|
||||
TypeVar::SpaceBefore(inner, _sp) | TypeVar::SpaceAfter(inner, _sp) => {
|
||||
*inner.normalize(arena)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Normalize<'a> for ValueDef<'a> {
|
||||
fn normalize(&self, arena: &'a Bump) -> Self {
|
||||
use ValueDef::*;
|
||||
|
@ -919,6 +931,7 @@ impl<'a> Normalize<'a> for Pattern<'a> {
|
|||
Pattern::StrLiteral(a) => Pattern::StrLiteral(a.normalize(arena)),
|
||||
Pattern::Underscore(a) => Pattern::Underscore(a),
|
||||
Pattern::Malformed(a) => Pattern::Malformed(a),
|
||||
Pattern::MalformedExpr(a) => Pattern::MalformedExpr(arena.alloc(a.normalize(arena))),
|
||||
Pattern::MalformedIdent(a, b) => Pattern::MalformedIdent(a, remove_spaces_bad_ident(b)),
|
||||
Pattern::QualifiedIdentifier { module_name, ident } => {
|
||||
Pattern::QualifiedIdentifier { module_name, ident }
|
||||
|
@ -1201,6 +1214,7 @@ impl<'a> Normalize<'a> for EClosure<'a> {
|
|||
EClosure::IndentArrow(_) => EClosure::IndentArrow(Position::zero()),
|
||||
EClosure::IndentBody(_) => EClosure::IndentBody(Position::zero()),
|
||||
EClosure::IndentArg(_) => EClosure::IndentArg(Position::zero()),
|
||||
EClosure::Bar(_) => EClosure::Bar(Position::zero()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -747,6 +747,7 @@ impl<'a> EInParens<'a> {
|
|||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub enum EClosure<'a> {
|
||||
Bar(Position),
|
||||
Space(BadInputError, Position),
|
||||
Start(Position),
|
||||
Arrow(Position),
|
||||
|
@ -768,7 +769,8 @@ impl<'a> EClosure<'a> {
|
|||
EClosure::Body(expr, _) => expr.get_region(),
|
||||
|
||||
// Cases with Position values
|
||||
EClosure::Space(_, p)
|
||||
EClosure::Bar(p)
|
||||
| EClosure::Space(_, p)
|
||||
| EClosure::Start(p)
|
||||
| EClosure::Arrow(p)
|
||||
| EClosure::Comma(p)
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
use crate::ast::{
|
||||
AbilityImpls, AssignedField, Collection, CommentOrNewline, Expr, FunctionArrow,
|
||||
ImplementsAbilities, ImplementsAbility, ImplementsClause, Pattern, Spaceable, Spaced,
|
||||
SpacesBefore, Tag, TypeAnnotation, TypeHeader,
|
||||
ImplementsAbilities, ImplementsAbility, ImplementsClause, Spaceable, Spaced, SpacesBefore, Tag,
|
||||
TypeAnnotation, TypeHeader, TypeVar,
|
||||
};
|
||||
use crate::blankspace::{
|
||||
self, plain_spaces_before, space0_around_ee, space0_before, space0_before_e, space0_e,
|
||||
|
@ -74,7 +74,7 @@ fn check_type_alias<'a>(
|
|||
var_names.reserve(vars.len());
|
||||
for var in vars {
|
||||
if let TypeAnnotation::BoundVariable(v) = var.value {
|
||||
var_names.push(Loc::at(var.region, Pattern::Identifier { ident: v }));
|
||||
var_names.push(Loc::at(var.region, TypeVar::Identifier(v)));
|
||||
} else {
|
||||
return Err(ETypeInlineAlias::ArgumentNotLowercase(var.region.start()));
|
||||
}
|
||||
|
|
|
@ -3479,7 +3479,7 @@ mod solve_expr {
|
|||
Ok { position, cost: 0.0 }
|
||||
|
||||
Set.walk model.open_set (Ok { position: boom {}, cost: 0.0 }) folder
|
||||
|> Result.map (\x -> x.position)
|
||||
|> Result.map_ok (\x -> x.position)
|
||||
|
||||
astar : Model position -> Result position [KeyNotFound] where position implements Hash & Eq
|
||||
astar = \model -> cheapest_open model
|
||||
|
|
|
@ -337,7 +337,7 @@ fn decode() {
|
|||
# impl MDecoding for MyU8
|
||||
decoder = @MDecoder \lst, fmt ->
|
||||
{ result, rest } = decode_with lst u8 fmt
|
||||
{ result: Result.map result (\n -> @MyU8 n), rest }
|
||||
{ result: Result.map_ok result (\n -> @MyU8 n), rest }
|
||||
|
||||
myU8 =
|
||||
when from_bytes [15] (@Linear {}) is
|
||||
|
@ -2210,7 +2210,7 @@ fn issue_4772_weakened_monomorphic_destructure() {
|
|||
Err (ParsingFailure "not a number")
|
||||
|
||||
main =
|
||||
get_number |> Result.map .val |> Result.with_default 0
|
||||
get_number |> Result.map_ok .val |> Result.with_default 0
|
||||
"#
|
||||
),
|
||||
1234i64,
|
||||
|
|
|
@ -554,7 +554,7 @@ fn list_split_first() {
|
|||
assert_evals_to!(
|
||||
r"
|
||||
List.split_first [2, 3, 0, 4, 0, 6, 0, 8, 9] 0
|
||||
|> Result.map .before
|
||||
|> Result.map_ok .before
|
||||
",
|
||||
RocResult::ok(RocList::<i64>::from_slice(&[2, 3])),
|
||||
RocResult<RocList<i64>, ()>
|
||||
|
@ -562,7 +562,7 @@ fn list_split_first() {
|
|||
assert_evals_to!(
|
||||
r"
|
||||
List.split_first [2, 3, 0, 4, 0, 6, 0, 8, 9] 0
|
||||
|> Result.map .after
|
||||
|> Result.map_ok .after
|
||||
",
|
||||
RocResult::ok(RocList::<i64>::from_slice(&[4, 0, 6, 0, 8, 9])),
|
||||
RocResult<RocList<i64>, ()>
|
||||
|
@ -587,7 +587,7 @@ fn list_split_last() {
|
|||
assert_evals_to!(
|
||||
r"
|
||||
List.split_last [2, 3, 0, 4, 0, 6, 0, 8, 9] 0
|
||||
|> Result.map .before
|
||||
|> Result.map_ok .before
|
||||
",
|
||||
RocResult::ok(RocList::<i64>::from_slice(&[2, 3, 0, 4, 0, 6])),
|
||||
RocResult<RocList<i64>, ()>
|
||||
|
@ -595,7 +595,7 @@ fn list_split_last() {
|
|||
assert_evals_to!(
|
||||
r"
|
||||
List.split_last [2, 3, 0, 4, 0, 6, 0, 8, 9] 0
|
||||
|> Result.map .after
|
||||
|> Result.map_ok .after
|
||||
",
|
||||
RocResult::ok(RocList::<i64>::from_slice(&[8, 9])),
|
||||
RocResult<RocList<i64>, ()>
|
||||
|
@ -2164,7 +2164,7 @@ fn first_wildcard_empty_list() {
|
|||
assert_evals_to!(
|
||||
indoc!(
|
||||
r"
|
||||
List.last [] |> Result.map (\_ -> 0i64)
|
||||
List.last [] |> Result.map_ok (\_ -> 0i64)
|
||||
"
|
||||
),
|
||||
RocResult::err(()),
|
||||
|
@ -2209,7 +2209,7 @@ fn last_wildcard_empty_list() {
|
|||
assert_evals_to!(
|
||||
indoc!(
|
||||
r"
|
||||
List.last [] |> Result.map (\_ -> 0i64)
|
||||
List.last [] |> Result.map_ok (\_ -> 0i64)
|
||||
"
|
||||
),
|
||||
RocResult::err(()),
|
||||
|
@ -2261,7 +2261,7 @@ fn get_wildcard_empty_list() {
|
|||
indoc!(
|
||||
r"
|
||||
List.get [] 0
|
||||
|> Result.map (\_ -> {})
|
||||
|> Result.map_ok (\_ -> {})
|
||||
"
|
||||
),
|
||||
RocResult::err(()),
|
||||
|
@ -2978,7 +2978,7 @@ fn list_min() {
|
|||
indoc!(
|
||||
r"
|
||||
List.min []
|
||||
|> Result.map (\_ -> {})
|
||||
|> Result.map_ok (\_ -> {})
|
||||
"
|
||||
),
|
||||
RocResult::err(()),
|
||||
|
@ -3003,7 +3003,7 @@ fn list_max() {
|
|||
indoc!(
|
||||
r"
|
||||
List.max []
|
||||
|> Result.map (\_ -> {})
|
||||
|> Result.map_ok (\_ -> {})
|
||||
"
|
||||
),
|
||||
RocResult::err(()),
|
||||
|
|
|
@ -56,7 +56,7 @@ fn result_map() {
|
|||
result = Ok 2
|
||||
|
||||
result
|
||||
|> Result.map (\x -> x + 1)
|
||||
|> Result.map_ok (\x -> x + 1)
|
||||
|> Result.with_default 0
|
||||
"
|
||||
),
|
||||
|
@ -71,7 +71,7 @@ fn result_map() {
|
|||
result = Err {}
|
||||
|
||||
result
|
||||
|> Result.map (\x -> x + 1)
|
||||
|> Result.map_ok (\x -> x + 1)
|
||||
|> Result.with_default 0
|
||||
"
|
||||
),
|
||||
|
@ -120,7 +120,7 @@ fn err_type_var() {
|
|||
assert_evals_to!(
|
||||
indoc!(
|
||||
r"
|
||||
Result.map (Ok 3) (\x -> x + 1)
|
||||
Result.map_ok (Ok 3) (\x -> x + 1)
|
||||
|> Result.with_default -1
|
||||
"
|
||||
),
|
||||
|
@ -138,7 +138,7 @@ fn err_type_var_annotation() {
|
|||
ok : Result I64 *
|
||||
ok = Ok 3
|
||||
|
||||
Result.map ok (\x -> x + 1)
|
||||
Result.map_ok ok (\x -> x + 1)
|
||||
|> Result.with_default -1
|
||||
"
|
||||
),
|
||||
|
@ -156,7 +156,7 @@ fn err_empty_tag_union() {
|
|||
ok : Result I64 []
|
||||
ok = Ok 3
|
||||
|
||||
Result.map ok (\x -> x + 1)
|
||||
Result.map_ok ok (\x -> x + 1)
|
||||
|> Result.with_default -1
|
||||
"
|
||||
),
|
||||
|
|
|
@ -30,7 +30,7 @@ procedure Test.2 (Test.6):
|
|||
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:
|
||||
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', 'map_err', 'with_default', 'try', 'is_ok', 'map_both', 'map2', 'on_err!'] }";
|
||||
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;
|
||||
|
|
|
@ -1 +1 @@
|
|||
Expr(Pattern(Start(@4), @4), @0)
|
||||
Expr(Ability(DemandAlignment(-1, @18), @17), @0)
|
|
@ -0,0 +1,10 @@
|
|||
O {
|
||||
p?? if
|
||||
a
|
||||
then
|
||||
A
|
||||
else
|
||||
&m,
|
||||
} #
|
||||
: e
|
||||
i
|
|
@ -20,49 +20,52 @@
|
|||
name: @0-1 "O",
|
||||
vars: [
|
||||
@1-23 SpaceAfter(
|
||||
RecordDestructure(
|
||||
[
|
||||
@2-22 OptionalField(
|
||||
"p",
|
||||
@4-22 If {
|
||||
if_thens: [
|
||||
(
|
||||
@7-8 SpaceBefore(
|
||||
SpaceAfter(
|
||||
Var {
|
||||
module_name: "",
|
||||
ident: "a",
|
||||
},
|
||||
[
|
||||
Newline,
|
||||
],
|
||||
),
|
||||
[
|
||||
Newline,
|
||||
],
|
||||
),
|
||||
@14-15 SpaceBefore(
|
||||
SpaceAfter(
|
||||
Tag(
|
||||
"A",
|
||||
Malformed(
|
||||
Record(
|
||||
[
|
||||
@2-22 OptionalValue(
|
||||
@2-3 "p",
|
||||
[],
|
||||
@4-22 If {
|
||||
if_thens: [
|
||||
(
|
||||
@7-8 SpaceBefore(
|
||||
SpaceAfter(
|
||||
Var {
|
||||
module_name: "",
|
||||
ident: "a",
|
||||
},
|
||||
[
|
||||
Newline,
|
||||
],
|
||||
),
|
||||
[
|
||||
Newline,
|
||||
],
|
||||
),
|
||||
@14-15 SpaceBefore(
|
||||
SpaceAfter(
|
||||
Tag(
|
||||
"A",
|
||||
),
|
||||
[
|
||||
Newline,
|
||||
],
|
||||
),
|
||||
[
|
||||
Newline,
|
||||
],
|
||||
),
|
||||
[
|
||||
Newline,
|
||||
],
|
||||
),
|
||||
],
|
||||
final_else: @20-22 RecordUpdater(
|
||||
"m",
|
||||
),
|
||||
],
|
||||
final_else: @20-22 RecordUpdater(
|
||||
"m",
|
||||
),
|
||||
indented_else: false,
|
||||
},
|
||||
),
|
||||
],
|
||||
indented_else: false,
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
[
|
||||
LineComment(
|
|
@ -19,8 +19,10 @@
|
|||
header: TypeHeader {
|
||||
name: @0-1 "C",
|
||||
vars: [
|
||||
@2-3 NumLiteral(
|
||||
"4",
|
||||
@2-3 Malformed(
|
||||
Num(
|
||||
"4",
|
||||
),
|
||||
),
|
||||
],
|
||||
},
|
|
@ -0,0 +1,2 @@
|
|||
N (implements) h (0) : B
|
||||
T
|
|
@ -19,14 +19,23 @@
|
|||
header: TypeHeader {
|
||||
name: @0-1 "N",
|
||||
vars: [
|
||||
@3-13 Identifier {
|
||||
ident: "implements",
|
||||
},
|
||||
@15-16 Identifier {
|
||||
ident: "h",
|
||||
},
|
||||
@18-19 NumLiteral(
|
||||
"0",
|
||||
@3-13 Malformed(
|
||||
ParensAround(
|
||||
Var {
|
||||
module_name: "",
|
||||
ident: "implements",
|
||||
},
|
||||
),
|
||||
),
|
||||
@15-16 Identifier(
|
||||
"h",
|
||||
),
|
||||
@18-19 Malformed(
|
||||
ParensAround(
|
||||
Num(
|
||||
"0",
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
},
|
|
@ -19,11 +19,15 @@
|
|||
header: TypeHeader {
|
||||
name: @0-1 "N",
|
||||
vars: [
|
||||
@2-4 NumLiteral(
|
||||
"-0",
|
||||
@2-4 Malformed(
|
||||
Num(
|
||||
"-0",
|
||||
),
|
||||
),
|
||||
@5-6 Tag(
|
||||
"T",
|
||||
@5-6 Malformed(
|
||||
Tag(
|
||||
"T",
|
||||
),
|
||||
),
|
||||
],
|
||||
},
|
|
@ -28,8 +28,10 @@
|
|||
header: TypeHeader {
|
||||
name: @0-1 "B",
|
||||
vars: [
|
||||
@1-3 OpaqueRef(
|
||||
"@A",
|
||||
@1-3 Malformed(
|
||||
OpaqueRef(
|
||||
"@A",
|
||||
),
|
||||
),
|
||||
],
|
||||
},
|
|
@ -0,0 +1,3 @@
|
|||
Zx (e #
|
||||
) f : i
|
||||
s
|
|
@ -19,19 +19,24 @@
|
|||
header: TypeHeader {
|
||||
name: @0-2 "Zx",
|
||||
vars: [
|
||||
@4-5 SpaceAfter(
|
||||
Identifier {
|
||||
ident: "e",
|
||||
},
|
||||
[
|
||||
LineComment(
|
||||
"",
|
||||
@4-5 Malformed(
|
||||
ParensAround(
|
||||
SpaceAfter(
|
||||
Var {
|
||||
module_name: "",
|
||||
ident: "e",
|
||||
},
|
||||
[
|
||||
LineComment(
|
||||
"",
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
@8-9 Identifier(
|
||||
"f",
|
||||
),
|
||||
@8-9 Identifier {
|
||||
ident: "f",
|
||||
},
|
||||
],
|
||||
},
|
||||
ann: @10-11 BoundVariable(
|
|
@ -19,15 +19,22 @@
|
|||
header: TypeHeader {
|
||||
name: @0-1 "U",
|
||||
vars: [
|
||||
@3-6 Apply(
|
||||
@3-4 Identifier {
|
||||
ident: "b",
|
||||
},
|
||||
[
|
||||
@5-6 Identifier {
|
||||
ident: "a",
|
||||
},
|
||||
],
|
||||
@3-6 Malformed(
|
||||
ParensAround(
|
||||
Apply(
|
||||
@3-4 Var {
|
||||
module_name: "",
|
||||
ident: "b",
|
||||
},
|
||||
[
|
||||
@5-6 Var {
|
||||
module_name: "",
|
||||
ident: "a",
|
||||
},
|
||||
],
|
||||
Space,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
},
|
|
@ -0,0 +1,10 @@
|
|||
M {
|
||||
s?? s
|
||||
{ J &
|
||||
},
|
||||
} {
|
||||
s?? s
|
||||
{ J &
|
||||
},
|
||||
} : p
|
||||
y
|
|
@ -0,0 +1,104 @@
|
|||
@0-25 SpaceAfter(
|
||||
Defs(
|
||||
Defs {
|
||||
tags: [
|
||||
EitherIndex(0),
|
||||
],
|
||||
regions: [
|
||||
@0-23,
|
||||
],
|
||||
space_before: [
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
|
||||
],
|
||||
space_after: [
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
|
||||
],
|
||||
spaces: [],
|
||||
type_defs: [
|
||||
Alias {
|
||||
header: TypeHeader {
|
||||
name: @0-1 "M",
|
||||
vars: [
|
||||
@1-11 Malformed(
|
||||
Record(
|
||||
[
|
||||
@2-10 OptionalValue(
|
||||
@2-3 "s",
|
||||
[],
|
||||
@4-10 Apply(
|
||||
@4-5 Var {
|
||||
module_name: "",
|
||||
ident: "s",
|
||||
},
|
||||
[
|
||||
@5-10 RecordUpdate {
|
||||
update: @6-7 Tag(
|
||||
"J",
|
||||
),
|
||||
fields: Collection {
|
||||
items: [],
|
||||
final_comments: [
|
||||
Newline,
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
Space,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
@11-21 Malformed(
|
||||
Record(
|
||||
[
|
||||
@12-20 OptionalValue(
|
||||
@12-13 "s",
|
||||
[],
|
||||
@14-20 Apply(
|
||||
@14-15 Var {
|
||||
module_name: "",
|
||||
ident: "s",
|
||||
},
|
||||
[
|
||||
@15-20 RecordUpdate {
|
||||
update: @16-17 Tag(
|
||||
"J",
|
||||
),
|
||||
fields: Collection {
|
||||
items: [],
|
||||
final_comments: [
|
||||
Newline,
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
Space,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
},
|
||||
ann: @22-23 BoundVariable(
|
||||
"p",
|
||||
),
|
||||
},
|
||||
],
|
||||
value_defs: [],
|
||||
},
|
||||
@24-25 SpaceBefore(
|
||||
Var {
|
||||
module_name: "",
|
||||
ident: "y",
|
||||
},
|
||||
[
|
||||
Newline,
|
||||
],
|
||||
),
|
||||
),
|
||||
[
|
||||
Newline,
|
||||
],
|
||||
)
|
|
@ -0,0 +1,5 @@
|
|||
Q (
|
||||
"""
|
||||
"""
|
||||
"") : a
|
||||
q
|
|
@ -19,19 +19,24 @@
|
|||
header: TypeHeader {
|
||||
name: @0-1 "Q",
|
||||
vars: [
|
||||
@3-11 Apply(
|
||||
@3-9 StrLiteral(
|
||||
Block(
|
||||
[],
|
||||
@3-11 Malformed(
|
||||
ParensAround(
|
||||
Apply(
|
||||
@3-9 Str(
|
||||
Block(
|
||||
[],
|
||||
),
|
||||
),
|
||||
[
|
||||
@9-11 Str(
|
||||
PlainLine(
|
||||
"",
|
||||
),
|
||||
),
|
||||
],
|
||||
Space,
|
||||
),
|
||||
),
|
||||
[
|
||||
@9-11 StrLiteral(
|
||||
PlainLine(
|
||||
"",
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
},
|
|
@ -0,0 +1,4 @@
|
|||
il3 |k| # w#z
|
||||
CCC @C ( # i
|
||||
t! K) : i
|
||||
C
|
|
@ -32,25 +32,33 @@
|
|||
header: TypeHeader {
|
||||
name: @13-16 "CCC",
|
||||
vars: [
|
||||
@16-18 OpaqueRef(
|
||||
"@C",
|
||||
),
|
||||
@23-26 SpaceBefore(
|
||||
Apply(
|
||||
@23-25 Identifier {
|
||||
ident: "t!",
|
||||
},
|
||||
[
|
||||
@25-26 Tag(
|
||||
"K",
|
||||
),
|
||||
],
|
||||
@16-18 Malformed(
|
||||
OpaqueRef(
|
||||
"@C",
|
||||
),
|
||||
[
|
||||
LineComment(
|
||||
"i",
|
||||
),
|
||||
@23-26 Malformed(
|
||||
ParensAround(
|
||||
SpaceBefore(
|
||||
Apply(
|
||||
@23-25 Var {
|
||||
module_name: "",
|
||||
ident: "t!",
|
||||
},
|
||||
[
|
||||
@25-26 Tag(
|
||||
"K",
|
||||
),
|
||||
],
|
||||
Space,
|
||||
),
|
||||
[
|
||||
LineComment(
|
||||
"i",
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
},
|
|
@ -19,9 +19,9 @@
|
|||
name: @0-1 "A",
|
||||
vars: [
|
||||
@4-5 SpaceBefore(
|
||||
Identifier {
|
||||
ident: "p",
|
||||
},
|
||||
Identifier(
|
||||
"p",
|
||||
),
|
||||
[
|
||||
LineComment(
|
||||
"",
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
launchTheNukes : {} => Result Bool LaunchNukeErr
|
||||
launchTheNukes = \{} ->
|
||||
launchTheNukes = |{}|
|
||||
crash "todo"
|
||||
|
||||
launchTheNukes
|
|
@ -1,2 +1,3 @@
|
|||
A p : e
|
||||
A
|
||||
p : e
|
||||
A
|
|
@ -19,9 +19,9 @@
|
|||
name: @0-1 "A",
|
||||
vars: [
|
||||
@3-4 SpaceBefore(
|
||||
Identifier {
|
||||
ident: "p",
|
||||
},
|
||||
Identifier(
|
||||
"p",
|
||||
),
|
||||
[
|
||||
Newline,
|
||||
],
|
||||
|
|
|
@ -20,9 +20,9 @@
|
|||
vars: [
|
||||
@3-4 SpaceAfter(
|
||||
SpaceBefore(
|
||||
Identifier {
|
||||
ident: "e",
|
||||
},
|
||||
Identifier(
|
||||
"e",
|
||||
),
|
||||
[
|
||||
Newline,
|
||||
],
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
! !(
|
||||
\w -> 2)
|
||||
|w| 2)
|
||||
n
|
|
@ -1,2 +1,2 @@
|
|||
foo
|
||||
|> Dict.keepIf \(k, _v) -> List.contains keysToDelete k |> Bool.not
|
||||
|> Dict.keepIf |(k, _v)| List.contains keysToDelete k |> Bool.not
|
|
@ -1,2 +1,2 @@
|
|||
\({ x, y } as point), (@Location inner as outer) ->
|
||||
|({ x, y } as point), (@Location inner as outer)|
|
||||
crash ""
|
|
@ -1,4 +1,4 @@
|
|||
d
|
||||
+
|
||||
(\w -> x)
|
||||
(|w| x)
|
||||
x
|
|
@ -1 +1 @@
|
|||
\L -> E #
|
||||
|L| E #
|
|
@ -1 +1 @@
|
|||
\(8) -> T #
|
||||
|(8)| T #
|
|
@ -1,3 +1,3 @@
|
|||
\I { p ?? Y
|
||||
Y } [] ->
|
||||
|I { p ?? Y
|
||||
Y } []|
|
||||
K # (
|
|
@ -1,2 +1,2 @@
|
|||
m0 \w -> w? e
|
||||
m0 |w| w? e
|
||||
/ s
|
|
@ -1,2 +1,2 @@
|
|||
i > \s -> s
|
||||
i > |s| s
|
||||
-a
|
|
@ -1,3 +1,3 @@
|
|||
\L -> {
|
||||
|L| {
|
||||
}
|
||||
Θ
|
|
@ -1 +1 @@
|
|||
\L(z) -> 42
|
||||
|L(z)| 42
|
|
@ -1,3 +1,3 @@
|
|||
\{ i, #
|
||||
|{ i, #
|
||||
e }
|
||||
-> a
|
||||
| a
|
|
@ -1,3 +1,3 @@
|
|||
m
|
||||
^ (-(\w -> m))
|
||||
^ (-(|w| m))
|
||||
w
|
|
@ -0,0 +1 @@
|
|||
|_, _name| 42
|
|
@ -19,9 +19,9 @@
|
|||
header: TypeHeader {
|
||||
name: @0-5 "Model",
|
||||
vars: [
|
||||
@6-14 Identifier {
|
||||
ident: "position",
|
||||
},
|
||||
@6-14 Identifier(
|
||||
"position",
|
||||
),
|
||||
],
|
||||
},
|
||||
ann: @21-164 SpaceBefore(
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
\L #
|
||||
|L #
|
||||
i
|
||||
-> -e
|
||||
| -e
|
|
@ -1,4 +1,4 @@
|
|||
\L,
|
||||
|L,
|
||||
M #
|
||||
Q
|
||||
-> f8
|
||||
| f8
|
|
@ -1,7 +1,7 @@
|
|||
_ = crash ""
|
||||
_ = crash "" ""
|
||||
_ = crash 15 123
|
||||
_ = try foo (\_ -> crash "")
|
||||
_ = try foo (|_| crash "")
|
||||
_ =
|
||||
_ = crash ""
|
||||
crash
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
launchTheNukes! = \{} ->
|
||||
launchTheNukes! = |{}|
|
||||
boom
|
||||
|
||||
launchTheNukes! {}
|
|
@ -1,3 +1,3 @@
|
|||
\j ->
|
||||
e \B -> B
|
||||
|j|
|
||||
e |B| B
|
||||
> s
|
|
@ -1,4 +1,4 @@
|
|||
\{} ->
|
||||
|{}|
|
||||
echo "Welcome to the DMV!"
|
||||
age = readInt
|
||||
|
||||
|
|
|
@ -3,5 +3,5 @@ table :
|
|||
height : Pixels,
|
||||
}
|
||||
-> Table
|
||||
table = \{ height } -> crash "not implemented"
|
||||
table = |{ height }| crash "not implemented"
|
||||
table
|
|
@ -1,4 +1,4 @@
|
|||
f : (Str)a -> (Str)a
|
||||
f = \x -> x
|
||||
f = |x| x
|
||||
|
||||
f ("Str", 42)
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue