diff --git a/cli/src/main.rs b/cli/src/main.rs index b7bacb92d6..43cd182217 100644 --- a/cli/src/main.rs +++ b/cli/src/main.rs @@ -46,7 +46,12 @@ fn main() -> io::Result<()> { )?), Some(CMD_RUN) => { // 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) } diff --git a/compiler/builtins/bitcode/src/main.zig b/compiler/builtins/bitcode/src/main.zig index a15899fad7..ced24b3395 100644 --- a/compiler/builtins/bitcode/src/main.zig +++ b/compiler/builtins/bitcode/src/main.zig @@ -88,7 +88,7 @@ comptime { exportStrFn(str.countSegments, "count_segments"); exportStrFn(str.countGraphemeClusters, "count_grapheme_clusters"); 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.strConcatC, "concat"); exportStrFn(str.strJoinWithC, "joinWith"); @@ -96,7 +96,7 @@ comptime { exportStrFn(str.strFromIntC, "from_int"); exportStrFn(str.strFromFloatC, "from_float"); exportStrFn(str.strEqual, "equal"); - exportStrFn(str.strToBytesC, "to_bytes"); + exportStrFn(str.strToUtf8C, "to_utf8"); exportStrFn(str.fromUtf8C, "from_utf8"); exportStrFn(str.fromUtf8RangeC, "from_utf8_range"); } diff --git a/compiler/builtins/bitcode/src/str.zig b/compiler/builtins/bitcode/src/str.zig index 998f4efacf..65db1fc924 100644 --- a/compiler/builtins/bitcode/src/str.zig +++ b/compiler/builtins/bitcode/src/str.zig @@ -865,8 +865,8 @@ pub fn startsWith(string: RocStr, prefix: RocStr) callconv(.C) bool { return true; } -// Str.startsWithCodePoint -pub fn startsWithCodePoint(string: RocStr, prefix: u32) callconv(.C) bool { +// Str.startsWithCodePt +pub fn startsWithCodePt(string: RocStr, prefix: u32) callconv(.C) bool { const bytes_len = string.len(); const bytes_ptr = string.asU8ptr(); @@ -886,18 +886,18 @@ pub fn startsWithCodePoint(string: RocStr, prefix: u32) callconv(.C) bool { return true; } -test "startsWithCodePoint: ascii char" { +test "startsWithCodePt: ascii char" { const whole = RocStr.init("foobar", 6); 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 no = RocStr.init("foobar", 6); const prefix = 'πŸ’–'; - try expect(startsWithCodePoint(yes, prefix)); - try expect(!startsWithCodePoint(no, prefix)); + try expect(startsWithCodePt(yes, prefix)); + try expect(!startsWithCodePt(no, prefix)); } test "startsWith: foo starts with fo" { @@ -1129,8 +1129,8 @@ test "RocStr.joinWith: result is big" { try expect(roc_result.eq(result)); } -// Str.toBytes -pub fn strToBytesC(arg: RocStr) callconv(.C) RocList { +// Str.toUtf8 +pub fn strToUtf8C(arg: RocStr) callconv(.C) RocList { return @call(.{ .modifier = always_inline }, strToBytes, .{arg}); } diff --git a/compiler/builtins/docs/Bool.roc b/compiler/builtins/docs/Bool.roc index 65a8662b5a..642733ec3d 100644 --- a/compiler/builtins/docs/Bool.roc +++ b/compiler/builtins/docs/Bool.roc @@ -55,7 +55,7 @@ and : Bool, Bool -> Bool ## ## In some languages, `&&` and `||` are special-cased in the compiler to skip ## 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 ## Exclusive or diff --git a/compiler/builtins/docs/Dict.roc b/compiler/builtins/docs/Dict.roc index 3aad9e4eee..05b1f82815 100644 --- a/compiler/builtins/docs/Dict.roc +++ b/compiler/builtins/docs/Dict.roc @@ -1,5 +1,21 @@ interface Dict - exposes [ isEmpty, map ] + exposes + [ + Dict, + empty, + single, + get, + walk, + insert, + len, + remove, + contains, + keys, + values, + union, + intersection, + difference + ] imports [] size : Dict * * -> Nat @@ -14,7 +30,7 @@ isEmpty : Dict * * -> Bool ## >>> Dict.map {[ "", "a", "bc" ]} Str.isEmpty ## ## `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 : Dict beforeKey beforeValue, ({ key: beforeKey, value: beforeValue } -> { key: afterKey, value: afterValue }) diff --git a/compiler/builtins/docs/List.roc b/compiler/builtins/docs/List.roc index 2f3248da4b..f8f9a2df43 100644 --- a/compiler/builtins/docs/List.roc +++ b/compiler/builtins/docs/List.roc @@ -1,79 +1,64 @@ -interface List2 +interface List exposes - [ List - , single - , empty - , repeat - , range - , reverse - , sort - , map - , mapWithIndex - , mapOrCancel - , mapOks - , update - , updater - , allOks - , append - , prepend - , concat - , join - , joinMap - , oks - , zip - , zipMap - , keepIf - , dropIf - , first - , last - , get - , max - , min - , put - , drop - , append - , prepend - , dropLast - , dropFirst - , takeFirst - , takeLast - , split - , sublist - , walk - , walkBackwards - , walkUntil - , walkBackwardsUntil - , len - , isEmpty - , contains - , all - , any + [ + List, + isEmpty, + get, + set, + append, + map, + len, + walkBackwards, + concat, + first, + single, + repeat, + reverse, + prepend, + join, + keepIf, + contains, + sum, + walk, + last, + keepOks, + keepErrs, + mapWithIndex, + map2, + map3, + product, + walkUntil, + range, + sortWith, + drop, + swap ] imports [] ## Types ## 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 same type. If you want to put a mix of #Int and #Str values into a list, try this: +## 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 [I64] and [Str] values into a list, try this: ## ## ``` ## mixedList : List [ IntElem I64, StrElem Str ]* ## 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 ## 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 ## to be created just fine, but then crashing later.) ## -## > The theoretical maximum length for a list created in Roc is -## > #Int.maxNat divided by 2. Attempting to create a list bigger than that +## > The theoretical maximum length for a list created in Roc is half of +## > `Num.maxNat`. Attempting to create a list bigger than that ## > in Roc code will always fail, although in practice it is likely to fail ## > at much smaller lengths due to insufficient memory being available. ## @@ -147,13 +132,13 @@ interface List2 ## ## 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 ## decremented all the way to 0. At that point, nothing is referencing the list ## anymore, and its memory will get freed. ## ## 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. ## ## Here's the example using a list of numbers. @@ -165,7 +150,7 @@ interface List2 ## ## 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: ## @@ -189,12 +174,12 @@ interface List2 ## their own refcounts - to go inside that list. (The empty list at the end ## 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. -## * 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 -## * 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! +## * 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. +## * 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 ] ## Initialize @@ -262,18 +247,18 @@ sortDesc : List elem, (elem -> Num *) -> List elem ## > List.map [ "", "a", "bc" ] Str.isEmpty ## ## `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 -## 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. 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. 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. ## ## >>> List.mapOks [ [ "a", "b" ], [], [], [ "c", "d", "e" ] ] List.last @@ -287,16 +272,16 @@ mapOks : List before, (before -> Result after *) -> List after ## the given function. ## ## 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 ## ## 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. 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. updater : List elem, Nat -> { elem, new : (elem -> List elem) } @@ -337,15 +322,15 @@ concat : List elem, List elem -> List elem ## >>> List.join [] 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. 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. ## ## 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 ] ] ## >>> |> List.map List.first @@ -387,16 +372,16 @@ zipMap : List a, List b, (a, b -> c) -> List c ## ## ## 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 ## 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. ## ## (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.) ## -## 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 all elements in the list end up being kept, Roc will return the original ## list unaltered. @@ -410,7 +395,7 @@ keepIf : List elem, (elem -> Bool) -> List elem ## ## ## 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! 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 ## 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 ## 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. ## -## 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 ## Adds a new element to the end of the list. @@ -466,12 +451,12 @@ append : List elem, elem -> List elem ## ## Performance Details ## ## 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 -## #List.prepend 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 -## #List.prepend will run at about the same speedβ€”since #List.prepend always +## If you have a Unique list instead, [List.append] will run much faster than +## [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 +## [List.append] will run at about the same speedβ€”since [List.append] always ## has to clone and grow. ## ## | Unique list | Shared list | @@ -493,11 +478,11 @@ prepend : List elem, elem -> List elem ## ## ## Performance Details ## -## 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, +## 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, ## 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 ## takes much longer. 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 ## -## 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 +## 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 ## 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 ## 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 | ##-----------+----------------------------------+---------------------------------+ -## dropFirst | #List.last + length change | #List.last + clone rest of list | -## dropLast | #List.last + clone rest of list | #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 | dropFirst : List elem -> Result { first: elem, others : List elem } [ ListWasEmpty ]* ## 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 ] ## -## 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, -## 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 ## ## 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 -## slightly faster than #List.takeLast. +## slightly faster than `List.takeLast`. ## ## 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. takeFirst : List elem, Nat -> List elem @@ -561,22 +546,22 @@ takeFirst : List elem, Nat -> List elem ## ## >>> 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, -## 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 ## ## 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, ## 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 -## 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. 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 ] ## ## > 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. 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`) ## * 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`. ## ## `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`. 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 ## -## 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. ## 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. ## -## 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. 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 ## Check ## 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 ## returns can always be safely converted to an #I32 without losing any data. len : List * -> Nat diff --git a/compiler/builtins/docs/Num.roc b/compiler/builtins/docs/Num.roc index db8d41bd7f..ca7c046f20 100644 --- a/compiler/builtins/docs/Num.roc +++ b/compiler/builtins/docs/Num.roc @@ -1,10 +1,97 @@ -interface Num2 - exposes [ Num, neg, abs, add, sub, mul, isOdd, isEven, isPositive, isNegative, isZero ] +interface Num + 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 [] -## 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: ## @@ -19,8 +106,8 @@ interface Num2 ## technically has the type `Num (Integer *)`, so when you pass two of them to ## [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)`, -## so `3.0 : Num (Fraction *)` is the same value as `3.0 : Frac *`. +## 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 : Float *`. ## 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 ## `2 : Int *`. @@ -40,7 +127,7 @@ interface Num2 ## ends up having the type `Nat`. ## ## 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)` ## still has the type `Num *`. When this happens, `Num *` defaults to ## 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 ## subtraction, but 10-20 times longer to perform multiplication and division. ## [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. ## @@ -166,7 +253,7 @@ Dec : Frac [ @Decimal128 ] ## 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 ## 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. ## @@ -331,7 +418,7 @@ Int size : Num [ @Int size ] ## ## >>> 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. ## 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. ## -## (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 ## Return the absolute value of the number. @@ -356,7 +443,7 @@ neg : Num a -> Num a ## ## >>> 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. ## 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. ## -## (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`. ## @@ -417,7 +504,7 @@ addCheckOverflow : Num a, Num a -> Result (Num a) [ Overflow ]* ## 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`. ## @@ -444,7 +531,7 @@ subCheckOverflow : Num a, Num a -> Result (Num a) [ Overflow ]* ## 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`. ## @@ -478,7 +565,7 @@ mulCheckOverflow : Num a, Num a -> Result (Num a) [ Overflow ]* ## ## >>> 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 ## @@ -553,16 +640,16 @@ format : -> Str ## Round off the given float to the nearest integer. -round : Frac * -> Int * -ceil : Frac * -> Int * -floor : Frac * -> Int * -trunc : Frac * -> Int * +round : Float * -> Int * +ceil : Float * -> Int * +floor : Float * -> Int * +trunc : Float * -> Int * ## 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 ## 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 ## 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. @@ -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 ## 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. toNat : Int * -> Nat ## 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. toI8 : Int * -> I8 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, ## 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. ## @@ -749,28 +836,28 @@ maxDec : Dec ## Constants ## An approximation of e, specifically 2.718281828459045. -e : Frac * +e : Float * ## An approximation of pi, specifically 3.141592653589793. -pi : Frac * +pi : Float * ## 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?) -## Divide one [Frac] by another. +## Divide one [Float] by another. ## ## `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 ## > 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]. ## ## >>> 5.0 / 7.0 @@ -800,9 +887,9 @@ atan : Frac a -> Frac a ## ## >>> Num.pi ## >>> |> 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, ## but if either number is negative, then modulo works differently. @@ -825,20 +912,20 @@ div : Frac a, Frac a -> Frac a ## ## >>> Num.pi ## >>> |> 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. -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 ## itself the given number of times. ## ## 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. ## ## >>> Num.exp 5 0 @@ -855,9 +942,9 @@ pow : Frac a, Frac a -> Frac a ## overflow 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 ## 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.0dec -sqrt : Frac a -> Frac a +sqrt : Float a -> Float a ## Bit shifts @@ -911,21 +998,21 @@ shrWrap : Int a, Int a -> Int a ## [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. -toBytes : Num *, Endi -> List U8 +# toBytes : Num *, Endi -> List U8 ## when Num.parseBytes bytes Big is ## Ok { val: f64, rest } -> ... ## 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 ## Ok f64 -> ... ## Err (ExpectedNum (Float Binary64)) -> ... -fromBytes : List U8, Endi -> Result (Num a) [ ExpectedNum a ]* +# fromBytes : List U8, Endi -> Result (Num a) [ ExpectedNum a ]* ## 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 ## 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 -## by using [List.sortAsc] or [List.sortDesc] instead. +## Although this can be passed to `List.sort`, you'll get better performance +## by using `List.sortAsc` or `List.sortDesc` instead. compare : Num a, Num a -> [ Lt, Eq, Gt ] ## 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 ## [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 ## ∞ or -∞, and `False` otherwise. @@ -1014,7 +1101,7 @@ isFinite : Frac * -> Bool ## ## This is the opposite of [isFinite], except when given [*NaN*](Num.isNaN). Both ## [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 ## *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 ## 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. -isNaN : Frac * -> Bool +isNaN : Float * -> Bool diff --git a/compiler/builtins/docs/Result.roc b/compiler/builtins/docs/Result.roc new file mode 100644 index 0000000000..df56b27e66 --- /dev/null +++ b/compiler/builtins/docs/Result.roc @@ -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 diff --git a/compiler/builtins/docs/Set.roc b/compiler/builtins/docs/Set.roc index 24c29dac0e..eb8e9722d7 100644 --- a/compiler/builtins/docs/Set.roc +++ b/compiler/builtins/docs/Set.roc @@ -1,9 +1,22 @@ 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 [] -## Set - ## A Set is an unordered collection of unique elements. Set elem : [ @Set elem ] @@ -36,7 +49,7 @@ drop : Set elem, elem -> Set elem ## >>> Set.map {: "", "a", "bc" :} Str.isEmpty ## ## `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 # Original signature: `map : Set 'elem, ('before -> 'after) -> Set 'after` map : Set elem, (before -> after) -> Set after diff --git a/compiler/builtins/docs/Str.roc b/compiler/builtins/docs/Str.roc index 3a5ed0c88e..753bc8ebaf 100644 --- a/compiler/builtins/docs/Str.roc +++ b/compiler/builtins/docs/Str.roc @@ -2,36 +2,21 @@ interface Str exposes [ Str, - decimal, - split, isEmpty, + append, + concat, + joinWith, + split, + countGraphemes, startsWith, endsWith, - contains, - anyGraphemes, - allGraphemes, - join, - joinWith, - padGraphemesStart, - padGraphemesEnd, - graphemes, - reverseGraphemes, - isCaseInsensitiveEq, - isCaseInsensitiveNeq, - walkGraphemes, - isCapitalized, - isAllUppercase, - isAllLowercase, + fromInt, + fromFloat, + fromUtf8, + Utf8Problem, + Utf8ByteProblem, toUtf8, - toUtf16, - toUtf32, - trim, - walkUtf8, - walkUtf16, - walkUtf32, - walkRevUtf8, - walkRevUtf16, - walkRevUtf32 + startsWithCodePt ] imports [] @@ -63,7 +48,7 @@ interface Str ## 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." ## -## 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 "ζŠ˜γ‚Šη΄™" @@ -126,7 +111,7 @@ interface Str ## 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.) ## -## 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) ## mentioned earlier) in a particular encoding. If you need encoding-specific functions, ## you should take a look at the [roc/unicode](roc/unicode) package. @@ -137,15 +122,15 @@ Str : [ @Str ] ## 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 -## 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. +## If you want to keep all the digits, use [Str.num] instead. decimal : Float *, Nat -> Str + +## Convert a [Num] to a string. +num : Float *, Nat -> Str + ## Split a string around a separator. ## ## >>> Str.split "1,2,3" "," @@ -155,13 +140,13 @@ decimal : Float *, Nat -> Str ## ## >>> 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 a string around newlines. ## ## 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 ## 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!" ## -## 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. lines : Str, Str -> List Str @@ -192,13 +177,13 @@ startsWith : Str, Str -> Bool ## ## **Performance Note:** This runs slightly faster than [Str.startsWith], so ## 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 '鹏'` -## instead of `Str.startsWithCodePoint "鹏"`. ('鹏' evaluates to the [U32] +## in a single code point, you can use (for example) `Str.startsWithCodePt '鹏'` +## instead of `Str.startsWithCodePt "鹏"`. ('鹏' evaluates to the [U32] ## 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 -## single [U32]. You'd need to use `Str.startsWithCodePoint "πŸ•Š"` instead. -startsWithCodePoint : Str, U32 -> Bool +## single [U32]. You'd need to use `Str.startsWithCodePt "πŸ•Š"` instead. +startsWithCodePt : Str, U32 -> Bool endsWith : Str, Str -> Bool @@ -255,9 +240,13 @@ padGraphemesEnd : Str, Nat, Str -> 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 "七巧板" # 3 ## Str.countGraphemes "πŸ•Š" # 1 +countGraphemes : Str -> Nat ## 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), ## return a [Str] containing only that scalar. 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 ]* ## 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). -fromUtf16Be : List U8 -> Result Str [ BadUtf16Be Endi ]* +# ## 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 ]* -## 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 ]* +# ## 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 ]* -## 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 ]* +# ## 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 ]* -## 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 ]* +# ## 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 ]* -## 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 ]* +# ## 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 ]* -## Convert from UTF-8, substituting the replacement character ("οΏ½") for any -## invalid sequences encountered. -fromUtf8Sub : List U8 -> Str -fromUtf16Sub : List U8, Endi -> Str -fromUtf16BomSub : List U8 -> Result Str [ NoBom ]* +# ## Convert from UTF-8, substituting the replacement character ("οΏ½") for any +# ## invalid sequences encountered. +# fromUtf8Sub : List U8 -> Str +# fromUtf16Sub : List U8, Endi -> Str +# fromUtf16BomSub : List U8 -> Result Str [ NoBom ]* -## 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, -## see #Str.split and #Str.graphemes.) +## 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, +## see [Str.split] and `Str.graphemes`.) ## ## >>> Str.toUtf8 "πŸ‘©β€πŸ‘©β€πŸ‘¦β€πŸ‘¦" ## @@ -393,15 +382,15 @@ fromUtf16BomSub : List U8 -> Result Str [ NoBom ]* ## ## >>> Str.toUtf8 "🐦" ## -## For a more flexible function that walks through each of these #U8 code units -## without creating a #List, see #Str.walkUtf8 and #Str.walkRevUtf8. +## For a more flexible function that walks through each of these [U8] code units +## without creating a [List], see `Str.walkUtf8` and `Str.walkRevUtf8`. toUtf8 : Str -> List U8 toUtf16Be : Str -> List U8 toUtf16Le : Str -> List U8 -toUtf16Bom : Str, Endi -> List U8 +# toUtf16Bom : Str, Endi -> List U8 toUtf32Be : Str -> List U8 toUtf32Le : Str -> List U8 -toUtf32Bom : Str, Endi -> List U8 +# toUtf32Bom : Str, Endi -> List U8 # 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 ## 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 ## 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) ## 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. ## @@ -475,20 +464,20 @@ toNum : Str -> Result (Num a) [ ExpectedNum a ]* ## If the string begins with `"NaN"`, `"∞"`, and `"-∞"` (which do not represent ## [finite](Num.isFinite) numbers), they will be accepted only when parsing ## [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: ## * 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`, `Allowed` allows the separator to appear anywhere. -NumParseConfig : - { - base ? [ Decimal, Hexadecimal, Octal, Binary ], - notation ? [ Standard, Scientific, Any ], - decimalMark ? [ Allowed Str, Required Str, Disallowed ], - decimalDigits ? [ Any, AtLeast U16, Exactly U16 ], - wholeDigits ? [ Any, AtLeast U16, Exactly U16 ], - leadingZeroes ? [ Allowed, Disallowed ], - trailingZeroes ? [ Allowed, Disallowed ], - wholeSep ? { mark : Str, policy : [ Allowed, Required U64 ] } - } +# NumParseConfig : +# { +# base ? [ Decimal, Hexadecimal, Octal, Binary ], +# notation ? [ Standard, Scientific, Any ], +# decimalMark ? [ Allowed Str, Required Str, Disallowed ], +# decimalDigits ? [ Any, AtLeast U16, Exactly U16 ], +# wholeDigits ? [ Any, AtLeast U16, Exactly U16 ], +# leadingZeroes ? [ Allowed, Disallowed ], +# trailingZeroes ? [ Allowed, Disallowed ], +# wholeSep ? { mark : Str, policy : [ Allowed, Required U64 ] } +# } diff --git a/compiler/builtins/src/bitcode.rs b/compiler/builtins/src/bitcode.rs index a654cf1bce..86b81a92f4 100644 --- a/compiler/builtins/src/bitcode.rs +++ b/compiler/builtins/src/bitcode.rs @@ -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_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_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_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_FLOAT: &str = "roc_builtins.str.from_float"; 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_RANGE: &str = "roc_builtins.str.from_utf8_range"; diff --git a/compiler/builtins/src/std.rs b/compiler/builtins/src/std.rs index 4b8a9c16f9..96e36c2c6d 100644 --- a/compiler/builtins/src/std.rs +++ b/compiler/builtins/src/std.rs @@ -564,9 +564,9 @@ pub fn types() -> MutMap { Box::new(bool_type()) ); - // startsWithCodePoint : Str, U32 -> Bool + // startsWithCodePt : Str, U32 -> Bool add_top_level_function_type!( - Symbol::STR_STARTS_WITH_CODE_POINT, + Symbol::STR_STARTS_WITH_CODE_PT, vec![str_type(), u32_type()], Box::new(bool_type()) ); @@ -639,9 +639,9 @@ pub fn types() -> MutMap { ); } - // toBytes : Str -> List U8 + // toUtf8 : Str -> List U8 add_top_level_function_type!( - Symbol::STR_TO_BYTES, + Symbol::STR_TO_UTF8, vec![str_type()], Box::new(list_type(u8_type())) ); diff --git a/compiler/can/src/builtins.rs b/compiler/can/src/builtins.rs index 260550da4f..bd127dfe04 100644 --- a/compiler/can/src/builtins.rs +++ b/compiler/can/src/builtins.rs @@ -58,13 +58,13 @@ pub fn builtin_defs_map(symbol: Symbol, var_store: &mut VarStore) -> Option STR_SPLIT => str_split, STR_IS_EMPTY => str_is_empty, 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_COUNT_GRAPHEMES => str_count_graphemes, STR_FROM_INT => str_from_int, STR_FROM_UTF8 => str_from_utf8, 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, LIST_LEN => list_len, 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) } -/// Str.startsWithCodePoint : Str, U32 -> Bool +/// Str.startsWithCodePt : Str, U32 -> Bool 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 @@ -1562,9 +1562,9 @@ fn str_from_utf8_range(symbol: Symbol, var_store: &mut VarStore) -> Def { } -/// Str.toBytes : Str -> List U8 -fn str_to_bytes(symbol: Symbol, var_store: &mut VarStore) -> Def { - lowlevel_1(symbol, LowLevel::StrToBytes, var_store) +/// Str.toUtf8 : Str -> List U8 +fn str_to_utf8(symbol: Symbol, var_store: &mut VarStore) -> Def { + lowlevel_1(symbol, LowLevel::StrToUtf8, var_store) } /// Str.fromFloat : Float * -> Str diff --git a/compiler/can/src/expr.rs b/compiler/can/src/expr.rs index efa82ca8c9..264e6fefe6 100644 --- a/compiler/can/src/expr.rs +++ b/compiler/can/src/expr.rs @@ -1600,10 +1600,10 @@ fn flatten_str_lines<'a>( buf.push(ch); } None => { - env.problem(Problem::InvalidUnicodeCodePoint(loc_hex_digits.region)); + env.problem(Problem::InvalidUnicodeCodePt(loc_hex_digits.region)); return ( - Expr::RuntimeError(RuntimeError::InvalidUnicodeCodePoint( + Expr::RuntimeError(RuntimeError::InvalidUnicodeCodePt( loc_hex_digits.region, )), output, diff --git a/compiler/can/src/string.rs b/compiler/can/src/string.rs index 5911ce1a3a..1baef3ee3b 100644 --- a/compiler/can/src/string.rs +++ b/compiler/can/src/string.rs @@ -313,7 +313,7 @@ pub fn canonical_string_literal<'a>(_arena: &Bump, _raw: &'a str, _region: Regio // problems.push(Loc { // region, -// value: Problem::UnicodeCodePointTooLarge, +// value: Problem::UnicodeCodePtTooLarge, // }); // } else { // // 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), // None => { // problems.push(loc_escaped_unicode( -// Problem::InvalidUnicodeCodePoint, +// Problem::InvalidUnicodeCodePt, // &state, // start_of_unicode, // 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() { // Problem::NoUnicodeDigits // } else { -// Problem::NonHexCharsInUnicodeCodePoint +// Problem::NonHexCharsInUnicodeCodePt // }; // problems.push(loc_escaped_unicode( diff --git a/compiler/can/tests/test_can.rs b/compiler/can/tests/test_can.rs index 7e2d615b8a..a937af1c45 100644 --- a/compiler/can/tests/test_can.rs +++ b/compiler/can/tests/test_can.rs @@ -1590,7 +1590,7 @@ mod test_can { // // (Rust has this restriction. I assume it's a good idea.) // assert_malformed_str( // r#""abc\u{110000}def""#, - // vec![Located::new(0, 7, 0, 12, Problem::UnicodeCodePointTooLarge)], + // vec![Located::new(0, 7, 0, 12, Problem::UnicodeCodePtTooLarge)], // ); // } diff --git a/compiler/gen_llvm/src/llvm/build.rs b/compiler/gen_llvm/src/llvm/build.rs index bc95466658..cdd2d7f2fc 100644 --- a/compiler/gen_llvm/src/llvm/build.rs +++ b/compiler/gen_llvm/src/llvm/build.rs @@ -14,8 +14,8 @@ use crate::llvm::build_list::{ }; use crate::llvm::build_str::{ 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_starts_with_code_point, str_to_bytes, + str_from_utf8, str_from_utf8_range, str_join_with, str_number_of_bytes, str_split, + str_starts_with, str_starts_with_code_point, str_to_utf8, }; use crate::llvm::compare::{generic_eq, generic_neq}; use crate::llvm::convert::{ @@ -4412,8 +4412,8 @@ fn run_low_level<'a, 'ctx, 'env>( str_starts_with(env, scope, args[0], args[1]) } - StrStartsWithCodePoint => { - // Str.startsWithCodePoint : Str, U32 -> Bool + StrStartsWithCodePt => { + // Str.startsWithCodePt : Str, U32 -> Bool debug_assert_eq!(args.len(), 2); 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) } StrFromUtf8Range => { - // Str.fromUtf8 : List U8 -> Result Str Utf8Problem debug_assert_eq!(args.len(), 2); 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) } - StrToBytes => { + StrToUtf8 => { // Str.fromInt : Str -> List U8 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 let string = load_symbol(scope, &args[0]); - str_to_bytes(env, string.into_struct_value()) + str_to_utf8(env, string.into_struct_value()) } StrSplit => { // Str.split : Str, Str -> List Str diff --git a/compiler/gen_llvm/src/llvm/build_str.rs b/compiler/gen_llvm/src/llvm/build_str.rs index 6e62e775e7..e86343ac84 100644 --- a/compiler/gen_llvm/src/llvm/build_str.rs +++ b/compiler/gen_llvm/src/llvm/build_str.rs @@ -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>( env: &Env<'a, 'ctx, 'env>, scope: &Scope<'a, 'ctx>, @@ -188,7 +188,7 @@ pub fn str_starts_with_code_point<'a, 'ctx, 'env>( call_bitcode_fn( env, &[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) } -/// Str.toBytes : Str -> List U8 -pub fn str_to_bytes<'a, 'ctx, 'env>( +/// Str.toUtf8 : Str -> List U8 +pub fn str_to_utf8<'a, 'ctx, 'env>( env: &Env<'a, 'ctx, 'env>, original_wrapper: StructValue<'ctx>, ) -> BasicValueEnum<'ctx> { @@ -244,10 +244,10 @@ pub fn str_to_bytes<'a, 'ctx, 'env>( env.builder, original_wrapper.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 } diff --git a/compiler/module/src/low_level.rs b/compiler/module/src/low_level.rs index de3c61e659..3c7b189f72 100644 --- a/compiler/module/src/low_level.rs +++ b/compiler/module/src/low_level.rs @@ -7,14 +7,14 @@ pub enum LowLevel { StrJoinWith, StrIsEmpty, StrStartsWith, - StrStartsWithCodePoint, + StrStartsWithCodePt, StrEndsWith, StrSplit, StrCountGraphemes, StrFromInt, StrFromUtf8, StrFromUtf8Range, - StrToBytes, + StrToUtf8, StrFromFloat, ListLen, ListGetUnsafe, @@ -110,92 +110,22 @@ impl LowLevel { use LowLevel::*; match self { - StrConcat - | StrJoinWith - | StrIsEmpty - | StrStartsWith - | StrStartsWithCodePoint - | StrEndsWith - | StrSplit - | StrCountGraphemes - | StrFromInt - | StrFromUtf8 - | StrFromUtf8Range - | StrToBytes - | StrFromFloat - | ListLen - | ListGetUnsafe - | 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, + StrConcat | StrJoinWith | StrIsEmpty | StrStartsWith | StrStartsWithCodePt + | StrEndsWith | StrSplit | StrCountGraphemes | StrFromInt | StrFromUtf8 | StrFromUtf8Range | StrToUtf8 + | StrFromFloat | ListLen | ListGetUnsafe | 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 | ListWalkUntil | ListWalkBackwards | ListKeepOks | ListKeepErrs | ListSortWith diff --git a/compiler/module/src/symbol.rs b/compiler/module/src/symbol.rs index 4d48c908da..3b32c1fd8e 100644 --- a/compiler/module/src/symbol.rs +++ b/compiler/module/src/symbol.rs @@ -918,8 +918,8 @@ define_builtins! { 12 STR_FROM_UTF8: "fromUtf8" 13 STR_UT8_PROBLEM: "Utf8Problem" // the Utf8Problem type alias 14 STR_UT8_BYTE_PROBLEM: "Utf8ByteProblem" // the Utf8ByteProblem type alias - 15 STR_TO_BYTES: "toBytes" - 16 STR_STARTS_WITH_CODE_POINT: "startsWithCodePoint" + 15 STR_TO_UTF8: "toUtf8" + 16 STR_STARTS_WITH_CODE_PT: "startsWithCodePt" 17 STR_ALIAS_ANALYSIS_STATIC: "#aliasAnalysisStatic" // string with the static lifetime 18 STR_FROM_UTF8_RANGE: "fromUtf8Range" } @@ -989,8 +989,6 @@ define_builtins! { 14 DICT_UNION: "union" 15 DICT_INTERSECTION: "intersection" 16 DICT_DIFFERENCE: "difference" - - } 7 SET: "Set" => { 0 SET_SET: "Set" imported // the Set.Set type alias diff --git a/compiler/mono/src/borrow.rs b/compiler/mono/src/borrow.rs index c13f177414..b5b2584801 100644 --- a/compiler/mono/src/borrow.rs +++ b/compiler/mono/src/borrow.rs @@ -1013,10 +1013,10 @@ pub fn lowlevel_borrow_signature(arena: &Bump, op: LowLevel) -> &[bool] { | NumCeiling | NumFloor | NumToFloat | Not | NumIsFinite | NumAtan | NumAcos | NumAsin | NumIntCast => arena.alloc_slice_copy(&[irrelevant]), 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]), 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]), Hash => arena.alloc_slice_copy(&[borrowed, irrelevant]), DictSize => arena.alloc_slice_copy(&[borrowed]), diff --git a/compiler/parse/src/parser.rs b/compiler/parse/src/parser.rs index 966bed4bf2..8e8c4efc0e 100644 --- a/compiler/parse/src/parser.rs +++ b/compiler/parse/src/parser.rs @@ -436,8 +436,8 @@ pub enum Number { pub enum EString<'a> { Open(Row, Col), - CodePointOpen(Row, Col), - CodePointEnd(Row, Col), + CodePtOpen(Row, Col), + CodePtEnd(Row, Col), Space(BadInputError, Row, Col), EndlessSingle(Row, Col), diff --git a/compiler/parse/src/problems.rs b/compiler/parse/src/problems.rs index a812e3256b..28c3318e31 100644 --- a/compiler/parse/src/problems.rs +++ b/compiler/parse/src/problems.rs @@ -6,10 +6,10 @@ pub type Problems = Vec>; pub enum Problem { // UNICODE CODE POINT /// 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}. - UnicodeCodePointTooLarge, - InvalidUnicodeCodePoint, + UnicodeCodePtTooLarge, + InvalidUnicodeCodePt, MalformedEscapedUnicode, NoUnicodeDigits, diff --git a/compiler/parse/src/string_literal.rs b/compiler/parse/src/string_literal.rs index 740ef52f29..74b3ae66ab 100644 --- a/compiler/parse/src/string_literal.rs +++ b/compiler/parse/src/string_literal.rs @@ -18,7 +18,7 @@ fn ascii_hex_digits<'a>() -> impl Parser<'a, &'a str, EString<'a>> { // We didn't find any hex digits! return Err(( NoProgress, - EString::CodePointEnd(state.line, state.column), + EString::CodePtEnd(state.line, state.column), state, )); } else { @@ -32,7 +32,7 @@ fn ascii_hex_digits<'a>() -> impl Parser<'a, &'a str, EString<'a>> { Err(( NoProgress, - EString::CodePointEnd(state.line, state.column), + EString::CodePtEnd(state.line, state.column), state, )) } @@ -257,9 +257,9 @@ pub fn parse<'a>() -> impl Parser<'a, StrLiteral<'a>, EString<'a>> { // give a canonicalization error if the digits form // an invalid unicode code point. let (_progress, loc_digits, new_state) = between!( - word1(b'(', EString::CodePointOpen), + word1(b'(', EString::CodePtOpen), loc(ascii_hex_digits()), - word1(b')', EString::CodePointEnd) + word1(b')', EString::CodePtEnd) ) .parse(arena, state)?; diff --git a/compiler/problem/src/can.rs b/compiler/problem/src/can.rs index 134e175e6a..c5bfa42f53 100644 --- a/compiler/problem/src/can.rs +++ b/compiler/problem/src/can.rs @@ -77,7 +77,7 @@ pub enum Problem { }, InvalidInterpolation(Region), InvalidHexadecimal(Region), - InvalidUnicodeCodePoint(Region), + InvalidUnicodeCodePt(Region), } #[derive(Clone, Debug, PartialEq)] @@ -160,7 +160,7 @@ pub enum RuntimeError { InvalidInterpolation(Region), InvalidHexadecimal(Region), - InvalidUnicodeCodePoint(Region), + InvalidUnicodeCodePt(Region), /// When the author specifies a type annotation but no implementation NoImplementationNamed { diff --git a/compiler/reporting/src/error/canonicalize.rs b/compiler/reporting/src/error/canonicalize.rs index 4e07a5677c..4a354989ed 100644 --- a/compiler/reporting/src/error/canonicalize.rs +++ b/compiler/reporting/src/error/canonicalize.rs @@ -313,7 +313,7 @@ pub fn can_problem<'b>( ]), 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.region(region), alloc.reflow("Learn more about working with unicode in roc at TODO"), @@ -931,7 +931,7 @@ fn pretty_runtime_error<'b>( region ); } - RuntimeError::InvalidUnicodeCodePoint(region) => { + RuntimeError::InvalidUnicodeCodePt(region) => { todo!( "TODO runtime error for an invalid \\u(...) code point at region {:?}", region diff --git a/compiler/reporting/src/error/parse.rs b/compiler/reporting/src/error/parse.rs index 1fbfac0d3a..94591d5528 100644 --- a/compiler/reporting/src/error/parse.rs +++ b/compiler/reporting/src/error/parse.rs @@ -826,7 +826,7 @@ fn to_str_report<'a>( 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 region = Region::from_row_col(row, col); diff --git a/compiler/solve/src/solve.rs b/compiler/solve/src/solve.rs index ee3f57c90c..856771a2d3 100644 --- a/compiler/solve/src/solve.rs +++ b/compiler/solve/src/solve.rs @@ -1392,24 +1392,39 @@ fn deep_copy_var_help( same @ EmptyRecord | same @ EmptyTagUnion | same @ Erroneous(_) => same, Record(fields, ext_var) => { - let mut new_vars = Vec::with_capacity(fields.len()); - for index in fields.iter_variables() { - let var = subs[index]; - let copy_var = deep_copy_var_help(subs, max_rank, pools, var); - new_vars.push(copy_var); - } + let record_fields = { + let mut new_vars = Vec::with_capacity(fields.len()); - let it = fields.iter_all().zip(new_vars).map(|((i1, _, i3), var)| { - let label = subs[i1].clone(); - let record_field = subs[i3].map(|_| var); + for index in fields.iter_variables() { + let var = subs[index]; + let copy_var = deep_copy_var_help(subs, max_rank, pools, var); - (label, record_field) - }); + new_vars.push(copy_var); + } - // lifetime troubles - let vec: Vec<_> = it.collect(); + let field_names_start = subs.field_names.len() as u32; + 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_fields, diff --git a/compiler/test_gen/src/gen_str.rs b/compiler/test_gen/src/gen_str.rs index a06ab69f05..6e7ce971f0 100644 --- a/compiler/test_gen/src/gen_str.rs +++ b/compiler/test_gen/src/gen_str.rs @@ -423,12 +423,12 @@ fn str_starts_with() { #[test] fn str_starts_with_code_point() { assert_evals_to!( - &format!(r#"Str.startsWithCodePoint "foobar" {}"#, 'f' as u32), + &format!(r#"Str.startsWithCodePt "foobar" {}"#, 'f' as u32), true, bool ); assert_evals_to!( - &format!(r#"Str.startsWithCodePoint "zoobar" {}"#, 'f' as u32), + &format!(r#"Str.startsWithCodePt "zoobar" {}"#, 'f' as u32), false, bool ); @@ -819,10 +819,10 @@ fn str_from_float() { } #[test] -fn str_to_bytes() { - assert_evals_to!(r#"Str.toBytes "hello""#, &[104, 101, 108, 108, 111], &[u8]); +fn str_to_utf8() { + assert_evals_to!(r#"Str.toUtf8 "hello""#, &[104, 101, 108, 108, 111], &[u8]); 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, 105, 110, 103 @@ -836,7 +836,7 @@ fn str_from_utf8_range() { assert_evals_to!( indoc!( r#" - bytes = Str.toBytes "hello" + bytes = Str.toUtf8 "hello" when Str.fromUtf8Range bytes { count: 5, start: 0 } is Ok utf8String -> utf8String _ -> "" @@ -852,7 +852,7 @@ fn str_from_utf8_range_slice() { assert_evals_to!( indoc!( r#" - bytes = Str.toBytes "hello" + bytes = Str.toUtf8 "hello" when Str.fromUtf8Range bytes { count: 4, start: 1 } is Ok utf8String -> utf8String _ -> "" @@ -868,7 +868,7 @@ fn str_from_utf8_range_slice_not_end() { assert_evals_to!( indoc!( r#" - bytes = Str.toBytes "hello" + bytes = Str.toUtf8 "hello" when Str.fromUtf8Range bytes { count: 3, start: 1 } is Ok utf8String -> utf8String _ -> "" @@ -884,7 +884,7 @@ fn str_from_utf8_range_order_does_not_matter() { assert_evals_to!( indoc!( r#" - bytes = Str.toBytes "hello" + bytes = Str.toUtf8 "hello" when Str.fromUtf8Range bytes { start: 1, count: 3 } is Ok utf8String -> utf8String _ -> "" @@ -895,18 +895,18 @@ fn str_from_utf8_range_order_does_not_matter() { ); } -#[test] -fn str_from_utf8_range_out_of_bounds() { - assert_evals_to!( - indoc!( - r#" - bytes = Str.toBytes "hello" - when Str.fromUtf8Range bytes { start: 7, count: 3 } is - Ok _ -> "" - Err OutOfBounds -> "out of bounds" - "# - ), - RocStr::from("out of bounds"), - RocStr - ); -} +// #[test] +// fn str_from_utf8_range_out_of_bounds() { +// assert_evals_to!( +// indoc!( +// r#" +// bytes = Str.toUtf8 "hello" +// when Str.fromUtf8Range bytes { start: 7, count: 3 } is +// Ok _ -> "" +// Err OutOfBounds -> "out of bounds" +// "# +// ), +// RocStr::from("out of bounds"), +// RocStr +// ); +// } diff --git a/compiler/test_gen/src/gen_tags.rs b/compiler/test_gen/src/gen_tags.rs index 247bebde87..ea0c366cad 100644 --- a/compiler/test_gen/src/gen_tags.rs +++ b/compiler/test_gen/src/gen_tags.rs @@ -477,7 +477,7 @@ fn if_guard_vanilla() { r#" when "fooz" is s if s == "foo" -> 0 - s -> List.len (Str.toBytes s) + s -> List.len (Str.toUtf8 s) "# ), 4, diff --git a/docs/src/lib.rs b/docs/src/lib.rs index 28aaab99be..40a788aa80 100644 --- a/docs/src/lib.rs +++ b/docs/src/lib.rs @@ -52,21 +52,28 @@ pub fn generate(filenames: Vec, std_lib: StdLib, build_dir: &Path) { ) .expect("TODO gracefully handle failing to make the favicon"); - let template_html = include_str!("./static/index.html").replace( - "", - 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::>(); + let template_html = include_str!("./static/index.html") + .replace("", &format!("{}search.js", base_href())) + .replace("", &format!("{}styles.css", base_href())) + .replace( + "", + &format!("{}favicon.svg", base_href()), + ) + .replace( + "", + 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::>(); - (exposed_values, d) - }) - })) - .as_str(), - ); + (exposed_values, d) + }) + })) + .as_str(), + ); // Write each package's module docs html file for loaded_module in package.modules.iter_mut() { @@ -143,7 +150,7 @@ fn render_main_content( if should_render_entry { match entry { DocEntry::DocDef(doc_def) => { - let mut href = String::new(); + let mut href = base_href(); href.push('#'); 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 } +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 { let mut buf = String::new(); + let mut href = base_href(); - let mut href = String::new(); - href.push('/'); href.push_str(name); buf.push_str( @@ -255,7 +291,7 @@ fn render_name_and_version(name: &str, version: &str) -> String { .as_str(), ); - let mut versions_href = String::new(); + let mut versions_href = base_href(); versions_href.push('/'); versions_href.push_str(name); @@ -285,8 +321,7 @@ fn render_sidebar<'a, I: Iterator, &'a ModuleDocumentation)> let name = module.name.as_str(); let href = { - let mut href_buf = String::new(); - href_buf.push('/'); + let mut href_buf = base_href(); href_buf.push_str(name); href_buf }; @@ -686,8 +721,8 @@ fn doc_url<'a>( if !exposed_values.contains(&ident) { // TODO return Err here panic!( - "Tried to generate an automatic link in docs for `{}.{}`, but `{}` is not declared in `{}`.", - module_name, ident, ident, module_name); + "Tried to generate an automatic link in docs for `{}.{}`, but `{}` does not expose `{}`.", + module_name, ident, module_name, ident); } } else { // 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: // // module_name: "Str", ident: "join" => "/Str#join" - url.push('/'); url.push_str(module_name); url.push('#'); url.push_str(ident); @@ -757,7 +791,7 @@ fn markdown_to_html( let state = State::new(link.reference.as_bytes()); // Reset the bump arena so we aren't constantly reallocating - // more memory. + // more memory as we iterate through these. arena.reset(); match parse_ident(&arena, state) { diff --git a/docs/src/static/index.html b/docs/src/static/index.html index 5e358eea74..48ffc31509 100644 --- a/docs/src/static/index.html +++ b/docs/src/static/index.html @@ -6,9 +6,9 @@ - - - + + + diff --git a/editor/src/editor/ed_error.rs b/editor/src/editor/ed_error.rs index b534ca52bb..2cbd9f35c7 100644 --- a/editor/src/editor/ed_error.rs +++ b/editor/src/editor/ed_error.rs @@ -73,7 +73,7 @@ pub enum EdError { }, #[snafu(display( - "MissingSelection: ed_model.selected_expr2_id was Some(NodeId) 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 }, diff --git a/editor/src/editor/grid_node_map.rs b/editor/src/editor/grid_node_map.rs index f455782099..f0abf111fe 100644 --- a/editor/src/editor/grid_node_map.rs +++ b/editor/src/editor/grid_node_map.rs @@ -6,8 +6,7 @@ use crate::editor::slow_pool::MarkNodeId; use crate::editor::slow_pool::SlowPool; use crate::editor::util::first_last_index_of; use crate::editor::util::index_of; -use crate::lang::ast::Expr2; -use crate::lang::pool::NodeId; +use crate::lang::ast::ExprId; use crate::ui::text::selection::Selection; use crate::ui::text::text_pos::TextPos; use crate::ui::ui_error::UIResult; @@ -139,7 +138,7 @@ impl GridNodeMap { &self, caret_pos: TextPos, ed_model: &EdModel, - ) -> EdResult<(TextPos, TextPos, NodeId, MarkNodeId)> { + ) -> EdResult<(TextPos, TextPos, ExprId, MarkNodeId)> { let line = slice_get(caret_pos.line, &self.lines)?; let node_id = slice_get(caret_pos.column, line)?; let node = ed_model.markup_node_pool.get(*node_id); diff --git a/editor/src/editor/markup/nodes.rs b/editor/src/editor/markup/nodes.rs index fdbfc98213..26047fa230 100644 --- a/editor/src/editor/markup/nodes.rs +++ b/editor/src/editor/markup/nodes.rs @@ -7,13 +7,8 @@ use crate::editor::slow_pool::MarkNodeId; use crate::editor::slow_pool::SlowPool; use crate::editor::syntax_highlight::HighlightStyle; use crate::editor::util::index_of; -use crate::lang::ast::ExprId; -use crate::lang::ast::RecordField; -use crate::lang::{ - ast::Expr2, - expr::Env, - pool::{NodeId, PoolStr}, -}; +use crate::lang::ast::{Expr2, ExprId, RecordField}; +use crate::lang::{expr::Env, pool::PoolStr}; use crate::ui::util::slice_get; use bumpalo::Bump; use std::fmt; @@ -21,19 +16,19 @@ use std::fmt; #[derive(Debug)] pub enum MarkupNode { Nested { - ast_node_id: NodeId, + ast_node_id: ExprId, children_ids: Vec, parent_id_opt: Option, }, Text { content: String, - ast_node_id: NodeId, + ast_node_id: ExprId, syn_high_style: HighlightStyle, attributes: Attributes, parent_id_opt: Option, }, Blank { - ast_node_id: NodeId, + ast_node_id: ExprId, attributes: Attributes, syn_high_style: HighlightStyle, // TODO remove HighlightStyle, this is always HighlightStyle::Blank parent_id_opt: Option, @@ -41,7 +36,7 @@ pub enum MarkupNode { } impl MarkupNode { - pub fn get_ast_node_id(&self) -> NodeId { + pub fn get_ast_node_id(&self) -> ExprId { match self { MarkupNode::Nested { 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( text: String, - node_id: NodeId, + node_id: ExprId, highlight_style: HighlightStyle, markup_node_pool: &mut SlowPool, ) -> MarkNodeId { @@ -251,7 +246,7 @@ pub fn expr2_to_markup<'a, 'b>( arena: &'a Bump, env: &mut Env<'b>, expr2: &Expr2, - expr2_node_id: NodeId, + expr2_node_id: ExprId, markup_node_pool: &mut SlowPool, ) -> MarkNodeId { match expr2 { diff --git a/editor/src/editor/mvc/ed_model.rs b/editor/src/editor/mvc/ed_model.rs index 6cd9373408..51f59ee993 100644 --- a/editor/src/editor/mvc/ed_model.rs +++ b/editor/src/editor/mvc/ed_model.rs @@ -9,9 +9,8 @@ use crate::editor::{ markup::nodes::{expr2_to_markup, set_parent_for_all, MarkupNode}, }; 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::pool::NodeId; use crate::lang::pool::PoolStr; use crate::lang::scope::Scope; use crate::ui::text::caret_w_select::CaretWSelect; @@ -47,7 +46,7 @@ pub struct EdModel<'a> { #[derive(Debug, Copy, Clone)] pub struct SelectedExpression { - pub ast_node_id: NodeId, + pub ast_node_id: ExprId, pub mark_node_id: MarkNodeId, pub type_str: PoolStr, } @@ -162,7 +161,7 @@ impl<'a> EdModel<'a> { #[derive(Debug)] pub struct EdModule<'a> { pub env: Env<'a>, - pub ast_root_id: NodeId, + pub ast_root_id: ExprId, } // for debugging diff --git a/editor/src/editor/mvc/ed_update.rs b/editor/src/editor/mvc/ed_update.rs index 9fd9caee31..22f113bd10 100644 --- a/editor/src/editor/mvc/ed_update.rs +++ b/editor/src/editor/mvc/ed_update.rs @@ -25,9 +25,8 @@ use crate::editor::mvc::string_update::update_string; use crate::editor::slow_pool::MarkNodeId; use crate::editor::slow_pool::SlowPool; 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::pool::NodeId; use crate::lang::pool::Pool; use crate::lang::pool::PoolStr; use crate::lang::types::Type2; @@ -176,7 +175,7 @@ impl<'a> EdModel<'a> { &mut self, expr_start_pos: TextPos, expr_end_pos: TextPos, - ast_node_id: NodeId, + ast_node_id: ExprId, mark_node_id: MarkNodeId, ) -> EdResult<()> { self.set_raw_sel(RawSelection { @@ -239,7 +238,7 @@ impl<'a> EdModel<'a> { Ok(()) } - fn expr2_to_type(&mut self, expr2_id: NodeId) -> PoolStr { + fn expr2_to_type(&mut self, expr2_id: ExprId) -> PoolStr { let var = self.module.env.var_store.fresh(); let expr = self.module.env.pool.get(expr2_id); let arena = Bump::new(); @@ -528,7 +527,7 @@ pub struct NodeContext<'a> { pub curr_mark_node_id: MarkNodeId, pub curr_mark_node: &'a MarkupNode, pub parent_id_opt: Option, - pub ast_node_id: NodeId, + pub ast_node_id: ExprId, } pub fn get_node_context<'a>(ed_model: &'a EdModel) -> EdResult> { diff --git a/editor/src/editor/mvc/lookup_update.rs b/editor/src/editor/mvc/lookup_update.rs index 112e25f7e4..972f6e3a7e 100644 --- a/editor/src/editor/mvc/lookup_update.rs +++ b/editor/src/editor/mvc/lookup_update.rs @@ -2,8 +2,7 @@ use crate::editor::ed_error::EdResult; use crate::editor::mvc::app_update::InputOutcome; use crate::editor::mvc::ed_model::EdModel; use crate::editor::slow_pool::MarkNodeId; -use crate::lang::ast::Expr2; -use crate::lang::pool::NodeId; +use crate::lang::ast::{Expr2, ExprId}; use crate::lang::pool::PoolStr; use crate::ui::text::lines::SelectableLines; @@ -11,7 +10,7 @@ pub fn update_invalid_lookup( input_str: &str, old_pool_str: &PoolStr, curr_mark_node_id: MarkNodeId, - ast_node_id: NodeId, + ast_node_id: ExprId, ed_model: &mut EdModel, ) -> EdResult { if input_str.chars().all(|ch| ch.is_ascii_alphanumeric()) { diff --git a/editor/src/editor/mvc/record_update.rs b/editor/src/editor/mvc/record_update.rs index bacb17db46..628028953d 100644 --- a/editor/src/editor/mvc/record_update.rs +++ b/editor/src/editor/mvc/record_update.rs @@ -11,9 +11,8 @@ use crate::editor::mvc::ed_update::NodeContext; use crate::editor::slow_pool::MarkNodeId; use crate::editor::syntax_highlight::HighlightStyle; use crate::editor::util::index_of; -use crate::lang::ast::Expr2; -use crate::lang::ast::RecordField; -use crate::lang::pool::{NodeId, PoolStr, PoolVec}; +use crate::lang::ast::{Expr2, ExprId, RecordField}; +use crate::lang::pool::{PoolStr, PoolVec}; use crate::ui::text::text_pos::TextPos; use snafu::OptionExt; @@ -174,7 +173,7 @@ pub fn update_empty_record( pub fn update_record_colon( ed_model: &mut EdModel, - record_ast_node_id: NodeId, + record_ast_node_id: ExprId, ) -> EdResult { let NodeContext { old_caret_pos, diff --git a/editor/src/lang/ast.rs b/editor/src/lang/ast.rs index bc1f0084fd..69fb7f4d56 100644 --- a/editor/src/lang/ast.rs +++ b/editor/src/lang/ast.rs @@ -64,7 +64,7 @@ pub enum FloatVal { pub enum RecordField { InvalidLabelOnly(PoolStr, Variable), LabelOnly(PoolStr, Variable, Symbol), - LabeledValue(PoolStr, Variable, NodeId), + LabeledValue(PoolStr, Variable, ExprId), } #[test] @@ -124,49 +124,49 @@ pub enum Expr2 { elems: PoolVec, // 8B }, If { - cond_var: Variable, // 4B - expr_var: Variable, // 4B - branches: PoolVec<(NodeId, NodeId)>, // 8B - final_else: NodeId, // 4B + cond_var: Variable, // 4B + expr_var: Variable, // 4B + branches: PoolVec<(ExprId, ExprId)>, // 8B + final_else: ExprId, // 4B }, When { cond_var: Variable, // 4B expr_var: Variable, // 4B branches: PoolVec, // 8B - cond: NodeId, // 4B + cond: ExprId, // 4B }, LetRec { defs: PoolVec, // 8B body_var: Variable, // 8B - body_id: NodeId, // 4B + body_id: ExprId, // 4B }, LetFunction { def_id: NodeId, // 4B body_var: Variable, // 8B - body_id: NodeId, // 4B + body_id: ExprId, // 4B }, LetValue { def_id: NodeId, // 4B - body_id: NodeId, // 4B + body_id: ExprId, // 4B body_var: Variable, // 4B }, Call { - args: PoolVec<(Variable, NodeId)>, // 8B - expr: NodeId, // 4B - expr_var: Variable, // 4B - fn_var: Variable, // 4B - closure_var: Variable, // 4B - called_via: CalledVia, // 2B + args: PoolVec<(Variable, ExprId)>, // 8B + expr: ExprId, // 4B + expr_var: Variable, // 4B + fn_var: Variable, // 4B + closure_var: Variable, // 4B + called_via: CalledVia, // 2B }, RunLowLevel { - op: LowLevel, // 1B - args: PoolVec<(Variable, NodeId)>, // 8B - ret_var: Variable, // 4B + op: LowLevel, // 1B + args: PoolVec<(Variable, ExprId)>, // 8B + ret_var: Variable, // 4B }, Closure { args: PoolVec<(Variable, NodeId)>, // 8B name: Symbol, // 8B - body: NodeId, // 4B + body: ExprId, // 4B function_type: Variable, // 4B recursive: Recursive, // 1B extra: NodeId, // 4B @@ -181,7 +181,7 @@ pub enum Expr2 { /// Look up exactly one field on a record, e.g. (expr).foo. Access { field: PoolStr, // 4B - expr: NodeId, // 4B + expr: ExprId, // 4B record_var: Variable, // 4B ext_var: Variable, // 4B field_var: Variable, // 4B @@ -205,16 +205,16 @@ pub enum Expr2 { // Sum Types GlobalTag { - name: PoolStr, // 4B - variant_var: Variable, // 4B - ext_var: Variable, // 4B - arguments: PoolVec<(Variable, NodeId)>, // 8B + name: PoolStr, // 4B + variant_var: Variable, // 4B + ext_var: Variable, // 4B + arguments: PoolVec<(Variable, ExprId)>, // 8B }, PrivateTag { - name: Symbol, // 8B - variant_var: Variable, // 4B - ext_var: Variable, // 4B - arguments: PoolVec<(Variable, NodeId)>, // 8B + name: Symbol, // 8B + variant_var: Variable, // 4B + ext_var: Variable, // 4B + arguments: PoolVec<(Variable, ExprId)>, // 8B }, Blank, // Rendered as empty box in editor @@ -270,17 +270,17 @@ impl ShallowClone for ValueDef { #[derive(Debug)] pub enum FunctionDef { WithAnnotation { - name: Symbol, // 8B - arguments: PoolVec<(Pattern2, Type2)>, // 8B - rigids: NodeId, // 4B - return_type: TypeId, // 4B - body: ExprId, // 4B + name: Symbol, // 8B + arguments: PoolVec<(PatternId, Type2)>, // 8B + rigids: NodeId, // 4B + return_type: TypeId, // 4B + body: ExprId, // 4B }, NoAnnotation { - name: Symbol, // 8B - arguments: PoolVec<(Pattern2, Variable)>, // 8B - return_var: Variable, // 4B - body: ExprId, // 4B + name: Symbol, // 8B + arguments: PoolVec<(PatternId, Variable)>, // 8B + return_var: Variable, // 4B + body: ExprId, // 4B }, } @@ -394,11 +394,12 @@ pub struct ClosureExtra { #[derive(Debug)] pub struct WhenBranch { - pub patterns: PoolVec, // 4B - pub body: NodeId, // 3B - pub guard: Option>, // 4B + pub patterns: PoolVec, // 4B + pub body: ExprId, // 3B + pub guard: Option, // 4B } +// TODO make the inner types private? pub type ExprId = NodeId; use RecordField::*; @@ -427,7 +428,7 @@ impl RecordField { } } - pub fn get_record_field_val_node_id(&self) -> Option> { + pub fn get_record_field_val_node_id(&self) -> Option { match self { InvalidLabelOnly(_, _) => None, LabelOnly(_, _, _) => None, @@ -436,7 +437,7 @@ impl RecordField { } } -pub fn expr2_to_string(node_id: NodeId, pool: &Pool) -> String { +pub fn expr2_to_string(node_id: ExprId, pool: &Pool) -> String { let mut full_string = String::new(); let expr2 = pool.get(node_id); diff --git a/editor/src/lang/constrain.rs b/editor/src/lang/constrain.rs index b2b8e4ec8e..77d4edb0cd 100644 --- a/editor/src/lang/constrain.rs +++ b/editor/src/lang/constrain.rs @@ -1,7 +1,7 @@ use bumpalo::{collections::Vec as BumpVec, Bump}; use crate::lang::{ - ast::{Expr2, ExprId, RecordField, ValueDef, WhenBranch}, + ast::{ClosureExtra, Expr2, ExprId, RecordField, ValueDef, WhenBranch}, expr::Env, pattern::{DestructType, Pattern2, PatternId, PatternState2, RecordDestruct}, pool::{Pool, PoolStr, PoolVec, ShallowClone}, @@ -934,7 +934,94 @@ pub fn constrain_expr<'a>( 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::>(); + + // 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::LetFunction { .. } => todo!(), } @@ -1406,6 +1493,115 @@ fn constrain_tag_pattern<'a>( 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)] fn builtin_type(symbol: Symbol, args: PoolVec) -> Type2 { Type2::Apply(symbol, args) diff --git a/editor/src/lang/def.rs b/editor/src/lang/def.rs index 0cd3b25ba0..c179145a7c 100644 --- a/editor/src/lang/def.rs +++ b/editor/src/lang/def.rs @@ -514,7 +514,7 @@ fn canonicalize_pending_def<'a>( // parent commit for the bug this fixed! let refs = References::new(); - let arguments: PoolVec<(Pattern2, Type2)> = + let arguments: PoolVec<(PatternId, Type2)> = PoolVec::with_capacity(closure_args.len() as u32, env.pool); let return_type: TypeId; @@ -551,9 +551,7 @@ fn canonicalize_pending_def<'a>( for (node_id, ((_, pattern_id), typ)) in arguments.iter_node_ids().zip(it.into_iter()) { - let pattern = &env.pool[pattern_id]; - - env.pool[node_id] = (pattern.shallow_clone(), typ); + env.pool[node_id] = (pattern_id, typ); } return_type = return_type_id; @@ -683,16 +681,14 @@ fn canonicalize_pending_def<'a>( // parent commit for the bug this fixed! let refs = References::new(); - let arguments: PoolVec<(Pattern2, Variable)> = + let arguments: PoolVec<(PatternId, Variable)> = PoolVec::with_capacity(closure_args.len() as u32, env.pool); 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()) { - let pattern = &env.pool[pattern_id]; - - env.pool[node_id] = (pattern.shallow_clone(), env.var_store.fresh()); + env.pool[node_id] = (pattern_id, env.var_store.fresh()); } let function_def = FunctionDef::NoAnnotation { diff --git a/editor/src/lang/expr.rs b/editor/src/lang/expr.rs index bb6e024e99..2321870946 100644 --- a/editor/src/lang/expr.rs +++ b/editor/src/lang/expr.rs @@ -405,8 +405,7 @@ pub fn to_expr2<'a>( let mut output = Output::default(); let output_ref = &mut output; - let elems: PoolVec> = - PoolVec::with_capacity(items.len() as u32, env.pool); + let elems: PoolVec = PoolVec::with_capacity(items.len() as u32, env.pool); for (node_id, item) in elems.iter_node_ids().zip(items.iter()) { let (expr, sub_output) = to_expr2(env, scope, &item.value, item.region); @@ -1015,10 +1014,10 @@ fn flatten_str_lines<'a>( buf.push(ch); } None => { - // env.problem(Problem::InvalidUnicodeCodePoint(loc_hex_digits.region)); + // env.problem(Problem::InvalidUnicodeCodePt(loc_hex_digits.region)); // // return ( - // Expr::RuntimeError(RuntimeError::InvalidUnicodeCodePoint( + // Expr::RuntimeError(RuntimeError::InvalidUnicodeCodePt( // loc_hex_digits.region, // )), // output, diff --git a/editor/src/lang/pool.rs b/editor/src/lang/pool.rs index e68199e339..2504cfcffc 100644 --- a/editor/src/lang/pool.rs +++ b/editor/src/lang/pool.rs @@ -13,6 +13,7 @@ use libc::{c_void, MAP_ANONYMOUS, MAP_PRIVATE, PROT_READ, PROT_WRITE}; use roc_can::expected::Expected; use roc_can::expected::PExpected; +use std::any::type_name; use std::cmp::Ordering; use std::marker::PhantomData; use std::mem::size_of; @@ -132,8 +133,14 @@ impl Pool { } pub fn add(&mut self, node: T) -> NodeId { - // It's only safe to store this if T is the same size as S. - debug_assert_eq!(size_of::(), NODE_BYTES); + // It's only safe to store this if T fits in S. + debug_assert!( + size_of::() <= NODE_BYTES, + "{} has a size of {}, but it needs to be at most {}", + type_name::(), + size_of::(), + NODE_BYTES + ); let node_id = self.reserve(1); 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 { } pub fn with_capacity(len: u32, pool: &mut Pool) -> Self { - debug_assert!(size_of::() <= NODE_BYTES, "{}", size_of::()); + debug_assert!( + size_of::() <= NODE_BYTES, + "{} has a size of {}", + type_name::(), + size_of::() + ); if len == 0 { Self::empty(pool) diff --git a/editor/src/lang/solve.rs b/editor/src/lang/solve.rs index 4c1f0d7064..8a5a547eb7 100644 --- a/editor/src/lang/solve.rs +++ b/editor/src/lang/solve.rs @@ -1519,24 +1519,39 @@ fn deep_copy_var_help( same @ EmptyRecord | same @ EmptyTagUnion | same @ Erroneous(_) => same, Record(fields, ext_var) => { - let mut new_vars = Vec::with_capacity(fields.len()); - for index in fields.iter_variables() { - let var = subs[index]; - let copy_var = deep_copy_var_help(subs, max_rank, pools, var); - new_vars.push(copy_var); - } + let record_fields = { + let mut new_vars = Vec::with_capacity(fields.len()); - let it = fields.iter_all().zip(new_vars).map(|((i1, _, i3), var)| { - let label = subs[i1].clone(); - let record_field = subs[i3].map(|_| var); + for index in fields.iter_variables() { + let var = subs[index]; + let copy_var = deep_copy_var_help(subs, max_rank, pools, var); - (label, record_field) - }); + new_vars.push(copy_var); + } - // lifetime troubles - let vec: Vec<_> = it.collect(); + let field_names_start = subs.field_names.len() as u32; + 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_fields, diff --git a/editor/tests/solve_expr2.rs b/editor/tests/solve_expr2.rs index 7f5e1b2787..88f5e5248b 100644 --- a/editor/tests/solve_expr2.rs +++ b/editor/tests/solve_expr2.rs @@ -356,3 +356,17 @@ fn constrain_run_low_level() { "List Str", ) } + +#[test] +fn constrain_closure() { + infer_eq( + indoc!( + r#" + x = 1 + + \{} -> x + "# + ), + "{}* -> Num *", + ) +} diff --git a/examples/benchmarks/Base64.roc b/examples/benchmarks/Base64.roc index 81eda202e5..76e0cd14c6 100644 --- a/examples/benchmarks/Base64.roc +++ b/examples/benchmarks/Base64.roc @@ -10,7 +10,7 @@ fromBytes = \bytes -> # base 64 encoding from a string fromStr : Str -> Result Str [ InvalidInput ]* fromStr = \str -> - fromBytes (Str.toBytes str) + fromBytes (Str.toUtf8 str) # base64-encode bytes to the original toBytes : Str -> Result (List U8) [ InvalidInput ]* diff --git a/examples/benchmarks/Base64/Encode.roc b/examples/benchmarks/Base64/Encode.roc index 4a6a33d128..3a33d18658 100644 --- a/examples/benchmarks/Base64/Encode.roc +++ b/examples/benchmarks/Base64/Encode.roc @@ -9,7 +9,7 @@ InvalidChar : U8 toBytes : Str -> List U8 toBytes = \str -> str - |> Str.toBytes + |> Str.toUtf8 |> encodeChunks |> Bytes.Encode.sequence |> Bytes.Encode.encode diff --git a/examples/benchmarks/TestBase64.roc b/examples/benchmarks/TestBase64.roc index e7cbd3aedd..3259eb057e 100644 --- a/examples/benchmarks/TestBase64.roc +++ b/examples/benchmarks/TestBase64.roc @@ -7,7 +7,7 @@ IO a : Task.Task a [] main : IO {} main = - when Base64.fromBytes (Str.toBytes "Hello World") is + when Base64.fromBytes (Str.toUtf8 "Hello World") is Err _ -> Task.putLine "sadness" diff --git a/packages/parser/src/Bytes/Parser.roc b/packages/parser/src/Bytes/Parser.roc index 3d7d8c44d2..31a61654d6 100644 --- a/packages/parser/src/Bytes/Parser.roc +++ b/packages/parser/src/Bytes/Parser.roc @@ -40,8 +40,8 @@ Problem : NumF32 Endi, Utf8 Str, Utf16 Str Endi, - CodePointUtf8, - CodePointUtf16 Endi, + CodePtUtf8, + CodePtUtf16 Endi, GraphemeUtf8, GraphemeUtf16 Endi, End, diff --git a/packages/unicode/Package-Config.roc b/packages/unicode/Package-Config.roc index 5e73e18ca9..4c1899127d 100644 --- a/packages/unicode/Package-Config.roc +++ b/packages/unicode/Package-Config.roc @@ -1,6 +1,6 @@ package roc/unicode 0.1.0 roc 0.0.0 - exposes [ Unicode, Unicode.Scalar, Unicode.CodePoint ] + exposes [ Unicode, Unicode.Scalar, Unicode.CodePt ] packages {} license UPL-1.0 diff --git a/packages/unicode/src/Unicode/Scalar.roc b/packages/unicode/src/Unicode/Scalar.roc index 87aec618ba..e8a3537d9a 100644 --- a/packages/unicode/src/Unicode/Scalar.roc +++ b/packages/unicode/src/Unicode/Scalar.roc @@ -3,8 +3,8 @@ interface Unicode.Scalar [ Scalar, toStr, - toCodePoint, - fromCodePoint, + toCodePt, + fromCodePt, parseUtf8, parseUtf16, chompUtf8, @@ -12,8 +12,8 @@ interface Unicode.Scalar ] imports [ - Unicode.CodePoint.Internal as Internal - Unicode.CodePoint.{ CodePoint }, + Unicode.CodePt.Internal as Internal + Unicode.CodePt.{ CodePt }, Bytes.{ Bytes } ] @@ -31,15 +31,15 @@ toStr = \@Scalar u32 # already validated this! toStr (@Scalar (scalar * 256)) -toCodePoint : Scalar -> CodePoint -toCodePoint = \@Scalar u32 -> Internal.fromU32Unchecked u32 +toCodePt : Scalar -> CodePt +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 ]* -parseUtf16 : Bytes, Endi -> Result { val : Scalar, rest : Bytes } [ Expected [ Utf16CodePoint Endi ]* Bytes ]* +parseUtf8 : Bytes -> Result { val : Scalar, rest : Bytes } [ Expected [ Utf8CodePt ]* Bytes ]* +parseUtf16 : Bytes, Endi -> Result { val : Scalar, rest : Bytes } [ Expected [ Utf16CodePt Endi ]* Bytes ]* -chompUtf8 : Bytes, CodePoint -> Result Str [ Expected [ ExactCodePoint CodePoint ]* Bytes ]* -chompUtf16 : Bytes, CodePoint, Endi -> Result Str [ Expected [ ExactCodePoint CodePoint ]* Bytes ]* +chompUtf8 : Bytes, CodePt -> Result Str [ Expected [ ExactCodePt CodePt ]* Bytes ]* +chompUtf16 : Bytes, CodePt, Endi -> Result Str [ Expected [ ExactCodePt CodePt ]* Bytes ]* -isAsciiDigit : CodePoint -> Bool +isAsciiDigit : CodePt -> Bool diff --git a/www/build.sh b/www/build.sh index caa67e53b6..2e09b3d12e 100755 --- a/www/build.sh +++ b/www/build.sh @@ -22,6 +22,10 @@ rustc --version # We set RUSTFLAGS to -Awarnings to ignore warnings during this build, # because when building without "the" llvm feature (which is only ever done # 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 popd diff --git a/www/public/search.js b/www/public/search.js deleted file mode 100644 index 0bc719563b..0000000000 --- a/www/public/search.js +++ /dev/null @@ -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(); -})();