diff --git a/compiler/builtins/docs/Num.roc b/compiler/builtins/docs/Num.roc index 813fe34e23..419672c852 100644 --- a/compiler/builtins/docs/Num.roc +++ b/compiler/builtins/docs/Num.roc @@ -533,7 +533,10 @@ mul : Num range, Num range -> Num range ## Convert -## Convert a number to a string, formatted as the traditional base 10 (decimal). +## Convert a number to a [Str]. +## +## This is the same as calling `Num.format {}` - so for more details on +## exact formatting, see [Num.format]. ## ## >>> Num.toStr 42 ## @@ -546,6 +549,68 @@ mul : Num range, Num range -> Num range ## For other bases see #toHexStr, #toOctalStr, and #toBinaryStr. toStr : Num * -> Str +## Convert a number into a [Str], formatted with the given options. +## +## Default options: +## * `base: Decimal` +## * `notation: Standard` +## * `decimalMark: HideForIntegers "."` +## * `decimalDigits: { min: 0, max: All }` +## * `minIntDigits: 1` +## * `wholeSep: { mark: ",", places: 3 }` +## +## ## Options +## +## +## ### decimalMark +## +## * `AlwaysShow` always shows the decimal mark, no matter what. +## * `HideForIntegers` hides the decimal mark if all the numbers after the decimal mark are 0. +## +## The [Str] included in either of these represents the mark itself. +## +## ### `decimalDigits +## +## With 0 decimal digits, the decimal mark will still be rendered if +## `decimalMark` is set to `AlwaysShow`. +## +## If `max` is less than `min`, then first the number will be truncated to `max` +## digits, and then zeroes will be added afterwards until it reaches `min` digits. +## +## >>> Num.format 1.23 { decPlaces: 0, decPointVis: AlwaysShow } +## +## ### minIntDigits +## +## If the integer portion of number is fewer than this many digits, zeroes will +## be added in front of it until there are at least `minWholeDigits` digits. +## +## If this is set to zero, then numbers less than 1 will begin with `"."` +## rather than `"0."`. +## +## ### wholeSep +## +## Examples: +## +## In some countries (e.g. USA and UK), a comma is used to separate thousands: +## >>> Num.format 1_000_000 { base: Decimal, wholeSep: { mark: ",", places: 3 } } +## +## Sometimes when rendering bits, it's nice to group them into groups of 4: +## >>> Num.format 1_000_000 { base: Binary, wholeSep: { mark: " ", places: 4 } } +## +## It's also common to render hexadecimal in groups of 2: +## >>> Num.format 1_000_000 { base: Hexadecimal, wholeSep: { mark: " ", places: 2 } } +format : + Num *, + { + base ? [ Decimal, Hexadecimal, Octal, Binary ], + notation ? [ Standard, Scientific ], + decimalMark ? [ AlwaysShow Str, HideForIntegers ], + decimalDigits ? { min : U16, max : [ All, Trunc U16, Round U16, Floor U16, Ceil U16 ] }, + minWholeDigits ? U16, + wholeSep ? { mark : Str, places : U64 } + } + -> Str + ## Round off the given float to the nearest integer. round : Float * -> Int * ceil : Float * -> Int * diff --git a/compiler/builtins/docs/Str.roc b/compiler/builtins/docs/Str.roc index 4789db17b2..b8a216d514 100644 --- a/compiler/builtins/docs/Str.roc +++ b/compiler/builtins/docs/Str.roc @@ -429,16 +429,22 @@ chompCodePoint : Str, U32 -> Result Str [ Expected [ ExactCodePoint U32 ]* Str ] ## If the string begins with digits which can represent a valid #U8, return ## that number along with the rest of the string after the digits. -parseU8 : Str -> Result { val : U8, rest : Str } [ Expected [ NumU8 ]* Str ]* -parseI8 : Str -> Result { val : I8, rest : Str } [ Expected [ NumI8 ]* Str ]* -parseU16 : Str -> Result { val : U16, rest : Str } [ Expected [ NumU16 ]* Str ]* -parseI16 : Str -> Result { val : I16, rest : Str } [ Expected [ NumI16 ]* Str ]* -parseU32 : Str -> Result { val : U32, rest : Str } [ Expected [ NumU32 ]* Str ]* -parseI32 : Str -> Result { val : I32, rest : Str } [ Expected [ NumI32 ]* Str ]* -parseU64 : Str -> Result { val : U64, rest : Str } [ Expected [ NumU64 ]* Str ]* -parseI64 : Str -> Result { val : I64, rest : Str } [ Expected [ NumI64 ]* Str ]* -parseU128 : Str -> Result { val : U128, rest : Str } [ Expected [ NumU128 ]* Str ]* -parseI128 : Str -> Result { val : I128, rest : Str } [ Expected [ NumI128 ]* Str ]* +parseU8 : Str, NumFormat -> Result { val : U8, rest : Str } [ Expected [ NumU8 ]* Str ]* +parseI8 : Str, NumFormat -> Result { val : I8, rest : Str } [ Expected [ NumI8 ]* Str ]* +parseU16 : Str, NumFormat -> Result { val : U16, rest : Str } [ Expected [ NumU16 ]* Str ]* +parseI16 : Str, NumFormat -> Result { val : I16, rest : Str } [ Expected [ NumI16 ]* Str ]* +parseU32 : Str, NumFormat -> Result { val : U32, rest : Str } [ Expected [ NumU32 ]* Str ]* +parseI32 : Str, NumFormat -> Result { val : I32, rest : Str } [ Expected [ NumI32 ]* Str ]* +parseU64 : Str, NumFormat -> Result { val : U64, rest : Str } [ Expected [ NumU64 ]* Str ]* +parseI64 : Str, NumFormat -> Result { val : I64, rest : Str } [ Expected [ NumI64 ]* Str ]* +parseU128 : Str, NumFormat -> Result { val : U128, rest : Str } [ Expected [ NumU128 ]* Str ]* +parseI128 : Str, NumFormat -> Result { val : I128, rest : Str } [ Expected [ NumI128 ]* Str ]* -parseF64 : Str -> Result { val : U128, rest : Str } [ Expected [ NumF64 ]* Str ]* -parseF32 : Str -> Result { val : I128, rest : Str } [ Expected [ NumF32 ]* Str ]* +parseF64 : Str, NumFormat -> Result { val : U128, rest : Str } [ Expected [ NumF64 ]* Str ]* +parseF32 : Str, NumFormat -> Result { val : I128, rest : Str } [ Expected [ NumF32 ]* Str ]* + +## TODO make this similar to the Num.format argument +## except more flexible - e.g. the policy for whole number separators +## might be to allow them, to require them, or to allow them only every N digits +## (e.g. 3 for thousands, 4 for bits, 2 for hex) +NumFormat : { }