mirror of
https://github.com/roc-lang/roc.git
synced 2025-09-28 14:24:45 +00:00
Fixes merge conflict
This commit is contained in:
commit
c00d400d85
54 changed files with 946 additions and 631 deletions
|
@ -46,7 +46,12 @@ fn main() -> io::Result<()> {
|
||||||
)?),
|
)?),
|
||||||
Some(CMD_RUN) => {
|
Some(CMD_RUN) => {
|
||||||
// TODO remove CMD_RUN altogether if it is currently September 2021 or later.
|
// TODO remove CMD_RUN altogether if it is currently September 2021 or later.
|
||||||
println!("`roc run` is deprecated! (You no longer need the `run` - just do `roc [FILE]` instead of `roc run [FILE]` like before.");
|
println!(
|
||||||
|
r#"`roc run` is deprecated!
|
||||||
|
If you're using a prebuilt binary, you no longer need the `run` - just do `roc [FILE]` instead of `roc run [FILE]`.
|
||||||
|
If you're building the compiler from source you'll want to do `cargo run [FILE]` instead of `cargo run run [FILE]`.
|
||||||
|
"#
|
||||||
|
);
|
||||||
|
|
||||||
Ok(1)
|
Ok(1)
|
||||||
}
|
}
|
||||||
|
|
|
@ -88,7 +88,7 @@ comptime {
|
||||||
exportStrFn(str.countSegments, "count_segments");
|
exportStrFn(str.countSegments, "count_segments");
|
||||||
exportStrFn(str.countGraphemeClusters, "count_grapheme_clusters");
|
exportStrFn(str.countGraphemeClusters, "count_grapheme_clusters");
|
||||||
exportStrFn(str.startsWith, "starts_with");
|
exportStrFn(str.startsWith, "starts_with");
|
||||||
exportStrFn(str.startsWithCodePoint, "starts_with_code_point");
|
exportStrFn(str.startsWithCodePt, "starts_with_code_point");
|
||||||
exportStrFn(str.endsWith, "ends_with");
|
exportStrFn(str.endsWith, "ends_with");
|
||||||
exportStrFn(str.strConcatC, "concat");
|
exportStrFn(str.strConcatC, "concat");
|
||||||
exportStrFn(str.strJoinWithC, "joinWith");
|
exportStrFn(str.strJoinWithC, "joinWith");
|
||||||
|
@ -96,7 +96,7 @@ comptime {
|
||||||
exportStrFn(str.strFromIntC, "from_int");
|
exportStrFn(str.strFromIntC, "from_int");
|
||||||
exportStrFn(str.strFromFloatC, "from_float");
|
exportStrFn(str.strFromFloatC, "from_float");
|
||||||
exportStrFn(str.strEqual, "equal");
|
exportStrFn(str.strEqual, "equal");
|
||||||
exportStrFn(str.strToBytesC, "to_bytes");
|
exportStrFn(str.strToUtf8C, "to_utf8");
|
||||||
exportStrFn(str.fromUtf8C, "from_utf8");
|
exportStrFn(str.fromUtf8C, "from_utf8");
|
||||||
exportStrFn(str.fromUtf8RangeC, "from_utf8_range");
|
exportStrFn(str.fromUtf8RangeC, "from_utf8_range");
|
||||||
}
|
}
|
||||||
|
|
|
@ -865,8 +865,8 @@ pub fn startsWith(string: RocStr, prefix: RocStr) callconv(.C) bool {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Str.startsWithCodePoint
|
// Str.startsWithCodePt
|
||||||
pub fn startsWithCodePoint(string: RocStr, prefix: u32) callconv(.C) bool {
|
pub fn startsWithCodePt(string: RocStr, prefix: u32) callconv(.C) bool {
|
||||||
const bytes_len = string.len();
|
const bytes_len = string.len();
|
||||||
const bytes_ptr = string.asU8ptr();
|
const bytes_ptr = string.asU8ptr();
|
||||||
|
|
||||||
|
@ -886,18 +886,18 @@ pub fn startsWithCodePoint(string: RocStr, prefix: u32) callconv(.C) bool {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
test "startsWithCodePoint: ascii char" {
|
test "startsWithCodePt: ascii char" {
|
||||||
const whole = RocStr.init("foobar", 6);
|
const whole = RocStr.init("foobar", 6);
|
||||||
const prefix = 'f';
|
const prefix = 'f';
|
||||||
try expect(startsWithCodePoint(whole, prefix));
|
try expect(startsWithCodePt(whole, prefix));
|
||||||
}
|
}
|
||||||
|
|
||||||
test "startsWithCodePoint: emoji" {
|
test "startsWithCodePt: emoji" {
|
||||||
const yes = RocStr.init("💖foobar", 10);
|
const yes = RocStr.init("💖foobar", 10);
|
||||||
const no = RocStr.init("foobar", 6);
|
const no = RocStr.init("foobar", 6);
|
||||||
const prefix = '💖';
|
const prefix = '💖';
|
||||||
try expect(startsWithCodePoint(yes, prefix));
|
try expect(startsWithCodePt(yes, prefix));
|
||||||
try expect(!startsWithCodePoint(no, prefix));
|
try expect(!startsWithCodePt(no, prefix));
|
||||||
}
|
}
|
||||||
|
|
||||||
test "startsWith: foo starts with fo" {
|
test "startsWith: foo starts with fo" {
|
||||||
|
@ -1129,8 +1129,8 @@ test "RocStr.joinWith: result is big" {
|
||||||
try expect(roc_result.eq(result));
|
try expect(roc_result.eq(result));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Str.toBytes
|
// Str.toUtf8
|
||||||
pub fn strToBytesC(arg: RocStr) callconv(.C) RocList {
|
pub fn strToUtf8C(arg: RocStr) callconv(.C) RocList {
|
||||||
return @call(.{ .modifier = always_inline }, strToBytes, .{arg});
|
return @call(.{ .modifier = always_inline }, strToBytes, .{arg});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -55,7 +55,7 @@ and : Bool, Bool -> Bool
|
||||||
##
|
##
|
||||||
## In some languages, `&&` and `||` are special-cased in the compiler to skip
|
## In some languages, `&&` and `||` are special-cased in the compiler to skip
|
||||||
## evaluating the expression after the operator under certain circumstances.
|
## evaluating the expression after the operator under certain circumstances.
|
||||||
## # In Roc, this is not the case. See the performance notes for [Bool.and] for details.
|
## In Roc, this is not the case. See the performance notes for [Bool.and] for details.
|
||||||
or : Bool, Bool -> Bool
|
or : Bool, Bool -> Bool
|
||||||
|
|
||||||
## Exclusive or
|
## Exclusive or
|
||||||
|
|
|
@ -1,5 +1,21 @@
|
||||||
interface Dict
|
interface Dict
|
||||||
exposes [ isEmpty, map ]
|
exposes
|
||||||
|
[
|
||||||
|
Dict,
|
||||||
|
empty,
|
||||||
|
single,
|
||||||
|
get,
|
||||||
|
walk,
|
||||||
|
insert,
|
||||||
|
len,
|
||||||
|
remove,
|
||||||
|
contains,
|
||||||
|
keys,
|
||||||
|
values,
|
||||||
|
union,
|
||||||
|
intersection,
|
||||||
|
difference
|
||||||
|
]
|
||||||
imports []
|
imports []
|
||||||
|
|
||||||
size : Dict * * -> Nat
|
size : Dict * * -> Nat
|
||||||
|
@ -14,7 +30,7 @@ isEmpty : Dict * * -> Bool
|
||||||
## >>> Dict.map {[ "", "a", "bc" ]} Str.isEmpty
|
## >>> Dict.map {[ "", "a", "bc" ]} Str.isEmpty
|
||||||
##
|
##
|
||||||
## `map` functions like this are common in Roc, and they all work similarly.
|
## `map` functions like this are common in Roc, and they all work similarly.
|
||||||
## See for example #Result.map, #List.map, and #Set.map.
|
## See for example [List.map], [Result.map], and `Set.map`.
|
||||||
map :
|
map :
|
||||||
Dict beforeKey beforeValue,
|
Dict beforeKey beforeValue,
|
||||||
({ key: beforeKey, value: beforeValue } -> { key: afterKey, value: afterValue })
|
({ key: beforeKey, value: beforeValue } -> { key: afterKey, value: afterValue })
|
||||||
|
|
|
@ -1,79 +1,64 @@
|
||||||
interface List2
|
interface List
|
||||||
exposes
|
exposes
|
||||||
[ List
|
[
|
||||||
, single
|
List,
|
||||||
, empty
|
isEmpty,
|
||||||
, repeat
|
get,
|
||||||
, range
|
set,
|
||||||
, reverse
|
append,
|
||||||
, sort
|
map,
|
||||||
, map
|
len,
|
||||||
, mapWithIndex
|
walkBackwards,
|
||||||
, mapOrCancel
|
concat,
|
||||||
, mapOks
|
first,
|
||||||
, update
|
single,
|
||||||
, updater
|
repeat,
|
||||||
, allOks
|
reverse,
|
||||||
, append
|
prepend,
|
||||||
, prepend
|
join,
|
||||||
, concat
|
keepIf,
|
||||||
, join
|
contains,
|
||||||
, joinMap
|
sum,
|
||||||
, oks
|
walk,
|
||||||
, zip
|
last,
|
||||||
, zipMap
|
keepOks,
|
||||||
, keepIf
|
keepErrs,
|
||||||
, dropIf
|
mapWithIndex,
|
||||||
, first
|
map2,
|
||||||
, last
|
map3,
|
||||||
, get
|
product,
|
||||||
, max
|
walkUntil,
|
||||||
, min
|
range,
|
||||||
, put
|
sortWith,
|
||||||
, drop
|
drop,
|
||||||
, append
|
swap
|
||||||
, prepend
|
|
||||||
, dropLast
|
|
||||||
, dropFirst
|
|
||||||
, takeFirst
|
|
||||||
, takeLast
|
|
||||||
, split
|
|
||||||
, sublist
|
|
||||||
, walk
|
|
||||||
, walkBackwards
|
|
||||||
, walkUntil
|
|
||||||
, walkBackwardsUntil
|
|
||||||
, len
|
|
||||||
, isEmpty
|
|
||||||
, contains
|
|
||||||
, all
|
|
||||||
, any
|
|
||||||
]
|
]
|
||||||
imports []
|
imports []
|
||||||
|
|
||||||
## Types
|
## Types
|
||||||
|
|
||||||
## A sequential list of values.
|
## A sequential list of values.
|
||||||
## # >>> [ 1, 2, 3 ] # a list of numbers # # >>> [ "a", "b", "c" ] # a list of strings
|
|
||||||
##
|
##
|
||||||
## >>> [ [ 1.1 ], [], [ 2.2, 3.3 ] ] # a list of lists of floats
|
## >>> [ 1, 2, 3 ] # a list of numbers
|
||||||
|
## >>> [ "a", "b", "c" ] # a list of strings
|
||||||
|
## >>> [ [ 1.1 ], [], [ 2.2, 3.3 ] ] # a list of lists of numbers
|
||||||
##
|
##
|
||||||
## The list [ 1, "a" ] gives an error, because each element in a list must have
|
## The list `[ 1, "a" ]` gives an error, because each element in a list must have
|
||||||
## the same type. If you want to put a mix of #Int and #Str values into a list, try this:
|
## the same type. If you want to put a mix of [I64] and [Str] values into a list, try this:
|
||||||
##
|
##
|
||||||
## ```
|
## ```
|
||||||
## mixedList : List [ IntElem I64, StrElem Str ]*
|
## mixedList : List [ IntElem I64, StrElem Str ]*
|
||||||
## mixedList = [ IntElem 1, IntElem 2, StrElem "a", StrElem "b" ]
|
## mixedList = [ IntElem 1, IntElem 2, StrElem "a", StrElem "b" ]
|
||||||
## ```
|
## ```
|
||||||
##
|
##
|
||||||
## The maximum size of a #List is limited by the amount of heap memory available
|
## The maximum size of a [List] is limited by the amount of heap memory available
|
||||||
## to the current process. If there is not enough memory available, attempting to
|
## to the current process. If there is not enough memory available, attempting to
|
||||||
## create the list could crash. (On Linux, where [overcommit](https://www.etalabs.net/overcommit.html)
|
## create the list could crash. (On Linux, where [overcommit](https://www.etalabs.net/overcommit.html)
|
||||||
## is normally enabled, not having enough memory could result in the list appearing
|
## is normally enabled, not having enough memory could result in the list appearing
|
||||||
## to be created just fine, but then crashing later.)
|
## to be created just fine, but then crashing later.)
|
||||||
##
|
##
|
||||||
## > The theoretical maximum length for a list created in Roc is
|
## > The theoretical maximum length for a list created in Roc is half of
|
||||||
## > #Int.maxNat divided by 2. Attempting to create a list bigger than that
|
## > `Num.maxNat`. Attempting to create a list bigger than that
|
||||||
## > in Roc code will always fail, although in practice it is likely to fail
|
## > in Roc code will always fail, although in practice it is likely to fail
|
||||||
## > at much smaller lengths due to insufficient memory being available.
|
## > at much smaller lengths due to insufficient memory being available.
|
||||||
##
|
##
|
||||||
|
@ -147,13 +132,13 @@ interface List2
|
||||||
##
|
##
|
||||||
## List.first (getRatings 5).bar
|
## List.first (getRatings 5).bar
|
||||||
##
|
##
|
||||||
## This call to #List.first means that even the list in the `bar` field has become
|
## This call to [List.first] means that even the list in the `bar` field has become
|
||||||
## inaccessible. As such, this line will cause the list's refcount to get
|
## inaccessible. As such, this line will cause the list's refcount to get
|
||||||
## decremented all the way to 0. At that point, nothing is referencing the list
|
## decremented all the way to 0. At that point, nothing is referencing the list
|
||||||
## anymore, and its memory will get freed.
|
## anymore, and its memory will get freed.
|
||||||
##
|
##
|
||||||
## Things are different if this is a list of lists instead of a list of numbers.
|
## Things are different if this is a list of lists instead of a list of numbers.
|
||||||
## Let's look at a simpler example using #List.first - first with a list of numbers,
|
## Let's look at a simpler example using [List.first] - first with a list of numbers,
|
||||||
## and then with a list of lists, to see how they differ.
|
## and then with a list of lists, to see how they differ.
|
||||||
##
|
##
|
||||||
## Here's the example using a list of numbers.
|
## Here's the example using a list of numbers.
|
||||||
|
@ -165,7 +150,7 @@ interface List2
|
||||||
##
|
##
|
||||||
## first
|
## first
|
||||||
##
|
##
|
||||||
## It makes a list, calls #List.first and #List.last on it, and then returns `first`.
|
## It makes a list, calls [List.first] and [List.last] on it, and then returns `first`.
|
||||||
##
|
##
|
||||||
## Here's the equivalent code with a list of lists:
|
## Here's the equivalent code with a list of lists:
|
||||||
##
|
##
|
||||||
|
@ -189,12 +174,12 @@ interface List2
|
||||||
## their own refcounts - to go inside that list. (The empty list at the end
|
## their own refcounts - to go inside that list. (The empty list at the end
|
||||||
## does not use heap memory, and thus has no refcount.)
|
## does not use heap memory, and thus has no refcount.)
|
||||||
##
|
##
|
||||||
## At the end, we once again call #List.first on the list, but this time
|
## At the end, we once again call [List.first] on the list, but this time
|
||||||
##
|
##
|
||||||
## * Copying small lists (64 elements or fewer) is typically slightly faster than copying small persistent data structures. This is because, at small sizes, persistent data structures tend to be thin wrappers around flat arrays anyway. They don't have any copying advantage until crossing a certain minimum size threshold.
|
## * Copying small lists (64 elements or fewer) is typically slightly faster than copying small persistent data structures. This is because, at small sizes, persistent data structures tend to be thin wrappers around flat arrays anyway. They don't have any copying advantage until crossing a certain minimum size threshold.
|
||||||
## * Even when copying is faster, other list operations may still be slightly slower with persistent data structures. For example, even if it were a persistent data structure, #List.map, #List.fold, and #List.keepIf would all need to traverse every element in the list and build up the result from scratch. These operations are all
|
## * Even when copying is faster, other list operations may still be slightly slower with persistent data structures. For example, even if it were a persistent data structure, [List.map], [List.walk], and [List.keepIf] would all need to traverse every element in the list and build up the result from scratch. These operations are all
|
||||||
## * Roc's compiler optimizes many list operations into in-place mutations behind the scenes, depending on how the list is being used. For example, #List.map, #List.keepIf, and #List.set can all be optimized to perform in-place mutations.
|
## * Roc's compiler optimizes many list operations into in-place mutations behind the scenes, depending on how the list is being used. For example, [List.map], [List.keepIf], and [List.set] can all be optimized to perform in-place mutations.
|
||||||
## * If possible, it is usually best for performance to use large lists in a way where the optimizer can turn them into in-place mutations. If this is not possible, a persistent data structure might be faster - but this is a rare enough scenario that it would not be good for the average Roc program's performance if this were the way #List worked by default. Instead, you can look outside Roc's standard modules for an implementation of a persistent data structure - likely built using #List under the hood!
|
## * If possible, it is usually best for performance to use large lists in a way where the optimizer can turn them into in-place mutations. If this is not possible, a persistent data structure might be faster - but this is a rare enough scenario that it would not be good for the average Roc program's performance if this were the way [List] worked by default. Instead, you can look outside Roc's standard modules for an implementation of a persistent data structure - likely built using [List] under the hood!
|
||||||
List elem : [ @List elem ]
|
List elem : [ @List elem ]
|
||||||
|
|
||||||
## Initialize
|
## Initialize
|
||||||
|
@ -262,18 +247,18 @@ sortDesc : List elem, (elem -> Num *) -> List elem
|
||||||
## > List.map [ "", "a", "bc" ] Str.isEmpty
|
## > List.map [ "", "a", "bc" ] Str.isEmpty
|
||||||
##
|
##
|
||||||
## `map` functions like this are common in Roc, and they all work similarly.
|
## `map` functions like this are common in Roc, and they all work similarly.
|
||||||
## See for example #Result.map, #Set.map, and #Map.map.
|
## See for example `Set.map`, `Dict.map`, and [Result.map].
|
||||||
map : List before, (before -> after) -> List after
|
map : List before, (before -> after) -> List after
|
||||||
|
|
||||||
## This works like #List.map, except it also passes the index
|
## This works like [List.map], except it also passes the index
|
||||||
## of the element to the conversion function.
|
## of the element to the conversion function.
|
||||||
mapWithIndex : List before, (before, Nat -> after) -> List after
|
mapWithIndex : List before, (before, Nat -> after) -> List after
|
||||||
|
|
||||||
## This works like #List.map, except at any time you can return `Err` to
|
## This works like [List.map], except at any time you can return `Err` to
|
||||||
## cancel the entire operation immediately, and return that #Err.
|
## cancel the entire operation immediately, and return that #Err.
|
||||||
mapOrCancel : List before, (before -> Result after err) -> Result (List after) err
|
mapOrCancel : List before, (before -> Result after err) -> Result (List after) err
|
||||||
|
|
||||||
## This works like #List.map, except only the transformed values that are
|
## This works like [List.map], except only the transformed values that are
|
||||||
## wrapped in `Ok` are kept. Any that are wrapped in `Err` are dropped.
|
## wrapped in `Ok` are kept. Any that are wrapped in `Err` are dropped.
|
||||||
##
|
##
|
||||||
## >>> List.mapOks [ [ "a", "b" ], [], [], [ "c", "d", "e" ] ] List.last
|
## >>> List.mapOks [ [ "a", "b" ], [], [], [ "c", "d", "e" ] ] List.last
|
||||||
|
@ -287,16 +272,16 @@ mapOks : List before, (before -> Result after *) -> List after
|
||||||
## the given function.
|
## the given function.
|
||||||
##
|
##
|
||||||
## For a version of this which gives you more control over when to perform
|
## For a version of this which gives you more control over when to perform
|
||||||
## the transformation, see #List.updater
|
## the transformation, see `List.updater`
|
||||||
##
|
##
|
||||||
## ## Performance notes
|
## ## Performance notes
|
||||||
##
|
##
|
||||||
## In particular when updating nested collections, this is potentially much more
|
## In particular when updating nested collections, this is potentially much more
|
||||||
## efficient than using #List.get to obtain the element, transforming it,
|
## efficient than using [List.get] to obtain the element, transforming it,
|
||||||
## and then putting it back in the same place.
|
## and then putting it back in the same place.
|
||||||
update : List elem, Nat, (elem -> elem) -> List elem
|
update : List elem, Nat, (elem -> elem) -> List elem
|
||||||
|
|
||||||
## A more flexible version of #List.update, which returns an "updater" function
|
## A more flexible version of `List.update`, which returns an "updater" function
|
||||||
## that lets you delay performing the update until later.
|
## that lets you delay performing the update until later.
|
||||||
updater : List elem, Nat -> { elem, new : (elem -> List elem) }
|
updater : List elem, Nat -> { elem, new : (elem -> List elem) }
|
||||||
|
|
||||||
|
@ -337,15 +322,15 @@ concat : List elem, List elem -> List elem
|
||||||
## >>> List.join []
|
## >>> List.join []
|
||||||
join : List (List elem) -> List elem
|
join : List (List elem) -> List elem
|
||||||
|
|
||||||
## Like #List.map, except the transformation function wraps the return value
|
## Like [List.map], except the transformation function wraps the return value
|
||||||
## in a list. At the end, all the lists get joined together into one list.
|
## in a list. At the end, all the lists get joined together into one list.
|
||||||
joinMap : List before, (before -> List after) -> List after
|
joinMap : List before, (before -> List after) -> List after
|
||||||
|
|
||||||
## Like #List.join, but only keeps elements tagged with `Ok`. Elements
|
## Like [List.join], but only keeps elements tagged with `Ok`. Elements
|
||||||
## tagged with `Err` are dropped.
|
## tagged with `Err` are dropped.
|
||||||
##
|
##
|
||||||
## This can be useful after using an operation that returns a #Result
|
## This can be useful after using an operation that returns a #Result
|
||||||
## on each element of a list, for example #List.first:
|
## on each element of a list, for example [List.first]:
|
||||||
##
|
##
|
||||||
## >>> [ [ 1, 2, 3 ], [], [], [ 4, 5 ] ]
|
## >>> [ [ 1, 2, 3 ], [], [], [ 4, 5 ] ]
|
||||||
## >>> |> List.map List.first
|
## >>> |> List.map List.first
|
||||||
|
@ -387,16 +372,16 @@ zipMap : List a, List b, (a, b -> c) -> List c
|
||||||
##
|
##
|
||||||
## ## Performance Details
|
## ## Performance Details
|
||||||
##
|
##
|
||||||
## #List.keepIf always returns a list that takes up exactly the same amount
|
## [List.keepIf] always returns a list that takes up exactly the same amount
|
||||||
## of memory as the original, even if its length decreases. This is becase it
|
## of memory as the original, even if its length decreases. This is becase it
|
||||||
## can't know in advance exactly how much space it will need, and if it guesses a
|
## can't know in advance exactly how much space it will need, and if it guesses a
|
||||||
## length that's too low, it would have to re-allocate.
|
## length that's too low, it would have to re-allocate.
|
||||||
##
|
##
|
||||||
## (If you want to do an operation like this which reduces the memory footprint
|
## (If you want to do an operation like this which reduces the memory footprint
|
||||||
## of the resulting list, you can do two passes over the lis with #List.fold - one
|
## of the resulting list, you can do two passes over the lis with [List.walk] - one
|
||||||
## to calculate the precise new size, and another to populate the new list.)
|
## to calculate the precise new size, and another to populate the new list.)
|
||||||
##
|
##
|
||||||
## If given a unique list, #List.keepIf will mutate it in place to assemble the appropriate list.
|
## If given a unique list, [List.keepIf] will mutate it in place to assemble the appropriate list.
|
||||||
## If that happens, this function will not allocate any new memory on the heap.
|
## If that happens, this function will not allocate any new memory on the heap.
|
||||||
## If all elements in the list end up being kept, Roc will return the original
|
## If all elements in the list end up being kept, Roc will return the original
|
||||||
## list unaltered.
|
## list unaltered.
|
||||||
|
@ -410,7 +395,7 @@ keepIf : List elem, (elem -> Bool) -> List elem
|
||||||
##
|
##
|
||||||
## ## Performance Details
|
## ## Performance Details
|
||||||
##
|
##
|
||||||
## #List.dropIf has the same performance characteristics as #List.keepIf.
|
## `List.dropIf` has the same performance characteristics as [List.keepIf].
|
||||||
## See its documentation for details on those characteristics!
|
## See its documentation for details on those characteristics!
|
||||||
dropIf : List elem, (elem -> Bool) -> List elem
|
dropIf : List elem, (elem -> Bool) -> List elem
|
||||||
|
|
||||||
|
@ -437,14 +422,14 @@ min : List (Num a) -> Result (Num a) [ ListWasEmpty ]*
|
||||||
## If the given index is outside the bounds of the list, returns the original
|
## If the given index is outside the bounds of the list, returns the original
|
||||||
## list unmodified.
|
## list unmodified.
|
||||||
##
|
##
|
||||||
## To drop the element at a given index, instead of replacing it, see #List.drop.
|
## To drop the element at a given index, instead of replacing it, see [List.drop].
|
||||||
set : List elem, Nat, elem -> List elem
|
set : List elem, Nat, elem -> List elem
|
||||||
|
|
||||||
## Drops the element at the given index from the list.
|
## Drops the element at the given index from the list.
|
||||||
##
|
##
|
||||||
## This has no effect if the given index is outside the bounds of the list.
|
## This has no effect if the given index is outside the bounds of the list.
|
||||||
##
|
##
|
||||||
## To replace the element at a given index, instead of dropping it, see #List.set.
|
## To replace the element at a given index, instead of dropping it, see [List.set].
|
||||||
drop : List elem, Nat -> List elem
|
drop : List elem, Nat -> List elem
|
||||||
|
|
||||||
## Adds a new element to the end of the list.
|
## Adds a new element to the end of the list.
|
||||||
|
@ -466,12 +451,12 @@ append : List elem, elem -> List elem
|
||||||
## ## Performance Details
|
## ## Performance Details
|
||||||
##
|
##
|
||||||
## This always clones the entire list, even when given a Unique list. That means
|
## This always clones the entire list, even when given a Unique list. That means
|
||||||
## it runs about as fast as #List.addLast when both are given a Shared list.
|
## it runs about as fast as `List.addLast` when both are given a Shared list.
|
||||||
##
|
##
|
||||||
## If you have a Unique list instead, #List.append will run much faster than
|
## If you have a Unique list instead, [List.append] will run much faster than
|
||||||
## #List.prepend except in the specific case where the list has no excess capacity,
|
## [List.append] except in the specific case where the list has no excess capacity,
|
||||||
## and needs to *clone and grow*. In that uncommon case, both #List.append and
|
## and needs to *clone and grow*. In that uncommon case, both [List.append] and
|
||||||
## #List.prepend will run at about the same speed—since #List.prepend always
|
## [List.append] will run at about the same speed—since [List.append] always
|
||||||
## has to clone and grow.
|
## has to clone and grow.
|
||||||
##
|
##
|
||||||
## | Unique list | Shared list |
|
## | Unique list | Shared list |
|
||||||
|
@ -493,11 +478,11 @@ prepend : List elem, elem -> List elem
|
||||||
##
|
##
|
||||||
## ## Performance Details
|
## ## Performance Details
|
||||||
##
|
##
|
||||||
## Calling #List.pop on a Unique list runs extremely fast. It's essentially
|
## Calling `List.pop` on a Unique list runs extremely fast. It's essentially
|
||||||
## the same as a #List.last except it also returns the #List it was given,
|
## the same as a [List.last] except it also returns the [List] it was given,
|
||||||
## with its length decreased by 1.
|
## with its length decreased by 1.
|
||||||
##
|
##
|
||||||
## In contrast, calling #List.pop on a Shared list creates a new list, then
|
## In contrast, calling `List.pop` on a Shared list creates a new list, then
|
||||||
## copies over every element in the original list except the last one. This
|
## copies over every element in the original list except the last one. This
|
||||||
## takes much longer.
|
## takes much longer.
|
||||||
dropLast : List elem -> Result { others : List elem, last : elem } [ ListWasEmpty ]*
|
dropLast : List elem -> Result { others : List elem, last : elem } [ ListWasEmpty ]*
|
||||||
|
@ -511,8 +496,8 @@ dropLast : List elem -> Result { others : List elem, last : elem } [ ListWasEmpt
|
||||||
##
|
##
|
||||||
## ## Performance Details
|
## ## Performance Details
|
||||||
##
|
##
|
||||||
## When calling either #List.dropFirst or #List.dropLast on a Unique list, #List.dropLast
|
## When calling either `List.dropFirst` or `List.dropLast` on a Unique list, `List.dropLast`
|
||||||
## runs *much* faster. This is because for #List.dropLast, removing the last element
|
## runs *much* faster. This is because for `List.dropLast`, removing the last element
|
||||||
## in-place is as easy as reducing the length of the list by 1. In contrast,
|
## in-place is as easy as reducing the length of the list by 1. In contrast,
|
||||||
## removing the first element from the list involves copying every other element
|
## removing the first element from the list involves copying every other element
|
||||||
## in the list into the index before it - which is massively more costly.
|
## in the list into the index before it - which is massively more costly.
|
||||||
|
@ -521,8 +506,8 @@ dropLast : List elem -> Result { others : List elem, last : elem } [ ListWasEmpt
|
||||||
##
|
##
|
||||||
## | Unique list | Shared list |
|
## | Unique list | Shared list |
|
||||||
##-----------+----------------------------------+---------------------------------+
|
##-----------+----------------------------------+---------------------------------+
|
||||||
## dropFirst | #List.last + length change | #List.last + clone rest of list |
|
## dropFirst | [List.last] + length change | [List.last] + clone rest of list |
|
||||||
## dropLast | #List.last + clone rest of list | #List.last + clone rest of list |
|
## dropLast | [List.last] + clone rest of list | [List.last] + clone rest of list |
|
||||||
dropFirst : List elem -> Result { first: elem, others : List elem } [ ListWasEmpty ]*
|
dropFirst : List elem -> Result { first: elem, others : List elem } [ ListWasEmpty ]*
|
||||||
|
|
||||||
## Returns the given number of elements from the beginning of the list.
|
## Returns the given number of elements from the beginning of the list.
|
||||||
|
@ -534,21 +519,21 @@ dropFirst : List elem -> Result { first: elem, others : List elem } [ ListWasEmp
|
||||||
##
|
##
|
||||||
## >>> List.takeFirst 5 [ 1, 2 ]
|
## >>> List.takeFirst 5 [ 1, 2 ]
|
||||||
##
|
##
|
||||||
## To *remove* elements from the beginning of the list, use #List.takeLast.
|
## To *remove* elements from the beginning of the list, use `List.takeLast`.
|
||||||
##
|
##
|
||||||
## To remove elements from both the beginning and end of the list,
|
## To remove elements from both the beginning and end of the list,
|
||||||
## use #List.sublist.
|
## use `List.sublist`.
|
||||||
##
|
##
|
||||||
## To split the list into two lists, use #List.split.
|
## To split the list into two lists, use `List.split`.
|
||||||
##
|
##
|
||||||
## ## Performance Details
|
## ## Performance Details
|
||||||
##
|
##
|
||||||
## When given a Unique list, this runs extremely fast. It sets the list's length
|
## When given a Unique list, this runs extremely fast. It sets the list's length
|
||||||
## to the given length value, and frees the leftover elements. This runs very
|
## to the given length value, and frees the leftover elements. This runs very
|
||||||
## slightly faster than #List.takeLast.
|
## slightly faster than `List.takeLast`.
|
||||||
##
|
##
|
||||||
## In fact, `List.takeFirst 1 list` runs faster than `List.first list` when given
|
## In fact, `List.takeFirst 1 list` runs faster than `List.first list` when given
|
||||||
## a Unique list, because #List.first returns the first element as well -
|
## a Unique list, because [List.first] returns the first element as well -
|
||||||
## which introduces a conditional bounds check as well as a memory load.
|
## which introduces a conditional bounds check as well as a memory load.
|
||||||
takeFirst : List elem, Nat -> List elem
|
takeFirst : List elem, Nat -> List elem
|
||||||
|
|
||||||
|
@ -561,22 +546,22 @@ takeFirst : List elem, Nat -> List elem
|
||||||
##
|
##
|
||||||
## >>> List.takeLast 5 [ 1, 2 ]
|
## >>> List.takeLast 5 [ 1, 2 ]
|
||||||
##
|
##
|
||||||
## To *remove* elements from the end of the list, use #List.takeFirst.
|
## To *remove* elements from the end of the list, use `List.takeFirst`.
|
||||||
##
|
##
|
||||||
## To remove elements from both the beginning and end of the list,
|
## To remove elements from both the beginning and end of the list,
|
||||||
## use #List.sublist.
|
## use `List.sublist`.
|
||||||
##
|
##
|
||||||
## To split the list into two lists, use #List.split.
|
## To split the list into two lists, use `List.split`.
|
||||||
##
|
##
|
||||||
## ## Performance Details
|
## ## Performance Details
|
||||||
##
|
##
|
||||||
## When given a Unique list, this runs extremely fast. It moves the list's
|
## When given a Unique list, this runs extremely fast. It moves the list's
|
||||||
## pointer to the index at the given length value, updates its length,
|
## pointer to the index at the given length value, updates its length,
|
||||||
## and frees the leftover elements. This runs very nearly as fast as
|
## and frees the leftover elements. This runs very nearly as fast as
|
||||||
## #List.takeFirst on a Unique list.
|
## `List.takeFirst` on a Unique list.
|
||||||
##
|
##
|
||||||
## In fact, `List.takeLast 1 list` runs faster than `List.first list` when given
|
## In fact, `List.takeLast 1 list` runs faster than `List.first list` when given
|
||||||
## a Unique list, because #List.first returns the first element as well -
|
## a Unique list, because [List.first] returns the first element as well -
|
||||||
## which introduces a conditional bounds check as well as a memory load.
|
## which introduces a conditional bounds check as well as a memory load.
|
||||||
takeLast : List elem, Nat -> List elem
|
takeLast : List elem, Nat -> List elem
|
||||||
|
|
||||||
|
@ -603,7 +588,7 @@ split : List elem, Nat -> { before: List elem, others: List elem }
|
||||||
## >>> List.sublist { start: 2, len: 10 } [ 1, 2, 3, 4, 5 ]
|
## >>> List.sublist { start: 2, len: 10 } [ 1, 2, 3, 4, 5 ]
|
||||||
##
|
##
|
||||||
## > If you want a sublist which goes all the way to the end of the list, no
|
## > If you want a sublist which goes all the way to the end of the list, no
|
||||||
## > matter how long the list is, #List.takeLast can do that more efficiently.
|
## > matter how long the list is, `List.takeLast` can do that more efficiently.
|
||||||
##
|
##
|
||||||
## Some languages have a function called **`slice`** which works similarly to this.
|
## Some languages have a function called **`slice`** which works similarly to this.
|
||||||
sublist : List elem, { start : Nat, len : Nat } -> List elem
|
sublist : List elem, { start : Nat, len : Nat } -> List elem
|
||||||
|
@ -623,7 +608,7 @@ sublist : List elem, { start : Nat, len : Nat } -> List elem
|
||||||
## * `state` starts at 0 (because of `start: 0`)
|
## * `state` starts at 0 (because of `start: 0`)
|
||||||
## * Each `step` runs `Num.add state elem`, and the return value becomes the new `state`.
|
## * Each `step` runs `Num.add state elem`, and the return value becomes the new `state`.
|
||||||
##
|
##
|
||||||
## Here is a table of how `state` changes as #List.walk walks over the elements
|
## Here is a table of how `state` changes as [List.walk] walks over the elements
|
||||||
## `[ 2, 4, 8 ]` using #Num.add as its `step` function to determine the next `state`.
|
## `[ 2, 4, 8 ]` using #Num.add as its `step` function to determine the next `state`.
|
||||||
##
|
##
|
||||||
## `state` | `elem` | `step state elem` (`Num.add state elem`)
|
## `state` | `elem` | `step state elem` (`Num.add state elem`)
|
||||||
|
@ -650,27 +635,27 @@ walk : List elem, { start : state, step : (state, elem -> state) } -> state
|
||||||
## `fold`, `foldRight`, or `foldr`.
|
## `fold`, `foldRight`, or `foldr`.
|
||||||
walkBackwards : List elem, { start : state, step : (state, elem -> state) } -> state
|
walkBackwards : List elem, { start : state, step : (state, elem -> state) } -> state
|
||||||
|
|
||||||
## Same as #List.walk, except you can stop walking early.
|
## Same as [List.walk], except you can stop walking early.
|
||||||
##
|
##
|
||||||
## ## Performance Details
|
## ## Performance Details
|
||||||
##
|
##
|
||||||
## Compared to #List.walk, this can potentially visit fewer elements (which can
|
## Compared to [List.walk], this can potentially visit fewer elements (which can
|
||||||
## improve performance) at the cost of making each step take longer.
|
## improve performance) at the cost of making each step take longer.
|
||||||
## However, the added cost to each step is extremely small, and can easily
|
## However, the added cost to each step is extremely small, and can easily
|
||||||
## be outweighed if it results in skipping even a small number of elements.
|
## be outweighed if it results in skipping even a small number of elements.
|
||||||
##
|
##
|
||||||
## As such, it is typically better for performance to use this over #List.walk
|
## As such, it is typically better for performance to use this over [List.walk]
|
||||||
## if returning `Done` earlier than the last element is expected to be common.
|
## if returning `Done` earlier than the last element is expected to be common.
|
||||||
walkUntil : List elem, { start : state, step : (state, elem -> [ Continue state, Done state ]) } -> state
|
walkUntil : List elem, { start : state, step : (state, elem -> [ Continue state, Done state ]) } -> state
|
||||||
|
|
||||||
# Same as #List.walkBackwards, except you can stop walking early.
|
# Same as [List.walk]Backwards, except you can stop walking early.
|
||||||
walkBackwardsUntil : List elem, { start : state, step : (state, elem -> [ Continue state, Done state ]) } -> state
|
walkBackwardsUntil : List elem, { start : state, step : (state, elem -> [ Continue state, Done state ]) } -> state
|
||||||
|
|
||||||
## Check
|
## Check
|
||||||
|
|
||||||
## Returns the length of the list - the number of elements it contains.
|
## Returns the length of the list - the number of elements it contains.
|
||||||
##
|
##
|
||||||
## One #List can store up to 2,147,483,648 elements (just over 2 billion), which
|
## One [List] can store up to 2,147,483,648 elements (just over 2 billion), which
|
||||||
## is exactly equal to the highest valid #I32 value. This means the #U32 this function
|
## is exactly equal to the highest valid #I32 value. This means the #U32 this function
|
||||||
## returns can always be safely converted to an #I32 without losing any data.
|
## returns can always be safely converted to an #I32 without losing any data.
|
||||||
len : List * -> Nat
|
len : List * -> Nat
|
||||||
|
|
|
@ -1,10 +1,97 @@
|
||||||
interface Num2
|
interface Num
|
||||||
exposes [ Num, neg, abs, add, sub, mul, isOdd, isEven, isPositive, isNegative, isZero ]
|
exposes
|
||||||
|
[
|
||||||
|
Num,
|
||||||
|
Int,
|
||||||
|
Float,
|
||||||
|
Natural,
|
||||||
|
Nat,
|
||||||
|
Decimal,
|
||||||
|
Dec,
|
||||||
|
Integer,
|
||||||
|
FloatingPoint,
|
||||||
|
I128,
|
||||||
|
U128,
|
||||||
|
I64,
|
||||||
|
U64,
|
||||||
|
I32,
|
||||||
|
U32,
|
||||||
|
I16,
|
||||||
|
U16,
|
||||||
|
I8,
|
||||||
|
U8,
|
||||||
|
F64,
|
||||||
|
F32,
|
||||||
|
maxInt,
|
||||||
|
minInt,
|
||||||
|
maxFloat,
|
||||||
|
minFloat,
|
||||||
|
abs,
|
||||||
|
neg,
|
||||||
|
add,
|
||||||
|
sub,
|
||||||
|
mul,
|
||||||
|
isLt,
|
||||||
|
isLte,
|
||||||
|
isGt,
|
||||||
|
isGte,
|
||||||
|
toFloat,
|
||||||
|
sin,
|
||||||
|
cos,
|
||||||
|
tan,
|
||||||
|
isZero,
|
||||||
|
isEven,
|
||||||
|
isOdd,
|
||||||
|
isPositive,
|
||||||
|
isNegative,
|
||||||
|
rem,
|
||||||
|
div,
|
||||||
|
divFloor,
|
||||||
|
modInt,
|
||||||
|
modFloat,
|
||||||
|
sqrt,
|
||||||
|
log,
|
||||||
|
round,
|
||||||
|
compare,
|
||||||
|
pow,
|
||||||
|
ceiling,
|
||||||
|
powInt,
|
||||||
|
floor,
|
||||||
|
addWrap,
|
||||||
|
addChecked,
|
||||||
|
atan,
|
||||||
|
acos,
|
||||||
|
Signed128,
|
||||||
|
Signed64,
|
||||||
|
Signed32,
|
||||||
|
Signed16,
|
||||||
|
Signed8,
|
||||||
|
Unsigned128,
|
||||||
|
Unsigned64,
|
||||||
|
Unsigned32,
|
||||||
|
Unsigned16,
|
||||||
|
Unsigned8,
|
||||||
|
Binary64,
|
||||||
|
Binary32,
|
||||||
|
bitwiseAnd,
|
||||||
|
bitwiseXor,
|
||||||
|
bitwiseOr,
|
||||||
|
shiftLeftBy,
|
||||||
|
shiftRightBy,
|
||||||
|
shiftRightZfBy,
|
||||||
|
subWrap,
|
||||||
|
subChecked,
|
||||||
|
mulWrap,
|
||||||
|
mulChecked,
|
||||||
|
intCast,
|
||||||
|
maxI128,
|
||||||
|
isMultipleOf
|
||||||
|
]
|
||||||
imports []
|
imports []
|
||||||
|
|
||||||
## Types
|
## ## Types
|
||||||
|
|
||||||
## Represents a number that could be either an [Int] or a [Frac].
|
## Represents a number that could be either an [Int] or a [Float].
|
||||||
##
|
##
|
||||||
## This is useful for functions that can work on either, for example #Num.add, whose type is:
|
## This is useful for functions that can work on either, for example #Num.add, whose type is:
|
||||||
##
|
##
|
||||||
|
@ -19,8 +106,8 @@ interface Num2
|
||||||
## technically has the type `Num (Integer *)`, so when you pass two of them to
|
## technically has the type `Num (Integer *)`, so when you pass two of them to
|
||||||
## [Num.add], the answer you get is `2 : Num (Integer *)`.
|
## [Num.add], the answer you get is `2 : Num (Integer *)`.
|
||||||
##
|
##
|
||||||
## The type [`Frac a`](#Frac) is defined to be an alias for `Num (Fraction a)`,
|
## The type [`Float a`]([Float]) is defined to be an alias for `Num (Fraction a)`,
|
||||||
## so `3.0 : Num (Fraction *)` is the same value as `3.0 : Frac *`.
|
## so `3.0 : Num (Fraction *)` is the same value as `3.0 : Float *`.
|
||||||
## Similarly, the type [`Int a`](#Int) is defined to be an alias for
|
## Similarly, the type [`Int a`](#Int) is defined to be an alias for
|
||||||
## `Num (Integer a)`, so `2 : Num (Integer *)` is the same value as
|
## `Num (Integer a)`, so `2 : Num (Integer *)` is the same value as
|
||||||
## `2 : Int *`.
|
## `2 : Int *`.
|
||||||
|
@ -40,7 +127,7 @@ interface Num2
|
||||||
## ends up having the type `Nat`.
|
## ends up having the type `Nat`.
|
||||||
##
|
##
|
||||||
## Sometimes number literals don't become more specific. For example,
|
## Sometimes number literals don't become more specific. For example,
|
||||||
## the [Num.toStr] function has the type `Num * -> Str`. This means that
|
## the `Num.toStr` function has the type `Num * -> Str`. This means that
|
||||||
## when calling `Num.toStr (5 + 6)`, the expression `(5 + 6)`
|
## when calling `Num.toStr (5 + 6)`, the expression `(5 + 6)`
|
||||||
## still has the type `Num *`. When this happens, `Num *` defaults to
|
## still has the type `Num *`. When this happens, `Num *` defaults to
|
||||||
## being an [I64] - so this addition expression would overflow
|
## being an [I64] - so this addition expression would overflow
|
||||||
|
@ -97,7 +184,7 @@ Num a : [ @Num a ]
|
||||||
## [Dec] typically takes slightly less time than [F64] to perform addition and
|
## [Dec] typically takes slightly less time than [F64] to perform addition and
|
||||||
## subtraction, but 10-20 times longer to perform multiplication and division.
|
## subtraction, but 10-20 times longer to perform multiplication and division.
|
||||||
## [sqrt] and trigonometry are massively slower with [Dec] than with [F64].
|
## [sqrt] and trigonometry are massively slower with [Dec] than with [F64].
|
||||||
Dec : Frac [ @Decimal128 ]
|
Dec : Float [ @Decimal128 ]
|
||||||
|
|
||||||
## A fixed-size number with a fractional component.
|
## A fixed-size number with a fractional component.
|
||||||
##
|
##
|
||||||
|
@ -166,7 +253,7 @@ Dec : Frac [ @Decimal128 ]
|
||||||
## loops and conditionals. If you need to do performance-critical trigonometry
|
## loops and conditionals. If you need to do performance-critical trigonometry
|
||||||
## or square roots, either [F64] or [F32] is probably a better choice than the
|
## or square roots, either [F64] or [F32] is probably a better choice than the
|
||||||
## usual default choice of [Dec], despite the precision problems they bring.
|
## usual default choice of [Dec], despite the precision problems they bring.
|
||||||
Frac a : Num [ @Fraction a ]
|
Float a : Num [ @Fraction a ]
|
||||||
|
|
||||||
## A fixed-size integer - that is, a number with no fractional component.
|
## A fixed-size integer - that is, a number with no fractional component.
|
||||||
##
|
##
|
||||||
|
@ -331,7 +418,7 @@ Int size : Num [ @Int size ]
|
||||||
##
|
##
|
||||||
## >>> Num.neg 0.0
|
## >>> Num.neg 0.0
|
||||||
##
|
##
|
||||||
## This is safe to use with any #Frac, but it can cause overflow when used with certain #Int values.
|
## This is safe to use with any [Float], but it can cause overflow when used with certain #Int values.
|
||||||
##
|
##
|
||||||
## For example, calling #Num.neg on the lowest value of a signed integer (such as #Int.lowestI64 or #Int.lowestI32) will cause overflow.
|
## For example, calling #Num.neg on the lowest value of a signed integer (such as #Int.lowestI64 or #Int.lowestI32) will cause overflow.
|
||||||
## This is because, for any given size of signed integer (32-bit, 64-bit, etc.) its negated lowest value turns out to be 1 higher than
|
## This is because, for any given size of signed integer (32-bit, 64-bit, etc.) its negated lowest value turns out to be 1 higher than
|
||||||
|
@ -339,7 +426,7 @@ Int size : Num [ @Int size ]
|
||||||
##
|
##
|
||||||
## Additionally, calling #Num.neg on any unsigned integer (such as any #U64 or #U32 value) other than zero will cause overflow.
|
## Additionally, calling #Num.neg on any unsigned integer (such as any #U64 or #U32 value) other than zero will cause overflow.
|
||||||
##
|
##
|
||||||
## (It will never crash when given a #Frac, however, because of how floating point numbers represent positive and negative numbers.)
|
## (It will never crash when given a [Float], however, because of how floating point numbers represent positive and negative numbers.)
|
||||||
neg : Num a -> Num a
|
neg : Num a -> Num a
|
||||||
|
|
||||||
## Return the absolute value of the number.
|
## Return the absolute value of the number.
|
||||||
|
@ -356,7 +443,7 @@ neg : Num a -> Num a
|
||||||
##
|
##
|
||||||
## >>> Num.abs 0.0
|
## >>> Num.abs 0.0
|
||||||
##
|
##
|
||||||
## This is safe to use with any #Frac, but it can cause overflow when used with certain #Int values.
|
## This is safe to use with any [Float], but it can cause overflow when used with certain #Int values.
|
||||||
##
|
##
|
||||||
## For example, calling #Num.abs on the lowest value of a signed integer (such as #Int.lowestI64 or #Int.lowestI32) will cause overflow.
|
## For example, calling #Num.abs on the lowest value of a signed integer (such as #Int.lowestI64 or #Int.lowestI32) will cause overflow.
|
||||||
## This is because, for any given size of signed integer (32-bit, 64-bit, etc.) its negated lowest value turns out to be 1 higher than
|
## This is because, for any given size of signed integer (32-bit, 64-bit, etc.) its negated lowest value turns out to be 1 higher than
|
||||||
|
@ -390,7 +477,7 @@ isOdd : Num * -> Bool
|
||||||
|
|
||||||
## Add two numbers of the same type.
|
## Add two numbers of the same type.
|
||||||
##
|
##
|
||||||
## (To add an #Int and a #Frac, first convert one so that they both have the same type. There are functions in the [`Frac`](/Frac) module that can convert both #Int to #Frac and the other way around.)
|
## (To add an #Int and a [Float], first convert one so that they both have the same type. There are functions in the [`Frac`](/Frac) module that can convert both #Int to [Float] and the other way around.)
|
||||||
##
|
##
|
||||||
## `a + b` is shorthand for `Num.add a b`.
|
## `a + b` is shorthand for `Num.add a b`.
|
||||||
##
|
##
|
||||||
|
@ -417,7 +504,7 @@ addCheckOverflow : Num a, Num a -> Result (Num a) [ Overflow ]*
|
||||||
|
|
||||||
## Subtract two numbers of the same type.
|
## Subtract two numbers of the same type.
|
||||||
##
|
##
|
||||||
## (To subtract an #Int and a #Frac, first convert one so that they both have the same type. There are functions in the [`Frac`](/Frac) module that can convert both #Int to #Frac and the other way around.)
|
## (To subtract an #Int and a [Float], first convert one so that they both have the same type. There are functions in the [`Frac`](/Frac) module that can convert both #Int to [Float] and the other way around.)
|
||||||
##
|
##
|
||||||
## `a - b` is shorthand for `Num.sub a b`.
|
## `a - b` is shorthand for `Num.sub a b`.
|
||||||
##
|
##
|
||||||
|
@ -444,7 +531,7 @@ subCheckOverflow : Num a, Num a -> Result (Num a) [ Overflow ]*
|
||||||
|
|
||||||
## Multiply two numbers of the same type.
|
## Multiply two numbers of the same type.
|
||||||
##
|
##
|
||||||
## (To multiply an #Int and a #Frac, first convert one so that they both have the same type. There are functions in the [`Frac`](/Frac) module that can convert both #Int to #Frac and the other way around.)
|
## (To multiply an #Int and a [Float], first convert one so that they both have the same type. There are functions in the [`Frac`](/Frac) module that can convert both #Int to [Float] and the other way around.)
|
||||||
##
|
##
|
||||||
## `a * b` is shorthand for `Num.mul a b`.
|
## `a * b` is shorthand for `Num.mul a b`.
|
||||||
##
|
##
|
||||||
|
@ -478,7 +565,7 @@ mulCheckOverflow : Num a, Num a -> Result (Num a) [ Overflow ]*
|
||||||
##
|
##
|
||||||
## >>> Num.toStr 42
|
## >>> Num.toStr 42
|
||||||
##
|
##
|
||||||
## Only #Frac values will include a decimal point, and they will always include one.
|
## Only [Float] values will include a decimal point, and they will always include one.
|
||||||
##
|
##
|
||||||
## >>> Num.toStr 4.2
|
## >>> Num.toStr 4.2
|
||||||
##
|
##
|
||||||
|
@ -553,16 +640,16 @@ format :
|
||||||
-> Str
|
-> Str
|
||||||
|
|
||||||
## Round off the given float to the nearest integer.
|
## Round off the given float to the nearest integer.
|
||||||
round : Frac * -> Int *
|
round : Float * -> Int *
|
||||||
ceil : Frac * -> Int *
|
ceil : Float * -> Int *
|
||||||
floor : Frac * -> Int *
|
floor : Float * -> Int *
|
||||||
trunc : Frac * -> Int *
|
trunc : Float * -> Int *
|
||||||
|
|
||||||
## Convert an #Int to a #Nat. If the given number doesn't fit in #Nat, it will be truncated.
|
## Convert an #Int to a #Nat. If the given number doesn't fit in #Nat, it will be truncated.
|
||||||
## Since #Nat has a different maximum number depending on the system you're building
|
## Since #Nat has a different maximum number depending on the system you're building
|
||||||
## for, this may give a different answer on different systems.
|
## for, this may give a different answer on different systems.
|
||||||
##
|
##
|
||||||
## For example, on a 32-bit system, #Num.maxNat will return the same answer as
|
## For example, on a 32-bit system, [Num.maxNat] will return the same answer as
|
||||||
## #Num.maxU32. This means that calling `Num.toNat 9_000_000_000` on a 32-bit
|
## #Num.maxU32. This means that calling `Num.toNat 9_000_000_000` on a 32-bit
|
||||||
## system will return #Num.maxU32 instead of 9 billion, because 9 billion is
|
## system will return #Num.maxU32 instead of 9 billion, because 9 billion is
|
||||||
## higher than #Num.maxU32 and will not fit in a #Nat on a 32-bit system.
|
## higher than #Num.maxU32 and will not fit in a #Nat on a 32-bit system.
|
||||||
|
@ -571,13 +658,13 @@ trunc : Frac * -> Int *
|
||||||
## the #Nat value of 9_000_000_000. This is because on a 64-bit system, #Nat can
|
## the #Nat value of 9_000_000_000. This is because on a 64-bit system, #Nat can
|
||||||
## hold up to #Num.maxU64, and 9_000_000_000 is lower than #Num.maxU64.
|
## hold up to #Num.maxU64, and 9_000_000_000 is lower than #Num.maxU64.
|
||||||
##
|
##
|
||||||
## To convert a #Frac to a #Nat, first call either #Num.round, #Num.ceil, or #Num.floor
|
## To convert a [Float] to a #Nat, first call either #Num.round, #Num.ceil, or #Num.floor
|
||||||
## on it, then call this on the resulting #Int.
|
## on it, then call this on the resulting #Int.
|
||||||
toNat : Int * -> Nat
|
toNat : Int * -> Nat
|
||||||
|
|
||||||
## Convert an #Int to an #I8. If the given number doesn't fit in #I8, it will be truncated.
|
## Convert an #Int to an #I8. If the given number doesn't fit in #I8, it will be truncated.
|
||||||
##
|
##
|
||||||
## To convert a #Frac to an #I8, first call either #Num.round, #Num.ceil, or #Num.floor
|
## To convert a [Float] to an #I8, first call either #Num.round, #Num.ceil, or #Num.floor
|
||||||
## on it, then call this on the resulting #Int.
|
## on it, then call this on the resulting #Int.
|
||||||
toI8 : Int * -> I8
|
toI8 : Int * -> I8
|
||||||
toI16 : Int * -> I16
|
toI16 : Int * -> I16
|
||||||
|
@ -633,9 +720,9 @@ divRound : Int a, Int a -> Int a
|
||||||
## Modulo is the same as remainder when working with positive numbers,
|
## Modulo is the same as remainder when working with positive numbers,
|
||||||
## but if either number is negative, then modulo works differently.
|
## but if either number is negative, then modulo works differently.
|
||||||
##
|
##
|
||||||
## Additionally, flooring modulo uses #Frac.floor on the result.
|
## Additionally, flooring modulo uses [Float].floor on the result.
|
||||||
##
|
##
|
||||||
## (Use #Frac.mod for non-flooring modulo.)
|
## (Use [Float].mod for non-flooring modulo.)
|
||||||
##
|
##
|
||||||
## Return `Err DivByZero` if the second integer is zero, because division by zero is undefined in mathematics.
|
## Return `Err DivByZero` if the second integer is zero, because division by zero is undefined in mathematics.
|
||||||
##
|
##
|
||||||
|
@ -749,28 +836,28 @@ maxDec : Dec
|
||||||
## Constants
|
## Constants
|
||||||
|
|
||||||
## An approximation of e, specifically 2.718281828459045.
|
## An approximation of e, specifically 2.718281828459045.
|
||||||
e : Frac *
|
e : Float *
|
||||||
|
|
||||||
## An approximation of pi, specifically 3.141592653589793.
|
## An approximation of pi, specifically 3.141592653589793.
|
||||||
pi : Frac *
|
pi : Float *
|
||||||
|
|
||||||
## Trigonometry
|
## Trigonometry
|
||||||
|
|
||||||
cos : Frac a -> Frac a
|
cos : Float a -> Float a
|
||||||
|
|
||||||
acos : Frac a -> Frac a
|
acos : Float a -> Float a
|
||||||
|
|
||||||
sin : Frac a -> Frac a
|
sin : Float a -> Float a
|
||||||
|
|
||||||
asin : Frac a -> Frac a
|
asin : Float a -> Float a
|
||||||
|
|
||||||
tan : Frac a -> Frac a
|
tan : Float a -> Float a
|
||||||
|
|
||||||
atan : Frac a -> Frac a
|
atan : Float a -> Float a
|
||||||
|
|
||||||
## Other Calculations (arithmetic?)
|
## Other Calculations (arithmetic?)
|
||||||
|
|
||||||
## Divide one [Frac] by another.
|
## Divide one [Float] by another.
|
||||||
##
|
##
|
||||||
## `a / b` is shorthand for `Num.div a b`.
|
## `a / b` is shorthand for `Num.div a b`.
|
||||||
##
|
##
|
||||||
|
@ -789,7 +876,7 @@ atan : Frac a -> Frac a
|
||||||
## > cost! Since the most common reason to choose [F64] or [F32] over [Dec] is
|
## > cost! Since the most common reason to choose [F64] or [F32] over [Dec] is
|
||||||
## > access to hardware-accelerated performance, Roc follows these rules exactly.
|
## > access to hardware-accelerated performance, Roc follows these rules exactly.
|
||||||
##
|
##
|
||||||
## To divide an [Int] and a [Frac], first convert the [Int] to a [Frac] using
|
## To divide an [Int] and a [Float], first convert the [Int] to a [Float] using
|
||||||
## one of the functions in this module like [toDec].
|
## one of the functions in this module like [toDec].
|
||||||
##
|
##
|
||||||
## >>> 5.0 / 7.0
|
## >>> 5.0 / 7.0
|
||||||
|
@ -800,9 +887,9 @@ atan : Frac a -> Frac a
|
||||||
##
|
##
|
||||||
## >>> Num.pi
|
## >>> Num.pi
|
||||||
## >>> |> Num.div 2.0
|
## >>> |> Num.div 2.0
|
||||||
div : Frac a, Frac a -> Frac a
|
div : Float a, Float a -> Float a
|
||||||
|
|
||||||
## Perform modulo on two [Frac]s.
|
## Perform modulo on two [Float]s.
|
||||||
##
|
##
|
||||||
## Modulo is the same as remainder when working with positive numbers,
|
## Modulo is the same as remainder when working with positive numbers,
|
||||||
## but if either number is negative, then modulo works differently.
|
## but if either number is negative, then modulo works differently.
|
||||||
|
@ -825,20 +912,20 @@ div : Frac a, Frac a -> Frac a
|
||||||
##
|
##
|
||||||
## >>> Num.pi
|
## >>> Num.pi
|
||||||
## >>> |> Num.mod 2.0
|
## >>> |> Num.mod 2.0
|
||||||
mod : Frac a, Frac a -> Frac a
|
mod : Float a, Float a -> Float a
|
||||||
|
|
||||||
## Raises a #Frac to the power of another #Frac.
|
## Raises a [Float] to the power of another [Float].
|
||||||
##
|
##
|
||||||
## `
|
## `
|
||||||
## For an #Int alternative to this function, see #Num.raise.
|
## For an #Int alternative to this function, see #Num.raise.
|
||||||
pow : Frac a, Frac a -> Frac a
|
pow : Float a, Float a -> Float a
|
||||||
|
|
||||||
## Raises an integer to the power of another, by multiplying the integer by
|
## Raises an integer to the power of another, by multiplying the integer by
|
||||||
## itself the given number of times.
|
## itself the given number of times.
|
||||||
##
|
##
|
||||||
## This process is known as [exponentiation by squaring](https://en.wikipedia.org/wiki/Exponentiation_by_squaring).
|
## This process is known as [exponentiation by squaring](https://en.wikipedia.org/wiki/Exponentiation_by_squaring).
|
||||||
##
|
##
|
||||||
## For a #Frac alternative to this function, which supports negative exponents,
|
## For a [Float] alternative to this function, which supports negative exponents,
|
||||||
## see #Num.exp.
|
## see #Num.exp.
|
||||||
##
|
##
|
||||||
## >>> Num.exp 5 0
|
## >>> Num.exp 5 0
|
||||||
|
@ -855,9 +942,9 @@ pow : Frac a, Frac a -> Frac a
|
||||||
## overflow
|
## overflow
|
||||||
expBySquaring : Int a, U8 -> Int a
|
expBySquaring : Int a, U8 -> Int a
|
||||||
|
|
||||||
## Returns an approximation of the absolute value of a [Frac]'s square root.
|
## Returns an approximation of the absolute value of a [Float]'s square root.
|
||||||
##
|
##
|
||||||
## The square root of a negative number is an irrational number, and [Frac] only
|
## The square root of a negative number is an irrational number, and [Float] only
|
||||||
## supports rational numbers. As such, you should make sure never to pass this
|
## supports rational numbers. As such, you should make sure never to pass this
|
||||||
## function a negative number! Calling [sqrt] on a negative [Dec] will cause a panic.
|
## function a negative number! Calling [sqrt] on a negative [Dec] will cause a panic.
|
||||||
##
|
##
|
||||||
|
@ -881,7 +968,7 @@ expBySquaring : Int a, U8 -> Int a
|
||||||
## >>> Frac.sqrt -4.0f64
|
## >>> Frac.sqrt -4.0f64
|
||||||
##
|
##
|
||||||
## >>> Frac.sqrt -4.0dec
|
## >>> Frac.sqrt -4.0dec
|
||||||
sqrt : Frac a -> Frac a
|
sqrt : Float a -> Float a
|
||||||
|
|
||||||
## Bit shifts
|
## Bit shifts
|
||||||
|
|
||||||
|
@ -911,21 +998,21 @@ shrWrap : Int a, Int a -> Int a
|
||||||
|
|
||||||
|
|
||||||
## [Endianness](https://en.wikipedia.org/wiki/Endianness)
|
## [Endianness](https://en.wikipedia.org/wiki/Endianness)
|
||||||
Endi : [ Big, Little ]
|
# Endi : [ Big, Little ]
|
||||||
|
|
||||||
## The [Endi] argument does not matter for [U8] and [I8], since they have
|
## The `Endi` argument does not matter for [U8] and [I8], since they have
|
||||||
## only one byte.
|
## only one byte.
|
||||||
toBytes : Num *, Endi -> List U8
|
# toBytes : Num *, Endi -> List U8
|
||||||
|
|
||||||
## when Num.parseBytes bytes Big is
|
## when Num.parseBytes bytes Big is
|
||||||
## Ok { val: f64, rest } -> ...
|
## Ok { val: f64, rest } -> ...
|
||||||
## Err (ExpectedNum (Float Binary64)) -> ...
|
## Err (ExpectedNum (Float Binary64)) -> ...
|
||||||
parseBytes : List U8, Endi -> Result { val : Num a, rest : List U8 } [ ExpectedNum a ]*
|
# parseBytes : List U8, Endi -> Result { val : Num a, rest : List U8 } [ ExpectedNum a ]*
|
||||||
|
|
||||||
## when Num.fromBytes bytes Big is
|
## when Num.fromBytes bytes Big is
|
||||||
## Ok f64 -> ...
|
## Ok f64 -> ...
|
||||||
## Err (ExpectedNum (Float Binary64)) -> ...
|
## Err (ExpectedNum (Float Binary64)) -> ...
|
||||||
fromBytes : List U8, Endi -> Result (Num a) [ ExpectedNum a ]*
|
# fromBytes : List U8, Endi -> Result (Num a) [ ExpectedNum a ]*
|
||||||
|
|
||||||
## Comparison
|
## Comparison
|
||||||
|
|
||||||
|
@ -992,8 +1079,8 @@ lower : Num a, Num a -> Num a
|
||||||
## Returns `Lt` if the first number is less than the second, `Gt` if
|
## Returns `Lt` if the first number is less than the second, `Gt` if
|
||||||
## the first is greater than the second, and `Eq` if they're equal.
|
## the first is greater than the second, and `Eq` if they're equal.
|
||||||
##
|
##
|
||||||
## Although this can be passed to [List.sort], you'll get better performance
|
## Although this can be passed to `List.sort`, you'll get better performance
|
||||||
## by using [List.sortAsc] or [List.sortDesc] instead.
|
## by using `List.sortAsc` or `List.sortDesc` instead.
|
||||||
compare : Num a, Num a -> [ Lt, Eq, Gt ]
|
compare : Num a, Num a -> [ Lt, Eq, Gt ]
|
||||||
|
|
||||||
## Special Floating-Point Values
|
## Special Floating-Point Values
|
||||||
|
@ -1005,7 +1092,7 @@ compare : Num a, Num a -> [ Lt, Eq, Gt ]
|
||||||
##
|
##
|
||||||
## This is the opposite of [isInfinite], except when given [*NaN*](Num.isNaN). Both
|
## This is the opposite of [isInfinite], except when given [*NaN*](Num.isNaN). Both
|
||||||
## [isFinite] and [isInfinite] return `False` for [*NaN*](Num.isNaN).
|
## [isFinite] and [isInfinite] return `False` for [*NaN*](Num.isNaN).
|
||||||
isFinite : Frac * -> Bool
|
isFinite : Float * -> Bool
|
||||||
|
|
||||||
## When given a [F64] or [F32] value, returns `True` if that value is either
|
## When given a [F64] or [F32] value, returns `True` if that value is either
|
||||||
## ∞ or -∞, and `False` otherwise.
|
## ∞ or -∞, and `False` otherwise.
|
||||||
|
@ -1014,7 +1101,7 @@ isFinite : Frac * -> Bool
|
||||||
##
|
##
|
||||||
## This is the opposite of [isFinite], except when given [*NaN*](Num.isNaN). Both
|
## This is the opposite of [isFinite], except when given [*NaN*](Num.isNaN). Both
|
||||||
## [isFinite] and [isInfinite] return `False` for [*NaN*](Num.isNaN).
|
## [isFinite] and [isInfinite] return `False` for [*NaN*](Num.isNaN).
|
||||||
isInfinite : Frac * -> Bool
|
isInfinite : Float * -> Bool
|
||||||
|
|
||||||
## When given a [F64] or [F32] value, returns `True` if that value is
|
## When given a [F64] or [F32] value, returns `True` if that value is
|
||||||
## *NaN* ([not a number](https://en.wikipedia.org/wiki/NaN)), and `False` otherwise.
|
## *NaN* ([not a number](https://en.wikipedia.org/wiki/NaN)), and `False` otherwise.
|
||||||
|
@ -1038,4 +1125,4 @@ isInfinite : Frac * -> Bool
|
||||||
## Note that you should never put a *NaN* into a [Set], or use it as the key in
|
## Note that you should never put a *NaN* into a [Set], or use it as the key in
|
||||||
## a [Dict]. The result is entries that can never be removed from those
|
## a [Dict]. The result is entries that can never be removed from those
|
||||||
## collections! See the documentation for [Set.add] and [Dict.insert] for details.
|
## collections! See the documentation for [Set.add] and [Dict.insert] for details.
|
||||||
isNaN : Frac * -> Bool
|
isNaN : Float * -> Bool
|
||||||
|
|
55
compiler/builtins/docs/Result.roc
Normal file
55
compiler/builtins/docs/Result.roc
Normal file
|
@ -0,0 +1,55 @@
|
||||||
|
interface Result
|
||||||
|
exposes
|
||||||
|
[
|
||||||
|
Result,
|
||||||
|
map,
|
||||||
|
mapErr,
|
||||||
|
withDefault,
|
||||||
|
after
|
||||||
|
]
|
||||||
|
imports []
|
||||||
|
|
||||||
|
## The result of an operation that could fail: either the operation went
|
||||||
|
## okay, or else there was an error of some sort.
|
||||||
|
Result ok err : [ @Result ok err ]
|
||||||
|
|
||||||
|
## If the result is `Ok`, return the value it holds. Otherwise, return
|
||||||
|
## the given default value.
|
||||||
|
##
|
||||||
|
## >>> Result.withDefault (Ok 7) 42
|
||||||
|
##
|
||||||
|
## >>> Result.withDefault (Err "uh oh") 42
|
||||||
|
withDefault : Result ok err, ok -> ok
|
||||||
|
|
||||||
|
## If the result is `Ok`, transform the entire result by running a conversion
|
||||||
|
## function on the value the `Ok` holds. Then return that new result.
|
||||||
|
##
|
||||||
|
## (If the result is `Err`, this has no effect. Use `afterErr` to transform an `Err`.)
|
||||||
|
##
|
||||||
|
## >>> Result.after (Ok -1) \num -> if num < 0 then Err "negative!" else Ok -num
|
||||||
|
##
|
||||||
|
## >>> Result.after (Err "yipes!") \num -> if num < 0 then Err "negative!" else Ok -num
|
||||||
|
after : Result before err, (before -> Result after err) -> Result after err
|
||||||
|
|
||||||
|
## If the result is `Ok`, transform the value it holds by running a conversion
|
||||||
|
## function on it. Then return a new `Ok` holding the transformed value.
|
||||||
|
##
|
||||||
|
## (If the result is `Err`, this has no effect. Use [mapErr] to transform an `Err`.)
|
||||||
|
##
|
||||||
|
## >>> Result.map (Ok 12) Num.negate
|
||||||
|
##
|
||||||
|
## >>> Result.map (Err "yipes!") Num.negate
|
||||||
|
##
|
||||||
|
## `map` functions like this are common in Roc, and they all work similarly.
|
||||||
|
## See for example [List.map], `Set.map`, and `Dict.map`.
|
||||||
|
map : Result before err, (before -> after) -> Result after err
|
||||||
|
|
||||||
|
## If the result is `Err`, transform the value it holds by running a conversion
|
||||||
|
## function on it. Then return a new `Err` holding the transformed value.
|
||||||
|
##
|
||||||
|
## (If the result is `Ok`, this has no effect. Use [map] to transform an `Ok`.)
|
||||||
|
##
|
||||||
|
## >>> Result.mapErr (Err "yipes!") Str.isEmpty
|
||||||
|
##
|
||||||
|
## >>> Result.mapErr (Ok 12) Str.isEmpty
|
||||||
|
mapErr : Result ok before, (before -> after) -> Result ok after
|
|
@ -1,9 +1,22 @@
|
||||||
interface Set
|
interface Set
|
||||||
exposes [ Set, empty, isEmpty, len, add, drop, map ]
|
exposes
|
||||||
|
[
|
||||||
|
Set,
|
||||||
|
empty,
|
||||||
|
single,
|
||||||
|
len,
|
||||||
|
insert,
|
||||||
|
remove,
|
||||||
|
union,
|
||||||
|
difference,
|
||||||
|
intersection,
|
||||||
|
toList,
|
||||||
|
fromList,
|
||||||
|
walk,
|
||||||
|
contains
|
||||||
|
]
|
||||||
imports []
|
imports []
|
||||||
|
|
||||||
## Set
|
|
||||||
|
|
||||||
## A Set is an unordered collection of unique elements.
|
## A Set is an unordered collection of unique elements.
|
||||||
Set elem : [ @Set elem ]
|
Set elem : [ @Set elem ]
|
||||||
|
|
||||||
|
@ -36,7 +49,7 @@ drop : Set elem, elem -> Set elem
|
||||||
## >>> Set.map {: "", "a", "bc" :} Str.isEmpty
|
## >>> Set.map {: "", "a", "bc" :} Str.isEmpty
|
||||||
##
|
##
|
||||||
## `map` functions like this are common in Roc, and they all work similarly.
|
## `map` functions like this are common in Roc, and they all work similarly.
|
||||||
## See for example #Result.map, #List.map, and #Map.map.
|
## See for example [List.map], `Dict.map`, and [Result.map].
|
||||||
# TODO: removed `'` from signature because parser does not support it yet
|
# TODO: removed `'` from signature because parser does not support it yet
|
||||||
# Original signature: `map : Set 'elem, ('before -> 'after) -> Set 'after`
|
# Original signature: `map : Set 'elem, ('before -> 'after) -> Set 'after`
|
||||||
map : Set elem, (before -> after) -> Set after
|
map : Set elem, (before -> after) -> Set after
|
||||||
|
|
|
@ -2,36 +2,21 @@ interface Str
|
||||||
exposes
|
exposes
|
||||||
[
|
[
|
||||||
Str,
|
Str,
|
||||||
decimal,
|
|
||||||
split,
|
|
||||||
isEmpty,
|
isEmpty,
|
||||||
|
append,
|
||||||
|
concat,
|
||||||
|
joinWith,
|
||||||
|
split,
|
||||||
|
countGraphemes,
|
||||||
startsWith,
|
startsWith,
|
||||||
endsWith,
|
endsWith,
|
||||||
contains,
|
fromInt,
|
||||||
anyGraphemes,
|
fromFloat,
|
||||||
allGraphemes,
|
fromUtf8,
|
||||||
join,
|
Utf8Problem,
|
||||||
joinWith,
|
Utf8ByteProblem,
|
||||||
padGraphemesStart,
|
|
||||||
padGraphemesEnd,
|
|
||||||
graphemes,
|
|
||||||
reverseGraphemes,
|
|
||||||
isCaseInsensitiveEq,
|
|
||||||
isCaseInsensitiveNeq,
|
|
||||||
walkGraphemes,
|
|
||||||
isCapitalized,
|
|
||||||
isAllUppercase,
|
|
||||||
isAllLowercase,
|
|
||||||
toUtf8,
|
toUtf8,
|
||||||
toUtf16,
|
startsWithCodePt
|
||||||
toUtf32,
|
|
||||||
trim,
|
|
||||||
walkUtf8,
|
|
||||||
walkUtf16,
|
|
||||||
walkUtf32,
|
|
||||||
walkRevUtf8,
|
|
||||||
walkRevUtf16,
|
|
||||||
walkRevUtf32
|
|
||||||
]
|
]
|
||||||
imports []
|
imports []
|
||||||
|
|
||||||
|
@ -63,7 +48,7 @@ interface Str
|
||||||
## programming, and "extended grapheme cluster" is a mouthful, in Roc we use the
|
## programming, and "extended grapheme cluster" is a mouthful, in Roc we use the
|
||||||
## term "grapheme" as a shorthand for the more precise "extended grapheme cluster."
|
## term "grapheme" as a shorthand for the more precise "extended grapheme cluster."
|
||||||
##
|
##
|
||||||
## You can get the number of graphemes in a string by calling #Str.countGraphemes on it:
|
## You can get the number of graphemes in a string by calling [Str.countGraphemes] on it:
|
||||||
##
|
##
|
||||||
## Str.countGraphemes "Roc!"
|
## Str.countGraphemes "Roc!"
|
||||||
## Str.countGraphemes "折り紙"
|
## Str.countGraphemes "折り紙"
|
||||||
|
@ -126,7 +111,7 @@ interface Str
|
||||||
## potentially change it without breaking existing Roc applications. (UTF-8
|
## potentially change it without breaking existing Roc applications. (UTF-8
|
||||||
## seems pretty great today, but so did UTF-16 at an earlier point in history.)
|
## seems pretty great today, but so did UTF-16 at an earlier point in history.)
|
||||||
##
|
##
|
||||||
## This module has functions to can convert a #Str to a #List of raw [code unit](https://unicode.org/glossary/#code_unit)
|
## This module has functions to can convert a [Str] to a [List] of raw [code unit](https://unicode.org/glossary/#code_unit)
|
||||||
## integers (not to be confused with the [code points](https://unicode.org/glossary/#code_point)
|
## integers (not to be confused with the [code points](https://unicode.org/glossary/#code_point)
|
||||||
## mentioned earlier) in a particular encoding. If you need encoding-specific functions,
|
## mentioned earlier) in a particular encoding. If you need encoding-specific functions,
|
||||||
## you should take a look at the [roc/unicode](roc/unicode) package.
|
## you should take a look at the [roc/unicode](roc/unicode) package.
|
||||||
|
@ -137,15 +122,15 @@ Str : [ @Str ]
|
||||||
|
|
||||||
## Convert
|
## Convert
|
||||||
|
|
||||||
## Convert a #Float to a decimal string, rounding off to the given number of decimal places.
|
## Convert a [Float] to a decimal string, rounding off to the given number of decimal places.
|
||||||
##
|
##
|
||||||
## Since #Float values are imprecise, it's usually best to limit this to the lowest
|
## If you want to keep all the digits, use [Str.num] instead.
|
||||||
## number you can choose that will make sense for what you want to display.
|
|
||||||
##
|
|
||||||
## If you want to keep all the digits, passing the same float to #Str.num
|
|
||||||
## will do that.
|
|
||||||
decimal : Float *, Nat -> Str
|
decimal : Float *, Nat -> Str
|
||||||
|
|
||||||
|
|
||||||
|
## Convert a [Num] to a string.
|
||||||
|
num : Float *, Nat -> Str
|
||||||
|
|
||||||
## Split a string around a separator.
|
## Split a string around a separator.
|
||||||
##
|
##
|
||||||
## >>> Str.split "1,2,3" ","
|
## >>> Str.split "1,2,3" ","
|
||||||
|
@ -155,13 +140,13 @@ decimal : Float *, Nat -> Str
|
||||||
##
|
##
|
||||||
## >>> Str.split "1,2,3" ""
|
## >>> Str.split "1,2,3" ""
|
||||||
##
|
##
|
||||||
## To split a string into its individual graphemes, use #Str.graphemes
|
## To split a string into its individual graphemes, use `Str.graphemes`
|
||||||
split : Str, Str -> List Str
|
split : Str, Str -> List Str
|
||||||
|
|
||||||
## Split a string around newlines.
|
## Split a string around newlines.
|
||||||
##
|
##
|
||||||
## On strings that use `"\n"` for their line endings, this gives the same answer
|
## On strings that use `"\n"` for their line endings, this gives the same answer
|
||||||
## as passing `"\n"` to #Str.split. However, on strings that use `"\n\r"` (such
|
## as passing `"\n"` to [Str.split]. However, on strings that use `"\n\r"` (such
|
||||||
## as [in Windows files](https://en.wikipedia.org/wiki/Newline#History)), this
|
## as [in Windows files](https://en.wikipedia.org/wiki/Newline#History)), this
|
||||||
## will consume the entire `"\n\r"` instead of just the `"\n"`.
|
## will consume the entire `"\n\r"` instead of just the `"\n"`.
|
||||||
##
|
##
|
||||||
|
@ -169,7 +154,7 @@ split : Str, Str -> List Str
|
||||||
##
|
##
|
||||||
## >>> Str.lines "Hello, World!\n\rNice to meet you!"
|
## >>> Str.lines "Hello, World!\n\rNice to meet you!"
|
||||||
##
|
##
|
||||||
## To split a string using a custom separator, use #Str.split. For more advanced
|
## To split a string using a custom separator, use [Str.split]. For more advanced
|
||||||
## string splitting, use a #Parser.
|
## string splitting, use a #Parser.
|
||||||
lines : Str, Str -> List Str
|
lines : Str, Str -> List Str
|
||||||
|
|
||||||
|
@ -192,13 +177,13 @@ startsWith : Str, Str -> Bool
|
||||||
##
|
##
|
||||||
## **Performance Note:** This runs slightly faster than [Str.startsWith], so
|
## **Performance Note:** This runs slightly faster than [Str.startsWith], so
|
||||||
## if you want to check whether a string begins with something that's representable
|
## if you want to check whether a string begins with something that's representable
|
||||||
## in a single code point, you can use (for example) `Str.startsWithCodePoint '鹏'`
|
## in a single code point, you can use (for example) `Str.startsWithCodePt '鹏'`
|
||||||
## instead of `Str.startsWithCodePoint "鹏"`. ('鹏' evaluates to the [U32]
|
## instead of `Str.startsWithCodePt "鹏"`. ('鹏' evaluates to the [U32]
|
||||||
## value `40527`.) This will not work for graphemes which take up multiple code
|
## value `40527`.) This will not work for graphemes which take up multiple code
|
||||||
## points, however; `Str.startsWithCodePoint '👩👩👦👦'` would be a compiler error
|
## points, however; `Str.startsWithCodePt '👩👩👦👦'` would be a compiler error
|
||||||
## because 👩👩👦👦 takes up multiple code points and cannot be represented as a
|
## because 👩👩👦👦 takes up multiple code points and cannot be represented as a
|
||||||
## single [U32]. You'd need to use `Str.startsWithCodePoint "🕊"` instead.
|
## single [U32]. You'd need to use `Str.startsWithCodePt "🕊"` instead.
|
||||||
startsWithCodePoint : Str, U32 -> Bool
|
startsWithCodePt : Str, U32 -> Bool
|
||||||
|
|
||||||
endsWith : Str, Str -> Bool
|
endsWith : Str, Str -> Bool
|
||||||
|
|
||||||
|
@ -255,9 +240,13 @@ padGraphemesEnd : Str, Nat, Str -> Str
|
||||||
##
|
##
|
||||||
graphemes : Str -> List Str
|
graphemes : Str -> List Str
|
||||||
|
|
||||||
|
## Count the number of [extended grapheme clusters](http://www.unicode.org/glossary/#extended_grapheme_cluster)
|
||||||
|
## in the string.
|
||||||
|
##
|
||||||
## Str.countGraphemes "Roc!" # 4
|
## Str.countGraphemes "Roc!" # 4
|
||||||
## Str.countGraphemes "七巧板" # 3
|
## Str.countGraphemes "七巧板" # 3
|
||||||
## Str.countGraphemes "🕊" # 1
|
## Str.countGraphemes "🕊" # 1
|
||||||
|
countGraphemes : Str -> Nat
|
||||||
|
|
||||||
## Reverse the order of the string's individual graphemes.
|
## Reverse the order of the string's individual graphemes.
|
||||||
##
|
##
|
||||||
|
@ -354,36 +343,36 @@ trim : Str -> Str
|
||||||
## If the given [U32] is a valid [Unicode Scalar Value](http://www.unicode.org/glossary/#unicode_scalar_value),
|
## If the given [U32] is a valid [Unicode Scalar Value](http://www.unicode.org/glossary/#unicode_scalar_value),
|
||||||
## return a [Str] containing only that scalar.
|
## return a [Str] containing only that scalar.
|
||||||
fromScalar : U32 -> Result Str [ BadScalar ]*
|
fromScalar : U32 -> Result Str [ BadScalar ]*
|
||||||
fromCodePoints : List U32 -> Result Str [ BadCodePoint U32 ]*
|
fromCodePts : List U32 -> Result Str [ BadCodePt U32 ]*
|
||||||
fromUtf8 : List U8 -> Result Str [ BadUtf8 ]*
|
fromUtf8 : List U8 -> Result Str [ BadUtf8 ]*
|
||||||
|
|
||||||
## Create a [Str] from bytes encoded as [UTF-16LE](https://en.wikipedia.org/wiki/UTF-16#Byte-order_encoding_schemes).
|
## Create a [Str] from bytes encoded as [UTF-16LE](https://en.wikipedia.org/wiki/UTF-16#Byte-order_encoding_schemes).
|
||||||
fromUtf16Le : List U8 -> Result Str [ BadUtf16Le Endi ]*
|
# fromUtf16Le : List U8 -> Result Str [ BadUtf16Le Endi ]*
|
||||||
|
|
||||||
## Create a [Str] from bytes encoded as [UTF-16BE](https://en.wikipedia.org/wiki/UTF-16#Byte-order_encoding_schemes).
|
# ## Create a [Str] from bytes encoded as [UTF-16BE](https://en.wikipedia.org/wiki/UTF-16#Byte-order_encoding_schemes).
|
||||||
fromUtf16Be : List U8 -> Result Str [ BadUtf16Be Endi ]*
|
# fromUtf16Be : List U8 -> Result Str [ BadUtf16Be Endi ]*
|
||||||
|
|
||||||
## Create a [Str] from bytes encoded as UTF-16 with a [Byte Order Mark](https://en.wikipedia.org/wiki/Byte_order_mark).
|
# ## Create a [Str] from bytes encoded as UTF-16 with a [Byte Order Mark](https://en.wikipedia.org/wiki/Byte_order_mark).
|
||||||
fromUtf16Bom : List U8 -> Result Str [ BadUtf16 Endi, NoBom ]*
|
# fromUtf16Bom : List U8 -> Result Str [ BadUtf16 Endi, NoBom ]*
|
||||||
|
|
||||||
## Create a [Str] from bytes encoded as [UTF-32LE](https://web.archive.org/web/20120322145307/http://mail.apps.ietf.org/ietf/charsets/msg01095.html)
|
# ## Create a [Str] from bytes encoded as [UTF-32LE](https://web.archive.org/web/20120322145307/http://mail.apps.ietf.org/ietf/charsets/msg01095.html)
|
||||||
fromUtf32Le : List U8 -> Result Str [ BadUtf32Le Endi ]*
|
# fromUtf32Le : List U8 -> Result Str [ BadUtf32Le Endi ]*
|
||||||
|
|
||||||
## Create a [Str] from bytes encoded as [UTF-32BE](https://web.archive.org/web/20120322145307/http://mail.apps.ietf.org/ietf/charsets/msg01095.html)
|
# ## Create a [Str] from bytes encoded as [UTF-32BE](https://web.archive.org/web/20120322145307/http://mail.apps.ietf.org/ietf/charsets/msg01095.html)
|
||||||
fromUtf32Be : List U8 -> Result Str [ BadUtf32Be Endi ]*
|
# fromUtf32Be : List U8 -> Result Str [ BadUtf32Be Endi ]*
|
||||||
|
|
||||||
## Create a [Str] from bytes encoded as UTF-32 with a [Byte Order Mark](https://en.wikipedia.org/wiki/Byte_order_mark).
|
# ## Create a [Str] from bytes encoded as UTF-32 with a [Byte Order Mark](https://en.wikipedia.org/wiki/Byte_order_mark).
|
||||||
fromUtf32Bom : List U8 -> Result Str [ BadUtf32 Endi, NoBom ]*
|
# fromUtf32Bom : List U8 -> Result Str [ BadUtf32 Endi, NoBom ]*
|
||||||
|
|
||||||
## Convert from UTF-8, substituting the replacement character ("<22>") for any
|
# ## Convert from UTF-8, substituting the replacement character ("<22>") for any
|
||||||
## invalid sequences encountered.
|
# ## invalid sequences encountered.
|
||||||
fromUtf8Sub : List U8 -> Str
|
# fromUtf8Sub : List U8 -> Str
|
||||||
fromUtf16Sub : List U8, Endi -> Str
|
# fromUtf16Sub : List U8, Endi -> Str
|
||||||
fromUtf16BomSub : List U8 -> Result Str [ NoBom ]*
|
# fromUtf16BomSub : List U8 -> Result Str [ NoBom ]*
|
||||||
|
|
||||||
## Return a #List of the string's #U8 UTF-8 [code units](https://unicode.org/glossary/#code_unit).
|
## Return a [List] of the string's #U8 UTF-8 [code units](https://unicode.org/glossary/#code_unit).
|
||||||
## (To split the string into a #List of smaller #Str values instead of #U8 values,
|
## (To split the string into a [List] of smaller [Str] values instead of #U8 values,
|
||||||
## see #Str.split and #Str.graphemes.)
|
## see [Str.split] and `Str.graphemes`.)
|
||||||
##
|
##
|
||||||
## >>> Str.toUtf8 "👩👩👦👦"
|
## >>> Str.toUtf8 "👩👩👦👦"
|
||||||
##
|
##
|
||||||
|
@ -393,15 +382,15 @@ fromUtf16BomSub : List U8 -> Result Str [ NoBom ]*
|
||||||
##
|
##
|
||||||
## >>> Str.toUtf8 "🐦"
|
## >>> Str.toUtf8 "🐦"
|
||||||
##
|
##
|
||||||
## For a more flexible function that walks through each of these #U8 code units
|
## For a more flexible function that walks through each of these [U8] code units
|
||||||
## without creating a #List, see #Str.walkUtf8 and #Str.walkRevUtf8.
|
## without creating a [List], see `Str.walkUtf8` and `Str.walkRevUtf8`.
|
||||||
toUtf8 : Str -> List U8
|
toUtf8 : Str -> List U8
|
||||||
toUtf16Be : Str -> List U8
|
toUtf16Be : Str -> List U8
|
||||||
toUtf16Le : Str -> List U8
|
toUtf16Le : Str -> List U8
|
||||||
toUtf16Bom : Str, Endi -> List U8
|
# toUtf16Bom : Str, Endi -> List U8
|
||||||
toUtf32Be : Str -> List U8
|
toUtf32Be : Str -> List U8
|
||||||
toUtf32Le : Str -> List U8
|
toUtf32Le : Str -> List U8
|
||||||
toUtf32Bom : Str, Endi -> List U8
|
# toUtf32Bom : Str, Endi -> List U8
|
||||||
|
|
||||||
# Parsing
|
# Parsing
|
||||||
|
|
||||||
|
@ -417,7 +406,7 @@ parseGrapheme : Str -> Result { val : Str, rest : Str } [ Expected [ Grapheme ]*
|
||||||
##
|
##
|
||||||
## If the string does not begin with a valid code point, for example because it was
|
## If the string does not begin with a valid code point, for example because it was
|
||||||
## empty, return `Err`.
|
## empty, return `Err`.
|
||||||
parseCodePoint : Str -> Result { val : U32, rest : Str } [ Expected [ CodePoint ]* Str ]*
|
parseCodePt : Str -> Result { val : U32, rest : Str } [ Expected [ CodePt ]* Str ]*
|
||||||
|
|
||||||
## If the first string begins with the second, return whatever comes
|
## If the first string begins with the second, return whatever comes
|
||||||
## after the second.
|
## after the second.
|
||||||
|
@ -425,7 +414,7 @@ chomp : Str, Str -> Result Str [ Expected [ ExactStr Str ]* Str ]*
|
||||||
|
|
||||||
## If the string begins with a [Unicode code point](http://www.unicode.org/glossary/#code_point)
|
## If the string begins with a [Unicode code point](http://www.unicode.org/glossary/#code_point)
|
||||||
## equal to the given [U32], return whatever comes after that code point.
|
## equal to the given [U32], return whatever comes after that code point.
|
||||||
chompCodePoint : Str, U32 -> Result Str [ Expected [ ExactCodePoint U32 ]* Str ]*
|
chompCodePt : Str, U32 -> Result Str [ Expected [ ExactCodePt U32 ]* Str ]*
|
||||||
|
|
||||||
## If the string represents a valid #U8 number, return that number.
|
## If the string represents a valid #U8 number, return that number.
|
||||||
##
|
##
|
||||||
|
@ -475,20 +464,20 @@ toNum : Str -> Result (Num a) [ ExpectedNum a ]*
|
||||||
## If the string begins with `"NaN"`, `"∞"`, and `"-∞"` (which do not represent
|
## If the string begins with `"NaN"`, `"∞"`, and `"-∞"` (which do not represent
|
||||||
## [finite](Num.isFinite) numbers), they will be accepted only when parsing
|
## [finite](Num.isFinite) numbers), they will be accepted only when parsing
|
||||||
## [F64] or [F32] numbers, and translated accordingly.
|
## [F64] or [F32] numbers, and translated accordingly.
|
||||||
parseNum : Str, NumParseConfig -> Result { val : Num a, rest : Str } [ ExpectedNum a ]*
|
# parseNum : Str, NumParseConfig -> Result { val : Num a, rest : Str } [ ExpectedNum a ]*
|
||||||
|
|
||||||
## Notes:
|
## Notes:
|
||||||
## * You can allow a decimal mark for integers; they'll only parse if the numbers after it are all 0.
|
## * You can allow a decimal mark for integers; they'll only parse if the numbers after it are all 0.
|
||||||
## * For `wholeSep`, `Required` has a payload for how many digits (e.g. "required every 3 digits")
|
## * For `wholeSep`, `Required` has a payload for how many digits (e.g. "required every 3 digits")
|
||||||
## * For `wholeSep`, `Allowed` allows the separator to appear anywhere.
|
## * For `wholeSep`, `Allowed` allows the separator to appear anywhere.
|
||||||
NumParseConfig :
|
# NumParseConfig :
|
||||||
{
|
# {
|
||||||
base ? [ Decimal, Hexadecimal, Octal, Binary ],
|
# base ? [ Decimal, Hexadecimal, Octal, Binary ],
|
||||||
notation ? [ Standard, Scientific, Any ],
|
# notation ? [ Standard, Scientific, Any ],
|
||||||
decimalMark ? [ Allowed Str, Required Str, Disallowed ],
|
# decimalMark ? [ Allowed Str, Required Str, Disallowed ],
|
||||||
decimalDigits ? [ Any, AtLeast U16, Exactly U16 ],
|
# decimalDigits ? [ Any, AtLeast U16, Exactly U16 ],
|
||||||
wholeDigits ? [ Any, AtLeast U16, Exactly U16 ],
|
# wholeDigits ? [ Any, AtLeast U16, Exactly U16 ],
|
||||||
leadingZeroes ? [ Allowed, Disallowed ],
|
# leadingZeroes ? [ Allowed, Disallowed ],
|
||||||
trailingZeroes ? [ Allowed, Disallowed ],
|
# trailingZeroes ? [ Allowed, Disallowed ],
|
||||||
wholeSep ? { mark : Str, policy : [ Allowed, Required U64 ] }
|
# wholeSep ? { mark : Str, policy : [ Allowed, Required U64 ] }
|
||||||
}
|
# }
|
||||||
|
|
|
@ -16,13 +16,13 @@ pub const STR_JOIN_WITH: &str = "roc_builtins.str.joinWith";
|
||||||
pub const STR_STR_SPLIT_IN_PLACE: &str = "roc_builtins.str.str_split_in_place";
|
pub const STR_STR_SPLIT_IN_PLACE: &str = "roc_builtins.str.str_split_in_place";
|
||||||
pub const STR_COUNT_GRAPEHEME_CLUSTERS: &str = "roc_builtins.str.count_grapheme_clusters";
|
pub const STR_COUNT_GRAPEHEME_CLUSTERS: &str = "roc_builtins.str.count_grapheme_clusters";
|
||||||
pub const STR_STARTS_WITH: &str = "roc_builtins.str.starts_with";
|
pub const STR_STARTS_WITH: &str = "roc_builtins.str.starts_with";
|
||||||
pub const STR_STARTS_WITH_CODE_POINT: &str = "roc_builtins.str.starts_with_code_point";
|
pub const STR_STARTS_WITH_CODE_PT: &str = "roc_builtins.str.starts_with_code_point";
|
||||||
pub const STR_ENDS_WITH: &str = "roc_builtins.str.ends_with";
|
pub const STR_ENDS_WITH: &str = "roc_builtins.str.ends_with";
|
||||||
pub const STR_NUMBER_OF_BYTES: &str = "roc_builtins.str.number_of_bytes";
|
pub const STR_NUMBER_OF_BYTES: &str = "roc_builtins.str.number_of_bytes";
|
||||||
pub const STR_FROM_INT: &str = "roc_builtins.str.from_int";
|
pub const STR_FROM_INT: &str = "roc_builtins.str.from_int";
|
||||||
pub const STR_FROM_FLOAT: &str = "roc_builtins.str.from_float";
|
pub const STR_FROM_FLOAT: &str = "roc_builtins.str.from_float";
|
||||||
pub const STR_EQUAL: &str = "roc_builtins.str.equal";
|
pub const STR_EQUAL: &str = "roc_builtins.str.equal";
|
||||||
pub const STR_TO_BYTES: &str = "roc_builtins.str.to_bytes";
|
pub const STR_TO_UTF8: &str = "roc_builtins.str.to_utf8";
|
||||||
pub const STR_FROM_UTF8: &str = "roc_builtins.str.from_utf8";
|
pub const STR_FROM_UTF8: &str = "roc_builtins.str.from_utf8";
|
||||||
pub const STR_FROM_UTF8_RANGE: &str = "roc_builtins.str.from_utf8_range";
|
pub const STR_FROM_UTF8_RANGE: &str = "roc_builtins.str.from_utf8_range";
|
||||||
|
|
||||||
|
|
|
@ -564,9 +564,9 @@ pub fn types() -> MutMap<Symbol, (SolvedType, Region)> {
|
||||||
Box::new(bool_type())
|
Box::new(bool_type())
|
||||||
);
|
);
|
||||||
|
|
||||||
// startsWithCodePoint : Str, U32 -> Bool
|
// startsWithCodePt : Str, U32 -> Bool
|
||||||
add_top_level_function_type!(
|
add_top_level_function_type!(
|
||||||
Symbol::STR_STARTS_WITH_CODE_POINT,
|
Symbol::STR_STARTS_WITH_CODE_PT,
|
||||||
vec![str_type(), u32_type()],
|
vec![str_type(), u32_type()],
|
||||||
Box::new(bool_type())
|
Box::new(bool_type())
|
||||||
);
|
);
|
||||||
|
@ -639,9 +639,9 @@ pub fn types() -> MutMap<Symbol, (SolvedType, Region)> {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// toBytes : Str -> List U8
|
// toUtf8 : Str -> List U8
|
||||||
add_top_level_function_type!(
|
add_top_level_function_type!(
|
||||||
Symbol::STR_TO_BYTES,
|
Symbol::STR_TO_UTF8,
|
||||||
vec![str_type()],
|
vec![str_type()],
|
||||||
Box::new(list_type(u8_type()))
|
Box::new(list_type(u8_type()))
|
||||||
);
|
);
|
||||||
|
|
|
@ -58,13 +58,13 @@ pub fn builtin_defs_map(symbol: Symbol, var_store: &mut VarStore) -> Option<Def>
|
||||||
STR_SPLIT => str_split,
|
STR_SPLIT => str_split,
|
||||||
STR_IS_EMPTY => str_is_empty,
|
STR_IS_EMPTY => str_is_empty,
|
||||||
STR_STARTS_WITH => str_starts_with,
|
STR_STARTS_WITH => str_starts_with,
|
||||||
STR_STARTS_WITH_CODE_POINT => str_starts_with_code_point,
|
STR_STARTS_WITH_CODE_PT => str_starts_with_code_point,
|
||||||
STR_ENDS_WITH => str_ends_with,
|
STR_ENDS_WITH => str_ends_with,
|
||||||
STR_COUNT_GRAPHEMES => str_count_graphemes,
|
STR_COUNT_GRAPHEMES => str_count_graphemes,
|
||||||
STR_FROM_INT => str_from_int,
|
STR_FROM_INT => str_from_int,
|
||||||
STR_FROM_UTF8 => str_from_utf8,
|
STR_FROM_UTF8 => str_from_utf8,
|
||||||
STR_FROM_UTF8_RANGE => str_from_utf8_range,
|
STR_FROM_UTF8_RANGE => str_from_utf8_range,
|
||||||
STR_TO_BYTES => str_to_bytes,
|
STR_TO_UTF8 => str_to_utf8,
|
||||||
STR_FROM_FLOAT=> str_from_float,
|
STR_FROM_FLOAT=> str_from_float,
|
||||||
LIST_LEN => list_len,
|
LIST_LEN => list_len,
|
||||||
LIST_GET => list_get,
|
LIST_GET => list_get,
|
||||||
|
@ -1288,9 +1288,9 @@ fn str_starts_with(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
||||||
lowlevel_2(symbol, LowLevel::StrStartsWith, var_store)
|
lowlevel_2(symbol, LowLevel::StrStartsWith, var_store)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Str.startsWithCodePoint : Str, U32 -> Bool
|
/// Str.startsWithCodePt : Str, U32 -> Bool
|
||||||
fn str_starts_with_code_point(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
fn str_starts_with_code_point(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
||||||
lowlevel_2(symbol, LowLevel::StrStartsWithCodePoint, var_store)
|
lowlevel_2(symbol, LowLevel::StrStartsWithCodePt, var_store)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Str.endsWith : Str, Str -> Bool
|
/// Str.endsWith : Str, Str -> Bool
|
||||||
|
@ -1562,9 +1562,9 @@ fn str_from_utf8_range(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// Str.toBytes : Str -> List U8
|
/// Str.toUtf8 : Str -> List U8
|
||||||
fn str_to_bytes(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
fn str_to_utf8(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
||||||
lowlevel_1(symbol, LowLevel::StrToBytes, var_store)
|
lowlevel_1(symbol, LowLevel::StrToUtf8, var_store)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Str.fromFloat : Float * -> Str
|
/// Str.fromFloat : Float * -> Str
|
||||||
|
|
|
@ -1600,10 +1600,10 @@ fn flatten_str_lines<'a>(
|
||||||
buf.push(ch);
|
buf.push(ch);
|
||||||
}
|
}
|
||||||
None => {
|
None => {
|
||||||
env.problem(Problem::InvalidUnicodeCodePoint(loc_hex_digits.region));
|
env.problem(Problem::InvalidUnicodeCodePt(loc_hex_digits.region));
|
||||||
|
|
||||||
return (
|
return (
|
||||||
Expr::RuntimeError(RuntimeError::InvalidUnicodeCodePoint(
|
Expr::RuntimeError(RuntimeError::InvalidUnicodeCodePt(
|
||||||
loc_hex_digits.region,
|
loc_hex_digits.region,
|
||||||
)),
|
)),
|
||||||
output,
|
output,
|
||||||
|
|
|
@ -313,7 +313,7 @@ pub fn canonical_string_literal<'a>(_arena: &Bump, _raw: &'a str, _region: Regio
|
||||||
|
|
||||||
// problems.push(Loc {
|
// problems.push(Loc {
|
||||||
// region,
|
// region,
|
||||||
// value: Problem::UnicodeCodePointTooLarge,
|
// value: Problem::UnicodeCodePtTooLarge,
|
||||||
// });
|
// });
|
||||||
// } else {
|
// } else {
|
||||||
// // If it all checked out, add it to
|
// // If it all checked out, add it to
|
||||||
|
@ -322,7 +322,7 @@ pub fn canonical_string_literal<'a>(_arena: &Bump, _raw: &'a str, _region: Regio
|
||||||
// Some(ch) => buf.push(ch),
|
// Some(ch) => buf.push(ch),
|
||||||
// None => {
|
// None => {
|
||||||
// problems.push(loc_escaped_unicode(
|
// problems.push(loc_escaped_unicode(
|
||||||
// Problem::InvalidUnicodeCodePoint,
|
// Problem::InvalidUnicodeCodePt,
|
||||||
// &state,
|
// &state,
|
||||||
// start_of_unicode,
|
// start_of_unicode,
|
||||||
// hex_str.len(),
|
// hex_str.len(),
|
||||||
|
@ -335,7 +335,7 @@ pub fn canonical_string_literal<'a>(_arena: &Bump, _raw: &'a str, _region: Regio
|
||||||
// let problem = if hex_str.is_empty() {
|
// let problem = if hex_str.is_empty() {
|
||||||
// Problem::NoUnicodeDigits
|
// Problem::NoUnicodeDigits
|
||||||
// } else {
|
// } else {
|
||||||
// Problem::NonHexCharsInUnicodeCodePoint
|
// Problem::NonHexCharsInUnicodeCodePt
|
||||||
// };
|
// };
|
||||||
|
|
||||||
// problems.push(loc_escaped_unicode(
|
// problems.push(loc_escaped_unicode(
|
||||||
|
|
|
@ -1590,7 +1590,7 @@ mod test_can {
|
||||||
// // (Rust has this restriction. I assume it's a good idea.)
|
// // (Rust has this restriction. I assume it's a good idea.)
|
||||||
// assert_malformed_str(
|
// assert_malformed_str(
|
||||||
// r#""abc\u{110000}def""#,
|
// r#""abc\u{110000}def""#,
|
||||||
// vec![Located::new(0, 7, 0, 12, Problem::UnicodeCodePointTooLarge)],
|
// vec![Located::new(0, 7, 0, 12, Problem::UnicodeCodePtTooLarge)],
|
||||||
// );
|
// );
|
||||||
// }
|
// }
|
||||||
|
|
||||||
|
|
|
@ -14,8 +14,8 @@ use crate::llvm::build_list::{
|
||||||
};
|
};
|
||||||
use crate::llvm::build_str::{
|
use crate::llvm::build_str::{
|
||||||
empty_str, str_concat, str_count_graphemes, str_ends_with, str_from_float, str_from_int,
|
empty_str, str_concat, str_count_graphemes, str_ends_with, str_from_float, str_from_int,
|
||||||
str_from_utf8, str_from_utf8_range, str_join_with, str_number_of_bytes, str_split, str_starts_with,
|
str_from_utf8, str_from_utf8_range, str_join_with, str_number_of_bytes, str_split,
|
||||||
str_starts_with_code_point, str_to_bytes,
|
str_starts_with, str_starts_with_code_point, str_to_utf8,
|
||||||
};
|
};
|
||||||
use crate::llvm::compare::{generic_eq, generic_neq};
|
use crate::llvm::compare::{generic_eq, generic_neq};
|
||||||
use crate::llvm::convert::{
|
use crate::llvm::convert::{
|
||||||
|
@ -4412,8 +4412,8 @@ fn run_low_level<'a, 'ctx, 'env>(
|
||||||
|
|
||||||
str_starts_with(env, scope, args[0], args[1])
|
str_starts_with(env, scope, args[0], args[1])
|
||||||
}
|
}
|
||||||
StrStartsWithCodePoint => {
|
StrStartsWithCodePt => {
|
||||||
// Str.startsWithCodePoint : Str, U32 -> Bool
|
// Str.startsWithCodePt : Str, U32 -> Bool
|
||||||
debug_assert_eq!(args.len(), 2);
|
debug_assert_eq!(args.len(), 2);
|
||||||
|
|
||||||
str_starts_with_code_point(env, scope, args[0], args[1])
|
str_starts_with_code_point(env, scope, args[0], args[1])
|
||||||
|
@ -4445,7 +4445,6 @@ fn run_low_level<'a, 'ctx, 'env>(
|
||||||
str_from_utf8(env, parent, original_wrapper)
|
str_from_utf8(env, parent, original_wrapper)
|
||||||
}
|
}
|
||||||
StrFromUtf8Range => {
|
StrFromUtf8Range => {
|
||||||
// Str.fromUtf8 : List U8 -> Result Str Utf8Problem
|
|
||||||
debug_assert_eq!(args.len(), 2);
|
debug_assert_eq!(args.len(), 2);
|
||||||
|
|
||||||
let list_wrapper = load_symbol(scope, &args[0]).into_struct_value();
|
let list_wrapper = load_symbol(scope, &args[0]).into_struct_value();
|
||||||
|
@ -4453,7 +4452,7 @@ fn run_low_level<'a, 'ctx, 'env>(
|
||||||
|
|
||||||
str_from_utf8_range(env, parent, list_wrapper, count_and_start)
|
str_from_utf8_range(env, parent, list_wrapper, count_and_start)
|
||||||
}
|
}
|
||||||
StrToBytes => {
|
StrToUtf8 => {
|
||||||
// Str.fromInt : Str -> List U8
|
// Str.fromInt : Str -> List U8
|
||||||
debug_assert_eq!(args.len(), 1);
|
debug_assert_eq!(args.len(), 1);
|
||||||
|
|
||||||
|
@ -4461,7 +4460,7 @@ fn run_low_level<'a, 'ctx, 'env>(
|
||||||
// we just implement it here to subvert the type system
|
// we just implement it here to subvert the type system
|
||||||
let string = load_symbol(scope, &args[0]);
|
let string = load_symbol(scope, &args[0]);
|
||||||
|
|
||||||
str_to_bytes(env, string.into_struct_value())
|
str_to_utf8(env, string.into_struct_value())
|
||||||
}
|
}
|
||||||
StrSplit => {
|
StrSplit => {
|
||||||
// Str.split : Str, Str -> List Str
|
// Str.split : Str, Str -> List Str
|
||||||
|
|
|
@ -175,7 +175,7 @@ pub fn str_starts_with<'a, 'ctx, 'env>(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Str.startsWithCodePoint : Str, U32 -> Bool
|
/// Str.startsWithCodePt : Str, U32 -> Bool
|
||||||
pub fn str_starts_with_code_point<'a, 'ctx, 'env>(
|
pub fn str_starts_with_code_point<'a, 'ctx, 'env>(
|
||||||
env: &Env<'a, 'ctx, 'env>,
|
env: &Env<'a, 'ctx, 'env>,
|
||||||
scope: &Scope<'a, 'ctx>,
|
scope: &Scope<'a, 'ctx>,
|
||||||
|
@ -188,7 +188,7 @@ pub fn str_starts_with_code_point<'a, 'ctx, 'env>(
|
||||||
call_bitcode_fn(
|
call_bitcode_fn(
|
||||||
env,
|
env,
|
||||||
&[str_i128.into(), prefix],
|
&[str_i128.into(), prefix],
|
||||||
bitcode::STR_STARTS_WITH_CODE_POINT,
|
bitcode::STR_STARTS_WITH_CODE_PT,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -235,8 +235,8 @@ pub fn str_from_int<'a, 'ctx, 'env>(
|
||||||
call_bitcode_fn(env, &[int], bitcode::STR_FROM_INT)
|
call_bitcode_fn(env, &[int], bitcode::STR_FROM_INT)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Str.toBytes : Str -> List U8
|
/// Str.toUtf8 : Str -> List U8
|
||||||
pub fn str_to_bytes<'a, 'ctx, 'env>(
|
pub fn str_to_utf8<'a, 'ctx, 'env>(
|
||||||
env: &Env<'a, 'ctx, 'env>,
|
env: &Env<'a, 'ctx, 'env>,
|
||||||
original_wrapper: StructValue<'ctx>,
|
original_wrapper: StructValue<'ctx>,
|
||||||
) -> BasicValueEnum<'ctx> {
|
) -> BasicValueEnum<'ctx> {
|
||||||
|
@ -244,10 +244,10 @@ pub fn str_to_bytes<'a, 'ctx, 'env>(
|
||||||
env.builder,
|
env.builder,
|
||||||
original_wrapper.into(),
|
original_wrapper.into(),
|
||||||
env.context.i128_type().into(),
|
env.context.i128_type().into(),
|
||||||
"to_bytes",
|
"to_utf8",
|
||||||
);
|
);
|
||||||
|
|
||||||
call_bitcode_fn_returns_list(env, &[string], bitcode::STR_TO_BYTES)
|
call_bitcode_fn_returns_list(env, &[string], bitcode::STR_TO_UTF8)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Str.fromUtf8 : List U8, { count : Nat, start : Nat } -> { a : Bool, b : Str, c : Nat, d : I8 }
|
/// Str.fromUtf8 : List U8, { count : Nat, start : Nat } -> { a : Bool, b : Str, c : Nat, d : I8 }
|
||||||
|
|
|
@ -7,14 +7,14 @@ pub enum LowLevel {
|
||||||
StrJoinWith,
|
StrJoinWith,
|
||||||
StrIsEmpty,
|
StrIsEmpty,
|
||||||
StrStartsWith,
|
StrStartsWith,
|
||||||
StrStartsWithCodePoint,
|
StrStartsWithCodePt,
|
||||||
StrEndsWith,
|
StrEndsWith,
|
||||||
StrSplit,
|
StrSplit,
|
||||||
StrCountGraphemes,
|
StrCountGraphemes,
|
||||||
StrFromInt,
|
StrFromInt,
|
||||||
StrFromUtf8,
|
StrFromUtf8,
|
||||||
StrFromUtf8Range,
|
StrFromUtf8Range,
|
||||||
StrToBytes,
|
StrToUtf8,
|
||||||
StrFromFloat,
|
StrFromFloat,
|
||||||
ListLen,
|
ListLen,
|
||||||
ListGetUnsafe,
|
ListGetUnsafe,
|
||||||
|
@ -110,92 +110,22 @@ impl LowLevel {
|
||||||
use LowLevel::*;
|
use LowLevel::*;
|
||||||
|
|
||||||
match self {
|
match self {
|
||||||
StrConcat
|
StrConcat | StrJoinWith | StrIsEmpty | StrStartsWith | StrStartsWithCodePt
|
||||||
| StrJoinWith
|
| StrEndsWith | StrSplit | StrCountGraphemes | StrFromInt | StrFromUtf8 | StrFromUtf8Range | StrToUtf8
|
||||||
| StrIsEmpty
|
| StrFromFloat | ListLen | ListGetUnsafe | ListSet | ListDrop | ListSingle
|
||||||
| StrStartsWith
|
| ListRepeat | ListReverse | ListConcat | ListContains | ListAppend | ListPrepend
|
||||||
| StrStartsWithCodePoint
|
| ListJoin | ListRange | ListSwap | DictSize | DictEmpty | DictInsert | DictRemove
|
||||||
| StrEndsWith
|
| DictContains | DictGetUnsafe | DictKeys | DictValues | DictUnion
|
||||||
| StrSplit
|
| DictIntersection | DictDifference | SetFromList | NumAdd | NumAddWrap
|
||||||
| StrCountGraphemes
|
| NumAddChecked | NumSub | NumSubWrap | NumSubChecked | NumMul | NumMulWrap
|
||||||
| StrFromInt
|
| NumMulChecked | NumGt | NumGte | NumLt | NumLte | NumCompare | NumDivUnchecked
|
||||||
| StrFromUtf8
|
| NumRemUnchecked | NumIsMultipleOf | NumAbs | NumNeg | NumSin | NumCos
|
||||||
| StrFromUtf8Range
|
| NumSqrtUnchecked | NumLogUnchecked | NumRound | NumToFloat | NumPow | NumCeiling
|
||||||
| StrToBytes
|
| NumPowInt | NumFloor | NumIsFinite | NumAtan | NumAcos | NumAsin | NumBitwiseAnd
|
||||||
| StrFromFloat
|
| NumBitwiseXor | NumBitwiseOr | NumShiftLeftBy | NumShiftRightBy
|
||||||
| ListLen
|
| NumShiftRightZfBy | NumIntCast | Eq | NotEq | And | Or | Not | Hash | ExpectTrue => {
|
||||||
| ListGetUnsafe
|
false
|
||||||
| ListSet
|
}
|
||||||
| ListDrop
|
|
||||||
| ListSingle
|
|
||||||
| ListRepeat
|
|
||||||
| ListReverse
|
|
||||||
| ListConcat
|
|
||||||
| ListContains
|
|
||||||
| ListAppend
|
|
||||||
| ListPrepend
|
|
||||||
| ListJoin
|
|
||||||
| ListRange
|
|
||||||
| ListSwap
|
|
||||||
| DictSize
|
|
||||||
| DictEmpty
|
|
||||||
| DictInsert
|
|
||||||
| DictRemove
|
|
||||||
| DictContains
|
|
||||||
| DictGetUnsafe
|
|
||||||
| DictKeys
|
|
||||||
| DictValues
|
|
||||||
| DictUnion
|
|
||||||
| DictIntersection
|
|
||||||
| DictDifference
|
|
||||||
| SetFromList
|
|
||||||
| NumAdd
|
|
||||||
| NumAddWrap
|
|
||||||
| NumAddChecked
|
|
||||||
| NumSub
|
|
||||||
| NumSubWrap
|
|
||||||
| NumSubChecked
|
|
||||||
| NumMul
|
|
||||||
| NumMulWrap
|
|
||||||
| NumMulChecked
|
|
||||||
| NumGt
|
|
||||||
| NumGte
|
|
||||||
| NumLt
|
|
||||||
| NumLte
|
|
||||||
| NumCompare
|
|
||||||
| NumDivUnchecked
|
|
||||||
| NumRemUnchecked
|
|
||||||
| NumIsMultipleOf
|
|
||||||
| NumAbs
|
|
||||||
| NumNeg
|
|
||||||
| NumSin
|
|
||||||
| NumCos
|
|
||||||
| NumSqrtUnchecked
|
|
||||||
| NumLogUnchecked
|
|
||||||
| NumRound
|
|
||||||
| NumToFloat
|
|
||||||
| NumPow
|
|
||||||
| NumCeiling
|
|
||||||
| NumPowInt
|
|
||||||
| NumFloor
|
|
||||||
| NumIsFinite
|
|
||||||
| NumAtan
|
|
||||||
| NumAcos
|
|
||||||
| NumAsin
|
|
||||||
| NumBitwiseAnd
|
|
||||||
| NumBitwiseXor
|
|
||||||
| NumBitwiseOr
|
|
||||||
| NumShiftLeftBy
|
|
||||||
| NumShiftRightBy
|
|
||||||
| NumShiftRightZfBy
|
|
||||||
| NumIntCast
|
|
||||||
| Eq
|
|
||||||
| NotEq
|
|
||||||
| And
|
|
||||||
| Or
|
|
||||||
| Not
|
|
||||||
| Hash
|
|
||||||
| ExpectTrue => false,
|
|
||||||
|
|
||||||
ListMap | ListMap2 | ListMap3 | ListMapWithIndex | ListKeepIf | ListWalk
|
ListMap | ListMap2 | ListMap3 | ListMapWithIndex | ListKeepIf | ListWalk
|
||||||
| ListWalkUntil | ListWalkBackwards | ListKeepOks | ListKeepErrs | ListSortWith
|
| ListWalkUntil | ListWalkBackwards | ListKeepOks | ListKeepErrs | ListSortWith
|
||||||
|
|
|
@ -918,8 +918,8 @@ define_builtins! {
|
||||||
12 STR_FROM_UTF8: "fromUtf8"
|
12 STR_FROM_UTF8: "fromUtf8"
|
||||||
13 STR_UT8_PROBLEM: "Utf8Problem" // the Utf8Problem type alias
|
13 STR_UT8_PROBLEM: "Utf8Problem" // the Utf8Problem type alias
|
||||||
14 STR_UT8_BYTE_PROBLEM: "Utf8ByteProblem" // the Utf8ByteProblem type alias
|
14 STR_UT8_BYTE_PROBLEM: "Utf8ByteProblem" // the Utf8ByteProblem type alias
|
||||||
15 STR_TO_BYTES: "toBytes"
|
15 STR_TO_UTF8: "toUtf8"
|
||||||
16 STR_STARTS_WITH_CODE_POINT: "startsWithCodePoint"
|
16 STR_STARTS_WITH_CODE_PT: "startsWithCodePt"
|
||||||
17 STR_ALIAS_ANALYSIS_STATIC: "#aliasAnalysisStatic" // string with the static lifetime
|
17 STR_ALIAS_ANALYSIS_STATIC: "#aliasAnalysisStatic" // string with the static lifetime
|
||||||
18 STR_FROM_UTF8_RANGE: "fromUtf8Range"
|
18 STR_FROM_UTF8_RANGE: "fromUtf8Range"
|
||||||
}
|
}
|
||||||
|
@ -989,8 +989,6 @@ define_builtins! {
|
||||||
14 DICT_UNION: "union"
|
14 DICT_UNION: "union"
|
||||||
15 DICT_INTERSECTION: "intersection"
|
15 DICT_INTERSECTION: "intersection"
|
||||||
16 DICT_DIFFERENCE: "difference"
|
16 DICT_DIFFERENCE: "difference"
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
7 SET: "Set" => {
|
7 SET: "Set" => {
|
||||||
0 SET_SET: "Set" imported // the Set.Set type alias
|
0 SET_SET: "Set" imported // the Set.Set type alias
|
||||||
|
|
|
@ -1013,10 +1013,10 @@ pub fn lowlevel_borrow_signature(arena: &Bump, op: LowLevel) -> &[bool] {
|
||||||
| NumCeiling | NumFloor | NumToFloat | Not | NumIsFinite | NumAtan | NumAcos | NumAsin
|
| NumCeiling | NumFloor | NumToFloat | Not | NumIsFinite | NumAtan | NumAcos | NumAsin
|
||||||
| NumIntCast => arena.alloc_slice_copy(&[irrelevant]),
|
| NumIntCast => arena.alloc_slice_copy(&[irrelevant]),
|
||||||
StrStartsWith | StrEndsWith => arena.alloc_slice_copy(&[owned, borrowed]),
|
StrStartsWith | StrEndsWith => arena.alloc_slice_copy(&[owned, borrowed]),
|
||||||
StrStartsWithCodePoint => arena.alloc_slice_copy(&[borrowed, irrelevant]),
|
StrStartsWithCodePt => arena.alloc_slice_copy(&[borrowed, irrelevant]),
|
||||||
StrFromUtf8 => arena.alloc_slice_copy(&[owned]),
|
StrFromUtf8 => arena.alloc_slice_copy(&[owned]),
|
||||||
StrFromUtf8Range => arena.alloc_slice_copy(&[borrowed, irrelevant]),
|
StrFromUtf8Range => arena.alloc_slice_copy(&[borrowed, irrelevant]),
|
||||||
StrToBytes => arena.alloc_slice_copy(&[owned]),
|
StrToUtf8 => arena.alloc_slice_copy(&[owned]),
|
||||||
StrFromInt | StrFromFloat => arena.alloc_slice_copy(&[irrelevant]),
|
StrFromInt | StrFromFloat => arena.alloc_slice_copy(&[irrelevant]),
|
||||||
Hash => arena.alloc_slice_copy(&[borrowed, irrelevant]),
|
Hash => arena.alloc_slice_copy(&[borrowed, irrelevant]),
|
||||||
DictSize => arena.alloc_slice_copy(&[borrowed]),
|
DictSize => arena.alloc_slice_copy(&[borrowed]),
|
||||||
|
|
|
@ -436,8 +436,8 @@ pub enum Number {
|
||||||
pub enum EString<'a> {
|
pub enum EString<'a> {
|
||||||
Open(Row, Col),
|
Open(Row, Col),
|
||||||
|
|
||||||
CodePointOpen(Row, Col),
|
CodePtOpen(Row, Col),
|
||||||
CodePointEnd(Row, Col),
|
CodePtEnd(Row, Col),
|
||||||
|
|
||||||
Space(BadInputError, Row, Col),
|
Space(BadInputError, Row, Col),
|
||||||
EndlessSingle(Row, Col),
|
EndlessSingle(Row, Col),
|
||||||
|
|
|
@ -6,10 +6,10 @@ pub type Problems = Vec<Loc<Problem>>;
|
||||||
pub enum Problem {
|
pub enum Problem {
|
||||||
// UNICODE CODE POINT
|
// UNICODE CODE POINT
|
||||||
/// TODO Invalid hex code - Unicode code points must be specified using hexadecimal characters (the numbers 0-9 and letters A-F)
|
/// TODO Invalid hex code - Unicode code points must be specified using hexadecimal characters (the numbers 0-9 and letters A-F)
|
||||||
NonHexCharsInUnicodeCodePoint,
|
NonHexCharsInUnicodeCodePt,
|
||||||
/// TODO Invalid Unicode code point. It must be no more than \\u{10FFFF}.
|
/// TODO Invalid Unicode code point. It must be no more than \\u{10FFFF}.
|
||||||
UnicodeCodePointTooLarge,
|
UnicodeCodePtTooLarge,
|
||||||
InvalidUnicodeCodePoint,
|
InvalidUnicodeCodePt,
|
||||||
MalformedEscapedUnicode,
|
MalformedEscapedUnicode,
|
||||||
NoUnicodeDigits,
|
NoUnicodeDigits,
|
||||||
|
|
||||||
|
|
|
@ -18,7 +18,7 @@ fn ascii_hex_digits<'a>() -> impl Parser<'a, &'a str, EString<'a>> {
|
||||||
// We didn't find any hex digits!
|
// We didn't find any hex digits!
|
||||||
return Err((
|
return Err((
|
||||||
NoProgress,
|
NoProgress,
|
||||||
EString::CodePointEnd(state.line, state.column),
|
EString::CodePtEnd(state.line, state.column),
|
||||||
state,
|
state,
|
||||||
));
|
));
|
||||||
} else {
|
} else {
|
||||||
|
@ -32,7 +32,7 @@ fn ascii_hex_digits<'a>() -> impl Parser<'a, &'a str, EString<'a>> {
|
||||||
|
|
||||||
Err((
|
Err((
|
||||||
NoProgress,
|
NoProgress,
|
||||||
EString::CodePointEnd(state.line, state.column),
|
EString::CodePtEnd(state.line, state.column),
|
||||||
state,
|
state,
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
@ -257,9 +257,9 @@ pub fn parse<'a>() -> impl Parser<'a, StrLiteral<'a>, EString<'a>> {
|
||||||
// give a canonicalization error if the digits form
|
// give a canonicalization error if the digits form
|
||||||
// an invalid unicode code point.
|
// an invalid unicode code point.
|
||||||
let (_progress, loc_digits, new_state) = between!(
|
let (_progress, loc_digits, new_state) = between!(
|
||||||
word1(b'(', EString::CodePointOpen),
|
word1(b'(', EString::CodePtOpen),
|
||||||
loc(ascii_hex_digits()),
|
loc(ascii_hex_digits()),
|
||||||
word1(b')', EString::CodePointEnd)
|
word1(b')', EString::CodePtEnd)
|
||||||
)
|
)
|
||||||
.parse(arena, state)?;
|
.parse(arena, state)?;
|
||||||
|
|
||||||
|
|
|
@ -77,7 +77,7 @@ pub enum Problem {
|
||||||
},
|
},
|
||||||
InvalidInterpolation(Region),
|
InvalidInterpolation(Region),
|
||||||
InvalidHexadecimal(Region),
|
InvalidHexadecimal(Region),
|
||||||
InvalidUnicodeCodePoint(Region),
|
InvalidUnicodeCodePt(Region),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq)]
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
|
@ -160,7 +160,7 @@ pub enum RuntimeError {
|
||||||
|
|
||||||
InvalidInterpolation(Region),
|
InvalidInterpolation(Region),
|
||||||
InvalidHexadecimal(Region),
|
InvalidHexadecimal(Region),
|
||||||
InvalidUnicodeCodePoint(Region),
|
InvalidUnicodeCodePt(Region),
|
||||||
|
|
||||||
/// When the author specifies a type annotation but no implementation
|
/// When the author specifies a type annotation but no implementation
|
||||||
NoImplementationNamed {
|
NoImplementationNamed {
|
||||||
|
|
|
@ -313,7 +313,7 @@ pub fn can_problem<'b>(
|
||||||
]),
|
]),
|
||||||
alloc.reflow(r"Learn more about working with unicode in roc at TODO"),
|
alloc.reflow(r"Learn more about working with unicode in roc at TODO"),
|
||||||
]),
|
]),
|
||||||
Problem::InvalidUnicodeCodePoint(region) => alloc.stack(vec![
|
Problem::InvalidUnicodeCodePt(region) => alloc.stack(vec![
|
||||||
alloc.reflow("This unicode code point is invalid:"),
|
alloc.reflow("This unicode code point is invalid:"),
|
||||||
alloc.region(region),
|
alloc.region(region),
|
||||||
alloc.reflow("Learn more about working with unicode in roc at TODO"),
|
alloc.reflow("Learn more about working with unicode in roc at TODO"),
|
||||||
|
@ -931,7 +931,7 @@ fn pretty_runtime_error<'b>(
|
||||||
region
|
region
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
RuntimeError::InvalidUnicodeCodePoint(region) => {
|
RuntimeError::InvalidUnicodeCodePt(region) => {
|
||||||
todo!(
|
todo!(
|
||||||
"TODO runtime error for an invalid \\u(...) code point at region {:?}",
|
"TODO runtime error for an invalid \\u(...) code point at region {:?}",
|
||||||
region
|
region
|
||||||
|
|
|
@ -826,7 +826,7 @@ fn to_str_report<'a>(
|
||||||
title: "WEIRD ESCAPE".to_string(),
|
title: "WEIRD ESCAPE".to_string(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
EString::CodePointOpen(row, col) | EString::CodePointEnd(row, col) => {
|
EString::CodePtOpen(row, col) | EString::CodePtEnd(row, col) => {
|
||||||
let surroundings = Region::from_rows_cols(start_row, start_col, row, col);
|
let surroundings = Region::from_rows_cols(start_row, start_col, row, col);
|
||||||
let region = Region::from_row_col(row, col);
|
let region = Region::from_row_col(row, col);
|
||||||
|
|
||||||
|
|
|
@ -1392,24 +1392,39 @@ fn deep_copy_var_help(
|
||||||
same @ EmptyRecord | same @ EmptyTagUnion | same @ Erroneous(_) => same,
|
same @ EmptyRecord | same @ EmptyTagUnion | same @ Erroneous(_) => same,
|
||||||
|
|
||||||
Record(fields, ext_var) => {
|
Record(fields, ext_var) => {
|
||||||
let mut new_vars = Vec::with_capacity(fields.len());
|
let record_fields = {
|
||||||
for index in fields.iter_variables() {
|
let mut new_vars = Vec::with_capacity(fields.len());
|
||||||
let var = subs[index];
|
|
||||||
let copy_var = deep_copy_var_help(subs, max_rank, pools, var);
|
|
||||||
new_vars.push(copy_var);
|
|
||||||
}
|
|
||||||
|
|
||||||
let it = fields.iter_all().zip(new_vars).map(|((i1, _, i3), var)| {
|
for index in fields.iter_variables() {
|
||||||
let label = subs[i1].clone();
|
let var = subs[index];
|
||||||
let record_field = subs[i3].map(|_| var);
|
let copy_var = deep_copy_var_help(subs, max_rank, pools, var);
|
||||||
|
|
||||||
(label, record_field)
|
new_vars.push(copy_var);
|
||||||
});
|
}
|
||||||
|
|
||||||
// lifetime troubles
|
let field_names_start = subs.field_names.len() as u32;
|
||||||
let vec: Vec<_> = it.collect();
|
let variables_start = subs.variables.len() as u32;
|
||||||
|
let field_types_start = subs.record_fields.len() as u32;
|
||||||
|
|
||||||
let record_fields = RecordFields::insert_into_subs(subs, vec);
|
let mut length = 0;
|
||||||
|
|
||||||
|
for ((i1, _, i3), var) in fields.iter_all().zip(new_vars) {
|
||||||
|
let record_field = subs[i3].map(|_| var);
|
||||||
|
|
||||||
|
subs.field_names.push(subs[i1].clone());
|
||||||
|
subs.record_fields.push(record_field.map(|_| ()));
|
||||||
|
subs.variables.push(*record_field.as_inner());
|
||||||
|
|
||||||
|
length += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
RecordFields {
|
||||||
|
length,
|
||||||
|
field_names_start,
|
||||||
|
variables_start,
|
||||||
|
field_types_start,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
Record(
|
Record(
|
||||||
record_fields,
|
record_fields,
|
||||||
|
|
|
@ -423,12 +423,12 @@ fn str_starts_with() {
|
||||||
#[test]
|
#[test]
|
||||||
fn str_starts_with_code_point() {
|
fn str_starts_with_code_point() {
|
||||||
assert_evals_to!(
|
assert_evals_to!(
|
||||||
&format!(r#"Str.startsWithCodePoint "foobar" {}"#, 'f' as u32),
|
&format!(r#"Str.startsWithCodePt "foobar" {}"#, 'f' as u32),
|
||||||
true,
|
true,
|
||||||
bool
|
bool
|
||||||
);
|
);
|
||||||
assert_evals_to!(
|
assert_evals_to!(
|
||||||
&format!(r#"Str.startsWithCodePoint "zoobar" {}"#, 'f' as u32),
|
&format!(r#"Str.startsWithCodePt "zoobar" {}"#, 'f' as u32),
|
||||||
false,
|
false,
|
||||||
bool
|
bool
|
||||||
);
|
);
|
||||||
|
@ -819,10 +819,10 @@ fn str_from_float() {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn str_to_bytes() {
|
fn str_to_utf8() {
|
||||||
assert_evals_to!(r#"Str.toBytes "hello""#, &[104, 101, 108, 108, 111], &[u8]);
|
assert_evals_to!(r#"Str.toUtf8 "hello""#, &[104, 101, 108, 108, 111], &[u8]);
|
||||||
assert_evals_to!(
|
assert_evals_to!(
|
||||||
r#"Str.toBytes "this is a long string""#,
|
r#"Str.toUtf8 "this is a long string""#,
|
||||||
&[
|
&[
|
||||||
116, 104, 105, 115, 32, 105, 115, 32, 97, 32, 108, 111, 110, 103, 32, 115, 116, 114,
|
116, 104, 105, 115, 32, 105, 115, 32, 97, 32, 108, 111, 110, 103, 32, 115, 116, 114,
|
||||||
105, 110, 103
|
105, 110, 103
|
||||||
|
@ -836,7 +836,7 @@ fn str_from_utf8_range() {
|
||||||
assert_evals_to!(
|
assert_evals_to!(
|
||||||
indoc!(
|
indoc!(
|
||||||
r#"
|
r#"
|
||||||
bytes = Str.toBytes "hello"
|
bytes = Str.toUtf8 "hello"
|
||||||
when Str.fromUtf8Range bytes { count: 5, start: 0 } is
|
when Str.fromUtf8Range bytes { count: 5, start: 0 } is
|
||||||
Ok utf8String -> utf8String
|
Ok utf8String -> utf8String
|
||||||
_ -> ""
|
_ -> ""
|
||||||
|
@ -852,7 +852,7 @@ fn str_from_utf8_range_slice() {
|
||||||
assert_evals_to!(
|
assert_evals_to!(
|
||||||
indoc!(
|
indoc!(
|
||||||
r#"
|
r#"
|
||||||
bytes = Str.toBytes "hello"
|
bytes = Str.toUtf8 "hello"
|
||||||
when Str.fromUtf8Range bytes { count: 4, start: 1 } is
|
when Str.fromUtf8Range bytes { count: 4, start: 1 } is
|
||||||
Ok utf8String -> utf8String
|
Ok utf8String -> utf8String
|
||||||
_ -> ""
|
_ -> ""
|
||||||
|
@ -868,7 +868,7 @@ fn str_from_utf8_range_slice_not_end() {
|
||||||
assert_evals_to!(
|
assert_evals_to!(
|
||||||
indoc!(
|
indoc!(
|
||||||
r#"
|
r#"
|
||||||
bytes = Str.toBytes "hello"
|
bytes = Str.toUtf8 "hello"
|
||||||
when Str.fromUtf8Range bytes { count: 3, start: 1 } is
|
when Str.fromUtf8Range bytes { count: 3, start: 1 } is
|
||||||
Ok utf8String -> utf8String
|
Ok utf8String -> utf8String
|
||||||
_ -> ""
|
_ -> ""
|
||||||
|
@ -884,7 +884,7 @@ fn str_from_utf8_range_order_does_not_matter() {
|
||||||
assert_evals_to!(
|
assert_evals_to!(
|
||||||
indoc!(
|
indoc!(
|
||||||
r#"
|
r#"
|
||||||
bytes = Str.toBytes "hello"
|
bytes = Str.toUtf8 "hello"
|
||||||
when Str.fromUtf8Range bytes { start: 1, count: 3 } is
|
when Str.fromUtf8Range bytes { start: 1, count: 3 } is
|
||||||
Ok utf8String -> utf8String
|
Ok utf8String -> utf8String
|
||||||
_ -> ""
|
_ -> ""
|
||||||
|
@ -895,18 +895,18 @@ fn str_from_utf8_range_order_does_not_matter() {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
// #[test]
|
||||||
fn str_from_utf8_range_out_of_bounds() {
|
// fn str_from_utf8_range_out_of_bounds() {
|
||||||
assert_evals_to!(
|
// assert_evals_to!(
|
||||||
indoc!(
|
// indoc!(
|
||||||
r#"
|
// r#"
|
||||||
bytes = Str.toBytes "hello"
|
// bytes = Str.toUtf8 "hello"
|
||||||
when Str.fromUtf8Range bytes { start: 7, count: 3 } is
|
// when Str.fromUtf8Range bytes { start: 7, count: 3 } is
|
||||||
Ok _ -> ""
|
// Ok _ -> ""
|
||||||
Err OutOfBounds -> "out of bounds"
|
// Err OutOfBounds -> "out of bounds"
|
||||||
"#
|
// "#
|
||||||
),
|
// ),
|
||||||
RocStr::from("out of bounds"),
|
// RocStr::from("out of bounds"),
|
||||||
RocStr
|
// RocStr
|
||||||
);
|
// );
|
||||||
}
|
// }
|
||||||
|
|
|
@ -477,7 +477,7 @@ fn if_guard_vanilla() {
|
||||||
r#"
|
r#"
|
||||||
when "fooz" is
|
when "fooz" is
|
||||||
s if s == "foo" -> 0
|
s if s == "foo" -> 0
|
||||||
s -> List.len (Str.toBytes s)
|
s -> List.len (Str.toUtf8 s)
|
||||||
"#
|
"#
|
||||||
),
|
),
|
||||||
4,
|
4,
|
||||||
|
|
|
@ -52,21 +52,28 @@ pub fn generate(filenames: Vec<PathBuf>, std_lib: StdLib, build_dir: &Path) {
|
||||||
)
|
)
|
||||||
.expect("TODO gracefully handle failing to make the favicon");
|
.expect("TODO gracefully handle failing to make the favicon");
|
||||||
|
|
||||||
let template_html = include_str!("./static/index.html").replace(
|
let template_html = include_str!("./static/index.html")
|
||||||
"<!-- Module links -->",
|
.replace("<!-- search.js -->", &format!("{}search.js", base_href()))
|
||||||
render_sidebar(package.modules.iter().flat_map(|loaded_module| {
|
.replace("<!-- styles.css -->", &format!("{}styles.css", base_href()))
|
||||||
loaded_module.documentation.values().map(move |d| {
|
.replace(
|
||||||
let exposed_values = loaded_module
|
"<!-- favicon.svg -->",
|
||||||
.exposed_values
|
&format!("{}favicon.svg", base_href()),
|
||||||
.iter()
|
)
|
||||||
.map(|symbol| symbol.ident_str(&loaded_module.interns).to_string())
|
.replace(
|
||||||
.collect::<Vec<String>>();
|
"<!-- Module links -->",
|
||||||
|
render_sidebar(package.modules.iter().flat_map(|loaded_module| {
|
||||||
|
loaded_module.documentation.values().map(move |d| {
|
||||||
|
let exposed_values = loaded_module
|
||||||
|
.exposed_values
|
||||||
|
.iter()
|
||||||
|
.map(|symbol| symbol.ident_str(&loaded_module.interns).to_string())
|
||||||
|
.collect::<Vec<String>>();
|
||||||
|
|
||||||
(exposed_values, d)
|
(exposed_values, d)
|
||||||
})
|
})
|
||||||
}))
|
}))
|
||||||
.as_str(),
|
.as_str(),
|
||||||
);
|
);
|
||||||
|
|
||||||
// Write each package's module docs html file
|
// Write each package's module docs html file
|
||||||
for loaded_module in package.modules.iter_mut() {
|
for loaded_module in package.modules.iter_mut() {
|
||||||
|
@ -143,7 +150,7 @@ fn render_main_content(
|
||||||
if should_render_entry {
|
if should_render_entry {
|
||||||
match entry {
|
match entry {
|
||||||
DocEntry::DocDef(doc_def) => {
|
DocEntry::DocDef(doc_def) => {
|
||||||
let mut href = String::new();
|
let mut href = base_href();
|
||||||
href.push('#');
|
href.push('#');
|
||||||
href.push_str(doc_def.name.as_str());
|
href.push_str(doc_def.name.as_str());
|
||||||
|
|
||||||
|
@ -239,11 +246,40 @@ fn html_node(tag_name: &str, attrs: Vec<(&str, &str)>, content: &str) -> String
|
||||||
buf
|
buf
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn base_href() -> String {
|
||||||
|
// e.g. "builtins/" in "https://roc-lang.org/builtins/Str"
|
||||||
|
//
|
||||||
|
// TODO make this a CLI flag to the `docs` subcommand instead of an env var
|
||||||
|
match std::env::var("ROC_DOCS_URL_ROOT") {
|
||||||
|
Ok(root_builtins_path) => {
|
||||||
|
let mut href = String::with_capacity(root_builtins_path.len() + 64);
|
||||||
|
|
||||||
|
if !root_builtins_path.starts_with('/') {
|
||||||
|
href.push('/');
|
||||||
|
}
|
||||||
|
|
||||||
|
href.push_str(&root_builtins_path);
|
||||||
|
|
||||||
|
if !root_builtins_path.ends_with('/') {
|
||||||
|
href.push('/');
|
||||||
|
}
|
||||||
|
|
||||||
|
href
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
let mut href = String::with_capacity(64);
|
||||||
|
|
||||||
|
href.push('/');
|
||||||
|
|
||||||
|
href
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn render_name_and_version(name: &str, version: &str) -> String {
|
fn render_name_and_version(name: &str, version: &str) -> String {
|
||||||
let mut buf = String::new();
|
let mut buf = String::new();
|
||||||
|
let mut href = base_href();
|
||||||
|
|
||||||
let mut href = String::new();
|
|
||||||
href.push('/');
|
|
||||||
href.push_str(name);
|
href.push_str(name);
|
||||||
|
|
||||||
buf.push_str(
|
buf.push_str(
|
||||||
|
@ -255,7 +291,7 @@ fn render_name_and_version(name: &str, version: &str) -> String {
|
||||||
.as_str(),
|
.as_str(),
|
||||||
);
|
);
|
||||||
|
|
||||||
let mut versions_href = String::new();
|
let mut versions_href = base_href();
|
||||||
|
|
||||||
versions_href.push('/');
|
versions_href.push('/');
|
||||||
versions_href.push_str(name);
|
versions_href.push_str(name);
|
||||||
|
@ -285,8 +321,7 @@ fn render_sidebar<'a, I: Iterator<Item = (Vec<String>, &'a ModuleDocumentation)>
|
||||||
let name = module.name.as_str();
|
let name = module.name.as_str();
|
||||||
|
|
||||||
let href = {
|
let href = {
|
||||||
let mut href_buf = String::new();
|
let mut href_buf = base_href();
|
||||||
href_buf.push('/');
|
|
||||||
href_buf.push_str(name);
|
href_buf.push_str(name);
|
||||||
href_buf
|
href_buf
|
||||||
};
|
};
|
||||||
|
@ -686,8 +721,8 @@ fn doc_url<'a>(
|
||||||
if !exposed_values.contains(&ident) {
|
if !exposed_values.contains(&ident) {
|
||||||
// TODO return Err here
|
// TODO return Err here
|
||||||
panic!(
|
panic!(
|
||||||
"Tried to generate an automatic link in docs for `{}.{}`, but `{}` is not declared in `{}`.",
|
"Tried to generate an automatic link in docs for `{}.{}`, but `{}` does not expose `{}`.",
|
||||||
module_name, ident, ident, module_name);
|
module_name, ident, module_name, ident);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// This is not the home module
|
// This is not the home module
|
||||||
|
@ -718,12 +753,11 @@ fn doc_url<'a>(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut url = String::new();
|
let mut url = base_href();
|
||||||
|
|
||||||
// Example:
|
// Example:
|
||||||
//
|
//
|
||||||
// module_name: "Str", ident: "join" => "/Str#join"
|
// module_name: "Str", ident: "join" => "/Str#join"
|
||||||
url.push('/');
|
|
||||||
url.push_str(module_name);
|
url.push_str(module_name);
|
||||||
url.push('#');
|
url.push('#');
|
||||||
url.push_str(ident);
|
url.push_str(ident);
|
||||||
|
@ -757,7 +791,7 @@ fn markdown_to_html(
|
||||||
let state = State::new(link.reference.as_bytes());
|
let state = State::new(link.reference.as_bytes());
|
||||||
|
|
||||||
// Reset the bump arena so we aren't constantly reallocating
|
// Reset the bump arena so we aren't constantly reallocating
|
||||||
// more memory.
|
// more memory as we iterate through these.
|
||||||
arena.reset();
|
arena.reset();
|
||||||
|
|
||||||
match parse_ident(&arena, state) {
|
match parse_ident(&arena, state) {
|
||||||
|
|
|
@ -6,9 +6,9 @@
|
||||||
<!-- <title>TODO populate this based on the module's name, e.g. "Parser - roc/parser"</title> -->
|
<!-- <title>TODO populate this based on the module's name, e.g. "Parser - roc/parser"</title> -->
|
||||||
<!-- <meta name="description" content="TODO populate this based on the module's description"> -->
|
<!-- <meta name="description" content="TODO populate this based on the module's description"> -->
|
||||||
<meta name="viewport" content="width=device-width">
|
<meta name="viewport" content="width=device-width">
|
||||||
<link rel="icon" href="/favicon.svg">
|
<link rel="icon" href="<!-- favicon.svg -->">
|
||||||
<script type="text/javascript" src="/search.js" defer></script>
|
<script type="text/javascript" src="<!-- search.js -->" defer></script>
|
||||||
<link rel="stylesheet" href="/styles.css">
|
<link rel="stylesheet" href="<!-- styles.css -->">
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
|
|
|
@ -73,7 +73,7 @@ pub enum EdError {
|
||||||
},
|
},
|
||||||
|
|
||||||
#[snafu(display(
|
#[snafu(display(
|
||||||
"MissingSelection: ed_model.selected_expr2_id was Some(NodeId<Expr2>) but ed_model.caret_w_sel_vec did not contain any Some(Selection)."
|
"MissingSelection: ed_model.selected_expr2_id was Some(ExprId) but ed_model.caret_w_sel_vec did not contain any Some(Selection)."
|
||||||
))]
|
))]
|
||||||
MissingSelection { backtrace: Backtrace },
|
MissingSelection { backtrace: Backtrace },
|
||||||
|
|
||||||
|
|
|
@ -6,8 +6,7 @@ use crate::editor::slow_pool::MarkNodeId;
|
||||||
use crate::editor::slow_pool::SlowPool;
|
use crate::editor::slow_pool::SlowPool;
|
||||||
use crate::editor::util::first_last_index_of;
|
use crate::editor::util::first_last_index_of;
|
||||||
use crate::editor::util::index_of;
|
use crate::editor::util::index_of;
|
||||||
use crate::lang::ast::Expr2;
|
use crate::lang::ast::ExprId;
|
||||||
use crate::lang::pool::NodeId;
|
|
||||||
use crate::ui::text::selection::Selection;
|
use crate::ui::text::selection::Selection;
|
||||||
use crate::ui::text::text_pos::TextPos;
|
use crate::ui::text::text_pos::TextPos;
|
||||||
use crate::ui::ui_error::UIResult;
|
use crate::ui::ui_error::UIResult;
|
||||||
|
@ -139,7 +138,7 @@ impl GridNodeMap {
|
||||||
&self,
|
&self,
|
||||||
caret_pos: TextPos,
|
caret_pos: TextPos,
|
||||||
ed_model: &EdModel,
|
ed_model: &EdModel,
|
||||||
) -> EdResult<(TextPos, TextPos, NodeId<Expr2>, MarkNodeId)> {
|
) -> EdResult<(TextPos, TextPos, ExprId, MarkNodeId)> {
|
||||||
let line = slice_get(caret_pos.line, &self.lines)?;
|
let line = slice_get(caret_pos.line, &self.lines)?;
|
||||||
let node_id = slice_get(caret_pos.column, line)?;
|
let node_id = slice_get(caret_pos.column, line)?;
|
||||||
let node = ed_model.markup_node_pool.get(*node_id);
|
let node = ed_model.markup_node_pool.get(*node_id);
|
||||||
|
|
|
@ -7,13 +7,8 @@ use crate::editor::slow_pool::MarkNodeId;
|
||||||
use crate::editor::slow_pool::SlowPool;
|
use crate::editor::slow_pool::SlowPool;
|
||||||
use crate::editor::syntax_highlight::HighlightStyle;
|
use crate::editor::syntax_highlight::HighlightStyle;
|
||||||
use crate::editor::util::index_of;
|
use crate::editor::util::index_of;
|
||||||
use crate::lang::ast::ExprId;
|
use crate::lang::ast::{Expr2, ExprId, RecordField};
|
||||||
use crate::lang::ast::RecordField;
|
use crate::lang::{expr::Env, pool::PoolStr};
|
||||||
use crate::lang::{
|
|
||||||
ast::Expr2,
|
|
||||||
expr::Env,
|
|
||||||
pool::{NodeId, PoolStr},
|
|
||||||
};
|
|
||||||
use crate::ui::util::slice_get;
|
use crate::ui::util::slice_get;
|
||||||
use bumpalo::Bump;
|
use bumpalo::Bump;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
|
@ -21,19 +16,19 @@ use std::fmt;
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum MarkupNode {
|
pub enum MarkupNode {
|
||||||
Nested {
|
Nested {
|
||||||
ast_node_id: NodeId<Expr2>,
|
ast_node_id: ExprId,
|
||||||
children_ids: Vec<MarkNodeId>,
|
children_ids: Vec<MarkNodeId>,
|
||||||
parent_id_opt: Option<MarkNodeId>,
|
parent_id_opt: Option<MarkNodeId>,
|
||||||
},
|
},
|
||||||
Text {
|
Text {
|
||||||
content: String,
|
content: String,
|
||||||
ast_node_id: NodeId<Expr2>,
|
ast_node_id: ExprId,
|
||||||
syn_high_style: HighlightStyle,
|
syn_high_style: HighlightStyle,
|
||||||
attributes: Attributes,
|
attributes: Attributes,
|
||||||
parent_id_opt: Option<MarkNodeId>,
|
parent_id_opt: Option<MarkNodeId>,
|
||||||
},
|
},
|
||||||
Blank {
|
Blank {
|
||||||
ast_node_id: NodeId<Expr2>,
|
ast_node_id: ExprId,
|
||||||
attributes: Attributes,
|
attributes: Attributes,
|
||||||
syn_high_style: HighlightStyle, // TODO remove HighlightStyle, this is always HighlightStyle::Blank
|
syn_high_style: HighlightStyle, // TODO remove HighlightStyle, this is always HighlightStyle::Blank
|
||||||
parent_id_opt: Option<MarkNodeId>,
|
parent_id_opt: Option<MarkNodeId>,
|
||||||
|
@ -41,7 +36,7 @@ pub enum MarkupNode {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl MarkupNode {
|
impl MarkupNode {
|
||||||
pub fn get_ast_node_id(&self) -> NodeId<Expr2> {
|
pub fn get_ast_node_id(&self) -> ExprId {
|
||||||
match self {
|
match self {
|
||||||
MarkupNode::Nested { ast_node_id, .. } => *ast_node_id,
|
MarkupNode::Nested { ast_node_id, .. } => *ast_node_id,
|
||||||
MarkupNode::Text { ast_node_id, .. } => *ast_node_id,
|
MarkupNode::Text { ast_node_id, .. } => *ast_node_id,
|
||||||
|
@ -231,7 +226,7 @@ pub const STRING_QUOTES: &str = "\"\"";
|
||||||
|
|
||||||
fn new_markup_node(
|
fn new_markup_node(
|
||||||
text: String,
|
text: String,
|
||||||
node_id: NodeId<Expr2>,
|
node_id: ExprId,
|
||||||
highlight_style: HighlightStyle,
|
highlight_style: HighlightStyle,
|
||||||
markup_node_pool: &mut SlowPool,
|
markup_node_pool: &mut SlowPool,
|
||||||
) -> MarkNodeId {
|
) -> MarkNodeId {
|
||||||
|
@ -251,7 +246,7 @@ pub fn expr2_to_markup<'a, 'b>(
|
||||||
arena: &'a Bump,
|
arena: &'a Bump,
|
||||||
env: &mut Env<'b>,
|
env: &mut Env<'b>,
|
||||||
expr2: &Expr2,
|
expr2: &Expr2,
|
||||||
expr2_node_id: NodeId<Expr2>,
|
expr2_node_id: ExprId,
|
||||||
markup_node_pool: &mut SlowPool,
|
markup_node_pool: &mut SlowPool,
|
||||||
) -> MarkNodeId {
|
) -> MarkNodeId {
|
||||||
match expr2 {
|
match expr2 {
|
||||||
|
|
|
@ -9,9 +9,8 @@ use crate::editor::{
|
||||||
markup::nodes::{expr2_to_markup, set_parent_for_all, MarkupNode},
|
markup::nodes::{expr2_to_markup, set_parent_for_all, MarkupNode},
|
||||||
};
|
};
|
||||||
use crate::graphics::primitives::rect::Rect;
|
use crate::graphics::primitives::rect::Rect;
|
||||||
use crate::lang::ast::Expr2;
|
use crate::lang::ast::{Expr2, ExprId};
|
||||||
use crate::lang::expr::{str_to_expr2, Env};
|
use crate::lang::expr::{str_to_expr2, Env};
|
||||||
use crate::lang::pool::NodeId;
|
|
||||||
use crate::lang::pool::PoolStr;
|
use crate::lang::pool::PoolStr;
|
||||||
use crate::lang::scope::Scope;
|
use crate::lang::scope::Scope;
|
||||||
use crate::ui::text::caret_w_select::CaretWSelect;
|
use crate::ui::text::caret_w_select::CaretWSelect;
|
||||||
|
@ -47,7 +46,7 @@ pub struct EdModel<'a> {
|
||||||
|
|
||||||
#[derive(Debug, Copy, Clone)]
|
#[derive(Debug, Copy, Clone)]
|
||||||
pub struct SelectedExpression {
|
pub struct SelectedExpression {
|
||||||
pub ast_node_id: NodeId<Expr2>,
|
pub ast_node_id: ExprId,
|
||||||
pub mark_node_id: MarkNodeId,
|
pub mark_node_id: MarkNodeId,
|
||||||
pub type_str: PoolStr,
|
pub type_str: PoolStr,
|
||||||
}
|
}
|
||||||
|
@ -162,7 +161,7 @@ impl<'a> EdModel<'a> {
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct EdModule<'a> {
|
pub struct EdModule<'a> {
|
||||||
pub env: Env<'a>,
|
pub env: Env<'a>,
|
||||||
pub ast_root_id: NodeId<Expr2>,
|
pub ast_root_id: ExprId,
|
||||||
}
|
}
|
||||||
|
|
||||||
// for debugging
|
// for debugging
|
||||||
|
|
|
@ -25,9 +25,8 @@ use crate::editor::mvc::string_update::update_string;
|
||||||
use crate::editor::slow_pool::MarkNodeId;
|
use crate::editor::slow_pool::MarkNodeId;
|
||||||
use crate::editor::slow_pool::SlowPool;
|
use crate::editor::slow_pool::SlowPool;
|
||||||
use crate::editor::syntax_highlight::HighlightStyle;
|
use crate::editor::syntax_highlight::HighlightStyle;
|
||||||
use crate::lang::ast::Expr2;
|
use crate::lang::ast::{Expr2, ExprId};
|
||||||
use crate::lang::constrain::constrain_expr;
|
use crate::lang::constrain::constrain_expr;
|
||||||
use crate::lang::pool::NodeId;
|
|
||||||
use crate::lang::pool::Pool;
|
use crate::lang::pool::Pool;
|
||||||
use crate::lang::pool::PoolStr;
|
use crate::lang::pool::PoolStr;
|
||||||
use crate::lang::types::Type2;
|
use crate::lang::types::Type2;
|
||||||
|
@ -176,7 +175,7 @@ impl<'a> EdModel<'a> {
|
||||||
&mut self,
|
&mut self,
|
||||||
expr_start_pos: TextPos,
|
expr_start_pos: TextPos,
|
||||||
expr_end_pos: TextPos,
|
expr_end_pos: TextPos,
|
||||||
ast_node_id: NodeId<Expr2>,
|
ast_node_id: ExprId,
|
||||||
mark_node_id: MarkNodeId,
|
mark_node_id: MarkNodeId,
|
||||||
) -> EdResult<()> {
|
) -> EdResult<()> {
|
||||||
self.set_raw_sel(RawSelection {
|
self.set_raw_sel(RawSelection {
|
||||||
|
@ -239,7 +238,7 @@ impl<'a> EdModel<'a> {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn expr2_to_type(&mut self, expr2_id: NodeId<Expr2>) -> PoolStr {
|
fn expr2_to_type(&mut self, expr2_id: ExprId) -> PoolStr {
|
||||||
let var = self.module.env.var_store.fresh();
|
let var = self.module.env.var_store.fresh();
|
||||||
let expr = self.module.env.pool.get(expr2_id);
|
let expr = self.module.env.pool.get(expr2_id);
|
||||||
let arena = Bump::new();
|
let arena = Bump::new();
|
||||||
|
@ -528,7 +527,7 @@ pub struct NodeContext<'a> {
|
||||||
pub curr_mark_node_id: MarkNodeId,
|
pub curr_mark_node_id: MarkNodeId,
|
||||||
pub curr_mark_node: &'a MarkupNode,
|
pub curr_mark_node: &'a MarkupNode,
|
||||||
pub parent_id_opt: Option<MarkNodeId>,
|
pub parent_id_opt: Option<MarkNodeId>,
|
||||||
pub ast_node_id: NodeId<Expr2>,
|
pub ast_node_id: ExprId,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_node_context<'a>(ed_model: &'a EdModel) -> EdResult<NodeContext<'a>> {
|
pub fn get_node_context<'a>(ed_model: &'a EdModel) -> EdResult<NodeContext<'a>> {
|
||||||
|
|
|
@ -2,8 +2,7 @@ use crate::editor::ed_error::EdResult;
|
||||||
use crate::editor::mvc::app_update::InputOutcome;
|
use crate::editor::mvc::app_update::InputOutcome;
|
||||||
use crate::editor::mvc::ed_model::EdModel;
|
use crate::editor::mvc::ed_model::EdModel;
|
||||||
use crate::editor::slow_pool::MarkNodeId;
|
use crate::editor::slow_pool::MarkNodeId;
|
||||||
use crate::lang::ast::Expr2;
|
use crate::lang::ast::{Expr2, ExprId};
|
||||||
use crate::lang::pool::NodeId;
|
|
||||||
use crate::lang::pool::PoolStr;
|
use crate::lang::pool::PoolStr;
|
||||||
use crate::ui::text::lines::SelectableLines;
|
use crate::ui::text::lines::SelectableLines;
|
||||||
|
|
||||||
|
@ -11,7 +10,7 @@ pub fn update_invalid_lookup(
|
||||||
input_str: &str,
|
input_str: &str,
|
||||||
old_pool_str: &PoolStr,
|
old_pool_str: &PoolStr,
|
||||||
curr_mark_node_id: MarkNodeId,
|
curr_mark_node_id: MarkNodeId,
|
||||||
ast_node_id: NodeId<Expr2>,
|
ast_node_id: ExprId,
|
||||||
ed_model: &mut EdModel,
|
ed_model: &mut EdModel,
|
||||||
) -> EdResult<InputOutcome> {
|
) -> EdResult<InputOutcome> {
|
||||||
if input_str.chars().all(|ch| ch.is_ascii_alphanumeric()) {
|
if input_str.chars().all(|ch| ch.is_ascii_alphanumeric()) {
|
||||||
|
|
|
@ -11,9 +11,8 @@ use crate::editor::mvc::ed_update::NodeContext;
|
||||||
use crate::editor::slow_pool::MarkNodeId;
|
use crate::editor::slow_pool::MarkNodeId;
|
||||||
use crate::editor::syntax_highlight::HighlightStyle;
|
use crate::editor::syntax_highlight::HighlightStyle;
|
||||||
use crate::editor::util::index_of;
|
use crate::editor::util::index_of;
|
||||||
use crate::lang::ast::Expr2;
|
use crate::lang::ast::{Expr2, ExprId, RecordField};
|
||||||
use crate::lang::ast::RecordField;
|
use crate::lang::pool::{PoolStr, PoolVec};
|
||||||
use crate::lang::pool::{NodeId, PoolStr, PoolVec};
|
|
||||||
use crate::ui::text::text_pos::TextPos;
|
use crate::ui::text::text_pos::TextPos;
|
||||||
use snafu::OptionExt;
|
use snafu::OptionExt;
|
||||||
|
|
||||||
|
@ -174,7 +173,7 @@ pub fn update_empty_record(
|
||||||
|
|
||||||
pub fn update_record_colon(
|
pub fn update_record_colon(
|
||||||
ed_model: &mut EdModel,
|
ed_model: &mut EdModel,
|
||||||
record_ast_node_id: NodeId<Expr2>,
|
record_ast_node_id: ExprId,
|
||||||
) -> EdResult<InputOutcome> {
|
) -> EdResult<InputOutcome> {
|
||||||
let NodeContext {
|
let NodeContext {
|
||||||
old_caret_pos,
|
old_caret_pos,
|
||||||
|
|
|
@ -64,7 +64,7 @@ pub enum FloatVal {
|
||||||
pub enum RecordField {
|
pub enum RecordField {
|
||||||
InvalidLabelOnly(PoolStr, Variable),
|
InvalidLabelOnly(PoolStr, Variable),
|
||||||
LabelOnly(PoolStr, Variable, Symbol),
|
LabelOnly(PoolStr, Variable, Symbol),
|
||||||
LabeledValue(PoolStr, Variable, NodeId<Expr2>),
|
LabeledValue(PoolStr, Variable, ExprId),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -124,49 +124,49 @@ pub enum Expr2 {
|
||||||
elems: PoolVec<ExprId>, // 8B
|
elems: PoolVec<ExprId>, // 8B
|
||||||
},
|
},
|
||||||
If {
|
If {
|
||||||
cond_var: Variable, // 4B
|
cond_var: Variable, // 4B
|
||||||
expr_var: Variable, // 4B
|
expr_var: Variable, // 4B
|
||||||
branches: PoolVec<(NodeId<Expr2>, NodeId<Expr2>)>, // 8B
|
branches: PoolVec<(ExprId, ExprId)>, // 8B
|
||||||
final_else: NodeId<Expr2>, // 4B
|
final_else: ExprId, // 4B
|
||||||
},
|
},
|
||||||
When {
|
When {
|
||||||
cond_var: Variable, // 4B
|
cond_var: Variable, // 4B
|
||||||
expr_var: Variable, // 4B
|
expr_var: Variable, // 4B
|
||||||
branches: PoolVec<WhenBranch>, // 8B
|
branches: PoolVec<WhenBranch>, // 8B
|
||||||
cond: NodeId<Expr2>, // 4B
|
cond: ExprId, // 4B
|
||||||
},
|
},
|
||||||
LetRec {
|
LetRec {
|
||||||
defs: PoolVec<FunctionDef>, // 8B
|
defs: PoolVec<FunctionDef>, // 8B
|
||||||
body_var: Variable, // 8B
|
body_var: Variable, // 8B
|
||||||
body_id: NodeId<Expr2>, // 4B
|
body_id: ExprId, // 4B
|
||||||
},
|
},
|
||||||
LetFunction {
|
LetFunction {
|
||||||
def_id: NodeId<FunctionDef>, // 4B
|
def_id: NodeId<FunctionDef>, // 4B
|
||||||
body_var: Variable, // 8B
|
body_var: Variable, // 8B
|
||||||
body_id: NodeId<Expr2>, // 4B
|
body_id: ExprId, // 4B
|
||||||
},
|
},
|
||||||
LetValue {
|
LetValue {
|
||||||
def_id: NodeId<ValueDef>, // 4B
|
def_id: NodeId<ValueDef>, // 4B
|
||||||
body_id: NodeId<Expr2>, // 4B
|
body_id: ExprId, // 4B
|
||||||
body_var: Variable, // 4B
|
body_var: Variable, // 4B
|
||||||
},
|
},
|
||||||
Call {
|
Call {
|
||||||
args: PoolVec<(Variable, NodeId<Expr2>)>, // 8B
|
args: PoolVec<(Variable, ExprId)>, // 8B
|
||||||
expr: NodeId<Expr2>, // 4B
|
expr: ExprId, // 4B
|
||||||
expr_var: Variable, // 4B
|
expr_var: Variable, // 4B
|
||||||
fn_var: Variable, // 4B
|
fn_var: Variable, // 4B
|
||||||
closure_var: Variable, // 4B
|
closure_var: Variable, // 4B
|
||||||
called_via: CalledVia, // 2B
|
called_via: CalledVia, // 2B
|
||||||
},
|
},
|
||||||
RunLowLevel {
|
RunLowLevel {
|
||||||
op: LowLevel, // 1B
|
op: LowLevel, // 1B
|
||||||
args: PoolVec<(Variable, NodeId<Expr2>)>, // 8B
|
args: PoolVec<(Variable, ExprId)>, // 8B
|
||||||
ret_var: Variable, // 4B
|
ret_var: Variable, // 4B
|
||||||
},
|
},
|
||||||
Closure {
|
Closure {
|
||||||
args: PoolVec<(Variable, NodeId<Pattern2>)>, // 8B
|
args: PoolVec<(Variable, NodeId<Pattern2>)>, // 8B
|
||||||
name: Symbol, // 8B
|
name: Symbol, // 8B
|
||||||
body: NodeId<Expr2>, // 4B
|
body: ExprId, // 4B
|
||||||
function_type: Variable, // 4B
|
function_type: Variable, // 4B
|
||||||
recursive: Recursive, // 1B
|
recursive: Recursive, // 1B
|
||||||
extra: NodeId<ClosureExtra>, // 4B
|
extra: NodeId<ClosureExtra>, // 4B
|
||||||
|
@ -181,7 +181,7 @@ pub enum Expr2 {
|
||||||
/// Look up exactly one field on a record, e.g. (expr).foo.
|
/// Look up exactly one field on a record, e.g. (expr).foo.
|
||||||
Access {
|
Access {
|
||||||
field: PoolStr, // 4B
|
field: PoolStr, // 4B
|
||||||
expr: NodeId<Expr2>, // 4B
|
expr: ExprId, // 4B
|
||||||
record_var: Variable, // 4B
|
record_var: Variable, // 4B
|
||||||
ext_var: Variable, // 4B
|
ext_var: Variable, // 4B
|
||||||
field_var: Variable, // 4B
|
field_var: Variable, // 4B
|
||||||
|
@ -205,16 +205,16 @@ pub enum Expr2 {
|
||||||
|
|
||||||
// Sum Types
|
// Sum Types
|
||||||
GlobalTag {
|
GlobalTag {
|
||||||
name: PoolStr, // 4B
|
name: PoolStr, // 4B
|
||||||
variant_var: Variable, // 4B
|
variant_var: Variable, // 4B
|
||||||
ext_var: Variable, // 4B
|
ext_var: Variable, // 4B
|
||||||
arguments: PoolVec<(Variable, NodeId<Expr2>)>, // 8B
|
arguments: PoolVec<(Variable, ExprId)>, // 8B
|
||||||
},
|
},
|
||||||
PrivateTag {
|
PrivateTag {
|
||||||
name: Symbol, // 8B
|
name: Symbol, // 8B
|
||||||
variant_var: Variable, // 4B
|
variant_var: Variable, // 4B
|
||||||
ext_var: Variable, // 4B
|
ext_var: Variable, // 4B
|
||||||
arguments: PoolVec<(Variable, NodeId<Expr2>)>, // 8B
|
arguments: PoolVec<(Variable, ExprId)>, // 8B
|
||||||
},
|
},
|
||||||
Blank, // Rendered as empty box in editor
|
Blank, // Rendered as empty box in editor
|
||||||
|
|
||||||
|
@ -270,17 +270,17 @@ impl ShallowClone for ValueDef {
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum FunctionDef {
|
pub enum FunctionDef {
|
||||||
WithAnnotation {
|
WithAnnotation {
|
||||||
name: Symbol, // 8B
|
name: Symbol, // 8B
|
||||||
arguments: PoolVec<(Pattern2, Type2)>, // 8B
|
arguments: PoolVec<(PatternId, Type2)>, // 8B
|
||||||
rigids: NodeId<Rigids>, // 4B
|
rigids: NodeId<Rigids>, // 4B
|
||||||
return_type: TypeId, // 4B
|
return_type: TypeId, // 4B
|
||||||
body: ExprId, // 4B
|
body: ExprId, // 4B
|
||||||
},
|
},
|
||||||
NoAnnotation {
|
NoAnnotation {
|
||||||
name: Symbol, // 8B
|
name: Symbol, // 8B
|
||||||
arguments: PoolVec<(Pattern2, Variable)>, // 8B
|
arguments: PoolVec<(PatternId, Variable)>, // 8B
|
||||||
return_var: Variable, // 4B
|
return_var: Variable, // 4B
|
||||||
body: ExprId, // 4B
|
body: ExprId, // 4B
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -394,11 +394,12 @@ pub struct ClosureExtra {
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct WhenBranch {
|
pub struct WhenBranch {
|
||||||
pub patterns: PoolVec<Pattern2>, // 4B
|
pub patterns: PoolVec<Pattern2>, // 4B
|
||||||
pub body: NodeId<Expr2>, // 3B
|
pub body: ExprId, // 3B
|
||||||
pub guard: Option<NodeId<Expr2>>, // 4B
|
pub guard: Option<ExprId>, // 4B
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO make the inner types private?
|
||||||
pub type ExprId = NodeId<Expr2>;
|
pub type ExprId = NodeId<Expr2>;
|
||||||
|
|
||||||
use RecordField::*;
|
use RecordField::*;
|
||||||
|
@ -427,7 +428,7 @@ impl RecordField {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_record_field_val_node_id(&self) -> Option<NodeId<Expr2>> {
|
pub fn get_record_field_val_node_id(&self) -> Option<ExprId> {
|
||||||
match self {
|
match self {
|
||||||
InvalidLabelOnly(_, _) => None,
|
InvalidLabelOnly(_, _) => None,
|
||||||
LabelOnly(_, _, _) => None,
|
LabelOnly(_, _, _) => None,
|
||||||
|
@ -436,7 +437,7 @@ impl RecordField {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn expr2_to_string(node_id: NodeId<Expr2>, pool: &Pool) -> String {
|
pub fn expr2_to_string(node_id: ExprId, pool: &Pool) -> String {
|
||||||
let mut full_string = String::new();
|
let mut full_string = String::new();
|
||||||
let expr2 = pool.get(node_id);
|
let expr2 = pool.get(node_id);
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
use bumpalo::{collections::Vec as BumpVec, Bump};
|
use bumpalo::{collections::Vec as BumpVec, Bump};
|
||||||
|
|
||||||
use crate::lang::{
|
use crate::lang::{
|
||||||
ast::{Expr2, ExprId, RecordField, ValueDef, WhenBranch},
|
ast::{ClosureExtra, Expr2, ExprId, RecordField, ValueDef, WhenBranch},
|
||||||
expr::Env,
|
expr::Env,
|
||||||
pattern::{DestructType, Pattern2, PatternId, PatternState2, RecordDestruct},
|
pattern::{DestructType, Pattern2, PatternId, PatternState2, RecordDestruct},
|
||||||
pool::{Pool, PoolStr, PoolVec, ShallowClone},
|
pool::{Pool, PoolStr, PoolVec, ShallowClone},
|
||||||
|
@ -934,7 +934,94 @@ pub fn constrain_expr<'a>(
|
||||||
|
|
||||||
exists(arena, vars, And(and_constraints))
|
exists(arena, vars, And(and_constraints))
|
||||||
}
|
}
|
||||||
Expr2::Closure { .. } => todo!(),
|
Expr2::Closure {
|
||||||
|
args,
|
||||||
|
name,
|
||||||
|
body: body_id,
|
||||||
|
function_type: fn_var,
|
||||||
|
extra,
|
||||||
|
..
|
||||||
|
} => {
|
||||||
|
// NOTE defs are treated somewhere else!
|
||||||
|
let body = env.pool.get(*body_id);
|
||||||
|
|
||||||
|
let ClosureExtra {
|
||||||
|
captured_symbols,
|
||||||
|
return_type: ret_var,
|
||||||
|
closure_type: closure_var,
|
||||||
|
closure_ext_var,
|
||||||
|
} = env.pool.get(*extra);
|
||||||
|
|
||||||
|
let closure_type = Type2::Variable(*closure_var);
|
||||||
|
let return_type = Type2::Variable(*ret_var);
|
||||||
|
|
||||||
|
let (mut vars, pattern_state, function_type) =
|
||||||
|
constrain_untyped_args(arena, env, args, closure_type, return_type.shallow_clone());
|
||||||
|
|
||||||
|
vars.push(*ret_var);
|
||||||
|
vars.push(*closure_var);
|
||||||
|
vars.push(*closure_ext_var);
|
||||||
|
vars.push(*fn_var);
|
||||||
|
|
||||||
|
let expected_body_type = Expected::NoExpectation(return_type);
|
||||||
|
// Region here should come from body expr
|
||||||
|
let ret_constraint = constrain_expr(arena, env, body, expected_body_type, region);
|
||||||
|
|
||||||
|
let captured_symbols_as_vec = captured_symbols
|
||||||
|
.iter(env.pool)
|
||||||
|
.copied()
|
||||||
|
.collect::<Vec<(Symbol, Variable)>>();
|
||||||
|
|
||||||
|
// make sure the captured symbols are sorted!
|
||||||
|
debug_assert_eq!(captured_symbols_as_vec, {
|
||||||
|
let mut copy: Vec<(Symbol, Variable)> = captured_symbols_as_vec.clone();
|
||||||
|
|
||||||
|
copy.sort();
|
||||||
|
|
||||||
|
copy
|
||||||
|
});
|
||||||
|
|
||||||
|
let closure_constraint = constrain_closure_size(
|
||||||
|
arena,
|
||||||
|
env,
|
||||||
|
*name,
|
||||||
|
region,
|
||||||
|
captured_symbols,
|
||||||
|
*closure_var,
|
||||||
|
*closure_ext_var,
|
||||||
|
&mut vars,
|
||||||
|
);
|
||||||
|
|
||||||
|
let mut and_constraints = BumpVec::with_capacity_in(4, arena);
|
||||||
|
|
||||||
|
and_constraints.push(Let(arena.alloc(LetConstraint {
|
||||||
|
rigid_vars: BumpVec::new_in(arena),
|
||||||
|
flex_vars: pattern_state.vars,
|
||||||
|
def_types: pattern_state.headers,
|
||||||
|
defs_constraint: And(pattern_state.constraints),
|
||||||
|
ret_constraint,
|
||||||
|
})));
|
||||||
|
|
||||||
|
// "the closure's type is equal to expected type"
|
||||||
|
and_constraints.push(Eq(
|
||||||
|
function_type.shallow_clone(),
|
||||||
|
expected,
|
||||||
|
Category::Lambda,
|
||||||
|
region,
|
||||||
|
));
|
||||||
|
|
||||||
|
// "fn_var is equal to the closure's type" - fn_var is used in code gen
|
||||||
|
and_constraints.push(Eq(
|
||||||
|
Type2::Variable(*fn_var),
|
||||||
|
Expected::NoExpectation(function_type),
|
||||||
|
Category::Storage(std::file!(), std::line!()),
|
||||||
|
region,
|
||||||
|
));
|
||||||
|
|
||||||
|
and_constraints.push(closure_constraint);
|
||||||
|
|
||||||
|
exists(arena, vars, And(and_constraints))
|
||||||
|
}
|
||||||
Expr2::LetRec { .. } => todo!(),
|
Expr2::LetRec { .. } => todo!(),
|
||||||
Expr2::LetFunction { .. } => todo!(),
|
Expr2::LetFunction { .. } => todo!(),
|
||||||
}
|
}
|
||||||
|
@ -1406,6 +1493,115 @@ fn constrain_tag_pattern<'a>(
|
||||||
state.constraints.push(tag_con);
|
state.constraints.push(tag_con);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn constrain_untyped_args<'a>(
|
||||||
|
arena: &'a Bump,
|
||||||
|
env: &mut Env,
|
||||||
|
arguments: &PoolVec<(Variable, PatternId)>,
|
||||||
|
closure_type: Type2,
|
||||||
|
return_type: Type2,
|
||||||
|
) -> (BumpVec<'a, Variable>, PatternState2<'a>, Type2) {
|
||||||
|
let mut vars = BumpVec::with_capacity_in(arguments.len(), arena);
|
||||||
|
|
||||||
|
let pattern_types = PoolVec::with_capacity(arguments.len() as u32, env.pool);
|
||||||
|
|
||||||
|
let mut pattern_state = PatternState2 {
|
||||||
|
headers: BumpMap::new_in(arena),
|
||||||
|
vars: BumpVec::with_capacity_in(1, arena),
|
||||||
|
constraints: BumpVec::with_capacity_in(1, arena),
|
||||||
|
};
|
||||||
|
|
||||||
|
for (arg_node_id, pattern_type_id) in
|
||||||
|
arguments.iter_node_ids().zip(pattern_types.iter_node_ids())
|
||||||
|
{
|
||||||
|
let (pattern_var, pattern_id) = env.pool.get(arg_node_id);
|
||||||
|
let pattern = env.pool.get(*pattern_id);
|
||||||
|
|
||||||
|
let pattern_type = Type2::Variable(*pattern_var);
|
||||||
|
let pattern_expected = PExpected::NoExpectation(pattern_type.shallow_clone());
|
||||||
|
|
||||||
|
env.pool[pattern_type_id] = pattern_type;
|
||||||
|
|
||||||
|
constrain_pattern(
|
||||||
|
arena,
|
||||||
|
env,
|
||||||
|
pattern,
|
||||||
|
// TODO needs to come from pattern
|
||||||
|
Region::zero(),
|
||||||
|
pattern_expected,
|
||||||
|
&mut pattern_state,
|
||||||
|
);
|
||||||
|
|
||||||
|
vars.push(*pattern_var);
|
||||||
|
}
|
||||||
|
|
||||||
|
let function_type = Type2::Function(
|
||||||
|
pattern_types,
|
||||||
|
env.pool.add(closure_type),
|
||||||
|
env.pool.add(return_type),
|
||||||
|
);
|
||||||
|
|
||||||
|
(vars, pattern_state, function_type)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(clippy::too_many_arguments)]
|
||||||
|
fn constrain_closure_size<'a>(
|
||||||
|
arena: &'a Bump,
|
||||||
|
env: &mut Env,
|
||||||
|
name: Symbol,
|
||||||
|
region: Region,
|
||||||
|
captured_symbols: &PoolVec<(Symbol, Variable)>,
|
||||||
|
closure_var: Variable,
|
||||||
|
closure_ext_var: Variable,
|
||||||
|
variables: &mut BumpVec<'a, Variable>,
|
||||||
|
) -> Constraint<'a> {
|
||||||
|
use Constraint::*;
|
||||||
|
|
||||||
|
debug_assert!(variables.iter().any(|s| *s == closure_var));
|
||||||
|
debug_assert!(variables.iter().any(|s| *s == closure_ext_var));
|
||||||
|
|
||||||
|
let tag_arguments = PoolVec::with_capacity(captured_symbols.len() as u32, env.pool);
|
||||||
|
let mut captured_symbols_constraints = BumpVec::with_capacity_in(captured_symbols.len(), arena);
|
||||||
|
|
||||||
|
for (captured_symbol_id, tag_arg_id) in captured_symbols
|
||||||
|
.iter_node_ids()
|
||||||
|
.zip(tag_arguments.iter_node_ids())
|
||||||
|
{
|
||||||
|
let (symbol, var) = env.pool.get(captured_symbol_id);
|
||||||
|
|
||||||
|
// make sure the variable is registered
|
||||||
|
variables.push(*var);
|
||||||
|
|
||||||
|
let tag_arg_type = Type2::Variable(*var);
|
||||||
|
|
||||||
|
// this symbol is captured, so it must be part of the closure type
|
||||||
|
env.pool[tag_arg_id] = tag_arg_type.shallow_clone();
|
||||||
|
|
||||||
|
// make the variable equal to the looked-up type of symbol
|
||||||
|
captured_symbols_constraints.push(Lookup(
|
||||||
|
*symbol,
|
||||||
|
Expected::NoExpectation(tag_arg_type),
|
||||||
|
Region::zero(),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
let tag_name = TagName::Closure(name);
|
||||||
|
let closure_type = Type2::TagUnion(
|
||||||
|
PoolVec::new(vec![(tag_name, tag_arguments)].into_iter(), env.pool),
|
||||||
|
env.pool.add(Type2::Variable(closure_ext_var)),
|
||||||
|
);
|
||||||
|
|
||||||
|
let finalizer = Eq(
|
||||||
|
Type2::Variable(closure_var),
|
||||||
|
Expected::NoExpectation(closure_type),
|
||||||
|
Category::ClosureSize,
|
||||||
|
region,
|
||||||
|
);
|
||||||
|
|
||||||
|
captured_symbols_constraints.push(finalizer);
|
||||||
|
|
||||||
|
And(captured_symbols_constraints)
|
||||||
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn builtin_type(symbol: Symbol, args: PoolVec<Type2>) -> Type2 {
|
fn builtin_type(symbol: Symbol, args: PoolVec<Type2>) -> Type2 {
|
||||||
Type2::Apply(symbol, args)
|
Type2::Apply(symbol, args)
|
||||||
|
|
|
@ -514,7 +514,7 @@ fn canonicalize_pending_def<'a>(
|
||||||
// parent commit for the bug this fixed!
|
// parent commit for the bug this fixed!
|
||||||
let refs = References::new();
|
let refs = References::new();
|
||||||
|
|
||||||
let arguments: PoolVec<(Pattern2, Type2)> =
|
let arguments: PoolVec<(PatternId, Type2)> =
|
||||||
PoolVec::with_capacity(closure_args.len() as u32, env.pool);
|
PoolVec::with_capacity(closure_args.len() as u32, env.pool);
|
||||||
|
|
||||||
let return_type: TypeId;
|
let return_type: TypeId;
|
||||||
|
@ -551,9 +551,7 @@ fn canonicalize_pending_def<'a>(
|
||||||
for (node_id, ((_, pattern_id), typ)) in
|
for (node_id, ((_, pattern_id), typ)) in
|
||||||
arguments.iter_node_ids().zip(it.into_iter())
|
arguments.iter_node_ids().zip(it.into_iter())
|
||||||
{
|
{
|
||||||
let pattern = &env.pool[pattern_id];
|
env.pool[node_id] = (pattern_id, typ);
|
||||||
|
|
||||||
env.pool[node_id] = (pattern.shallow_clone(), typ);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return_type = return_type_id;
|
return_type = return_type_id;
|
||||||
|
@ -683,16 +681,14 @@ fn canonicalize_pending_def<'a>(
|
||||||
// parent commit for the bug this fixed!
|
// parent commit for the bug this fixed!
|
||||||
let refs = References::new();
|
let refs = References::new();
|
||||||
|
|
||||||
let arguments: PoolVec<(Pattern2, Variable)> =
|
let arguments: PoolVec<(PatternId, Variable)> =
|
||||||
PoolVec::with_capacity(closure_args.len() as u32, env.pool);
|
PoolVec::with_capacity(closure_args.len() as u32, env.pool);
|
||||||
|
|
||||||
let it: Vec<_> = closure_args.iter(env.pool).map(|(x, y)| (*x, *y)).collect();
|
let it: Vec<_> = closure_args.iter(env.pool).map(|(x, y)| (*x, *y)).collect();
|
||||||
|
|
||||||
for (node_id, (_, pattern_id)) in arguments.iter_node_ids().zip(it.into_iter())
|
for (node_id, (_, pattern_id)) in arguments.iter_node_ids().zip(it.into_iter())
|
||||||
{
|
{
|
||||||
let pattern = &env.pool[pattern_id];
|
env.pool[node_id] = (pattern_id, env.var_store.fresh());
|
||||||
|
|
||||||
env.pool[node_id] = (pattern.shallow_clone(), env.var_store.fresh());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let function_def = FunctionDef::NoAnnotation {
|
let function_def = FunctionDef::NoAnnotation {
|
||||||
|
|
|
@ -405,8 +405,7 @@ pub fn to_expr2<'a>(
|
||||||
let mut output = Output::default();
|
let mut output = Output::default();
|
||||||
let output_ref = &mut output;
|
let output_ref = &mut output;
|
||||||
|
|
||||||
let elems: PoolVec<NodeId<Expr2>> =
|
let elems: PoolVec<ExprId> = PoolVec::with_capacity(items.len() as u32, env.pool);
|
||||||
PoolVec::with_capacity(items.len() as u32, env.pool);
|
|
||||||
|
|
||||||
for (node_id, item) in elems.iter_node_ids().zip(items.iter()) {
|
for (node_id, item) in elems.iter_node_ids().zip(items.iter()) {
|
||||||
let (expr, sub_output) = to_expr2(env, scope, &item.value, item.region);
|
let (expr, sub_output) = to_expr2(env, scope, &item.value, item.region);
|
||||||
|
@ -1015,10 +1014,10 @@ fn flatten_str_lines<'a>(
|
||||||
buf.push(ch);
|
buf.push(ch);
|
||||||
}
|
}
|
||||||
None => {
|
None => {
|
||||||
// env.problem(Problem::InvalidUnicodeCodePoint(loc_hex_digits.region));
|
// env.problem(Problem::InvalidUnicodeCodePt(loc_hex_digits.region));
|
||||||
//
|
//
|
||||||
// return (
|
// return (
|
||||||
// Expr::RuntimeError(RuntimeError::InvalidUnicodeCodePoint(
|
// Expr::RuntimeError(RuntimeError::InvalidUnicodeCodePt(
|
||||||
// loc_hex_digits.region,
|
// loc_hex_digits.region,
|
||||||
// )),
|
// )),
|
||||||
// output,
|
// output,
|
||||||
|
|
|
@ -13,6 +13,7 @@
|
||||||
use libc::{c_void, MAP_ANONYMOUS, MAP_PRIVATE, PROT_READ, PROT_WRITE};
|
use libc::{c_void, MAP_ANONYMOUS, MAP_PRIVATE, PROT_READ, PROT_WRITE};
|
||||||
use roc_can::expected::Expected;
|
use roc_can::expected::Expected;
|
||||||
use roc_can::expected::PExpected;
|
use roc_can::expected::PExpected;
|
||||||
|
use std::any::type_name;
|
||||||
use std::cmp::Ordering;
|
use std::cmp::Ordering;
|
||||||
use std::marker::PhantomData;
|
use std::marker::PhantomData;
|
||||||
use std::mem::size_of;
|
use std::mem::size_of;
|
||||||
|
@ -132,8 +133,14 @@ impl Pool {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn add<T>(&mut self, node: T) -> NodeId<T> {
|
pub fn add<T>(&mut self, node: T) -> NodeId<T> {
|
||||||
// It's only safe to store this if T is the same size as S.
|
// It's only safe to store this if T fits in S.
|
||||||
debug_assert_eq!(size_of::<T>(), NODE_BYTES);
|
debug_assert!(
|
||||||
|
size_of::<T>() <= NODE_BYTES,
|
||||||
|
"{} has a size of {}, but it needs to be at most {}",
|
||||||
|
type_name::<T>(),
|
||||||
|
size_of::<T>(),
|
||||||
|
NODE_BYTES
|
||||||
|
);
|
||||||
|
|
||||||
let node_id = self.reserve(1);
|
let node_id = self.reserve(1);
|
||||||
let node_ptr = unsafe { self.nodes.offset(node_id.index as isize) } as *mut T;
|
let node_ptr = unsafe { self.nodes.offset(node_id.index as isize) } as *mut T;
|
||||||
|
@ -322,7 +329,12 @@ impl<'a, T: 'a + Sized> PoolVec<T> {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn with_capacity(len: u32, pool: &mut Pool) -> Self {
|
pub fn with_capacity(len: u32, pool: &mut Pool) -> Self {
|
||||||
debug_assert!(size_of::<T>() <= NODE_BYTES, "{}", size_of::<T>());
|
debug_assert!(
|
||||||
|
size_of::<T>() <= NODE_BYTES,
|
||||||
|
"{} has a size of {}",
|
||||||
|
type_name::<T>(),
|
||||||
|
size_of::<T>()
|
||||||
|
);
|
||||||
|
|
||||||
if len == 0 {
|
if len == 0 {
|
||||||
Self::empty(pool)
|
Self::empty(pool)
|
||||||
|
|
|
@ -1519,24 +1519,39 @@ fn deep_copy_var_help(
|
||||||
same @ EmptyRecord | same @ EmptyTagUnion | same @ Erroneous(_) => same,
|
same @ EmptyRecord | same @ EmptyTagUnion | same @ Erroneous(_) => same,
|
||||||
|
|
||||||
Record(fields, ext_var) => {
|
Record(fields, ext_var) => {
|
||||||
let mut new_vars = Vec::with_capacity(fields.len());
|
let record_fields = {
|
||||||
for index in fields.iter_variables() {
|
let mut new_vars = Vec::with_capacity(fields.len());
|
||||||
let var = subs[index];
|
|
||||||
let copy_var = deep_copy_var_help(subs, max_rank, pools, var);
|
|
||||||
new_vars.push(copy_var);
|
|
||||||
}
|
|
||||||
|
|
||||||
let it = fields.iter_all().zip(new_vars).map(|((i1, _, i3), var)| {
|
for index in fields.iter_variables() {
|
||||||
let label = subs[i1].clone();
|
let var = subs[index];
|
||||||
let record_field = subs[i3].map(|_| var);
|
let copy_var = deep_copy_var_help(subs, max_rank, pools, var);
|
||||||
|
|
||||||
(label, record_field)
|
new_vars.push(copy_var);
|
||||||
});
|
}
|
||||||
|
|
||||||
// lifetime troubles
|
let field_names_start = subs.field_names.len() as u32;
|
||||||
let vec: Vec<_> = it.collect();
|
let variables_start = subs.variables.len() as u32;
|
||||||
|
let field_types_start = subs.record_fields.len() as u32;
|
||||||
|
|
||||||
let record_fields = RecordFields::insert_into_subs(subs, vec);
|
let mut length = 0;
|
||||||
|
|
||||||
|
for ((i1, _, i3), var) in fields.iter_all().zip(new_vars) {
|
||||||
|
let record_field = subs[i3].map(|_| var);
|
||||||
|
|
||||||
|
subs.field_names.push(subs[i1].clone());
|
||||||
|
subs.record_fields.push(record_field.map(|_| ()));
|
||||||
|
subs.variables.push(*record_field.as_inner());
|
||||||
|
|
||||||
|
length += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
RecordFields {
|
||||||
|
length,
|
||||||
|
field_names_start,
|
||||||
|
variables_start,
|
||||||
|
field_types_start,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
Record(
|
Record(
|
||||||
record_fields,
|
record_fields,
|
||||||
|
|
|
@ -356,3 +356,17 @@ fn constrain_run_low_level() {
|
||||||
"List Str",
|
"List Str",
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn constrain_closure() {
|
||||||
|
infer_eq(
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
x = 1
|
||||||
|
|
||||||
|
\{} -> x
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
"{}* -> Num *",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
|
@ -10,7 +10,7 @@ fromBytes = \bytes ->
|
||||||
# base 64 encoding from a string
|
# base 64 encoding from a string
|
||||||
fromStr : Str -> Result Str [ InvalidInput ]*
|
fromStr : Str -> Result Str [ InvalidInput ]*
|
||||||
fromStr = \str ->
|
fromStr = \str ->
|
||||||
fromBytes (Str.toBytes str)
|
fromBytes (Str.toUtf8 str)
|
||||||
|
|
||||||
# base64-encode bytes to the original
|
# base64-encode bytes to the original
|
||||||
toBytes : Str -> Result (List U8) [ InvalidInput ]*
|
toBytes : Str -> Result (List U8) [ InvalidInput ]*
|
||||||
|
|
|
@ -9,7 +9,7 @@ InvalidChar : U8
|
||||||
toBytes : Str -> List U8
|
toBytes : Str -> List U8
|
||||||
toBytes = \str ->
|
toBytes = \str ->
|
||||||
str
|
str
|
||||||
|> Str.toBytes
|
|> Str.toUtf8
|
||||||
|> encodeChunks
|
|> encodeChunks
|
||||||
|> Bytes.Encode.sequence
|
|> Bytes.Encode.sequence
|
||||||
|> Bytes.Encode.encode
|
|> Bytes.Encode.encode
|
||||||
|
|
|
@ -7,7 +7,7 @@ IO a : Task.Task a []
|
||||||
|
|
||||||
main : IO {}
|
main : IO {}
|
||||||
main =
|
main =
|
||||||
when Base64.fromBytes (Str.toBytes "Hello World") is
|
when Base64.fromBytes (Str.toUtf8 "Hello World") is
|
||||||
Err _ ->
|
Err _ ->
|
||||||
Task.putLine "sadness"
|
Task.putLine "sadness"
|
||||||
|
|
||||||
|
|
|
@ -40,8 +40,8 @@ Problem :
|
||||||
NumF32 Endi,
|
NumF32 Endi,
|
||||||
Utf8 Str,
|
Utf8 Str,
|
||||||
Utf16 Str Endi,
|
Utf16 Str Endi,
|
||||||
CodePointUtf8,
|
CodePtUtf8,
|
||||||
CodePointUtf16 Endi,
|
CodePtUtf16 Endi,
|
||||||
GraphemeUtf8,
|
GraphemeUtf8,
|
||||||
GraphemeUtf16 Endi,
|
GraphemeUtf16 Endi,
|
||||||
End,
|
End,
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
package roc/unicode 0.1.0
|
package roc/unicode 0.1.0
|
||||||
roc 0.0.0
|
roc 0.0.0
|
||||||
exposes [ Unicode, Unicode.Scalar, Unicode.CodePoint ]
|
exposes [ Unicode, Unicode.Scalar, Unicode.CodePt ]
|
||||||
packages {}
|
packages {}
|
||||||
license UPL-1.0
|
license UPL-1.0
|
||||||
|
|
||||||
|
|
|
@ -3,8 +3,8 @@ interface Unicode.Scalar
|
||||||
[
|
[
|
||||||
Scalar,
|
Scalar,
|
||||||
toStr,
|
toStr,
|
||||||
toCodePoint,
|
toCodePt,
|
||||||
fromCodePoint,
|
fromCodePt,
|
||||||
parseUtf8,
|
parseUtf8,
|
||||||
parseUtf16,
|
parseUtf16,
|
||||||
chompUtf8,
|
chompUtf8,
|
||||||
|
@ -12,8 +12,8 @@ interface Unicode.Scalar
|
||||||
]
|
]
|
||||||
imports
|
imports
|
||||||
[
|
[
|
||||||
Unicode.CodePoint.Internal as Internal
|
Unicode.CodePt.Internal as Internal
|
||||||
Unicode.CodePoint.{ CodePoint },
|
Unicode.CodePt.{ CodePt },
|
||||||
Bytes.{ Bytes }
|
Bytes.{ Bytes }
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -31,15 +31,15 @@ toStr = \@Scalar u32
|
||||||
# already validated this!
|
# already validated this!
|
||||||
toStr (@Scalar (scalar * 256))
|
toStr (@Scalar (scalar * 256))
|
||||||
|
|
||||||
toCodePoint : Scalar -> CodePoint
|
toCodePt : Scalar -> CodePt
|
||||||
toCodePoint = \@Scalar u32 -> Internal.fromU32Unchecked u32
|
toCodePt = \@Scalar u32 -> Internal.fromU32Unchecked u32
|
||||||
|
|
||||||
fromCodePoint : CodePoint -> Result Scalar [ PointWasSurrogate ]*
|
fromCodePt : CodePt -> Result Scalar [ PointWasSurrogate ]*
|
||||||
|
|
||||||
parseUtf8 : Bytes -> Result { val : Scalar, rest : Bytes } [ Expected [ Utf8CodePoint ]* Bytes ]*
|
parseUtf8 : Bytes -> Result { val : Scalar, rest : Bytes } [ Expected [ Utf8CodePt ]* Bytes ]*
|
||||||
parseUtf16 : Bytes, Endi -> Result { val : Scalar, rest : Bytes } [ Expected [ Utf16CodePoint Endi ]* Bytes ]*
|
parseUtf16 : Bytes, Endi -> Result { val : Scalar, rest : Bytes } [ Expected [ Utf16CodePt Endi ]* Bytes ]*
|
||||||
|
|
||||||
chompUtf8 : Bytes, CodePoint -> Result Str [ Expected [ ExactCodePoint CodePoint ]* Bytes ]*
|
chompUtf8 : Bytes, CodePt -> Result Str [ Expected [ ExactCodePt CodePt ]* Bytes ]*
|
||||||
chompUtf16 : Bytes, CodePoint, Endi -> Result Str [ Expected [ ExactCodePoint CodePoint ]* Bytes ]*
|
chompUtf16 : Bytes, CodePt, Endi -> Result Str [ Expected [ ExactCodePt CodePt ]* Bytes ]*
|
||||||
|
|
||||||
isAsciiDigit : CodePoint -> Bool
|
isAsciiDigit : CodePt -> Bool
|
||||||
|
|
|
@ -22,6 +22,10 @@ rustc --version
|
||||||
# We set RUSTFLAGS to -Awarnings to ignore warnings during this build,
|
# We set RUSTFLAGS to -Awarnings to ignore warnings during this build,
|
||||||
# because when building without "the" llvm feature (which is only ever done
|
# because when building without "the" llvm feature (which is only ever done
|
||||||
# for this exact use case), the result is lots of "unused" warnings!
|
# for this exact use case), the result is lots of "unused" warnings!
|
||||||
RUSTFLAGS=-Awarnings cargo run -p roc_cli --no-default-features docs compiler/builtins/docs/Bool.roc
|
#
|
||||||
|
# We set ROC_DOCS_ROOT_DIR=builtins so that links will be generated relative to
|
||||||
|
# "/builtins/" rather than "/" - which is what we want based on how the server
|
||||||
|
# is set up to serve them.
|
||||||
|
RUSTFLAGS=-Awarnings ROC_DOCS_URL_ROOT=builtins cargo run -p roc_cli --no-default-features docs compiler/builtins/docs/*.roc
|
||||||
mv generated-docs/ www/build/builtins
|
mv generated-docs/ www/build/builtins
|
||||||
popd
|
popd
|
||||||
|
|
|
@ -1,38 +0,0 @@
|
||||||
(function() {
|
|
||||||
let sidebar = document.getElementById("sidebar-nav");
|
|
||||||
let searchBox = document.getElementById("module-search");
|
|
||||||
|
|
||||||
function search() {
|
|
||||||
let text = searchBox.value.toLowerCase(); // Search is case-insensitive.
|
|
||||||
|
|
||||||
if (text === "") {
|
|
||||||
// Un-hide everything
|
|
||||||
sidebar.querySelectorAll(".sidebar-entry a").forEach((entry) => entry.classList.remove("hidden"));
|
|
||||||
|
|
||||||
// Re-hide all the sub-entries except for those of the first module
|
|
||||||
sidebar.querySelectorAll(".sidebar-entry:not(:first-of-type) .sidebar-sub-entries a").forEach((entry) => entry.classList.add("hidden"));
|
|
||||||
} else {
|
|
||||||
// First, show/hide all the sub-entries within each module (top-level functions etc.)
|
|
||||||
sidebar.querySelectorAll(".sidebar-sub-entries a").forEach((entry) => {
|
|
||||||
if (entry.textContent.toLowerCase().includes(text)) {
|
|
||||||
entry.classList.remove("hidden");
|
|
||||||
} else {
|
|
||||||
entry.classList.add("hidden");
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Then, show/hide modules based on whether they match, or any of their sub-entries matched
|
|
||||||
sidebar.querySelectorAll(".sidebar-module-link").forEach((entry) => {
|
|
||||||
if (entry.textContent.toLowerCase().includes(text) || entry.parentNode.querySelectorAll(".sidebar-sub-entries a:not(.hidden)").length > 0) {
|
|
||||||
entry.classList.remove("hidden");
|
|
||||||
} else {
|
|
||||||
entry.classList.add("hidden");
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
searchBox.addEventListener("input", search);
|
|
||||||
|
|
||||||
search();
|
|
||||||
})();
|
|
Loading…
Add table
Add a link
Reference in a new issue