This commit is contained in:
2026-06-29 15:56:30 +02:00
commit 6fe57399c4
152 changed files with 32122 additions and 0 deletions
+24
View File
@@ -0,0 +1,24 @@
# markdown2
[![Package Version](https://img.shields.io/hexpm/v/markdown2)](https://hex.pm/packages/markdown2)
[![Hex Docs](https://img.shields.io/badge/hex-docs-ffaff3)](https://hexdocs.pm/markdown2/)
```sh
gleam add markdown2@1
```
```gleam
import markdown2
pub fn main() -> Nil {
// TODO: An example of the project in use
}
```
Further documentation can be found at <https://hexdocs.pm/markdown2>.
## Development
```sh
gleam run # Run the project
gleam test # Run the tests
```
+116
View File
@@ -0,0 +1,116 @@
# Goal
A markup language that makes writing unknown things fast and confident.
# Idea
An opinionated successor to Markdown that focuses on semantics instead of presentation.
The file describes meaning. The renderer decides how that meaning is displayed.
# Core Primitives
| Syntax | Dimension | Meaning |
| ------ | ---------- | ----------------------------------- |
| `#n` | Hierarchy | Level `n` in the document structure |
| `!n` | Importance | Importance level `n` |
| `>n` | Quote | Quote depth `n` |
| `@n` | Confidence | Confidence level `n` |
If no number is provided, `n = 1`.
Examples:
- `!` = `!1`
- `#` = `#1`
- `@` = `@1`
- `>` = `>1`
# Example
```text
#2 Computer Science
! I love PCs
@-2 They are going to replace humans one day
> Love is truth
#1 Science
```
Possible Markdown rendering:
```markdown
# Science
## Computer Science
==I love PCs==
~~They are going to replace humans one day~~
> Love is truth
```
This rendering is only an example. The original semantics are preserved regardless of how they are displayed.
# Combining Primitives
Primitives can be combined by placing them directly next to each other.
Example:
```text
@-2!>2 A slightly important statement I believe is wrong, quoted inside another quote.
```
Meaning:
- Confidence: -2
- Importance: 1
- Quote depth: 2
# Inline Scopes
Modifiers can be changed inline.
A modifier without a number is level 1.
A modifier followed by `-` pops one level from that modifier's stack.
Example:
```text
!2@3 important trusted but @- important !5 more important !- 2 important and !- normal
```
Interpretation:
1. Importance 2, Confidence 3
2. Pop Confidence
3. Push Importance 5
4. Pop back to Importance 2
5. Pop back to normal
# Design Principle
Do not describe how text should look.
Describe:
- how important it is
- how trustworthy it is
- where it belongs
- whether it is a quote
The renderer decides how those concepts are displayed.
## ===test=== **123**
#1 test
#2 test \2
@2 test
testt
-----
+21
View File
@@ -0,0 +1,21 @@
name = "markdown2"
version = "1.0.0"
# Fill out these fields if you intend to generate HTML documentation or publish
# your project to the Hex package manager.
#
# description = ""
# licences = ["Apache-2.0"]
# repository = { type = "github", user = "", repo = "" }
# links = [{ title = "Website", href = "" }]
#
# For a full reference of all the available options, you can have a look at
# https://gleam.run/writing-gleam/gleam-toml/.
[dependencies]
gleam_stdlib = ">= 1.0.0 and < 2.0.0"
simplifile = ">= 2.4.0 and < 3.0.0"
gleam_json = ">= 3.1.0 and < 4.0.0"
[dev_dependencies]
gleeunit = ">= 1.0.0 and < 2.0.0"
+21
View File
@@ -0,0 +1,21 @@
# Do not manually edit this file, it is managed by Gleam.
#
# This file locks the dependency versions used, to make your build
# deterministic and to prevent unexpected versions from being included
# in your application.
#
# You should check this file into your source control repository.
packages = [
{ name = "filepath", version = "1.1.2", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "filepath", source = "hex", outer_checksum = "B06A9AF0BF10E51401D64B98E4B627F1D2E48C154967DA7AF4D0914780A6D40A" },
{ name = "gleam_json", version = "3.1.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleam_json", source = "hex", outer_checksum = "44FDAA8847BE8FC48CA7A1C089706BD54BADCC4C45B237A992EDDF9F2CDB2836" },
{ name = "gleam_stdlib", version = "1.0.3", build_tools = ["gleam"], requirements = [], otp_app = "gleam_stdlib", source = "hex", outer_checksum = "1F543AFBA5D33DA493E6087F4E4C4F20D899411343512686C98A8ABB2963CF22" },
{ name = "gleeunit", version = "1.11.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleeunit", source = "hex", outer_checksum = "EC31ABA74256AEA531EDF8169931D775BBB384FED0A8A1BDC4DD9354E3E21826" },
{ name = "simplifile", version = "2.4.0", build_tools = ["gleam"], requirements = ["filepath", "gleam_stdlib"], otp_app = "simplifile", source = "hex", outer_checksum = "7C18AFA4FED0B4CE1FA5B0B4BAC1FA1744427054EA993565F6F3F82E5453170D" },
]
[requirements]
gleam_json = { version = ">= 3.1.0 and < 4.0.0" }
gleam_stdlib = { version = ">= 1.0.0 and < 2.0.0" }
gleeunit = { version = ">= 1.0.0 and < 2.0.0" }
simplifile = { version = ">= 2.4.0 and < 3.0.0" }
+7
View File
@@ -0,0 +1,7 @@
ljiosfgdjiosdfgj
@2! A slightly important statement !1 I believe is wrong, !- quoted inside another quote.
!3 A slightly important statement I believe is wrong, quoted inside another quote.
#3!2@2 A slightly important statement I believe is wrong, quoted inside another quote.
!2@3 important trusted but @- important !5 more important !- 2 important and !- normal
#1 heading #- !1 important !- @2 true @- >3 quote >-
+28
View File
@@ -0,0 +1,28 @@
import mode
pub type Line {
Line(List(Inline))
}
pub type Inline {
Text(String)
Push(Feeling)
//pop ignores the number
Pop(mode.Type)
}
pub type Feeling {
Hirachy(Int)
Truth(Int)
Marking(Int)
Quote(Int)
}
pub type State {
State(
hirachystate: List(Int),
truthstate: List(Int),
markingstate: List(Int),
quotestate: List(Int),
)
}
+75
View File
@@ -0,0 +1,75 @@
import mode
import gleam/io
import gleam/list
import definition
import gleam/json
import gleam/int
import parser
pub fn main() -> Nil {
let input = "ljiosfgdjiosdfgj
@2! A slightly important statement !1 I believe is wrong, !- quoted inside another quote.
!3 A slightly important statement I believe is wrong, quoted inside another quote.
#3!2@2 A slightly important statement I believe is wrong, quoted inside another quote.
!2@3 important trusted but @- important !5 more important !- 2 important and !- normal"
io.print(line_to_json(parser.handler(input)))
Nil
}
fn inline_to_json(inline: definition.Inline) -> json.Json {
case inline {
definition.Text(text) ->
json.object([
#("type", json.string("text")),
#("value", json.string(text)),
])
definition.Push(feeling) ->
case feeling {
definition.Marking(level) ->
json.object([
#("type", json.string("marking")),
#("value", json.int(level)),
])
definition.Hirachy(level) ->
json.object([
#("type", json.string("hirachy")),
#("value", json.int(level)),
])
definition.Truth(level) ->
json.object([
#("type", json.string("truth")),
#("value", json.int(level)),
])
definition.Quote(level) ->
json.object([
#("type", json.string("quote")),
#("value", json.int(level)),
])
}
definition.Pop(text) ->
json.object([
#("type", json.string("pop")),
#("value", case text {
mode.Hirachy -> json.string("Hirachy")
mode.Truth -> json.string("Truth")
mode.Marking -> json.string("Marking")
mode.Quote -> json.string("Quote")
}
),
])
}
}
fn line_to_json(line: definition.Line) -> String {
let definition.Line(inlines) = line
json.array(inlines, inline_to_json)
|> json.to_string}
pub fn testing(input: String) -> String {
line_to_json(parser.handler(input))
}
+6
View File
@@ -0,0 +1,6 @@
pub type Type {
Hirachy
Truth
Marking
Quote
}
+71
View File
@@ -0,0 +1,71 @@
import definition
import mode
import gleam/int
import gleam/result
import gleam/list
import gleam/dict
import gleam/string
pub fn handler(line: String) -> definition.Line {
let result = parse(line)
let result = echo list.map(result, whatsthat)
definition.Line(result)
}
fn whatsthat(mod: String) -> definition.Inline {
let modes = dict.from_list([
#("!", #(mode.Marking, definition.Marking)),
#("@", #(mode.Truth, definition.Truth)),
#(">", #(mode.Quote, definition.Quote)),
#("#", #(mode.Hirachy, definition.Hirachy)),
])
case string.pop_grapheme(mod) {
Ok(#(prefix, rest)) -> {
case dict.get(modes, prefix) {
Ok(#(m, make_definition)) -> {
case rest {
"-" -> definition.Pop(m)
_ -> {
let value = int.parse(rest) |> result.unwrap(1)
definition.Push(make_definition(value))
}
}
}
Error(_) -> definition.Text(mod)
}
}
Error(_) -> definition.Text(mod)
}
}fn parse(modificators: String) -> List(String) {
modificators
// 1. Target the tags + their trailing space, wrapping them in "|"
|> string.replace(each: "#", with: " |#")
|> string.replace(each: "@", with: " |@")
|> string.replace(each: "!", with: " |!")
|> string.replace(each: ">", with: " |>")
// 2. Split by the space-marker combination
|> string.split(on: " |")
// 3. For each block, split out the tag itself from any trailing words
|> list.flat_map(fn(block) {
case
string.starts_with(block, "#")
|| string.starts_with(block, "@")
|| string.starts_with(block, "!")
|| string.starts_with(block, ">")
{
True -> {
// Separate the tag from the text following it
case string.split_once(block, on: " ") {
Ok(#(tag, rest)) -> [tag, rest]
Error(_) -> [block]
}
}
False -> [block]
}
})
// 4. Clean up padding and drop empty elements
|> list.map(string.trim)
|> list.filter(fn(x) { x != "" })
}
+8
View File
@@ -0,0 +1,8 @@
<script type="module" src="main.js"></script>
<div id="text"></div>
<style>
div p {
display: inline;
margin: 0;
}
</style>
+425
View File
@@ -0,0 +1,425 @@
-module(filepath).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch]).
-export([join/2, split_unix/1, directory_name/1, is_absolute/1, split_windows/1, split/1, base_name/1, extension/1, strip_extension/1, expand/1]).
-if(?OTP_RELEASE >= 27).
-define(MODULEDOC(Str), -moduledoc(Str)).
-define(DOC(Str), -doc(Str)).
-else.
-define(MODULEDOC(Str), -compile([])).
-define(DOC(Str), -compile([])).
-endif.
?MODULEDOC(
" Work with file paths in Gleam!\n"
"\n"
" This library expects paths to be valid unicode. If you need to work with\n"
" non-unicode paths you will need to convert them to unicode before using\n"
" this library.\n"
).
-file("src/filepath.gleam", 49).
-spec relative(binary()) -> binary().
relative(Path) ->
case Path of
<<"/"/utf8, Path@1/binary>> ->
relative(Path@1);
_ ->
Path
end.
-file("src/filepath.gleam", 56).
-spec remove_trailing_slash(binary()) -> binary().
remove_trailing_slash(Path) ->
case gleam@string:ends_with(Path, <<"/"/utf8>>) of
true ->
gleam@string:drop_end(Path, 1);
false ->
Path
end.
-file("src/filepath.gleam", 35).
?DOC(
" Join two paths together.\n"
"\n"
" This function does not expand `..` or `.` segments, use the `expand`\n"
" function to do this.\n"
"\n"
" ## Examples\n"
"\n"
" ```gleam\n"
" join(\"/usr/local\", \"bin\")\n"
" // -> \"/usr/local/bin\"\n"
" ```\n"
).
-spec join(binary(), binary()) -> binary().
join(Left, Right) ->
_pipe@2 = case {Left, Right} of
{_, <<"/"/utf8>>} ->
Left;
{<<""/utf8>>, _} ->
relative(Right);
{<<"/"/utf8>>, <<"/"/utf8, _/binary>>} ->
Right;
{<<"/"/utf8>>, _} ->
<<Left/binary, Right/binary>>;
{_, _} ->
_pipe = remove_trailing_slash(Left),
_pipe@1 = gleam@string:append(_pipe, <<"/"/utf8>>),
gleam@string:append(_pipe@1, relative(Right))
end,
remove_trailing_slash(_pipe@2).
-file("src/filepath.gleam", 97).
?DOC(
" Split a path into its segments, using `/` as the path separator.\n"
"\n"
" Typically you would want to use `split` instead of this function, but if you\n"
" want non-Windows path behaviour on a Windows system then you can use this\n"
" function.\n"
"\n"
" ## Examples\n"
"\n"
" ```gleam\n"
" split(\"/usr/local/bin\", \"bin\")\n"
" // -> [\"/\", \"usr\", \"local\", \"bin\"]\n"
" ```\n"
).
-spec split_unix(binary()) -> list(binary()).
split_unix(Path) ->
_pipe = case gleam@string:split(Path, <<"/"/utf8>>) of
[<<""/utf8>>] ->
[];
[<<""/utf8>> | Rest] ->
[<<"/"/utf8>> | Rest];
Rest@1 ->
Rest@1
end,
gleam@list:filter(_pipe, fun(X) -> X /= <<""/utf8>> end).
-file("src/filepath.gleam", 267).
-spec get_directory_name(list(binary()), binary(), binary()) -> binary().
get_directory_name(Path, Acc, Segment) ->
case Path of
[<<"/"/utf8>> | Rest] ->
get_directory_name(
Rest,
<<Acc/binary, Segment/binary>>,
<<"/"/utf8>>
);
[First | Rest@1] ->
get_directory_name(Rest@1, Acc, <<Segment/binary, First/binary>>);
[] ->
Acc
end.
-file("src/filepath.gleam", 259).
?DOC(
" Get the directory name of a path, that is the path without the file name.\n"
"\n"
" ## Examples\n"
"\n"
" ```gleam\n"
" directory_name(\"/usr/local/bin\")\n"
" // -> \"/usr/local\"\n"
" ```\n"
).
-spec directory_name(binary()) -> binary().
directory_name(Path) ->
Path@1 = remove_trailing_slash(Path),
case Path@1 of
<<"/"/utf8, Rest/binary>> ->
get_directory_name(
gleam@string:to_graphemes(Rest),
<<"/"/utf8>>,
<<""/utf8>>
);
_ ->
get_directory_name(
gleam@string:to_graphemes(Path@1),
<<""/utf8>>,
<<""/utf8>>
)
end.
-file("src/filepath.gleam", 294).
?DOC(
" Check if a path is absolute.\n"
"\n"
" ## Examples\n"
"\n"
" ```gleam\n"
" is_absolute(\"/usr/local/bin\")\n"
" // -> True\n"
" ```\n"
"\n"
" ```gleam\n"
" is_absolute(\"usr/local/bin\")\n"
" // -> False\n"
" ```\n"
).
-spec is_absolute(binary()) -> boolean().
is_absolute(Path) ->
gleam@string:starts_with(Path, <<"/"/utf8>>).
-file("src/filepath.gleam", 339).
-spec expand_segments(list(binary()), list(binary())) -> {ok, binary()} |
{error, nil}.
expand_segments(Path, Base) ->
case {Base, Path} of
{[<<""/utf8>>], [<<".."/utf8>> | _]} ->
{error, nil};
{[], [<<".."/utf8>> | _]} ->
{error, nil};
{[_ | Base@1], [<<".."/utf8>> | Path@1]} ->
expand_segments(Path@1, Base@1);
{_, [<<"."/utf8>> | Path@2]} ->
expand_segments(Path@2, Base);
{_, [S | Path@3]} ->
expand_segments(Path@3, [S | Base]);
{_, []} ->
{ok, gleam@string:join(lists:reverse(Base), <<"/"/utf8>>)}
end.
-file("src/filepath.gleam", 364).
-spec root_slash_to_empty(list(binary())) -> list(binary()).
root_slash_to_empty(Segments) ->
case Segments of
[<<"/"/utf8>> | Rest] ->
[<<""/utf8>> | Rest];
_ ->
Segments
end.
-file("src/filepath.gleam", 153).
-spec pop_windows_drive_specifier(binary()) -> {gleam@option:option(binary()),
binary()}.
pop_windows_drive_specifier(Path) ->
Start = gleam@string:slice(Path, 0, 3),
Codepoints = gleam@string:to_utf_codepoints(Start),
case gleam@list:map(Codepoints, fun gleam@string:utf_codepoint_to_int/1) of
[Drive, Colon, Slash] when (((Slash =:= 47) orelse (Slash =:= 92)) andalso (Colon =:= 58)) andalso (((Drive >= 65) andalso (Drive =< 90)) orelse ((Drive >= 97) andalso (Drive =< 122))) ->
Drive_letter = gleam@string:slice(Path, 0, 1),
Drive@1 = <<(gleam@string:lowercase(Drive_letter))/binary,
":/"/utf8>>,
Path@1 = gleam@string:drop_start(Path, 3),
{{some, Drive@1}, Path@1};
_ ->
{none, Path}
end.
-file("src/filepath.gleam", 120).
?DOC(
" Split a path into its segments, using `/` and `\\` as the path separators. If\n"
" there is a drive letter at the start of the path then it is lowercased.\n"
"\n"
" Typically you would want to use `split` instead of this function, but if you\n"
" want Windows path behaviour on a non-Windows system then you can use this\n"
" function.\n"
"\n"
" ## Examples\n"
"\n"
" ```gleam\n"
" split(\"/usr/local/bin\", \"bin\")\n"
" // -> [\"/\", \"usr\", \"local\", \"bin\"]\n"
" ```\n"
).
-spec split_windows(binary()) -> list(binary()).
split_windows(Path) ->
{Drive, Path@1} = pop_windows_drive_specifier(Path),
Segments = begin
_pipe = gleam@string:split(Path@1, <<"/"/utf8>>),
gleam@list:flat_map(
_pipe,
fun(_capture) -> gleam@string:split(_capture, <<"\\"/utf8>>) end
)
end,
Segments@1 = case Drive of
{some, Drive@1} ->
[Drive@1 | Segments];
none ->
Segments
end,
case Segments@1 of
[<<""/utf8>>] ->
[];
[<<""/utf8>> | Rest] ->
[<<"/"/utf8>> | Rest];
Rest@1 ->
Rest@1
end.
-file("src/filepath.gleam", 77).
?DOC(
" Split a path into its segments.\n"
"\n"
" When running on Windows both `/` and `\\` are treated as path separators, and \n"
" if the path starts with a drive letter then the drive letter then it is\n"
" lowercased.\n"
"\n"
" ## Examples\n"
"\n"
" ```gleam\n"
" split(\"/usr/local/bin\", \"bin\")\n"
" // -> [\"/\", \"usr\", \"local\", \"bin\"]\n"
" ```\n"
).
-spec split(binary()) -> list(binary()).
split(Path) ->
case filepath_ffi:is_windows() of
true ->
split_windows(Path);
false ->
split_unix(Path)
end.
-file("src/filepath.gleam", 240).
?DOC(
" Get the base name of a path, that is the name of the file without the\n"
" containing directory.\n"
"\n"
" ## Examples\n"
"\n"
" ```gleam\n"
" base_name(\"/usr/local/bin\")\n"
" // -> \"bin\"\n"
" ```\n"
).
-spec base_name(binary()) -> binary().
base_name(Path) ->
gleam@bool:guard(Path =:= <<"/"/utf8>>, <<""/utf8>>, fun() -> _pipe = Path,
_pipe@1 = split(_pipe),
_pipe@2 = gleam@list:last(_pipe@1),
gleam@result:unwrap(_pipe@2, <<""/utf8>>) end).
-file("src/filepath.gleam", 190).
?DOC(
" Get the file extension of a path.\n"
"\n"
" ## Examples\n"
"\n"
" ```gleam\n"
" extension(\"src/main.gleam\")\n"
" // -> Ok(\"gleam\")\n"
" ```\n"
"\n"
" ```gleam\n"
" extension(\"package.tar.gz\")\n"
" // -> Ok(\"gz\")\n"
" ```\n"
).
-spec extension(binary()) -> {ok, binary()} | {error, nil}.
extension(Path) ->
File = base_name(Path),
case gleam@string:split(File, <<"."/utf8>>) of
[<<""/utf8>>, _] ->
{error, nil};
[_, Extension] ->
{ok, Extension};
[_ | Rest] ->
gleam@list:last(Rest);
_ ->
{error, nil}
end.
-file("src/filepath.gleam", 219).
?DOC(
" Remove the extension from a file, if it has any.\n"
" \n"
" ## Examples\n"
" \n"
" ```gleam\n"
" strip_extension(\"src/main.gleam\")\n"
" // -> \"src/main\"\n"
" ```\n"
" \n"
" ```gleam\n"
" strip_extension(\"package.tar.gz\")\n"
" // -> \"package.tar\"\n"
" ```\n"
" \n"
" ```gleam\n"
" strip_extension(\"src/gleam\")\n"
" // -> \"src/gleam\"\n"
" ```\n"
).
-spec strip_extension(binary()) -> binary().
strip_extension(Path) ->
case extension(Path) of
{ok, Extension} ->
gleam@string:drop_end(Path, gleam@string:length(Extension) + 1);
{error, nil} ->
Path
end.
-file("src/filepath.gleam", 324).
?DOC(
" Expand `..` and `.` segments in a path.\n"
"\n"
" If the path has a `..` segment that would go up past the root of the path\n"
" then an error is returned. This may be useful to example to ensure that a\n"
" path specified by a user does not go outside of a directory.\n"
"\n"
" If the path is absolute then the result will always be absolute.\n"
"\n"
" ## Examples\n"
"\n"
" ```gleam\n"
" expand(\"/usr/local/../bin\")\n"
" // -> Ok(\"/usr/bin\")\n"
" ```\n"
"\n"
" ```gleam\n"
" expand(\"/tmp/../..\")\n"
" // -> Error(Nil)\n"
" ```\n"
"\n"
" ```gleam\n"
" expand(\"src/../..\")\n"
" // -> Error(\"..\")\n"
" ```\n"
).
-spec expand(binary()) -> {ok, binary()} | {error, nil}.
expand(Path) ->
Is_absolute = is_absolute(Path),
Result = begin
_pipe = Path,
_pipe@1 = split(_pipe),
_pipe@2 = root_slash_to_empty(_pipe@1),
_pipe@3 = expand_segments(_pipe@2, []),
gleam@result:map(_pipe@3, fun remove_trailing_slash/1)
end,
case Is_absolute andalso (Result =:= {ok, <<""/utf8>>}) of
true ->
{ok, <<"/"/utf8>>};
false ->
Result
end.
+527
View File
@@ -0,0 +1,527 @@
import * as $bool from "../gleam_stdlib/gleam/bool.mjs";
import * as $list from "../gleam_stdlib/gleam/list.mjs";
import * as $option from "../gleam_stdlib/gleam/option.mjs";
import { None, Some } from "../gleam_stdlib/gleam/option.mjs";
import * as $result from "../gleam_stdlib/gleam/result.mjs";
import * as $string from "../gleam_stdlib/gleam/string.mjs";
import { is_windows } from "./filepath_ffi.mjs";
import { Ok, Error, toList, Empty as $Empty, prepend as listPrepend, isEqual } from "./gleam.mjs";
const codepoint_z_up = 122;
const codepoint_a_up = 97;
const codepoint_z = 90;
const codepoint_a = 65;
const codepoint_colon = 58;
const codepoint_backslash = 92;
const codepoint_slash = 47;
function remove_trailing_slash(path) {
let $ = $string.ends_with(path, "/");
if ($) {
return $string.drop_end(path, 1);
} else {
return path;
}
}
function relative(loop$path) {
while (true) {
let path = loop$path;
if (path.charCodeAt(0) === 47) {
let path$1 = path.slice(1);
loop$path = path$1;
} else {
return path;
}
}
}
/**
* Join two paths together.
*
* This function does not expand `..` or `.` segments, use the `expand`
* function to do this.
*
* ## Examples
*
* ```gleam
* join("/usr/local", "bin")
* // -> "/usr/local/bin"
* ```
*/
export function join(left, right) {
let _block;
if (right === "/") {
_block = left;
} else if (right.charCodeAt(0) === 47) {
if (left === "") {
_block = relative(right);
} else if (left === "/") {
_block = right;
} else {
let _pipe = remove_trailing_slash(left);
let _pipe$1 = $string.append(_pipe, "/");
_block = $string.append(_pipe$1, relative(right));
}
} else if (left === "") {
_block = relative(right);
} else if (left === "/") {
_block = left + right;
} else {
let _pipe = remove_trailing_slash(left);
let _pipe$1 = $string.append(_pipe, "/");
_block = $string.append(_pipe$1, relative(right));
}
let _pipe = _block;
return remove_trailing_slash(_pipe);
}
/**
* Split a path into its segments, using `/` as the path separator.
*
* Typically you would want to use `split` instead of this function, but if you
* want non-Windows path behaviour on a Windows system then you can use this
* function.
*
* ## Examples
*
* ```gleam
* split("/usr/local/bin", "bin")
* // -> ["/", "usr", "local", "bin"]
* ```
*/
export function split_unix(path) {
let _block;
let $ = $string.split(path, "/");
if ($ instanceof $Empty) {
_block = $;
} else {
let $1 = $.tail;
if ($1 instanceof $Empty) {
let $2 = $.head;
if ($2 === "") {
_block = toList([]);
} else {
_block = $;
}
} else {
let $2 = $.head;
if ($2 === "") {
let rest = $1;
_block = listPrepend("/", rest);
} else {
_block = $;
}
}
}
let _pipe = _block;
return $list.filter(_pipe, (x) => { return x !== ""; });
}
function pop_windows_drive_specifier(path) {
let start = $string.slice(path, 0, 3);
let codepoints = $string.to_utf_codepoints(start);
let $ = $list.map(codepoints, $string.utf_codepoint_to_int);
if ($ instanceof $Empty) {
return [new None(), path];
} else {
let $1 = $.tail;
if ($1 instanceof $Empty) {
return [new None(), path];
} else {
let $2 = $1.tail;
if ($2 instanceof $Empty) {
return [new None(), path];
} else {
let $3 = $2.tail;
if ($3 instanceof $Empty) {
let drive = $.head;
let colon = $1.head;
let slash = $2.head;
if (
(((slash === 47) || (slash === 92)) && (colon === 58)) && (((drive >= 65) && (drive <= 90)) || ((drive >= 97) && (drive <= 122)))
) {
let drive_letter = $string.slice(path, 0, 1);
let drive$1 = $string.lowercase(drive_letter) + ":/";
let path$1 = $string.drop_start(path, 3);
return [new Some(drive$1), path$1];
} else {
return [new None(), path];
}
} else {
return [new None(), path];
}
}
}
}
}
/**
* Split a path into its segments, using `/` and `\` as the path separators. If
* there is a drive letter at the start of the path then it is lowercased.
*
* Typically you would want to use `split` instead of this function, but if you
* want Windows path behaviour on a non-Windows system then you can use this
* function.
*
* ## Examples
*
* ```gleam
* split("/usr/local/bin", "bin")
* // -> ["/", "usr", "local", "bin"]
* ```
*/
export function split_windows(path) {
let $ = pop_windows_drive_specifier(path);
let drive = $[0];
let path$1 = $[1];
let _block;
let _pipe = $string.split(path$1, "/");
_block = $list.flat_map(
_pipe,
(_capture) => { return $string.split(_capture, "\\"); },
);
let segments = _block;
let _block$1;
if (drive instanceof Some) {
let drive$1 = drive[0];
_block$1 = listPrepend(drive$1, segments);
} else {
_block$1 = segments;
}
let segments$1 = _block$1;
if (segments$1 instanceof $Empty) {
return segments$1;
} else {
let $1 = segments$1.tail;
if ($1 instanceof $Empty) {
let $2 = segments$1.head;
if ($2 === "") {
return toList([]);
} else {
return segments$1;
}
} else {
let $2 = segments$1.head;
if ($2 === "") {
let rest = $1;
return listPrepend("/", rest);
} else {
return segments$1;
}
}
}
}
/**
* Split a path into its segments.
*
* When running on Windows both `/` and `\` are treated as path separators, and
* if the path starts with a drive letter then the drive letter then it is
* lowercased.
*
* ## Examples
*
* ```gleam
* split("/usr/local/bin", "bin")
* // -> ["/", "usr", "local", "bin"]
* ```
*/
export function split(path) {
let $ = is_windows();
if ($) {
return split_windows(path);
} else {
return split_unix(path);
}
}
/**
* Get the base name of a path, that is the name of the file without the
* containing directory.
*
* ## Examples
*
* ```gleam
* base_name("/usr/local/bin")
* // -> "bin"
* ```
*/
export function base_name(path) {
return $bool.guard(
path === "/",
"",
() => {
let _pipe = path;
let _pipe$1 = split(_pipe);
let _pipe$2 = $list.last(_pipe$1);
return $result.unwrap(_pipe$2, "");
},
);
}
/**
* Get the file extension of a path.
*
* ## Examples
*
* ```gleam
* extension("src/main.gleam")
* // -> Ok("gleam")
* ```
*
* ```gleam
* extension("package.tar.gz")
* // -> Ok("gz")
* ```
*/
export function extension(path) {
let file = base_name(path);
let $ = $string.split(file, ".");
if ($ instanceof $Empty) {
return new Error(undefined);
} else {
let $1 = $.tail;
if ($1 instanceof $Empty) {
let rest = $1;
return $list.last(rest);
} else {
let $2 = $1.tail;
if ($2 instanceof $Empty) {
let $3 = $.head;
if ($3 === "") {
return new Error(undefined);
} else {
let extension$1 = $1.head;
return new Ok(extension$1);
}
} else {
let rest = $1;
return $list.last(rest);
}
}
}
}
/**
* Remove the extension from a file, if it has any.
*
* ## Examples
*
* ```gleam
* strip_extension("src/main.gleam")
* // -> "src/main"
* ```
*
* ```gleam
* strip_extension("package.tar.gz")
* // -> "package.tar"
* ```
*
* ```gleam
* strip_extension("src/gleam")
* // -> "src/gleam"
* ```
*/
export function strip_extension(path) {
let $ = extension(path);
if ($ instanceof Ok) {
let extension$1 = $[0];
return $string.drop_end(path, $string.length(extension$1) + 1);
} else {
return path;
}
}
function get_directory_name(loop$path, loop$acc, loop$segment) {
while (true) {
let path = loop$path;
let acc = loop$acc;
let segment = loop$segment;
if (path instanceof $Empty) {
return acc;
} else {
let $ = path.head;
if ($ === "/") {
let rest = path.tail;
loop$path = rest;
loop$acc = acc + segment;
loop$segment = "/";
} else {
let first = $;
let rest = path.tail;
loop$path = rest;
loop$acc = acc;
loop$segment = segment + first;
}
}
}
}
/**
* Get the directory name of a path, that is the path without the file name.
*
* ## Examples
*
* ```gleam
* directory_name("/usr/local/bin")
* // -> "/usr/local"
* ```
*/
export function directory_name(path) {
let path$1 = remove_trailing_slash(path);
if (path$1.charCodeAt(0) === 47) {
let rest = path$1.slice(1);
return get_directory_name($string.to_graphemes(rest), "/", "");
} else {
return get_directory_name($string.to_graphemes(path$1), "", "");
}
}
/**
* Check if a path is absolute.
*
* ## Examples
*
* ```gleam
* is_absolute("/usr/local/bin")
* // -> True
* ```
*
* ```gleam
* is_absolute("usr/local/bin")
* // -> False
* ```
*/
export function is_absolute(path) {
return $string.starts_with(path, "/");
}
function expand_segments(loop$path, loop$base) {
while (true) {
let path = loop$path;
let base = loop$base;
if (path instanceof $Empty) {
return new Ok($string.join($list.reverse(base), "/"));
} else if (base instanceof $Empty) {
let $ = path.head;
if ($ === "..") {
return new Error(undefined);
} else if ($ === ".") {
let path$1 = path.tail;
loop$path = path$1;
loop$base = base;
} else {
let s = $;
let path$1 = path.tail;
loop$path = path$1;
loop$base = listPrepend(s, base);
}
} else {
let $ = base.tail;
if ($ instanceof $Empty) {
let $1 = path.head;
if ($1 === "..") {
let $2 = base.head;
if ($2 === "") {
return new Error(undefined);
} else {
let path$1 = path.tail;
let base$1 = $;
loop$path = path$1;
loop$base = base$1;
}
} else if ($1 === ".") {
let path$1 = path.tail;
loop$path = path$1;
loop$base = base;
} else {
let s = $1;
let path$1 = path.tail;
loop$path = path$1;
loop$base = listPrepend(s, base);
}
} else {
let $1 = path.head;
if ($1 === "..") {
let path$1 = path.tail;
let base$1 = $;
loop$path = path$1;
loop$base = base$1;
} else if ($1 === ".") {
let path$1 = path.tail;
loop$path = path$1;
loop$base = base;
} else {
let s = $1;
let path$1 = path.tail;
loop$path = path$1;
loop$base = listPrepend(s, base);
}
}
}
}
}
function root_slash_to_empty(segments) {
if (segments instanceof $Empty) {
return segments;
} else {
let $ = segments.head;
if ($ === "/") {
let rest = segments.tail;
return listPrepend("", rest);
} else {
return segments;
}
}
}
/**
* Expand `..` and `.` segments in a path.
*
* If the path has a `..` segment that would go up past the root of the path
* then an error is returned. This may be useful to example to ensure that a
* path specified by a user does not go outside of a directory.
*
* If the path is absolute then the result will always be absolute.
*
* ## Examples
*
* ```gleam
* expand("/usr/local/../bin")
* // -> Ok("/usr/bin")
* ```
*
* ```gleam
* expand("/tmp/../..")
* // -> Error(Nil)
* ```
*
* ```gleam
* expand("src/../..")
* // -> Error("..")
* ```
*/
export function expand(path) {
let is_absolute$1 = is_absolute(path);
let _block;
let _pipe = path;
let _pipe$1 = split(_pipe);
let _pipe$2 = root_slash_to_empty(_pipe$1);
let _pipe$3 = expand_segments(_pipe$2, toList([]));
_block = $result.map(_pipe$3, remove_trailing_slash);
let result = _block;
let $ = is_absolute$1 && (isEqual(result, new Ok("")));
if ($) {
return new Ok("/");
} else {
return result;
}
}
+9
View File
@@ -0,0 +1,9 @@
-module(filepath_ffi).
-export([is_windows/0]).
is_windows() ->
case os:type() of
{win32, _} -> true;
_ -> false
end.
+6
View File
@@ -0,0 +1,6 @@
export function is_windows() {
return (
globalThis?.process?.platform === "win32" ||
globalThis?.Deno?.build?.os === "windows"
);
}
+1
View File
@@ -0,0 +1 @@
export * from "../prelude.mjs";
+1
View File
@@ -0,0 +1 @@
1.17.0 0
+1
View File
@@ -0,0 +1 @@
export * from "../prelude.mjs";
+325
View File
@@ -0,0 +1,325 @@
import * as $bit_array from "../../gleam_stdlib/gleam/bit_array.mjs";
import * as $dict from "../../gleam_stdlib/gleam/dict.mjs";
import * as $dynamic from "../../gleam_stdlib/gleam/dynamic.mjs";
import * as $decode from "../../gleam_stdlib/gleam/dynamic/decode.mjs";
import * as $list from "../../gleam_stdlib/gleam/list.mjs";
import * as $option from "../../gleam_stdlib/gleam/option.mjs";
import { None, Some } from "../../gleam_stdlib/gleam/option.mjs";
import * as $result from "../../gleam_stdlib/gleam/result.mjs";
import * as $string_tree from "../../gleam_stdlib/gleam/string_tree.mjs";
import { Ok, Error, toList, prepend as listPrepend, CustomType as $CustomType } from "../gleam.mjs";
import {
decode as decode_string,
json_to_string as do_to_string,
json_to_string as to_string_tree,
identity as do_string,
identity as do_bool,
identity as do_int,
identity as do_float,
do_null,
object as do_object,
array as do_preprocessed_array,
} from "../gleam_json_ffi.mjs";
export { to_string_tree };
export class UnexpectedEndOfInput extends $CustomType {}
export const DecodeError$UnexpectedEndOfInput = () =>
new UnexpectedEndOfInput();
export const DecodeError$isUnexpectedEndOfInput = (value) =>
value instanceof UnexpectedEndOfInput;
export class UnexpectedByte extends $CustomType {
constructor($0) {
super();
this[0] = $0;
}
}
export const DecodeError$UnexpectedByte = ($0) => new UnexpectedByte($0);
export const DecodeError$isUnexpectedByte = (value) =>
value instanceof UnexpectedByte;
export const DecodeError$UnexpectedByte$0 = (value) => value[0];
export class UnexpectedSequence extends $CustomType {
constructor($0) {
super();
this[0] = $0;
}
}
export const DecodeError$UnexpectedSequence = ($0) =>
new UnexpectedSequence($0);
export const DecodeError$isUnexpectedSequence = (value) =>
value instanceof UnexpectedSequence;
export const DecodeError$UnexpectedSequence$0 = (value) => value[0];
export class UnableToDecode extends $CustomType {
constructor($0) {
super();
this[0] = $0;
}
}
export const DecodeError$UnableToDecode = ($0) => new UnableToDecode($0);
export const DecodeError$isUnableToDecode = (value) =>
value instanceof UnableToDecode;
export const DecodeError$UnableToDecode$0 = (value) => value[0];
function do_parse(json, decoder) {
return $result.try$(
decode_string(json),
(dynamic_value) => {
let _pipe = $decode.run(dynamic_value, decoder);
return $result.map_error(
_pipe,
(var0) => { return new UnableToDecode(var0); },
);
},
);
}
/**
* Decode a JSON string into dynamically typed data which can be decoded into
* typed data with the `gleam/dynamic` module.
*
* ## Examples
*
* ```gleam
* > parse("[1,2,3]", decode.list(of: decode.int))
* Ok([1, 2, 3])
* ```
*
* ```gleam
* > parse("[", decode.list(of: decode.int))
* Error(UnexpectedEndOfInput)
* ```
*
* ```gleam
* > parse("1", decode.string)
* Error(UnableToDecode([decode.DecodeError("String", "Int", [])]))
* ```
*/
export function parse(json, decoder) {
return do_parse(json, decoder);
}
function decode_to_dynamic(json) {
let $ = $bit_array.to_string(json);
if ($ instanceof Ok) {
let string$1 = $[0];
return decode_string(string$1);
} else {
return new Error(new UnexpectedByte(""));
}
}
/**
* Decode a JSON bit string into dynamically typed data which can be decoded
* into typed data with the `gleam/dynamic` module.
*
* ## Examples
*
* ```gleam
* > parse_bits(<<"[1,2,3]">>, decode.list(of: decode.int))
* Ok([1, 2, 3])
* ```
*
* ```gleam
* > parse_bits(<<"[">>, decode.list(of: decode.int))
* Error(UnexpectedEndOfInput)
* ```
*
* ```gleam
* > parse_bits(<<"1">>, decode.string)
* Error(UnableToDecode([decode.DecodeError("String", "Int", [])])),
* ```
*/
export function parse_bits(json, decoder) {
return $result.try$(
decode_to_dynamic(json),
(dynamic_value) => {
let _pipe = $decode.run(dynamic_value, decoder);
return $result.map_error(
_pipe,
(var0) => { return new UnableToDecode(var0); },
);
},
);
}
/**
* Convert a JSON value into a string.
*
* Where possible prefer the `to_string_tree` function as it is faster than
* this function, and BEAM VM IO is optimised for sending `StringTree` data.
*
* ## Examples
*
* ```gleam
* > to_string(array([1, 2, 3], of: int))
* "[1,2,3]"
* ```
*/
export function to_string(json) {
return do_to_string(json);
}
/**
* Encode a string into JSON, using normal JSON escaping.
*
* ## Examples
*
* ```gleam
* > to_string(string("Hello!"))
* "\"Hello!\""
* ```
*/
export function string(input) {
return do_string(input);
}
/**
* Encode a bool into JSON.
*
* ## Examples
*
* ```gleam
* > to_string(bool(False))
* "false"
* ```
*/
export function bool(input) {
return do_bool(input);
}
/**
* Encode an int into JSON.
*
* ## Examples
*
* ```gleam
* > to_string(int(50))
* "50"
* ```
*/
export function int(input) {
return do_int(input);
}
/**
* Encode a float into JSON.
*
* ## Examples
*
* ```gleam
* > to_string(float(4.7))
* "4.7"
* ```
*/
export function float(input) {
return do_float(input);
}
/**
* The JSON value null.
*
* ## Examples
*
* ```gleam
* > to_string(null())
* "null"
* ```
*/
export function null$() {
return do_null();
}
/**
* Encode an optional value into JSON, using null if it is the `None` variant.
*
* ## Examples
*
* ```gleam
* > to_string(nullable(Some(50), of: int))
* "50"
* ```
*
* ```gleam
* > to_string(nullable(None, of: int))
* "null"
* ```
*/
export function nullable(input, inner_type) {
if (input instanceof Some) {
let value = input[0];
return inner_type(value);
} else {
return null$();
}
}
/**
* Encode a list of key-value pairs into a JSON object.
*
* ## Examples
*
* ```gleam
* > to_string(object([
* #("game", string("Pac-Man")),
* #("score", int(3333360)),
* ]))
* "{\"game\":\"Pac-Mac\",\"score\":3333360}"
* ```
*/
export function object(entries) {
return do_object(entries);
}
/**
* Encode a list of JSON values into a JSON array.
*
* ## Examples
*
* ```gleam
* > to_string(preprocessed_array([int(1), float(2.0), string("3")]))
* "[1, 2.0, \"3\"]"
* ```
*/
export function preprocessed_array(from) {
return do_preprocessed_array(from);
}
/**
* Encode a list into a JSON array.
*
* ## Examples
*
* ```gleam
* > to_string(array([1, 2, 3], of: int))
* "[1, 2, 3]"
* ```
*/
export function array(entries, inner_type) {
let _pipe = entries;
let _pipe$1 = $list.map(_pipe, inner_type);
return preprocessed_array(_pipe$1);
}
/**
* Encode a Dict into a JSON object using the supplied functions to encode
* the keys and the values respectively.
*
* ## Examples
*
* ```gleam
* > to_string(dict(dict.from_list([ #(3, 3.0), #(4, 4.0)]), int.to_string, float)
* "{\"3\": 3.0, \"4\": 4.0}"
* ```
*/
export function dict(dict, keys, values) {
return object(
$dict.fold(
dict,
toList([]),
(acc, k, v) => { return listPrepend([keys(k), values(v)], acc); },
),
);
}
+304
View File
@@ -0,0 +1,304 @@
-module(gleam@json).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]).
-define(FILEPATH, "src/gleam/json.gleam").
-export([parse_bits/2, parse/2, to_string/1, to_string_tree/1, string/1, bool/1, int/1, float/1, null/0, nullable/2, object/1, preprocessed_array/1, array/2, dict/3]).
-export_type([json/0, decode_error/0]).
-if(?OTP_RELEASE >= 27).
-define(MODULEDOC(Str), -moduledoc(Str)).
-define(DOC(Str), -doc(Str)).
-else.
-define(MODULEDOC(Str), -compile([])).
-define(DOC(Str), -compile([])).
-endif.
-type json() :: any().
-type decode_error() :: unexpected_end_of_input |
{unexpected_byte, binary()} |
{unexpected_sequence, binary()} |
{unable_to_decode, list(gleam@dynamic@decode:decode_error())}.
-file("src/gleam/json.gleam", 88).
?DOC(
" Decode a JSON bit string into dynamically typed data which can be decoded\n"
" into typed data with the `gleam/dynamic` module.\n"
"\n"
" ## Examples\n"
"\n"
" ```gleam\n"
" > parse_bits(<<\"[1,2,3]\">>, decode.list(of: decode.int))\n"
" Ok([1, 2, 3])\n"
" ```\n"
"\n"
" ```gleam\n"
" > parse_bits(<<\"[\">>, decode.list(of: decode.int))\n"
" Error(UnexpectedEndOfInput)\n"
" ```\n"
"\n"
" ```gleam\n"
" > parse_bits(<<\"1\">>, decode.string)\n"
" Error(UnableToDecode([decode.DecodeError(\"String\", \"Int\", [])])),\n"
" ```\n"
).
-spec parse_bits(bitstring(), gleam@dynamic@decode:decoder(DNO)) -> {ok, DNO} |
{error, decode_error()}.
parse_bits(Json, Decoder) ->
gleam@result:'try'(
gleam_json_ffi:decode(Json),
fun(Dynamic_value) ->
_pipe = gleam@dynamic@decode:run(Dynamic_value, Decoder),
gleam@result:map_error(
_pipe,
fun(Field@0) -> {unable_to_decode, Field@0} end
)
end
).
-file("src/gleam/json.gleam", 47).
-spec do_parse(binary(), gleam@dynamic@decode:decoder(DNI)) -> {ok, DNI} |
{error, decode_error()}.
do_parse(Json, Decoder) ->
Bits = gleam_stdlib:identity(Json),
parse_bits(Bits, Decoder).
-file("src/gleam/json.gleam", 39).
?DOC(
" Decode a JSON string into dynamically typed data which can be decoded into\n"
" typed data with the `gleam/dynamic` module.\n"
"\n"
" ## Examples\n"
"\n"
" ```gleam\n"
" > parse(\"[1,2,3]\", decode.list(of: decode.int))\n"
" Ok([1, 2, 3])\n"
" ```\n"
"\n"
" ```gleam\n"
" > parse(\"[\", decode.list(of: decode.int))\n"
" Error(UnexpectedEndOfInput)\n"
" ```\n"
"\n"
" ```gleam\n"
" > parse(\"1\", decode.string)\n"
" Error(UnableToDecode([decode.DecodeError(\"String\", \"Int\", [])]))\n"
" ```\n"
).
-spec parse(binary(), gleam@dynamic@decode:decoder(DNE)) -> {ok, DNE} |
{error, decode_error()}.
parse(Json, Decoder) ->
do_parse(Json, Decoder).
-file("src/gleam/json.gleam", 117).
?DOC(
" Convert a JSON value into a string.\n"
"\n"
" Where possible prefer the `to_string_tree` function as it is faster than\n"
" this function, and BEAM VM IO is optimised for sending `StringTree` data.\n"
"\n"
" ## Examples\n"
"\n"
" ```gleam\n"
" > to_string(array([1, 2, 3], of: int))\n"
" \"[1,2,3]\"\n"
" ```\n"
).
-spec to_string(json()) -> binary().
to_string(Json) ->
gleam_json_ffi:json_to_string(Json).
-file("src/gleam/json.gleam", 140).
?DOC(
" Convert a JSON value into a string tree.\n"
"\n"
" Where possible prefer this function to the `to_string` function as it is\n"
" slower than this function, and BEAM VM IO is optimised for sending\n"
" `StringTree` data.\n"
"\n"
" ## Examples\n"
"\n"
" ```gleam\n"
" > to_string_tree(array([1, 2, 3], of: int))\n"
" string_tree.from_string(\"[1,2,3]\")\n"
" ```\n"
).
-spec to_string_tree(json()) -> gleam@string_tree:string_tree().
to_string_tree(Json) ->
gleam_json_ffi:json_to_iodata(Json).
-file("src/gleam/json.gleam", 151).
?DOC(
" Encode a string into JSON, using normal JSON escaping.\n"
"\n"
" ## Examples\n"
"\n"
" ```gleam\n"
" > to_string(string(\"Hello!\"))\n"
" \"\\\"Hello!\\\"\"\n"
" ```\n"
).
-spec string(binary()) -> json().
string(Input) ->
gleam_json_ffi:string(Input).
-file("src/gleam/json.gleam", 168).
?DOC(
" Encode a bool into JSON.\n"
"\n"
" ## Examples\n"
"\n"
" ```gleam\n"
" > to_string(bool(False))\n"
" \"false\"\n"
" ```\n"
).
-spec bool(boolean()) -> json().
bool(Input) ->
gleam_json_ffi:bool(Input).
-file("src/gleam/json.gleam", 185).
?DOC(
" Encode an int into JSON.\n"
"\n"
" ## Examples\n"
"\n"
" ```gleam\n"
" > to_string(int(50))\n"
" \"50\"\n"
" ```\n"
).
-spec int(integer()) -> json().
int(Input) ->
gleam_json_ffi:int(Input).
-file("src/gleam/json.gleam", 202).
?DOC(
" Encode a float into JSON.\n"
"\n"
" ## Examples\n"
"\n"
" ```gleam\n"
" > to_string(float(4.7))\n"
" \"4.7\"\n"
" ```\n"
).
-spec float(float()) -> json().
float(Input) ->
gleam_json_ffi:float(Input).
-file("src/gleam/json.gleam", 219).
?DOC(
" The JSON value null.\n"
"\n"
" ## Examples\n"
"\n"
" ```gleam\n"
" > to_string(null())\n"
" \"null\"\n"
" ```\n"
).
-spec null() -> json().
null() ->
gleam_json_ffi:null().
-file("src/gleam/json.gleam", 241).
?DOC(
" Encode an optional value into JSON, using null if it is the `None` variant.\n"
"\n"
" ## Examples\n"
"\n"
" ```gleam\n"
" > to_string(nullable(Some(50), of: int))\n"
" \"50\"\n"
" ```\n"
"\n"
" ```gleam\n"
" > to_string(nullable(None, of: int))\n"
" \"null\"\n"
" ```\n"
).
-spec nullable(gleam@option:option(DNU), fun((DNU) -> json())) -> json().
nullable(Input, Inner_type) ->
case Input of
{some, Value} ->
Inner_type(Value);
none ->
null()
end.
-file("src/gleam/json.gleam", 260).
?DOC(
" Encode a list of key-value pairs into a JSON object.\n"
"\n"
" ## Examples\n"
"\n"
" ```gleam\n"
" > to_string(object([\n"
" #(\"game\", string(\"Pac-Man\")),\n"
" #(\"score\", int(3333360)),\n"
" ]))\n"
" \"{\\\"game\\\":\\\"Pac-Mac\\\",\\\"score\\\":3333360}\"\n"
" ```\n"
).
-spec object(list({binary(), json()})) -> json().
object(Entries) ->
gleam_json_ffi:object(Entries).
-file("src/gleam/json.gleam", 292).
?DOC(
" Encode a list of JSON values into a JSON array.\n"
"\n"
" ## Examples\n"
"\n"
" ```gleam\n"
" > to_string(preprocessed_array([int(1), float(2.0), string(\"3\")]))\n"
" \"[1, 2.0, \\\"3\\\"]\"\n"
" ```\n"
).
-spec preprocessed_array(list(json())) -> json().
preprocessed_array(From) ->
gleam_json_ffi:array(From).
-file("src/gleam/json.gleam", 277).
?DOC(
" Encode a list into a JSON array.\n"
"\n"
" ## Examples\n"
"\n"
" ```gleam\n"
" > to_string(array([1, 2, 3], of: int))\n"
" \"[1, 2, 3]\"\n"
" ```\n"
).
-spec array(list(DNY), fun((DNY) -> json())) -> json().
array(Entries, Inner_type) ->
_pipe = Entries,
_pipe@1 = gleam@list:map(_pipe, Inner_type),
preprocessed_array(_pipe@1).
-file("src/gleam/json.gleam", 310).
?DOC(
" Encode a Dict into a JSON object using the supplied functions to encode\n"
" the keys and the values respectively.\n"
"\n"
" ## Examples\n"
"\n"
" ```gleam\n"
" > to_string(dict(dict.from_list([ #(3, 3.0), #(4, 4.0)]), int.to_string, float)\n"
" \"{\\\"3\\\": 3.0, \\\"4\\\": 4.0}\"\n"
" ```\n"
).
-spec dict(
gleam@dict:dict(DOC, DOD),
fun((DOC) -> binary()),
fun((DOD) -> json())
) -> json().
dict(Dict, Keys, Values) ->
object(
gleam@dict:fold(
Dict,
[],
fun(Acc, K, V) -> [{Keys(K), Values(V)} | Acc] end
)
).
@@ -0,0 +1,66 @@
-module(gleam_json_ffi).
-export([
decode/1, json_to_iodata/1, json_to_string/1, int/1, float/1, string/1,
bool/1, null/0, array/1, object/1
]).
-if(?OTP_RELEASE < 27).
-define(bad_version,
error({erlang_otp_27_required, << "Insufficient Erlang/OTP version.
`gleam_json` uses the Erlang `json` module introduced in Erlang/OTP 27.
You are using Erlang/OTP "/utf8, (integer_to_binary(?OTP_RELEASE))/binary, "
Please upgrade your Erlang install or downgrade to `gleam_json` v1.0.1.
"/utf8>>})).
decode(_) -> ?bad_version.
json_to_iodata(_) -> ?bad_version.
json_to_string(_) -> ?bad_version.
int(_) -> ?bad_version.
float(_) -> ?bad_version.
string(_) -> ?bad_version.
bool(_) -> ?bad_version.
array(_) -> ?bad_version.
object(_) -> ?bad_version.
null() -> ?bad_version.
-else.
decode(Json) ->
try
{ok, json:decode(Json)}
catch
error:unexpected_end -> {error, unexpected_end_of_input};
error:{invalid_byte, Byte} -> {error, {unexpected_byte, hex(Byte)}};
error:{unexpected_sequence, Byte} -> {error, {unexpected_sequence, Byte}}
end.
hex(I) ->
H = list_to_binary(integer_to_list(I, 16)),
<<"0x"/utf8, H/binary>>.
json_to_iodata(Json) ->
Json.
json_to_string(Json) when is_binary(Json) ->
Json;
json_to_string(Json) when is_list(Json) ->
list_to_binary(Json).
null() -> <<"null">>.
bool(true) -> <<"true">>;
bool(false) -> <<"false">>.
int(X) -> json:encode_integer(X).
float(X) -> json:encode_float(X).
string(X) -> json:encode_binary(X).
array([]) -> <<"[]">>;
array([First | Rest]) -> [$[, First | array_loop(Rest)].
array_loop([]) -> "]";
array_loop([Elem | Rest]) -> [$,, Elem | array_loop(Rest)].
object(List) -> encode_object([[$,, string(Key), $: | Value] || {Key, Value} <- List]).
encode_object([]) -> <<"{}">>;
encode_object([[_Comma | Entry] | Rest]) -> ["{", Entry, Rest, "}"].
-endif.
+201
View File
@@ -0,0 +1,201 @@
import {
Result$Ok,
Result$Error,
List$isNonEmpty,
List$NonEmpty$first,
List$NonEmpty$rest,
} from "./gleam.mjs";
import {
DecodeError$UnexpectedByte,
DecodeError$UnexpectedEndOfInput,
} from "./gleam/json.mjs";
export function json_to_string(json) {
return JSON.stringify(json);
}
export function object(entries) {
return Object.fromEntries(entries);
}
export function identity(x) {
return x;
}
export function array(list) {
const array = [];
while (List$isNonEmpty(list)) {
array.push(List$NonEmpty$first(list));
list = List$NonEmpty$rest(list);
}
return array;
}
export function do_null() {
return null;
}
export function decode(string) {
try {
const result = JSON.parse(string);
return Result$Ok(result);
} catch (err) {
return Result$Error(getJsonDecodeError(err, string));
}
}
export function getJsonDecodeError(stdErr, json) {
if (isUnexpectedEndOfInput(stdErr)) return DecodeError$UnexpectedEndOfInput();
return toUnexpectedByteError(stdErr, json);
}
/**
* Matches unexpected end of input messages in:
* - Chromium (edge, chrome, node)
* - Spidermonkey (firefox)
* - JavascriptCore (safari)
*
* Note that Spidermonkey and JavascriptCore will both incorrectly report some
* UnexpectedByte errors as UnexpectedEndOfInput errors. For example:
*
* @example
* // in JavascriptCore
* JSON.parse('{"a"]: "b"})
* // => JSON Parse error: Expected ':' before value
*
* JSON.parse('{"a"')
* // => JSON Parse error: Expected ':' before value
*
* // in Chromium (correct)
* JSON.parse('{"a"]: "b"})
* // => Unexpected token ] in JSON at position 4
*
* JSON.parse('{"a"')
* // => Unexpected end of JSON input
*/
function isUnexpectedEndOfInput(err) {
const unexpectedEndOfInputRegex =
/((unexpected (end|eof))|(end of data)|(unterminated string)|(json( parse error|\.parse)\: expected '(\:|\}|\])'))/i;
return unexpectedEndOfInputRegex.test(err.message);
}
/**
* Converts a SyntaxError to an UnexpectedByte error based on the JS runtime.
*
* For Chromium, the unexpected byte and position are reported by the runtime.
*
* For JavascriptCore, only the unexpected byte is reported by the runtime, so
* there is no way to know which position that character is in unless we then
* parse the string again ourselves. So instead, the position is reported as 0.
*
* For Spidermonkey, the position is reported by the runtime as a line and column number
* and the unexpected byte is found using those coordinates.
*/
function toUnexpectedByteError(err, json) {
let converters = [
v8UnexpectedByteError,
oldV8UnexpectedByteError,
jsCoreUnexpectedByteError,
spidermonkeyUnexpectedByteError,
];
for (let converter of converters) {
let result = converter(err, json);
if (result) return result;
}
return DecodeError$UnexpectedByte("");
}
/**
* Matches unexpected byte messages in:
* - V8 (edge, chrome, node)
*
* Matches the character but not the position as this is no longer reported by
* V8. Boo!
*/
function v8UnexpectedByteError(err) {
const regex = /unexpected token '(.)', ".+" is not valid JSON/i;
const match = regex.exec(err.message);
if (!match) return null;
const byte = toHex(match[1]);
return DecodeError$UnexpectedByte(byte);
}
/**
* Matches unexpected byte messages in:
* - V8 (edge, chrome, node)
*
* No longer works in current versions of V8.
*
* Matches the character and its position.
*/
function oldV8UnexpectedByteError(err) {
const regex = /unexpected token (.) in JSON at position (\d+)/i;
const match = regex.exec(err.message);
if (!match) return null;
const byte = toHex(match[1]);
return DecodeError$UnexpectedByte(byte);
}
/**
* Matches unexpected byte messages in:
* - Spidermonkey (firefox)
*
* Matches the position in a 2d grid only and not the character.
*/
function spidermonkeyUnexpectedByteError(err, json) {
const regex =
/(unexpected character|expected .*) at line (\d+) column (\d+)/i;
const match = regex.exec(err.message);
if (!match) return null;
const line = Number(match[2]);
const column = Number(match[3]);
const position = getPositionFromMultiline(line, column, json);
const byte = toHex(json[position]);
return DecodeError$UnexpectedByte(byte);
}
/**
* Matches unexpected byte messages in:
* - JavascriptCore (safari)
*
* JavascriptCore only reports what the character is and not its position.
*/
function jsCoreUnexpectedByteError(err) {
const regex = /unexpected (identifier|token) "(.)"/i;
const match = regex.exec(err.message);
if (!match) return null;
const byte = toHex(match[2]);
return DecodeError$UnexpectedByte(byte);
}
function toHex(char) {
return "0x" + char.charCodeAt(0).toString(16).toUpperCase();
}
/**
* Gets the position of a character in a flattened (i.e. single line) string
* from a line and column number. Note that the position is 0-indexed and
* the line and column numbers are 1-indexed.
*
* @param {number} line
* @param {number} column
* @param {string} string
*/
function getPositionFromMultiline(line, column, string) {
if (line === 1) return column - 1;
let currentLn = 1;
let position = 0;
string.split("").find((char, idx) => {
if (char === "\n") currentLn += 1;
if (currentLn === line) {
position = idx + column;
return true;
}
return false;
});
return position;
}
+710
View File
@@ -0,0 +1,710 @@
/**
* This file uses jsdoc to annotate types.
* These types can be checked using the typescript compiler with "checkjs" option.
*/
import { isEqual, Result$Error, Result$Ok } from "./gleam.mjs";
// -- HASH --------------------------------------------------------------------
const referenceMap = /* @__PURE__ */ new WeakMap();
const tempDataView = /* @__PURE__ */ new DataView(
/* @__PURE__ */ new ArrayBuffer(8),
);
let referenceUID = 0;
/**
* hash the object by reference using a weak map and incrementing uid
* @param {any} o
* @returns {number}
*/
function hashByReference(o) {
const known = referenceMap.get(o);
if (known !== undefined) {
return known;
}
const hash = referenceUID++;
if (referenceUID === 0x7fffffff) {
referenceUID = 0;
}
referenceMap.set(o, hash);
return hash;
}
/**
* merge two hashes in an order sensitive way
* @param {number} a
* @param {number} b
* @returns {number}
*/
function hashMerge(a, b) {
return (a ^ (b + 0x9e3779b9 + (a << 6) + (a >> 2))) | 0;
}
/**
* standard string hash popularised by Java
* @param {string} s
* @returns {number}
*/
function hashString(s) {
let hash = 0;
const len = s.length;
for (let i = 0; i < len; i++) {
hash = (Math.imul(31, hash) + s.charCodeAt(i)) | 0;
}
return hash;
}
/**
* hash a number by converting to two integers and do some jumbling
* @param {number} n
* @returns {number}
*/
function hashNumber(n) {
tempDataView.setFloat64(0, n);
const i = tempDataView.getInt32(0);
const j = tempDataView.getInt32(4);
return Math.imul(0x45d9f3b, (i >> 16) ^ i) ^ j;
}
/**
* hash a BigInt by converting it to a string and hashing that
* @param {BigInt} n
* @returns {number}
*/
function hashBigInt(n) {
return hashString(n.toString());
}
/**
* hash any js object
* @param {any} o
* @returns {number}
*/
function hashObject(o) {
const proto = Object.getPrototypeOf(o);
if (proto !== null && typeof proto.hashCode === "function") {
try {
const code = o.hashCode(o);
if (typeof code === "number") {
return code;
}
} catch {}
}
if (o instanceof Promise || o instanceof WeakSet || o instanceof WeakMap) {
return hashByReference(o);
}
if (o instanceof Date) {
return hashNumber(o.getTime());
}
let h = 0;
if (o instanceof ArrayBuffer) {
o = new Uint8Array(o);
}
if (Array.isArray(o) || o instanceof Uint8Array) {
for (let i = 0; i < o.length; i++) {
h = (Math.imul(31, h) + getHash(o[i])) | 0;
}
} else if (o instanceof Set) {
o.forEach((v) => {
h = (h + getHash(v)) | 0;
});
} else if (o instanceof Map) {
o.forEach((v, k) => {
h = (h + hashMerge(getHash(v), getHash(k))) | 0;
});
} else {
const keys = Object.keys(o);
for (let i = 0; i < keys.length; i++) {
const k = keys[i];
const v = o[k];
h = (h + hashMerge(getHash(v), hashString(k))) | 0;
}
}
return h;
}
/**
* hash any js value
* @param {any} u
* @returns {number}
*/
export function getHash(u) {
if (u === null) return 0x42108422;
if (u === undefined) return 0x42108423;
if (u === true) return 0x42108421;
if (u === false) return 0x42108420;
switch (typeof u) {
case "number":
return hashNumber(u);
case "string":
return hashString(u);
case "bigint":
return hashBigInt(u);
case "object":
return hashObject(u);
case "symbol":
return hashByReference(u);
case "function":
return hashByReference(u);
default:
return 0; // should be unreachable
}
}
// -- DICT --------------------------------------------------------------------
/**
* An implementation of the CHAMP data structure, an optimised HAMT.
*
* See: M. J. Steindorfer, J.J. Vinju (2015). Optimizing Hash-Array Mapped Tries for
Fast and Lean Immutable JVM Collections. Available: https://michael.steindorfer.name/publications/oopsla15.pdf
*/
export default class Dict {
constructor(size, root) {
this.size = size;
this.root = root;
}
}
/// The power-of-2 branching factor for the dict. For example, a value of `5` indicates a 32-ary tree.
const bits = 5;
const mask = (1 << bits) - 1;
/// This symbol is used internally to avoid constructing results.
const noElementMarker = Symbol();
/// This symbol is used to store the "generation" on a node.
/// Using a symbol makes the property not enumerable, which means the generation
/// will be ignored during equality checks.
const generationKey = Symbol();
// Some commonly used constants throughout the code.
const emptyNode = /* @__PURE__ */ newNode(0);
const emptyDict = /* @__PURE__ */ new Dict(0, emptyNode);
const errorNil = /* @__PURE__ */ Result$Error(undefined);
function makeNode(generation, datamap, nodemap, data) {
// The order of fields is important, as they define the order `isEqual` will
// compare our fields. Putting the bitmaps first means that equality can
// early-out if the bitmaps are not equal.
return {
// A node is a high-arity (32 in practice) hybrid tree node.
// Hybrid means that it stores data directly as well as pointers to child nodes.
//
// Each node contains 2 bitmaps:
// - The datamap has a bit set if that slot in the node contains direct data
// - The nodemap has a bit set if that slot in the node contains another node.
//
// Both are exclusive to on another, so datamap & nodemap == 0.
//
// Every key/hash value directly correlates to a specific bit by using a trie
// suffix (least significant bits first) encoding.
// For example, if the last 5 bits of the hash are 1101, the bit to check for
// that value is the 13th bit.
datamap,
nodemap,
// The slots itself are stored in a single contiguous array that contains
// both direct k/v-pairs and child nodes.
//
// The direct children come first, followed by the child nodes in _reverse order_:
//
// 7654321
// datamap: 1000100
// nodemap: 10011
// data: [key3, value3, key7, value7, child5, child2, child1]
// -------------------------> <---------------------
// datamap nodemap
//
// Every `1` bit in the datamap corresponds to a pair of [key, value] entries,
// and every `1` bit in the nodemap corresponds to a child node entry.
//
// Children are stored in reverse order to avoid having to store or calculate an
// "offset" value to skip over the direct children.
data,
// The generation is used to track which nodes need to be copied during transient updates.
// Using a symbol here makes `isEqual` ignore this field.
[generationKey]: generation,
};
}
function newNode(generation) {
return makeNode(generation, 0, 0, []);
}
/**
* Copies a node and its data array if it's from another generation, making it safe
* to mutate the node.
*/
function copyNode(node, generation) {
if (node[generationKey] === generation) {
return node;
}
const newData = node.data.slice(0);
return makeNode(generation, node.datamap, node.nodemap, newData);
}
/**
* Copies a node if needed and sets a new value.
*/
function copyAndSet(node, generation, idx, val) {
if (node.data[idx] === val) {
return node;
}
// Using copyNode is faster than a specialised implementation.
node = copyNode(node, generation);
node.data[idx] = val;
return node;
}
/**
* Copies a node if needed, and then inserts a new key-value pair.
*/
function copyAndInsertPair(node, generation, bit, idx, key, val) {
const data = node.data;
const length = data.length;
// the fastest way to insert a pair is to always copy.
const newData = new Array(length + 2);
let readIndex = 0;
let writeIndex = 0;
while (readIndex < idx) newData[writeIndex++] = data[readIndex++];
newData[writeIndex++] = key;
newData[writeIndex++] = val;
while (readIndex < length) newData[writeIndex++] = data[readIndex++];
return makeNode(generation, node.datamap | bit, node.nodemap, newData);
}
function copyAndRemovePair(node, generation, bit, idx) {
node = copyNode(node, generation);
const data = node.data;
const length = data.length;
for (let w = idx, r = idx + 2; r < length; ++r, ++w) {
data[w] = data[r];
}
data.pop();
data.pop();
node.datamap ^= bit;
return node;
}
export function make() {
return emptyDict;
}
export function from(iterable) {
let transient = toTransient(emptyDict);
for (const [key, value] of iterable) {
transient = destructiveTransientInsert(key, value, transient);
}
return fromTransient(transient);
}
export function size(dict) {
return dict.size;
}
export function get(dict, key) {
const result = lookup(dict.root, key, getHash(key));
return result !== noElementMarker ? Result$Ok(result) : errorNil;
}
export function has(dict, key) {
return lookup(dict.root, key, getHash(key)) !== noElementMarker;
}
function lookup(node, key, hash) {
for (let shift = 0; shift < 32; shift += bits) {
const data = node.data;
const bit = hashbit(hash, shift);
if (node.nodemap & bit) {
// we found our hash inside the nodemap, so we can continue our search there.
node = data[data.length - 1 - index(node.nodemap, bit)];
} else if (node.datamap & bit) {
// we store this hash directly!
//
// this also means that there are no other values with the same
// hash prefix in this dict.
//
// We still need to check if the key matches, but if it does we know for
// sure that this is the correct value, and if it doesn't that we don't
// contain the value in question.
const dataidx = Math.imul(index(node.datamap, bit), 2);
return isEqual(key, data[dataidx]) ? data[dataidx + 1] : noElementMarker;
} else {
// if the hash bit is not set in neither bitmaps, we immediately know that
// this key cannot be inside this dict.
return noElementMarker;
}
}
// our shift has exceeded 32 bits. Everything that follows is
// implicitely an overflow node and only contains direct children.
const overflow = node.data;
for (let i = 0; i < overflow.length; i += 2) {
if (isEqual(key, overflow[i])) {
return overflow[i + 1];
}
}
return noElementMarker;
}
/**
* We use "transient" values to allow for safer internal mutations of the data
* structure. This is an optimisation only. No mutable API is exposed to the user.
*
* Transients are to be treated as having a linear (single-use, think rust) type.
* A transient value becomes invalid as soon as it's passed to one of the functions.
*
* Internally, we track a "generation" value on each node. If the generation
* doesn't match the one for the current transient, we have to copy - the node
* could still be referenced by another dict instance!
* After that, no other references than the transient one exists, so it's safe
* to mutate in place.
*/
export function toTransient(dict) {
return {
generation: nextGeneration(dict),
root: dict.root,
size: dict.size,
dict: dict,
};
}
/**
* Consume a transient, producing a normal Dict again.
*/
export function fromTransient(transient) {
if (transient.root === transient.dict.root) {
return transient.dict;
}
return new Dict(transient.size, transient.root);
}
/**
* Find and allocate the next generation id.
*
* @template K,V
* @param {Dict<K,V>} dict
* @returns {number}
*/
function nextGeneration(dict) {
const root = dict.root;
if (root[generationKey] < Number.MAX_SAFE_INTEGER) {
return root[generationKey] + 1;
}
// we have reached MAX_SAFE_INTEGER generations -
// at this point, we have to walk the dictionary once to reset the counter
// on every node. This is safe since it's part of the contract for transient
// that only one of them exists at any given time.
//
const queue = [root];
while (queue.length) {
// order doesn't matter, so we can use push/pop for faster array usage.
const node = queue.pop();
// reset the generation to 0
node[generationKey] = 0;
// queue all other referenced nodes
// We need to query the length from the nodemap, as we don't know if this
// is an overflow node or not! if it is, it will never have datamap set!
const nodeStart = data.length - popcount(node.nodemap);
for (let i = nodeStart; i < node.data.length; ++i) {
queue.push(node.data[i]);
}
}
return 1;
}
/// Insert is the second-most performance-sensitive operation.
/// We use a global "transient" value here to avoid doing a memory allocation.
const globalTransient = /* @__PURE__ */ toTransient(emptyDict);
export function insert(dict, key, value) {
globalTransient.generation = nextGeneration(dict);
globalTransient.size = dict.size;
const hash = getHash(key);
const root = insertIntoNode(globalTransient, dict.root, key, value, hash, 0);
if (root === dict.root) {
return dict;
}
return new Dict(globalTransient.size, root);
}
/**
* Consume a transient, writing a new key/value pair into the dictionary it
* represents. If the key already exists, it will be overwritten.
*
* Returns a new transient.
*/
export function destructiveTransientInsert(key, value, transient) {
const hash = getHash(key);
transient.root = insertIntoNode(transient, transient.root, key, value, hash, 0);
return transient;
}
/**
* Consume a transient, writing a new key/value pair if the key doesn't exist or updating
* the existing value with a function if it does.
*
* Returns a new transient.
*/
export function destructiveTransientUpdateWith(key, fun, value, transient) {
const hash = getHash(key);
const existing = lookup(transient.root, key, hash);
if (existing !== noElementMarker) {
value = fun(existing);
}
transient.root = insertIntoNode(transient, transient.root, key, value, hash, 0);
return transient;
}
function insertIntoNode(transient, node, key, value, hash, shift) {
const data = node.data;
const generation = transient.generation;
// 1. Overflow Node
// overflow nodes only contain key/value-pairs. we walk the data linearly trying to find a match.
if (shift > 32) {
for (let i = 0; i < data.length; i += 2) {
if (isEqual(key, data[i])) {
return copyAndSet(node, generation, i + 1, value);
}
}
transient.size += 1;
return copyAndInsertPair(node, generation, 0, data.length, key, value);
}
const bit = hashbit(hash, shift);
// 2. Child Node
// We have to check first if there is already a child node we have to traverse to.
if (node.nodemap & bit) {
const nodeidx = data.length - 1 - index(node.nodemap, bit);
let child = data[nodeidx];
child = insertIntoNode(transient, child, key, value, hash, shift + bits);
return copyAndSet(node, generation, nodeidx, child);
}
// 3. New Data Node
// No child node and no data node exists yet, so we can potentially just insert a new value.
const dataidx = Math.imul(index(node.datamap, bit), 2);
if ((node.datamap & bit) === 0) {
transient.size += 1;
return copyAndInsertPair(node, generation, bit, dataidx, key, value);
}
// 4. Existing Data Node
// We have a match that we can update, or remove.
if (isEqual(key, data[dataidx])) {
return copyAndSet(node, generation, dataidx + 1, value);
}
// 5. Collision
// There is no child node, but a data node with the same hash, but with a different key.
// To resolve this, we push both nodes down one level.
const childShift = shift + bits;
let child = emptyNode;
child = insertIntoNode(transient, child, key, value, hash, childShift);
const key2 = data[dataidx];
const value2 = data[dataidx + 1];
const hash2 = getHash(key2);
child = insertIntoNode(transient, child, key2, value2, hash2, childShift);
// we inserted 2 elements, but implicitely deleted the one we pushed down from the datamap.
transient.size -= 1;
// remove the old data pair, and insert the new child node.
const length = data.length;
const nodeidx = length - 1 - index(node.nodemap, bit);
// writing these loops in javascript instead of a combination of splices
// turns out to be faster. Copying always turned out to be faster.
const newData = new Array(length - 1);
let readIndex = 0;
let writeIndex = 0;
// [0..dataidx, skip 2 elements, ..nodeidx, newChild, ..rest]
while (readIndex < dataidx) newData[writeIndex++] = data[readIndex++];
readIndex += 2;
while (readIndex <= nodeidx) newData[writeIndex++] = data[readIndex++];
newData[writeIndex++] = child;
while (readIndex < length) newData[writeIndex++] = data[readIndex++];
return makeNode(generation, node.datamap ^ bit, node.nodemap | bit, newData);
}
/**
* Consume a transient, removing a key if it exists.
* Returns a new transient.
*/
export function destructiveTransientDelete(key, transient) {
const hash = getHash(key);
transient.root = deleteFromNode(transient, transient.root, key, hash, 0);
return transient;
}
function deleteFromNode(transient, node, key, hash, shift) {
const data = node.data;
const generation = transient.generation;
// 1. Overflow Node
// overflow nodes only contain key/value-pairs. we walk the data linearly trying to find a match.
if (shift > 32) {
for (let i = 0; i < data.length; i += 2) {
if (isEqual(key, data[i])) {
transient.size -= 1;
return copyAndRemovePair(node, generation, 0, i);
}
}
return node;
}
const bit = hashbit(hash, shift);
const dataidx = Math.imul(index(node.datamap, bit), 2);
// 2. Child Node
// We have to check first if there is already a child node we have to traverse to.
if ((node.nodemap & bit) !== 0) {
const nodeidx = data.length - 1 - index(node.nodemap, bit);
let child = data[nodeidx];
child = deleteFromNode(transient, child, key, hash, shift + bits);
// the node did change, so let's copy to incorporate that change.
if (child.nodemap !== 0 || child.data.length > 2) {
return copyAndSet(node, generation, nodeidx, child);
}
// this node only has a single data (k/v-pair) child.
// to restore the CHAMP invariant, we "pull" that pair up into ourselves.
// this ensures that every tree stays in its single optimal representation,
// and allows dicts to be structurally compared.
const length = data.length;
const newData = new Array(length + 1);
let readIndex = 0;
let writeIndex = 0;
while (readIndex < dataidx) newData[writeIndex++] = data[readIndex++];
newData[writeIndex++] = child.data[0];
newData[writeIndex++] = child.data[1];
while (readIndex < nodeidx) newData[writeIndex++] = data[readIndex++];
readIndex++;
while (readIndex < length) newData[writeIndex++] = data[readIndex++];
return makeNode(generation, node.datamap | bit, node.nodemap ^ bit, newData);
}
// 3. Data Node
// There is no data entry here, or it is a prefix for a different key
if ((node.datamap & bit) === 0 || !isEqual(key, data[dataidx])) {
return node;
}
// we found a data entry that we can delete.
transient.size -= 1;
return copyAndRemovePair(node, generation, bit, dataidx);
}
export function map(dict, fun) {
// map can never modify the structure, so we can walk the dictionary directly,
// but still move to a new generation to make sure we get a new copy of every node.
const generation = nextGeneration(dict);
const root = copyNode(dict.root, generation);
const queue = [root];
while (queue.length) {
// order doesn't matter, so we can use push/pop for faster array usage.
const node = queue.pop();
const data = node.data;
// every node contains popcount(datamap) direct entries
// We need to query the length from the nodemap, as we don't know if this
// is an overflow node or not! if it is, it will never have datamap set!
const edgesStart = data.length - popcount(node.nodemap);
for (let i = 0; i < edgesStart; i += 2) {
// we copied the node while queueing it, so direct mutation here is safe.
data[i + 1] = fun(data[i], data[i + 1]);
}
// the remaining entries are other nodes we can queue
for (let i = edgesStart; i < data.length; ++i) {
// copy the node first to make it safe to mutate
data[i] = copyNode(data[i], generation);
queue.push(data[i]);
}
}
return new Dict(dict.size, root);
}
export function fold(dict, state, fun) {
const queue = [dict.root];
while (queue.length) {
// order doesn't matter, so we can use push/pop for faster array usage.
const node = queue.pop();
const data = node.data;
// every node contains popcount(datamap) direct entries
// We need to query the length from the nodemap, as we don't know if this
// is an overflow node or not! if it is, it will never have datamap set!
const edgesStart = data.length - popcount(node.nodemap);
for (let i = 0; i < edgesStart; i += 2) {
state = fun(state, data[i], data[i + 1]);
}
// the remaining entries are child nodes we can queue.
for (let i = edgesStart; i < data.length; ++i) {
queue.push(data[i]);
}
}
return state;
}
/**
* How many `1` bits are set in a 32-bit integer.
*/
function popcount(n) {
n -= (n >>> 1) & 0x55555555;
n = (n & 0x33333333) + ((n >>> 2) & 0x33333333);
return Math.imul((n + (n >>> 4)) & 0x0f0f0f0f, 0x01010101) >>> 24;
}
/**
* Given a population bitmap and a bit selected from that map, returns
* how many less significant 1 bits there are.
*
* For example, index(10101, 100) returns 1, since there is a single less
* significant `1` bit. This translates to the 0-based "index" of that bit.
*/
function index(bitmap, bit) {
return popcount(bitmap & (bit - 1));
}
/**
* Extracts a single slice of the hash, and returns a bitmask for the resulting value.
* For example, if the slice returns 5, this function returns 10000 = 1 << 5.
*/
function hashbit(hash, shift) {
return 1 << ((hash >>> shift) & mask);
}
+1
View File
@@ -0,0 +1 @@
export * from "../prelude.mjs";
@@ -0,0 +1,286 @@
import { Ok, toList, bitArraySlice, bitArraySliceToInt } from "../gleam.mjs";
import * as $int from "../gleam/int.mjs";
import * as $order from "../gleam/order.mjs";
import * as $string from "../gleam/string.mjs";
import {
bit_array_from_string as from_string,
bit_array_bit_size as bit_size,
bit_array_byte_size as byte_size,
bit_array_pad_to_bytes as pad_to_bytes,
bit_array_concat as concat,
bit_array_slice as slice,
bit_array_to_string as to_string,
base64_encode,
base64_decode as decode64,
base16_encode,
base16_decode,
bit_array_to_int_and_size,
bit_array_starts_with as starts_with,
} from "../gleam_stdlib.mjs";
export {
base16_decode,
base16_encode,
base64_encode,
bit_size,
byte_size,
concat,
from_string,
pad_to_bytes,
slice,
starts_with,
to_string,
};
/**
* Creates a new bit array by joining two bit arrays.
*
* ## Examples
*
* ```gleam
* assert append(to: from_string("butter"), suffix: from_string("fly"))
* == from_string("butterfly")
* ```
*/
export function append(first, second) {
return concat(toList([first, second]));
}
function is_utf8_loop(bits) {
let $ = to_string(bits);
if ($ instanceof Ok) {
return true;
} else {
return false;
}
}
/**
* Tests to see whether a bit array is valid UTF-8.
*/
export function is_utf8(bits) {
return is_utf8_loop(bits);
}
/**
* Decodes a base 64 encoded string into a `BitArray`.
*/
export function base64_decode(encoded) {
let _block;
let $ = byte_size(from_string(encoded)) % 4;
if ($ === 0) {
_block = encoded;
} else {
let n = $;
_block = $string.append(encoded, $string.repeat("=", 4 - n));
}
let padded = _block;
return decode64(padded);
}
/**
* Encodes a `BitArray` into a base 64 encoded string with URL and filename
* safe alphabet.
*
* If the bit array does not contain a whole number of bytes then it is padded
* with zero bits prior to being encoded.
*/
export function base64_url_encode(input, padding) {
let _pipe = input;
let _pipe$1 = base64_encode(_pipe, padding);
let _pipe$2 = $string.replace(_pipe$1, "+", "-");
return $string.replace(_pipe$2, "/", "_");
}
/**
* Decodes a base 64 encoded string with URL and filename safe alphabet into a
* `BitArray`.
*/
export function base64_url_decode(encoded) {
let _pipe = encoded;
let _pipe$1 = $string.replace(_pipe, "-", "+");
let _pipe$2 = $string.replace(_pipe$1, "_", "/");
return base64_decode(_pipe$2);
}
function inspect_loop(loop$input, loop$accumulator) {
while (true) {
let input = loop$input;
let accumulator = loop$accumulator;
if (input.bitSize === 0) {
return accumulator;
} else if (input.bitSize === 1) {
let x = bitArraySliceToInt(input, 0, 1, true, false);
return (accumulator + $int.to_string(x)) + ":size(1)";
} else if (input.bitSize === 2) {
let x = bitArraySliceToInt(input, 0, 2, true, false);
return (accumulator + $int.to_string(x)) + ":size(2)";
} else if (input.bitSize === 3) {
let x = bitArraySliceToInt(input, 0, 3, true, false);
return (accumulator + $int.to_string(x)) + ":size(3)";
} else if (input.bitSize === 4) {
let x = bitArraySliceToInt(input, 0, 4, true, false);
return (accumulator + $int.to_string(x)) + ":size(4)";
} else if (input.bitSize === 5) {
let x = bitArraySliceToInt(input, 0, 5, true, false);
return (accumulator + $int.to_string(x)) + ":size(5)";
} else if (input.bitSize === 6) {
let x = bitArraySliceToInt(input, 0, 6, true, false);
return (accumulator + $int.to_string(x)) + ":size(6)";
} else if (input.bitSize === 7) {
let x = bitArraySliceToInt(input, 0, 7, true, false);
return (accumulator + $int.to_string(x)) + ":size(7)";
} else if (input.bitSize >= 8) {
let x = input.byteAt(0);
let rest = bitArraySlice(input, 8);
let _block;
if (rest.bitSize === 0) {
_block = "";
} else {
_block = ", ";
}
let suffix = _block;
let accumulator$1 = (accumulator + $int.to_string(x)) + suffix;
loop$input = rest;
loop$accumulator = accumulator$1;
} else {
return accumulator;
}
}
}
/**
* Converts a bit array to a string containing the decimal value of each byte.
*
* Use this over `string.inspect` when you have a bit array you want printed
* in the array syntax even if it is valid UTF-8.
*
* ## Examples
*
* ```gleam
* assert inspect(<<0, 20, 0x20, 255>>) == "<<0, 20, 32, 255>>"
* ```
*
* ```gleam
* assert inspect(<<100, 5:3>>) == "<<100, 5:size(3)>>"
* ```
*/
export function inspect(input) {
return inspect_loop(input, "<<") + ">>";
}
/**
* Compare two bit arrays as sequences of bytes.
*
* ## Examples
*
* ```gleam
* assert compare(<<1>>, <<2>>) == Lt
* ```
*
* ```gleam
* assert compare(<<"AB":utf8>>, <<"AA":utf8>>) == Gt
* ```
*
* ```gleam
* assert compare(<<1, 2:size(2)>>, with: <<1, 2:size(2)>>) == Eq
* ```
*/
export function compare(loop$a, loop$b) {
while (true) {
let a = loop$a;
let b = loop$b;
if (a.bitSize >= 8) {
if (b.bitSize >= 8) {
let first_byte = a.byteAt(0);
let first_rest = bitArraySlice(a, 8);
let second_byte = b.byteAt(0);
let second_rest = bitArraySlice(b, 8);
let f = first_byte;
let s = second_byte;
if (f > s) {
return new $order.Gt();
} else {
let f = first_byte;
let s = second_byte;
if (f < s) {
return new $order.Lt();
} else {
loop$a = first_rest;
loop$b = second_rest;
}
}
} else if (b.bitSize === 0) {
return new $order.Gt();
} else {
let first = a;
let second = b;
let $ = bit_array_to_int_and_size(first);
let $1 = bit_array_to_int_and_size(second);
let a$1 = $[0];
let b$1 = $1[0];
if (a$1 > b$1) {
return new $order.Gt();
} else {
let a$1 = $[0];
let b$1 = $1[0];
if (a$1 < b$1) {
return new $order.Lt();
} else {
let size_a = $[1];
let size_b = $1[1];
if (size_a > size_b) {
return new $order.Gt();
} else {
let size_a = $[1];
let size_b = $1[1];
if (size_a < size_b) {
return new $order.Lt();
} else {
return new $order.Eq();
}
}
}
}
}
} else if (b.bitSize === 0) {
if (a.bitSize === 0) {
return new $order.Eq();
} else {
return new $order.Gt();
}
} else if (a.bitSize === 0) {
return new $order.Lt();
} else {
let first = a;
let second = b;
let $ = bit_array_to_int_and_size(first);
let $1 = bit_array_to_int_and_size(second);
let a$1 = $[0];
let b$1 = $1[0];
if (a$1 > b$1) {
return new $order.Gt();
} else {
let a$1 = $[0];
let b$1 = $1[0];
if (a$1 < b$1) {
return new $order.Lt();
} else {
let size_a = $[1];
let size_b = $1[1];
if (size_a > size_b) {
return new $order.Gt();
} else {
let size_a = $[1];
let size_b = $1[1];
if (size_a < size_b) {
return new $order.Lt();
} else {
return new $order.Eq();
}
}
}
}
}
}
}
+295
View File
@@ -0,0 +1,295 @@
/**
* Returns the and of two bools, but it evaluates both arguments.
*
* It's the function equivalent of the `&&` operator.
* This function is useful in higher order functions or pipes.
*
* ## Examples
*
* ```gleam
* assert and(True, True)
* ```
*
* ```gleam
* assert !and(False, True)
* ```
*
* ```gleam
* assert !and(False, True)
* ```
*
* ```gleam
* assert !and(False, False)
* ```
*/
export function and(a, b) {
return a && b;
}
/**
* Returns the or of two bools, but it evaluates both arguments.
*
* It's the function equivalent of the `||` operator.
* This function is useful in higher order functions or pipes.
*
* ## Examples
*
* ```gleam
* assert or(True, True)
* ```
*
* ```gleam
* assert or(False, True)
* ```
*
* ```gleam
* assert or(True, False)
* ```
*
* ```gleam
* assert !or(False, False)
* ```
*/
export function or(a, b) {
return a || b;
}
/**
* Returns the opposite bool value.
*
* This is the same as the `!` or `not` operators in some other languages.
*
* ## Examples
*
* ```gleam
* assert !negate(True)
* ```
*
* ```gleam
* assert negate(False)
* ```
*/
export function negate(bool) {
return !bool;
}
/**
* Returns the nor of two bools.
*
* ## Examples
*
* ```gleam
* assert nor(False, False)
* ```
*
* ```gleam
* assert !nor(False, True)
* ```
*
* ```gleam
* assert !nor(True, False)
* ```
*
* ```gleam
* assert !nor(True, True)
* ```
*/
export function nor(a, b) {
return !(a || b);
}
/**
* Returns the nand of two bools.
*
* ## Examples
*
* ```gleam
* assert nand(False, False)
* ```
*
* ```gleam
* assert nand(False, True)
* ```
*
* ```gleam
* assert nand(True, False)
* ```
*
* ```gleam
* assert !nand(True, True)
* ```
*/
export function nand(a, b) {
return !(a && b);
}
/**
* Returns the exclusive or of two bools.
*
* ## Examples
*
* ```gleam
* assert !exclusive_or(False, False)
* ```
*
* ```gleam
* assert exclusive_or(False, True)
* ```
*
* ```gleam
* assert exclusive_or(True, False)
* ```
*
* ```gleam
* assert !exclusive_or(True, True)
* ```
*/
export function exclusive_or(a, b) {
return a !== b;
}
/**
* Returns the exclusive nor of two bools.
*
* ## Examples
*
* ```gleam
* assert exclusive_nor(False, False)
* ```
*
* ```gleam
* assert !exclusive_nor(False, True)
* ```
*
* ```gleam
* assert !exclusive_nor(True, False)
* ```
*
* ```gleam
* assert exclusive_nor(True, True)
* ```
*/
export function exclusive_nor(a, b) {
return a === b;
}
/**
* Returns a string representation of the given bool.
*
* ## Examples
*
* ```gleam
* assert to_string(True) == "True"
* ```
*
* ```gleam
* assert to_string(False) == "False"
* ```
*/
export function to_string(bool) {
if (bool) {
return "True";
} else {
return "False";
}
}
/**
* Run a callback function if the given bool is `False`, otherwise return a
* default value.
*
* With a `use` expression this function can simulate the early-return pattern
* found in some other programming languages.
*
* In a procedural language:
*
* ```js
* if (predicate) return value;
* // ...
* ```
*
* In Gleam with a `use` expression:
*
* ```gleam
* use <- guard(when: predicate, return: value)
* // ...
* ```
*
* Like everything in Gleam `use` is an expression, so it short circuits the
* current block, not the entire function. As a result you can assign the value
* to a variable:
*
* ```gleam
* let x = {
* use <- guard(when: predicate, return: value)
* // ...
* }
* ```
*
* Note that unlike in procedural languages the `return` value is evaluated
* even when the predicate is `False`, so it is advisable not to perform
* expensive computation nor side-effects there.
*
*
* ## Examples
*
* ```gleam
* let name = ""
* use <- guard(when: name == "", return: "Welcome!")
* "Hello, " <> name
* // -> "Welcome!"
* ```
*
* ```gleam
* let name = "Kamaka"
* use <- guard(when: name == "", return: "Welcome!")
* "Hello, " <> name
* // -> "Hello, Kamaka"
* ```
*/
export function guard(requirement, consequence, alternative) {
if (requirement) {
return consequence;
} else {
return alternative();
}
}
/**
* Runs a callback function if the given bool is `True`, otherwise runs an
* alternative callback function.
*
* Useful when further computation should be delayed regardless of the given
* bool's value.
*
* See [`guard`](#guard) for more info.
*
* ## Examples
*
* ```gleam
* let name = "Kamaka"
* let inquiry = fn() { "How may we address you?" }
* use <- lazy_guard(when: name == "", return: inquiry)
* "Hello, " <> name
* // -> "Hello, Kamaka"
* ```
*
* ```gleam
* import gleam/int
*
* let name = ""
* let greeting = fn() { "Hello, " <> name }
* use <- lazy_guard(when: name == "", otherwise: greeting)
* let number = int.random(99)
* let name = "User " <> int.to_string(number)
* "Welcome, " <> name
* // -> "Welcome, User 54"
* ```
*/
export function lazy_guard(requirement, consequence, alternative) {
if (requirement) {
return consequence();
} else {
return alternative();
}
}
@@ -0,0 +1,225 @@
import {
toList,
Empty as $Empty,
prepend as listPrepend,
CustomType as $CustomType,
} from "../gleam.mjs";
import * as $bit_array from "../gleam/bit_array.mjs";
import * as $list from "../gleam/list.mjs";
import * as $string_tree from "../gleam/string_tree.mjs";
class Bytes extends $CustomType {
constructor($0) {
super();
this[0] = $0;
}
}
class Text extends $CustomType {
constructor($0) {
super();
this[0] = $0;
}
}
class Many extends $CustomType {
constructor($0) {
super();
this[0] = $0;
}
}
/**
* Joins a list of bytes trees into a single one.
*
* Runs in constant time.
*/
export function concat(trees) {
return new Many(trees);
}
/**
* Create an empty `BytesTree`. Useful as the start of a pipe chaining many
* trees together.
*/
export function new$() {
return concat(toList([]));
}
function wrap_list(bits) {
return new Bytes(bits);
}
/**
* Creates a new bytes tree from a bit array.
*
* Runs in constant time.
*/
export function from_bit_array(bits) {
let _pipe = bits;
let _pipe$1 = $bit_array.pad_to_bytes(_pipe);
return wrap_list(_pipe$1);
}
/**
* Appends a bytes tree onto the end of another.
*
* Runs in constant time.
*/
export function append_tree(first, second) {
if (second instanceof Bytes) {
return new Many(toList([first, second]));
} else if (second instanceof Text) {
return new Many(toList([first, second]));
} else {
let trees = second[0];
return new Many(listPrepend(first, trees));
}
}
/**
* Prepends a bit array to the start of a bytes tree.
*
* Runs in constant time.
*/
export function prepend(second, first) {
return append_tree(from_bit_array(first), second);
}
/**
* Appends a bit array to the end of a bytes tree.
*
* Runs in constant time.
*/
export function append(first, second) {
return append_tree(first, from_bit_array(second));
}
/**
* Prepends a bytes tree onto the start of another.
*
* Runs in constant time.
*/
export function prepend_tree(second, first) {
return append_tree(first, second);
}
/**
* Creates a new bytes tree from a string.
*
* Runs in constant time when running on Erlang.
* Runs in linear time otherwise.
*/
export function from_string(string) {
return new Text($string_tree.from_string(string));
}
/**
* Prepends a string onto the start of a bytes tree.
*
* Runs in constant time when running on Erlang.
* Runs in linear time with the length of the string otherwise.
*/
export function prepend_string(second, first) {
return append_tree(from_string(first), second);
}
/**
* Appends a string onto the end of a bytes tree.
*
* Runs in constant time when running on Erlang.
* Runs in linear time with the length of the string otherwise.
*/
export function append_string(first, second) {
return append_tree(first, from_string(second));
}
/**
* Joins a list of bit arrays into a single bytes tree.
*
* Runs in constant time.
*/
export function concat_bit_arrays(bits) {
let _pipe = bits;
let _pipe$1 = $list.map(_pipe, from_bit_array);
return concat(_pipe$1);
}
/**
* Creates a new bytes tree from a string tree.
*
* Runs in constant time when running on Erlang.
* Runs in linear time otherwise.
*/
export function from_string_tree(tree) {
return new Text(tree);
}
function to_list(loop$stack, loop$acc) {
while (true) {
let stack = loop$stack;
let acc = loop$acc;
if (stack instanceof $Empty) {
return acc;
} else {
let $ = stack.head;
if ($ instanceof $Empty) {
let remaining_stack = stack.tail;
loop$stack = remaining_stack;
loop$acc = acc;
} else {
let $1 = $.head;
if ($1 instanceof Bytes) {
let remaining_stack = stack.tail;
let rest = $.tail;
let bits = $1[0];
loop$stack = listPrepend(rest, remaining_stack);
loop$acc = listPrepend(bits, acc);
} else if ($1 instanceof Text) {
let remaining_stack = stack.tail;
let rest = $.tail;
let tree = $1[0];
let bits = $bit_array.from_string($string_tree.to_string(tree));
loop$stack = listPrepend(rest, remaining_stack);
loop$acc = listPrepend(bits, acc);
} else {
let remaining_stack = stack.tail;
let rest = $.tail;
let trees = $1[0];
loop$stack = listPrepend(trees, listPrepend(rest, remaining_stack));
loop$acc = acc;
}
}
}
}
}
/**
* Turns a bytes tree into a bit array.
*
* Runs in linear time.
*
* When running on Erlang this function is implemented natively by the
* virtual machine and is highly optimised.
*/
export function to_bit_array(tree) {
let _pipe = toList([toList([tree])]);
let _pipe$1 = to_list(_pipe, toList([]));
let _pipe$2 = $list.reverse(_pipe$1);
return $bit_array.concat(_pipe$2);
}
/**
* Returns the size of the bytes tree's content in bytes.
*
* Runs in linear time.
*/
export function byte_size(tree) {
let _pipe = toList([toList([tree])]);
let _pipe$1 = to_list(_pipe, toList([]));
return $list.fold(
_pipe$1,
0,
(acc, bits) => { return $bit_array.byte_size(bits) + acc; },
);
}
+452
View File
@@ -0,0 +1,452 @@
import {
toTransient as to_transient,
fromTransient as from_transient,
size,
fold,
make as new$,
destructiveTransientInsert as transient_insert,
has as has_key,
get,
insert,
map as map_values,
destructiveTransientUpdateWith as transient_update_with,
destructiveTransientDelete as transient_delete,
} from "../dict.mjs";
import { Ok, toList, Empty as $Empty, prepend as listPrepend } from "../gleam.mjs";
import * as $option from "../gleam/option.mjs";
export { fold, get, has_key, insert, map_values, new$, size };
/**
* Determines whether or not the dict is empty.
*
* ## Examples
*
* ```gleam
* assert new() |> is_empty
* ```
*
* ```gleam
* assert !{ new() |> insert("b", 1) |> is_empty }
* ```
*/
export function is_empty(dict) {
return size(dict) === 0;
}
/**
* Converts the dict to a list of 2-element tuples `#(key, value)`, one for
* each key-value pair in the dict.
*
* The tuples in the list have no specific order.
*
* ## Examples
*
* Calling `to_list` on an empty `dict` returns an empty list.
*
* ```gleam
* assert new() |> to_list == []
* ```
*
* The ordering of elements in the resulting list is an implementation detail
* that should not be relied upon.
*
* ```gleam
* assert new()
* |> insert("b", 1)
* |> insert("a", 0)
* |> insert("c", 2)
* |> to_list
* == [#("a", 0), #("b", 1), #("c", 2)]
* ```
*/
export function to_list(dict) {
return fold(
dict,
toList([]),
(acc, key, value) => { return listPrepend([key, value], acc); },
);
}
function from_list_loop(loop$transient, loop$list) {
while (true) {
let transient = loop$transient;
let list = loop$list;
if (list instanceof $Empty) {
return from_transient(transient);
} else {
let rest = list.tail;
let key = list.head[0];
let value = list.head[1];
loop$transient = transient_insert(key, value, transient);
loop$list = rest;
}
}
}
/**
* Converts a list of 2-element tuples `#(key, value)` to a dict.
*
* If two tuples have the same key the last one in the list will be the one
* that is present in the dict.
*/
export function from_list(list) {
return from_list_loop(to_transient(new$()), list);
}
/**
* Gets a list of all keys in a given dict.
*
* Dicts are not ordered so the keys are not returned in any specific order. Do
* not write code that relies on the order keys are returned by this function
* as it may change in later versions of Gleam or Erlang.
*
* ## Examples
*
* ```gleam
* assert from_list([#("a", 0), #("b", 1)]) |> keys == ["a", "b"]
* ```
*/
export function keys(dict) {
return fold(
dict,
toList([]),
(acc, key, _) => { return listPrepend(key, acc); },
);
}
/**
* Gets a list of all values in a given dict.
*
* Dicts are not ordered so the values are not returned in any specific order. Do
* not write code that relies on the order values are returned by this function
* as it may change in later versions of Gleam or Erlang.
*
* ## Examples
*
* ```gleam
* assert from_list([#("a", 0), #("b", 1)]) |> values == [0, 1]
* ```
*/
export function values(dict) {
return fold(
dict,
toList([]),
(acc, _, value) => { return listPrepend(value, acc); },
);
}
function do_filter(f, dict) {
let _pipe = to_transient(new$());
let _pipe$1 = fold(
dict,
_pipe,
(transient, key, value) => {
let $ = f(key, value);
if ($) {
return transient_insert(key, value, transient);
} else {
return transient;
}
},
);
return from_transient(_pipe$1);
}
/**
* Creates a new dict from a given dict, minus any entries that a given function
* returns `False` for.
*
* ## Examples
*
* ```gleam
* assert from_list([#("a", 0), #("b", 1)])
* |> filter(fn(key, value) { value != 0 })
* == from_list([#("b", 1)])
* ```
*
* ```gleam
* assert from_list([#("a", 0), #("b", 1)])
* |> filter(fn(key, value) { True })
* == from_list([#("a", 0), #("b", 1)])
* ```
*/
export function filter(dict, predicate) {
return do_filter(predicate, dict);
}
function do_take_loop(loop$dict, loop$desired_keys, loop$acc) {
while (true) {
let dict = loop$dict;
let desired_keys = loop$desired_keys;
let acc = loop$acc;
if (desired_keys instanceof $Empty) {
return from_transient(acc);
} else {
let key = desired_keys.head;
let rest = desired_keys.tail;
let $ = get(dict, key);
if ($ instanceof Ok) {
let value = $[0];
loop$dict = dict;
loop$desired_keys = rest;
loop$acc = transient_insert(key, value, acc);
} else {
loop$dict = dict;
loop$desired_keys = rest;
loop$acc = acc;
}
}
}
}
function do_take(desired_keys, dict) {
return do_take_loop(dict, desired_keys, to_transient(new$()));
}
/**
* Creates a new dict from a given dict, only including any entries for which the
* keys are in a given list.
*
* ## Examples
*
* ```gleam
* assert from_list([#("a", 0), #("b", 1)])
* |> take(["b"])
* == from_list([#("b", 1)])
* ```
*
* ```gleam
* assert from_list([#("a", 0), #("b", 1)])
* |> take(["a", "b", "c"])
* == from_list([#("a", 0), #("b", 1)])
* ```
*/
export function take(dict, desired_keys) {
return do_take(desired_keys, dict);
}
function do_combine(combine, left, right) {
let _block;
let $1 = size(left) >= size(right);
if ($1) {
_block = [left, right, combine];
} else {
_block = [right, left, (k, l, r) => { return combine(k, r, l); }];
}
let $ = _block;
let big = $[0];
let small = $[1];
let combine$1 = $[2];
let _pipe = to_transient(big);
let _pipe$1 = fold(
small,
_pipe,
(transient, key, value) => {
let update = (existing) => { return combine$1(key, existing, value); };
return transient_update_with(key, update, value, transient);
},
);
return from_transient(_pipe$1);
}
/**
* Creates a new dict from a pair of given dicts by combining their entries.
*
* If there are entries with the same keys in both dicts the given function is
* used to determine the new value to use in the resulting dict.
*
* ## Examples
*
* ```gleam
* let a = from_list([#("a", 0), #("b", 1)])
* let b = from_list([#("a", 2), #("c", 3)])
* assert combine(a, b, fn(one, other) { one + other })
* == from_list([#("a", 2), #("b", 1), #("c", 3)])
* ```
*/
export function combine(dict, other, fun) {
return do_combine((_, l, r) => { return fun(l, r); }, dict, other);
}
/**
* Creates a new dict from a pair of given dicts by combining their entries.
*
* If there are entries with the same keys in both dicts the entry from the
* second dict takes precedence.
*
* ## Examples
*
* ```gleam
* let a = from_list([#("a", 0), #("b", 1)])
* let b = from_list([#("b", 2), #("c", 3)])
* assert merge(a, b) == from_list([#("a", 0), #("b", 2), #("c", 3)])
* ```
*/
export function merge(dict, new_entries) {
return combine(dict, new_entries, (_, new_entry) => { return new_entry; });
}
/**
* Creates a new dict from a given dict with all the same entries except for the
* one with a given key, if it exists.
*
* ## Examples
*
* ```gleam
* assert from_list([#("a", 0), #("b", 1)]) |> delete("a")
* == from_list([#("b", 1)])
* ```
*
* ```gleam
* assert from_list([#("a", 0), #("b", 1)]) |> delete("c")
* == from_list([#("a", 0), #("b", 1)])
* ```
*/
export function delete$(dict, key) {
let _pipe = to_transient(dict);
let _pipe$1 = ((_capture) => { return transient_delete(key, _capture); })(
_pipe,
);
return from_transient(_pipe$1);
}
function drop_loop(loop$transient, loop$disallowed_keys) {
while (true) {
let transient = loop$transient;
let disallowed_keys = loop$disallowed_keys;
if (disallowed_keys instanceof $Empty) {
return from_transient(transient);
} else {
let key = disallowed_keys.head;
let rest = disallowed_keys.tail;
loop$transient = transient_delete(key, transient);
loop$disallowed_keys = rest;
}
}
}
function do_drop(disallowed_keys, dict) {
return drop_loop(to_transient(dict), disallowed_keys);
}
/**
* Creates a new dict from a given dict with all the same entries except any with
* keys found in a given list.
*
* ## Examples
*
* ```gleam
* assert from_list([#("a", 0), #("b", 1)]) |> drop(["a"])
* == from_list([#("b", 1)])
* ```
*
* ```gleam
* assert from_list([#("a", 0), #("b", 1)]) |> drop(["c"])
* == from_list([#("a", 0), #("b", 1)])
* ```
*
* ```gleam
* assert from_list([#("a", 0), #("b", 1)]) |> drop(["a", "b", "c"])
* == from_list([])
* ```
*/
export function drop(dict, disallowed_keys) {
return do_drop(disallowed_keys, dict);
}
/**
* Creates a new dict with one entry inserted or updated using a given function.
*
* If there was not an entry in the dict for the given key then the function
* gets `None` as its argument, otherwise it gets `Some(value)`.
*
* ## Examples
*
* ```gleam
* let dict = from_list([#("a", 0)])
* let increment = fn(x) {
* case x {
* Some(i) -> i + 1
* None -> 0
* }
* }
*
* assert upsert(dict, "a", increment) == from_list([#("a", 1)])
* ```
*
* ```gleam
* assert upsert(dict, "b", increment) == from_list([#("a", 0), #("b", 0)])
* ```
*/
export function upsert(dict, key, fun) {
let $ = get(dict, key);
if ($ instanceof Ok) {
let value = $[0];
return insert(dict, key, fun(new $option.Some(value)));
} else {
return insert(dict, key, fun(new $option.None()));
}
}
/**
* Calls a function for each key and value in a dict, discarding the return
* value.
*
* Useful for producing a side effect for every item of a dict.
*
* ```gleam
* import gleam/io
*
* let dict = from_list([#("a", "apple"), #("b", "banana"), #("c", "cherry")])
*
* assert
* each(dict, fn(k, v) {
* io.println(k <> " => " <> v)
* })
* == Nil
* // a => apple
* // b => banana
* // c => cherry
* ```
*
* The order of elements in the iteration is an implementation detail that
* should not be relied upon.
*/
export function each(dict, fun) {
return fold(
dict,
undefined,
(nil, k, v) => {
fun(k, v);
return nil;
},
);
}
function group_loop(loop$transient, loop$to_key, loop$list) {
while (true) {
let transient = loop$transient;
let to_key = loop$to_key;
let list = loop$list;
if (list instanceof $Empty) {
return from_transient(transient);
} else {
let value = list.head;
let rest = list.tail;
let key = to_key(value);
let update = (existing) => { return listPrepend(value, existing); };
let _pipe = transient;
let _pipe$1 = ((_capture) => {
return transient_update_with(key, update, toList([value]), _capture);
})(_pipe);
loop$transient = _pipe$1;
loop$to_key = to_key;
loop$list = rest;
}
}
}
export function group(key, list) {
return group_loop(to_transient(new$()), key, list);
}
@@ -0,0 +1,35 @@
import * as $dict from "../gleam/dict.mjs";
import {
classify_dynamic as classify,
identity as bool,
identity as string,
identity as float,
identity as int,
identity as bit_array,
identity as list,
list_to_array as array,
identity as cast,
} from "../gleam_stdlib.mjs";
export { array, bit_array, bool, classify, float, int, list, string };
/**
* Create a dynamic value made of an unordered series of keys and values, where
* the keys are unique.
*
* On Erlang this will be a map, on JavaScript this will be a Gleam dict
* object.
*/
export function properties(entries) {
return cast($dict.from_list(entries));
}
/**
* A dynamic value representing nothing.
*
* On Erlang this will be the atom `nil`, on JavaScript this will be
* `undefined`.
*/
export function nil() {
return cast(undefined);
}
@@ -0,0 +1,963 @@
import {
Ok,
Error,
toList,
Empty as $Empty,
prepend as listPrepend,
CustomType as $CustomType,
isEqual,
} from "../../gleam.mjs";
import * as $bit_array from "../../gleam/bit_array.mjs";
import * as $dict from "../../gleam/dict.mjs";
import * as $dynamic from "../../gleam/dynamic.mjs";
import * as $float from "../../gleam/float.mjs";
import * as $int from "../../gleam/int.mjs";
import * as $list from "../../gleam/list.mjs";
import * as $option from "../../gleam/option.mjs";
import { None, Some } from "../../gleam/option.mjs";
import {
float as dynamic_float,
int as dynamic_int,
bit_array as dynamic_bit_array,
string as dynamic_string,
identity as cast,
list as decode_list,
index as bare_index,
dict as decode_dict,
is_null,
} from "../../gleam_stdlib.mjs";
export class DecodeError extends $CustomType {
constructor(expected, found, path) {
super();
this.expected = expected;
this.found = found;
this.path = path;
}
}
export const DecodeError$DecodeError = (expected, found, path) =>
new DecodeError(expected, found, path);
export const DecodeError$isDecodeError = (value) =>
value instanceof DecodeError;
export const DecodeError$DecodeError$expected = (value) => value.expected;
export const DecodeError$DecodeError$0 = (value) => value.expected;
export const DecodeError$DecodeError$found = (value) => value.found;
export const DecodeError$DecodeError$1 = (value) => value.found;
export const DecodeError$DecodeError$path = (value) => value.path;
export const DecodeError$DecodeError$2 = (value) => value.path;
class Decoder extends $CustomType {
constructor(function$) {
super();
this.function = function$;
}
}
/**
* A decoder that decodes `Dynamic` values. This decoder never returns an error.
*
* ## Examples
*
* ```gleam
* let result = decode.run(dynamic.float(3.14), decode.dynamic)
* assert result == Ok(dynamic.float(3.14))
* ```
*/
export const dynamic = /* @__PURE__ */ new Decoder(decode_dynamic);
/**
* A decoder that decodes `Float` values.
*
* This will not coerse int values into float values, so on platforms with
* distinct runtime int and float types (Erlang, not JavaScript) it will fail
* for ints. One time this may happen is when decoding JSON data.
*
* If you want to decode both ints and floats you may want to use the `one_of`
* function.
*
* ## Examples
*
* ```gleam
* let result = decode.run(dynamic.float(3.14), decode.float)
* assert result == Ok(3.14)
* ```
*/
export const float = /* @__PURE__ */ new Decoder(decode_float);
/**
* A decoder that decodes `Int` values.
*
* This will not coerse float values into int values, so on platforms with
* distinct runtime int and float types (Erlang, not JavaScript) it will fail,
* even if the float is a whole number (e.g. 1.0).
*
* If you want to decode both ints and floats you may want to use the `one_of`
* function.
*
* ## Examples
*
* ```gleam
* let result = decode.run(dynamic.int(147), decode.int)
* assert result == Ok(147)
* ```
*/
export const int = /* @__PURE__ */ new Decoder(decode_int);
/**
* A decoder that decodes `BitArray` values. This decoder never returns an error.
*
* ## Examples
*
* ```gleam
* let result = decode.run(dynamic.bit_array(<<5, 7>>), decode.bit_array)
* assert result == Ok(<<5, 7>>)
* ```
*/
export const bit_array = /* @__PURE__ */ new Decoder(decode_bit_array);
/**
* A decoder that decodes `String` values.
*
* ## Examples
*
* ```gleam
* let result = decode.run(dynamic.string("Hello!"), decode.string)
* assert result == Ok("Hello!")
* ```
*/
export const string = /* @__PURE__ */ new Decoder(decode_string);
/**
* A decoder that decodes `Bool` values.
*
* ## Examples
*
* ```gleam
* let result = decode.run(dynamic.bool(True), decode.bool)
* assert result == Ok(True)
* ```
*/
export const bool = /* @__PURE__ */ new Decoder(decode_bool);
function decode_dynamic(data) {
return [data, toList([])];
}
/**
* Run a decoder on a `Dynamic` value, decoding the value if it is of the
* desired type, or returning errors.
*
* ## Examples
*
* ```gleam
* let decoder = {
* use name <- decode.field("name", decode.string)
* use email <- decode.field("email", decode.string)
* decode.success(SignUp(name: name, email: email))
* }
*
* decode.run(data, decoder)
* ```
*/
export function run(data, decoder) {
let $ = decoder.function(data);
let maybe_invalid_data = $[0];
let errors = $[1];
if (errors instanceof $Empty) {
return new Ok(maybe_invalid_data);
} else {
return new Error(errors);
}
}
function run_dynamic_function(data, name, f) {
let $ = f(data);
if ($ instanceof Ok) {
let data$1 = $[0];
return [data$1, toList([])];
} else {
let placeholder = $[0];
return [
placeholder,
toList([new DecodeError(name, $dynamic.classify(data), toList([]))]),
];
}
}
function decode_float(data) {
return run_dynamic_function(data, "Float", dynamic_float);
}
/**
* Apply a transformation function to any value decoded by the decoder.
*
* ## Examples
*
* ```gleam
* let decoder = decode.int |> decode.map(int.to_string)
* let result = decode.run(dynamic.int(1000), decoder)
* assert result == Ok("1000")
* ```
*/
export function map(decoder, transformer) {
return new Decoder(
(d) => {
let $ = decoder.function(d);
let data = $[0];
let errors = $[1];
return [transformer(data), errors];
},
);
}
function decode_int(data) {
return run_dynamic_function(data, "Int", dynamic_int);
}
function decode_bit_array(data) {
return run_dynamic_function(data, "BitArray", dynamic_bit_array);
}
function decode_string(data) {
return run_dynamic_function(data, "String", dynamic_string);
}
function run_decoders(loop$data, loop$failure, loop$decoders) {
while (true) {
let data = loop$data;
let failure = loop$failure;
let decoders = loop$decoders;
if (decoders instanceof $Empty) {
return failure;
} else {
let decoder = decoders.head;
let decoders$1 = decoders.tail;
let $ = decoder.function(data);
let layer = $;
let errors = $[1];
if (errors instanceof $Empty) {
return layer;
} else {
loop$data = data;
loop$failure = failure;
loop$decoders = decoders$1;
}
}
}
}
/**
* Create a new decoder from several other decoders. Each of the inner
* decoders is run in turn, and the value from the first to succeed is used.
*
* If no decoder succeeds then the errors from the first decoder are used.
* If you wish for different errors then you may wish to use the
* `collapse_errors` or `map_errors` functions.
*
* ## Examples
*
* ```gleam
* let decoder = decode.one_of(decode.string, or: [
* decode.int |> decode.map(int.to_string),
* decode.float |> decode.map(float.to_string),
* ])
* assert decode.run(dynamic.int(1000), decoder) == Ok("1000")
* ```
*/
export function one_of(first, alternatives) {
return new Decoder(
(dynamic_data) => {
let $ = first.function(dynamic_data);
let layer = $;
let errors = $[1];
if (errors instanceof $Empty) {
return layer;
} else {
return run_decoders(dynamic_data, layer, alternatives);
}
},
);
}
function path_segment_to_string(key) {
let decoder = one_of(
string,
toList([
(() => {
let _pipe = int;
return map(_pipe, $int.to_string);
})(),
(() => {
let _pipe = float;
return map(_pipe, $float.to_string);
})(),
]),
);
let $ = run(key, decoder);
if ($ instanceof Ok) {
let key$1 = $[0];
return key$1;
} else {
return ("<" + $dynamic.classify(key)) + ">";
}
}
function push_path(layer, path) {
let path$1 = $list.map(
path,
(key) => {
let _pipe = key;
let _pipe$1 = cast(_pipe);
return path_segment_to_string(_pipe$1);
},
);
let errors = $list.map(
layer[1],
(error) => {
return new DecodeError(
error.expected,
error.found,
$list.append(path$1, error.path),
);
},
);
return [layer[0], errors];
}
/**
* A decoder that decodes lists where all elements are decoded with a given
* decoder.
*
* ## Examples
*
* ```gleam
* let result =
* [1, 2, 3]
* |> list.map(dynamic.int)
* |> dynamic.list
* |> decode.run(decode.list(of: decode.int))
* assert result == Ok([1, 2, 3])
* ```
*/
export function list(inner) {
return new Decoder(
(data) => {
return decode_list(
data,
inner.function,
(p, k) => { return push_path(p, toList([k])); },
0,
toList([]),
);
},
);
}
function index(
loop$path,
loop$position,
loop$inner,
loop$data,
loop$handle_miss
) {
while (true) {
let path = loop$path;
let position = loop$position;
let inner = loop$inner;
let data = loop$data;
let handle_miss = loop$handle_miss;
if (path instanceof $Empty) {
let _pipe = data;
let _pipe$1 = inner(_pipe);
return push_path(_pipe$1, $list.reverse(position));
} else {
let key = path.head;
let path$1 = path.tail;
let $ = bare_index(data, key);
if ($ instanceof Ok) {
let $1 = $[0];
if ($1 instanceof Some) {
let data$1 = $1[0];
loop$path = path$1;
loop$position = listPrepend(key, position);
loop$inner = inner;
loop$data = data$1;
loop$handle_miss = handle_miss;
} else {
return handle_miss(data, listPrepend(key, position));
}
} else {
let kind = $[0];
let $1 = inner(data);
let default$ = $1[0];
let _pipe = [
default$,
toList([new DecodeError(kind, $dynamic.classify(data), toList([]))]),
];
return push_path(_pipe, $list.reverse(position));
}
}
}
}
/**
* The same as [`field`](#field), except taking a path to the value rather
* than a field name.
*
* This function will index into dictionaries with any key type, and if the key is
* an int then it'll also index into Erlang tuples and JavaScript arrays, and
* the first eight elements of Gleam lists.
*
* ## Examples
*
* ```gleam
* let data = dynamic.properties([
* #(dynamic.string("data"), dynamic.properties([
* #(dynamic.string("email"), dynamic.string("lucy@example.com")),
* #(dynamic.string("name"), dynamic.string("Lucy")),
* ])
* ])
*
* let decoder = {
* use name <- decode.subfield(["data", "name"], decode.string)
* use email <- decode.subfield(["data", "email"], decode.string)
* decode.success(SignUp(name: name, email: email))
* }
* let result = decode.run(data, decoder)
* assert result == Ok(SignUp(name: "Lucy", email: "lucy@example.com"))
* ```
*/
export function subfield(field_path, field_decoder, next) {
return new Decoder(
(data) => {
let $ = index(
field_path,
toList([]),
field_decoder.function,
data,
(data, position) => {
let $1 = field_decoder.function(data);
let default$ = $1[0];
let _pipe = [
default$,
toList([new DecodeError("Field", "Nothing", toList([]))]),
];
return push_path(_pipe, $list.reverse(position));
},
);
let out = $[0];
let errors1 = $[1];
let $1 = next(out).function(data);
let out$1 = $1[0];
let errors2 = $1[1];
return [out$1, $list.append(errors1, errors2)];
},
);
}
/**
* A decoder that decodes a value that is nested within other values. For
* example, decoding a value that is within some deeply nested JSON objects.
*
* This function will index into dictionaries with any key type, and if the key is
* an int then it'll also index into Erlang tuples and JavaScript arrays, and
* the first eight elements of Gleam lists.
*
* ## Examples
*
* ```gleam
* let decoder = decode.at(["one", "two"], decode.int)
*
* let data = dynamic.properties([
* #(dynamic.string("one"), dynamic.properties([
* #(dynamic.string("two"), dynamic.int(1000)),
* ]),
* ])
*
* assert decode.run(data, decoder) == Ok(1000)
* ```
*
* ```gleam
* assert dynamic.nil()
* |> decode.run(decode.optional(decode.int))
* == Ok(option.None)
* ```
*/
export function at(path, inner) {
return new Decoder(
(data) => {
return index(
path,
toList([]),
inner.function,
data,
(data, position) => {
let $ = inner.function(data);
let default$ = $[0];
let _pipe = [
default$,
toList([new DecodeError("Field", "Nothing", toList([]))]),
];
return push_path(_pipe, $list.reverse(position));
},
);
},
);
}
/**
* Finalise a decoder having successfully extracted a value.
*
* ## Examples
*
* ```gleam
* let data = dynamic.properties([
* #(dynamic.string("email"), dynamic.string("lucy@example.com")),
* #(dynamic.string("name"), dynamic.string("Lucy")),
* ])
*
* let decoder = {
* use name <- decode.field("name", string)
* use email <- decode.field("email", string)
* decode.success(SignUp(name: name, email: email))
* }
*
* let result = decode.run(data, decoder)
* assert result == Ok(SignUp(name: "Lucy", email: "lucy@example.com"))
* ```
*/
export function success(data) {
return new Decoder((_) => { return [data, toList([])]; });
}
/**
* Construct a decode error for some unexpected dynamic data.
*/
export function decode_error(expected, found) {
return toList([
new DecodeError(expected, $dynamic.classify(found), toList([])),
]);
}
/**
* Run a decoder on a field of a `Dynamic` value, decoding the value if it is
* of the desired type, or returning errors. An error is returned if there is
* no field for the specified key.
*
* This function will index into dictionaries with any key type, and if the key is
* an int then it'll also index into Erlang tuples and JavaScript arrays, and
* the first eight elements of Gleam lists.
*
* ## Examples
*
* ```gleam
* let data = dynamic.properties([
* #(dynamic.string("email"), dynamic.string("lucy@example.com")),
* #(dynamic.string("name"), dynamic.string("Lucy")),
* ])
*
* let decoder = {
* use name <- decode.field("name", string)
* use email <- decode.field("email", string)
* decode.success(SignUp(name: name, email: email))
* }
*
* let result = decode.run(data, decoder)
* assert result == Ok(SignUp(name: "Lucy", email: "lucy@example.com"))
* ```
*
* If you wish to decode a value that is more deeply nested within the dynamic
* data, see [`subfield`](#subfield) and [`at`](#at).
*
* If you wish to return a default in the event that a field is not present,
* see [`optional_field`](#optional_field) and / [`optionally_at`](#optionally_at).
*/
export function field(field_name, field_decoder, next) {
return subfield(toList([field_name]), field_decoder, next);
}
/**
* Run a decoder on a field of a `Dynamic` value, decoding the value if it is
* of the desired type, or returning errors. The given default value is
* returned if there is no field for the specified key.
*
* This function will index into dictionaries with any key type, and if the key is
* an int then it'll also index into Erlang tuples and JavaScript arrays, and
* the first eight elements of Gleam lists.
*
* ## Examples
*
* ```gleam
* let data = dynamic.properties([
* #(dynamic.string("name"), dynamic.string("Lucy")),
* ])
*
* let decoder = {
* use name <- decode.field("name", string)
* use email <- decode.optional_field("email", "n/a", string)
* decode.success(SignUp(name: name, email: email))
* }
*
* let result = decode.run(data, decoder)
* assert result == Ok(SignUp(name: "Lucy", email: "n/a"))
* ```
*/
export function optional_field(key, default$, field_decoder, next) {
return new Decoder(
(data) => {
let _block;
let _block$1;
let $1 = bare_index(data, key);
if ($1 instanceof Ok) {
let $2 = $1[0];
if ($2 instanceof Some) {
let data$1 = $2[0];
_block$1 = field_decoder.function(data$1);
} else {
_block$1 = [default$, toList([])];
}
} else {
let kind = $1[0];
_block$1 = [
default$,
toList([new DecodeError(kind, $dynamic.classify(data), toList([]))]),
];
}
let _pipe = _block$1;
_block = push_path(_pipe, toList([key]));
let $ = _block;
let out = $[0];
let errors1 = $[1];
let $2 = next(out).function(data);
let out$1 = $2[0];
let errors2 = $2[1];
return [out$1, $list.append(errors1, errors2)];
},
);
}
/**
* A decoder that decodes a value that is nested within other values. For
* example, decoding a value that is within some deeply nested JSON objects.
*
* This function will index into dictionaries with any key type, and if the key is
* an int then it'll also index into Erlang tuples and JavaScript arrays, and
* the first eight elements of Gleam lists.
*
* ## Examples
*
* ```gleam
* let decoder = decode.optionally_at(["one", "two"], 100, decode.int)
*
* let data = dynamic.properties([
* #(dynamic.string("one"), dynamic.properties([])),
* ])
*
* assert decode.run(data, decoder) == Ok(100)
* ```
*/
export function optionally_at(path, default$, inner) {
return new Decoder(
(data) => {
return index(
path,
toList([]),
inner.function,
data,
(_, _1) => { return [default$, toList([])]; },
);
},
);
}
function decode_bool(data) {
let $ = isEqual(cast(true), data);
if ($) {
return [true, toList([])];
} else {
let $1 = isEqual(cast(false), data);
if ($1) {
return [false, toList([])];
} else {
return [false, decode_error("Bool", data)];
}
}
}
function fold_dict(acc, key, value, key_decoder, value_decoder) {
let $ = key_decoder(key);
let $1 = $[1];
if ($1 instanceof $Empty) {
let key_decoded = $[0];
let $2 = value_decoder(value);
let $3 = $2[1];
if ($3 instanceof $Empty) {
let value$1 = $2[0];
let dict$1 = $dict.insert(acc[0], key_decoded, value$1);
return [dict$1, acc[1]];
} else {
let errors = $3;
let key_identifier = path_segment_to_string(key);
return push_path([$dict.new$(), errors], toList([key_identifier]));
}
} else {
let errors = $1;
return push_path([$dict.new$(), errors], toList(["keys"]));
}
}
/**
* A decoder that decodes dicts where all keys and values are decoded with
* given decoders.
*
* ## Examples
*
* ```gleam
* let values = dynamic.properties([
* #(dynamic.string("one"), dynamic.int(1)),
* #(dynamic.string("two"), dynamic.int(2)),
* ])
*
* let result =
* decode.run(values, decode.dict(decode.string, decode.int))
* assert result == Ok(values)
* ```
*/
export function dict(key, value) {
return new Decoder(
(data) => {
let $ = decode_dict(data);
if ($ instanceof Ok) {
let dict$1 = $[0];
return $dict.fold(
dict$1,
[$dict.new$(), toList([])],
(a, k, v) => {
let $1 = a[1];
if ($1 instanceof $Empty) {
return fold_dict(a, k, v, key.function, value.function);
} else {
return a;
}
},
);
} else {
return [$dict.new$(), decode_error("Dict", data)];
}
},
);
}
/**
* A decoder that decodes nullable values of a type decoded by with a given
* decoder.
*
* This function can handle common representations of null on all runtimes, such as
* `nil`, `null`, and `undefined` on Erlang, and `undefined` and `null` on
* JavaScript.
*
* ## Examples
*
* ```gleam
* let result = decode.run(dynamic.int(100), decode.optional(decode.int))
* assert result == Ok(option.Some(100))
* ```
*
* ```gleam
* let result = decode.run(dynamic.nil(), decode.optional(decode.int))
* assert result == Ok(option.None)
* ```
*/
export function optional(inner) {
return new Decoder(
(data) => {
let $ = is_null(data);
if ($) {
return [new $option.None(), toList([])];
} else {
let $1 = inner.function(data);
let data$1 = $1[0];
let errors = $1[1];
return [new $option.Some(data$1), errors];
}
},
);
}
/**
* Apply a transformation function to any errors returned by the decoder.
*/
export function map_errors(decoder, transformer) {
return new Decoder(
(d) => {
let $ = decoder.function(d);
let data = $[0];
let errors = $[1];
return [data, transformer(errors)];
},
);
}
/**
* Replace all errors produced by a decoder with one single error for a named
* expected type.
*
* This function may be useful if you wish to simplify errors before
* presenting them to a user, particularly when using the `one_of` function.
*
* ## Examples
*
* ```gleam
* let decoder = decode.string |> decode.collapse_errors("MyThing")
* let result = decode.run(dynamic.int(1000), decoder)
* assert result == Error([DecodeError("MyThing", "Int", [])])
* ```
*/
export function collapse_errors(decoder, name) {
return new Decoder(
(dynamic_data) => {
let $ = decoder.function(dynamic_data);
let layer = $;
let data = $[0];
let errors = $[1];
if (errors instanceof $Empty) {
return layer;
} else {
return [data, decode_error(name, dynamic_data)];
}
},
);
}
/**
* Create a new decoder based upon the value of a previous decoder.
*
* This may be useful to run one previous decoder to use in further decoding.
*/
export function then$(decoder, next) {
return new Decoder(
(dynamic_data) => {
let $ = decoder.function(dynamic_data);
let data = $[0];
let errors = $[1];
let decoder$1 = next(data);
let $1 = decoder$1.function(dynamic_data);
let layer = $1;
let data$1 = $1[0];
if (errors instanceof $Empty) {
return layer;
} else {
return [data$1, errors];
}
},
);
}
/**
* Define a decoder that always fails.
*
* The first parameter is a "placeholder" value, which is some default value that the
* decoder uses internally in place of the value that would have been produced
* if the decoder was successful. It doesn't matter what this value is, it is
* never returned by the decoder or shown to the user, so pick some arbitrary
* value. If it is an int you might pick `0`, if it is a list you might pick
* `[]`.
*
* The second parameter is the name of the type that has failed to decode.
*
* ```gleam
* decode.failure(User(name: "", score: 0, tags: []), expected: "User")
* ```
*/
export function failure(placeholder, name) {
return new Decoder((d) => { return [placeholder, decode_error(name, d)]; });
}
/**
* Create a decoder for a new data type from a decoding function.
*
* This function is used for new primitive types. For example, you might
* define a decoder for Erlang's pid type.
*
* A default "placeholder" value is also required to make a decoder. When this
* decoder is used as part of a larger decoder this placeholder value is used
* so that the rest of the decoder can continue to run and
* collect all decoding errors. It doesn't matter what this value is, it is
* never returned by the decoder or shown to the user, so pick some arbitrary
* value. If it is an int you might pick `0`, if it is a list you might pick
* `[]`.
*
* If you were to make a decoder for the `Int` type (rather than using the
* built-in `Int` decoder) you would define it like so:
*
* ```gleam
* pub fn int_decoder() -> decode.Decoder(Int) {
* let default = ""
* decode.new_primitive_decoder("Int", int_from_dynamic)
* }
*
* @external(erlang, "my_module", "int_from_dynamic")
* fn int_from_dynamic(data: Int) -> Result(Int, Int)
* ```
*
* ```erlang
* -module(my_module).
* -export([int_from_dynamic/1]).
*
* int_from_dynamic(Data) ->
* case is_integer(Data) of
* true -> {ok, Data};
* false -> {error, 0}
* end.
* ```
*/
export function new_primitive_decoder(name, decoding_function) {
return new Decoder(
(d) => {
let $ = decoding_function(d);
if ($ instanceof Ok) {
let t = $[0];
return [t, toList([])];
} else {
let placeholder = $[0];
return [
placeholder,
toList([new DecodeError(name, $dynamic.classify(d), toList([]))]),
];
}
},
);
}
/**
* Create a decoder that can refer to itself, useful for decoding deeply
* nested data.
*
* Attempting to create a recursive decoder without this function could result
* in an infinite loop. If you are using `field` or other `use`able functions
* then you may not need to use this function.
*
* ## Examples
*
* ```gleam
* type Nested {
* Nested(List(Nested))
* Value(String)
* }
*
* fn nested_decoder() -> decode.Decoder(Nested) {
* use <- decode.recursive
* decode.one_of(decode.string |> decode.map(Value), [
* decode.list(nested_decoder()) |> decode.map(Nested),
* ])
* }
* ```
*/
export function recursive(inner) {
return new Decoder(
(data) => {
let decoder = inner();
return decoder.function(data);
},
);
}
+528
View File
@@ -0,0 +1,528 @@
import { Ok, Error, Empty as $Empty, divideFloat } from "../gleam.mjs";
import * as $order from "../gleam/order.mjs";
import {
parse_float as parse,
float_to_string as to_string,
ceiling,
floor,
round as js_round,
truncate,
identity as do_to_float,
power as do_power,
random_uniform as random,
log as do_log,
exp as exponential,
} from "../gleam_stdlib.mjs";
export { ceiling, exponential, floor, parse, random, to_string, truncate };
/**
* Compares two `Float`s, returning the larger of the two.
*
* ## Examples
*
* ```gleam
* assert max(2.0, 2.3) == 2.3
* ```
*/
export function max(a, b) {
let $ = a > b;
if ($) {
return a;
} else {
return b;
}
}
/**
* Compares two `Float`s, returning the smaller of the two.
*
* ## Examples
*
* ```gleam
* assert min(2.0, 2.3) == 2.0
* ```
*/
export function min(a, b) {
let $ = a < b;
if ($) {
return a;
} else {
return b;
}
}
/**
* Restricts a float between two bounds.
*
* Note: If the `min` argument is larger than the `max` argument then they
* will be swapped, so the minimum bound is always lower than the maximum
* bound.
*
*
* ## Examples
*
* ```gleam
* assert clamp(1.2, min: 1.4, max: 1.6) == 1.4
* ```
*
* ```gleam
* assert clamp(1.2, min: 1.4, max: 0.6) == 1.2
* ```
*/
export function clamp(x, min_bound, max_bound) {
let $ = min_bound >= max_bound;
if ($) {
let _pipe = x;
let _pipe$1 = min(_pipe, min_bound);
return max(_pipe$1, max_bound);
} else {
let _pipe = x;
let _pipe$1 = min(_pipe, max_bound);
return max(_pipe$1, min_bound);
}
}
/**
* Compares two `Float`s, returning an `Order`:
* `Lt` for lower than, `Eq` for equals, or `Gt` for greater than.
*
* ## Examples
*
* ```gleam
* assert compare(2.0, 2.3) == Lt
* ```
*
* To handle
* [Floating Point Imprecision](https://en.wikipedia.org/wiki/Floating-point_arithmetic#Accuracy_problems)
* you may use [`loosely_compare`](#loosely_compare) instead.
*/
export function compare(a, b) {
let $ = a === b;
if ($) {
return new $order.Eq();
} else {
let $1 = a < b;
if ($1) {
return new $order.Lt();
} else {
return new $order.Gt();
}
}
}
/**
* Returns the absolute value of the input as a `Float`.
*
* ## Examples
*
* ```gleam
* assert absolute_value(-12.5) == 12.5
* ```
*
* ```gleam
* assert absolute_value(10.2) == 10.2
* ```
*/
export function absolute_value(x) {
let $ = x >= 0.0;
if ($) {
return x;
} else {
return 0.0 - x;
}
}
/**
* Compares two `Float`s within a tolerance, returning an `Order`:
* `Lt` for lower than, `Eq` for equals, or `Gt` for greater than.
*
* This function allows Float comparison while handling
* [Floating Point Imprecision](https://en.wikipedia.org/wiki/Floating-point_arithmetic#Accuracy_problems).
*
* Notice: For `Float`s the tolerance won't be exact:
* `5.3 - 5.0` is not exactly `0.3`.
*
* ## Examples
*
* ```gleam
* assert loosely_compare(5.0, with: 5.3, tolerating: 0.5) == Eq
* ```
*
* If you want to check only for equality you may use
* [`loosely_equals`](#loosely_equals) instead.
*/
export function loosely_compare(a, b, tolerance) {
let difference = absolute_value(a - b);
let $ = difference <= tolerance;
if ($) {
return new $order.Eq();
} else {
return compare(a, b);
}
}
/**
* Checks for equality of two `Float`s within a tolerance,
* returning a `Bool`.
*
* This function allows Float comparison while handling
* [Floating Point Imprecision](https://en.wikipedia.org/wiki/Floating-point_arithmetic#Accuracy_problems).
*
* Notice: For `Float`s the tolerance won't be exact:
* `5.3 - 5.0` is not exactly `0.3`.
*
* ## Examples
*
* ```gleam
* assert loosely_equals(5.0, with: 5.3, tolerating: 0.5)
* ```
*
* ```gleam
* assert !loosely_equals(5.0, with: 5.1, tolerating: 0.1)
* ```
*/
export function loosely_equals(a, b, tolerance) {
let difference = absolute_value(a - b);
return difference <= tolerance;
}
/**
* Returns the negative of the value provided.
*
* ## Examples
*
* ```gleam
* assert negate(1.0) == -1.0
* ```
*/
export function negate(x) {
return -1.0 * x;
}
/**
* Rounds the value to the nearest whole number as an `Int`.
*
* ## Examples
*
* ```gleam
* assert round(2.3) == 2
* ```
*
* ```gleam
* assert round(2.5) == 3
* ```
*/
export function round(x) {
let $ = x >= 0.0;
if ($) {
return js_round(x);
} else {
return 0 - js_round(negate(x));
}
}
/**
* Converts the value to a given precision as a `Float`.
* The precision is the number of allowed decimal places.
* Negative precisions are allowed and force rounding
* to the nearest tenth, hundredth, thousandth etc.
*
* ## Examples
*
* ```gleam
* assert to_precision(2.43434348473, 2) == 2.43
* ```
*
* ```gleam
* assert to_precision(547890.453444, -3) == 548000.0
* ```
*/
export function to_precision(x, precision) {
let $ = precision <= 0;
if ($) {
let factor = do_power(10.0, do_to_float(- precision));
return do_to_float(round(divideFloat(x, factor))) * factor;
} else {
let factor = do_power(10.0, do_to_float(precision));
return divideFloat(do_to_float(round(x * factor)), factor);
}
}
/**
* Returns the result of the base being raised to the power of the
* exponent, as a `Float`.
*
* ## Examples
*
* ```gleam
* assert power(2.0, -1.0) == Ok(0.5)
* ```
*
* ```gleam
* assert power(2.0, 2.0) == Ok(4.0)
* ```
*
* ```gleam
* assert power(8.0, 1.5) == Ok(22.627416997969522)
* ```
*
* ```gleam
* assert 4.0 |> power(of: 2.0) == Ok(16.0)
* ```
*
* ```gleam
* assert power(-1.0, 0.5) == Error(Nil)
* ```
*/
export function power(base, exponent) {
let fractional = (ceiling(exponent) - exponent) > 0.0;
let $ = ((base < 0.0) && fractional) || ((base === 0.0) && (exponent < 0.0));
if ($) {
return new Error(undefined);
} else {
return new Ok(do_power(base, exponent));
}
}
/**
* Returns the square root of the input as a `Float`.
*
* ## Examples
*
* ```gleam
* assert square_root(4.0) == Ok(2.0)
* ```
*
* ```gleam
* assert square_root(-16.0) == Error(Nil)
* ```
*/
export function square_root(x) {
return power(x, 0.5);
}
function sum_loop(loop$numbers, loop$initial) {
while (true) {
let numbers = loop$numbers;
let initial = loop$initial;
if (numbers instanceof $Empty) {
return initial;
} else {
let first = numbers.head;
let rest = numbers.tail;
loop$numbers = rest;
loop$initial = first + initial;
}
}
}
/**
* Sums a list of `Float`s.
*
* ## Example
*
* ```gleam
* assert sum([1.0, 2.2, 3.3]) == 6.5
* ```
*/
export function sum(numbers) {
return sum_loop(numbers, 0.0);
}
function product_loop(loop$numbers, loop$initial) {
while (true) {
let numbers = loop$numbers;
let initial = loop$initial;
if (numbers instanceof $Empty) {
return initial;
} else {
let first = numbers.head;
let rest = numbers.tail;
loop$numbers = rest;
loop$initial = first * initial;
}
}
}
/**
* Multiplies a list of `Float`s and returns the product.
*
* ## Example
*
* ```gleam
* assert product([2.5, 3.2, 4.2]) == 33.6
* ```
*/
export function product(numbers) {
return product_loop(numbers, 1.0);
}
/**
* Computes the modulo of a float division of inputs as a `Result`.
*
* Returns division of the inputs as a `Result`: If the given divisor equals
* `0`, this function returns an `Error`.
*
* The computed value will always have the same sign as the `divisor`.
*
* ## Examples
*
* ```gleam
* assert modulo(13.3, by: 3.3) == Ok(0.1)
* ```
*
* ```gleam
* assert modulo(-13.3, by: 3.3) == Ok(3.2)
* ```
*
* ```gleam
* assert modulo(13.3, by: -3.3) == Ok(-3.2)
* ```
*
* ```gleam
* assert modulo(-13.3, by: -3.3) == Ok(-0.1)
* ```
*/
export function modulo(dividend, divisor) {
if (divisor === 0.0) {
return new Error(undefined);
} else {
return new Ok(dividend - (floor(divideFloat(dividend, divisor)) * divisor));
}
}
/**
* Returns division of the inputs as a `Result`.
*
* ## Examples
*
* ```gleam
* assert divide(0.0, 1.0) == Ok(0.0)
* ```
*
* ```gleam
* assert divide(1.0, 0.0) == Error(Nil)
* ```
*/
export function divide(a, b) {
if (b === 0.0) {
return new Error(undefined);
} else {
let b$1 = b;
return new Ok(divideFloat(a, b$1));
}
}
/**
* Adds two floats together.
*
* It's the function equivalent of the `+.` operator.
* This function is useful in higher order functions or pipes.
*
* ## Examples
*
* ```gleam
* assert add(1.0, 2.0) == 3.0
* ```
*
* ```gleam
* import gleam/list
*
* assert list.fold([1.0, 2.0, 3.0], 0.0, add) == 6.0
* ```
*
* ```gleam
* assert 3.0 |> add(2.0) == 5.0
* ```
*/
export function add(a, b) {
return a + b;
}
/**
* Multiplies two floats together.
*
* It's the function equivalent of the `*.` operator.
* This function is useful in higher order functions or pipes.
*
* ## Examples
*
* ```gleam
* assert multiply(2.0, 4.0) == 8.0
* ```
*
* ```gleam
* import gleam/list
*
* assert list.fold([2.0, 3.0, 4.0], 1.0, multiply) == 24.0
* ```
*
* ```gleam
* assert 3.0 |> multiply(2.0) == 6.0
* ```
*/
export function multiply(a, b) {
return a * b;
}
/**
* Subtracts one float from another.
*
* It's the function equivalent of the `-.` operator.
* This function is useful in higher order functions or pipes.
*
* ## Examples
*
* ```gleam
* assert subtract(3.0, 1.0) == 2.0
* ```
*
* ```gleam
* import gleam/list
*
* assert list.fold([1.0, 2.0, 3.0], 10.0, subtract) == 4.0
* ```
*
* ```gleam
* assert 3.0 |> subtract(_, 2.0) == 1.0
* ```
*
* ```gleam
* assert 3.0 |> subtract(2.0, _) == -1.0
* ```
*/
export function subtract(a, b) {
return a - b;
}
/**
* Returns the natural logarithm (base e) of the given `Float` as a `Result`. If the
* input is less than or equal to 0, returns `Error(Nil)`.
*
* ## Examples
*
* ```gleam
* assert logarithm(1.0) == Ok(0.0)
* ```
*
* ```gleam
* assert logarithm(2.718281828459045) == Ok(1.0)
* ```
*
* ```gleam
* assert logarithm(0.0) == Error(Nil)
* ```
*
* ```gleam
* assert logarithm(-1.0) == Error(Nil)
* ```
*/
export function logarithm(x) {
let $ = x <= 0.0;
if ($) {
return new Error(undefined);
} else {
return new Ok(do_log(x));
}
}
@@ -0,0 +1,6 @@
/**
* Takes a single argument and always returns its input value.
*/
export function identity(x) {
return x;
}
+764
View File
@@ -0,0 +1,764 @@
import { Ok, Error, Empty as $Empty, remainderInt, divideInt } from "../gleam.mjs";
import * as $float from "../gleam/float.mjs";
import * as $order from "../gleam/order.mjs";
import {
identity as to_float,
parse_int as parse,
int_from_base_string as do_base_parse,
to_string,
int_to_base_string as do_to_base_string,
bitwise_and,
bitwise_not,
bitwise_or,
bitwise_exclusive_or,
bitwise_shift_left,
bitwise_shift_right,
} from "../gleam_stdlib.mjs";
export {
bitwise_and,
bitwise_exclusive_or,
bitwise_not,
bitwise_or,
bitwise_shift_left,
bitwise_shift_right,
parse,
to_float,
to_string,
};
/**
* Returns the absolute value of the input.
*
* ## Examples
*
* ```gleam
* assert absolute_value(-12) == 12
* ```
*
* ```gleam
* assert absolute_value(10) == 10
* ```
*/
export function absolute_value(x) {
let $ = x >= 0;
if ($) {
return x;
} else {
return x * -1;
}
}
/**
* Returns the result of the base being raised to the power of the
* exponent, as a `Float`.
*
* ## Examples
*
* ```gleam
* assert power(2, -1.0) == Ok(0.5)
* ```
*
* ```gleam
* assert power(2, 2.0) == Ok(4.0)
* ```
*
* ```gleam
* assert power(8, 1.5) == Ok(22.627416997969522)
* ```
*
* ```gleam
* assert 4 |> power(of: 2.0) == Ok(16.0)
* ```
*
* ```gleam
* assert power(-1, 0.5) == Error(Nil)
* ```
*/
export function power(base, exponent) {
let _pipe = base;
let _pipe$1 = to_float(_pipe);
return $float.power(_pipe$1, exponent);
}
/**
* Returns the square root of the input as a `Float`.
*
* ## Examples
*
* ```gleam
* assert square_root(4) == Ok(2.0)
* ```
*
* ```gleam
* assert square_root(-16) == Error(Nil)
* ```
*/
export function square_root(x) {
let _pipe = x;
let _pipe$1 = to_float(_pipe);
return $float.square_root(_pipe$1);
}
/**
* Parses a given string as an int in a given base if possible.
* Supports only bases 2 to 36, for values outside of which this function returns an `Error(Nil)`.
*
* ## Examples
*
* ```gleam
* assert base_parse("10", 2) == Ok(2)
* ```
*
* ```gleam
* assert base_parse("30", 16) == Ok(48)
* ```
*
* ```gleam
* assert base_parse("1C", 36) == Ok(48)
* ```
*
* ```gleam
* assert base_parse("48", 1) == Error(Nil)
* ```
*
* ```gleam
* assert base_parse("48", 37) == Error(Nil)
* ```
*/
export function base_parse(string, base) {
let $ = (base >= 2) && (base <= 36);
if ($) {
return do_base_parse(string, base);
} else {
return new Error(undefined);
}
}
/**
* Prints a given int to a string using the base number provided.
* Supports only bases 2 to 36, for values outside of which this function returns an `Error(Nil)`.
* For common bases (2, 8, 16, 36), use the `to_baseN` functions.
*
* ## Examples
*
* ```gleam
* assert to_base_string(2, 2) == Ok("10")
* ```
*
* ```gleam
* assert to_base_string(48, 16) == Ok("30")
* ```
*
* ```gleam
* assert to_base_string(48, 36) == Ok("1C")
* ```
*
* ```gleam
* assert to_base_string(48, 1) == Error(Nil)
* ```
*
* ```gleam
* assert to_base_string(48, 37) == Error(Nil)
* ```
*/
export function to_base_string(x, base) {
let $ = (base >= 2) && (base <= 36);
if ($) {
return new Ok(do_to_base_string(x, base));
} else {
return new Error(undefined);
}
}
/**
* Prints a given int to a string using base-2.
*
* ## Examples
*
* ```gleam
* assert to_base2(2) == "10"
* ```
*/
export function to_base2(x) {
return do_to_base_string(x, 2);
}
/**
* Prints a given int to a string using base-8.
*
* ## Examples
*
* ```gleam
* assert to_base8(15) == "17"
* ```
*/
export function to_base8(x) {
return do_to_base_string(x, 8);
}
/**
* Prints a given int to a string using base-16.
*
* ## Examples
*
* ```gleam
* assert to_base16(48) == "30"
* ```
*/
export function to_base16(x) {
return do_to_base_string(x, 16);
}
/**
* Prints a given int to a string using base-36.
*
* ## Examples
*
* ```gleam
* assert to_base36(48) == "1C"
* ```
*/
export function to_base36(x) {
return do_to_base_string(x, 36);
}
/**
* Compares two ints, returning the larger of the two.
*
* ## Examples
*
* ```gleam
* assert max(2, 3) == 3
* ```
*/
export function max(a, b) {
let $ = a > b;
if ($) {
return a;
} else {
return b;
}
}
/**
* Compares two ints, returning the smaller of the two.
*
* ## Examples
*
* ```gleam
* assert min(2, 3) == 2
* ```
*/
export function min(a, b) {
let $ = a < b;
if ($) {
return a;
} else {
return b;
}
}
/**
* Restricts an int between two bounds.
*
* Note: If the `min` argument is larger than the `max` argument then they
* will be swapped, so the minimum bound is always lower than the maximum
* bound.
*
* ## Examples
*
* ```gleam
* assert clamp(40, min: 50, max: 60) == 50
* ```
*
* ```gleam
* assert clamp(40, min: 50, max: 30) == 40
* ```
*/
export function clamp(x, min_bound, max_bound) {
let $ = min_bound >= max_bound;
if ($) {
let _pipe = x;
let _pipe$1 = min(_pipe, min_bound);
return max(_pipe$1, max_bound);
} else {
let _pipe = x;
let _pipe$1 = min(_pipe, max_bound);
return max(_pipe$1, min_bound);
}
}
/**
* Compares two ints, returning an order.
*
* ## Examples
*
* ```gleam
* assert compare(2, 3) == Lt
* ```
*
* ```gleam
* assert compare(4, 3) == Gt
* ```
*
* ```gleam
* assert compare(3, 3) == Eq
* ```
*/
export function compare(a, b) {
let $ = a === b;
if ($) {
return new $order.Eq();
} else {
let $1 = a < b;
if ($1) {
return new $order.Lt();
} else {
return new $order.Gt();
}
}
}
/**
* Returns whether the value provided is even.
*
* ## Examples
*
* ```gleam
* assert is_even(2)
* ```
*
* ```gleam
* assert !is_even(3)
* ```
*/
export function is_even(x) {
return (x % 2) === 0;
}
/**
* Returns whether the value provided is odd.
*
* ## Examples
*
* ```gleam
* assert is_odd(3)
* ```
*
* ```gleam
* assert !is_odd(2)
* ```
*/
export function is_odd(x) {
return (x % 2) !== 0;
}
/**
* Returns the negative of the value provided.
*
* ## Examples
*
* ```gleam
* assert negate(1) == -1
* ```
*/
export function negate(x) {
return -1 * x;
}
function sum_loop(loop$numbers, loop$initial) {
while (true) {
let numbers = loop$numbers;
let initial = loop$initial;
if (numbers instanceof $Empty) {
return initial;
} else {
let first = numbers.head;
let rest = numbers.tail;
loop$numbers = rest;
loop$initial = first + initial;
}
}
}
/**
* Sums a list of ints.
*
* ## Example
*
* ```gleam
* assert sum([1, 2, 3]) == 6
* ```
*/
export function sum(numbers) {
return sum_loop(numbers, 0);
}
function product_loop(loop$numbers, loop$initial) {
while (true) {
let numbers = loop$numbers;
let initial = loop$initial;
if (numbers instanceof $Empty) {
return initial;
} else {
let first = numbers.head;
let rest = numbers.tail;
loop$numbers = rest;
loop$initial = first * initial;
}
}
}
/**
* Multiplies a list of ints and returns the product.
*
* ## Example
*
* ```gleam
* assert product([2, 3, 4]) == 24
* ```
*/
export function product(numbers) {
return product_loop(numbers, 1);
}
/**
* Generates a random int between zero and the given maximum.
*
* The lower number is inclusive, the upper number is exclusive.
*
* ## Examples
*
* ```gleam
* random(10)
* // -> 4
* ```
*
* ```gleam
* random(1)
* // -> 0
* ```
*
* ```gleam
* random(-1)
* // -> -1
* ```
*/
export function random(max) {
let _pipe = ($float.random() * to_float(max));
let _pipe$1 = $float.floor(_pipe);
return $float.round(_pipe$1);
}
/**
* Performs a truncated integer division.
*
* Returns division of the inputs as a `Result`: If the given divisor equals
* `0`, this function returns an `Error`.
*
* ## Examples
*
* ```gleam
* assert divide(0, 1) == Ok(0)
* ```
*
* ```gleam
* assert divide(1, 0) == Error(Nil)
* ```
*
* ```gleam
* assert divide(5, 2) == Ok(2)
* ```
*
* ```gleam
* assert divide(-99, 2) == Ok(-49)
* ```
*/
export function divide(dividend, divisor) {
if (divisor === 0) {
return new Error(undefined);
} else {
let divisor$1 = divisor;
return new Ok(divideInt(dividend, divisor$1));
}
}
/**
* Computes the remainder of an integer division of inputs as a `Result`.
*
* Returns division of the inputs as a `Result`: If the given divisor equals
* `0`, this function returns an `Error`.
*
* Most of the time you will want to use the `%` operator instead of this
* function.
*
* ## Examples
*
* ```gleam
* assert remainder(3, 2) == Ok(1)
* ```
*
* ```gleam
* assert remainder(1, 0) == Error(Nil)
* ```
*
* ```gleam
* assert remainder(10, -1) == Ok(0)
* ```
*
* ```gleam
* assert remainder(13, by: 3) == Ok(1)
* ```
*
* ```gleam
* assert remainder(-13, by: 3) == Ok(-1)
* ```
*
* ```gleam
* assert remainder(13, by: -3) == Ok(1)
* ```
*
* ```gleam
* assert remainder(-13, by: -3) == Ok(-1)
* ```
*/
export function remainder(dividend, divisor) {
if (divisor === 0) {
return new Error(undefined);
} else {
let divisor$1 = divisor;
return new Ok(remainderInt(dividend, divisor$1));
}
}
/**
* Computes the modulo of an integer division of inputs as a `Result`.
*
* Returns division of the inputs as a `Result`: If the given divisor equals
* `0`, this function returns an `Error`.
*
* Note that this is different from `int.remainder` and `%` in that the
* computed value will always have the same sign as the `divisor`.
*
* ## Examples
*
* ```gleam
* assert modulo(3, 2) == Ok(1)
* ```
*
* ```gleam
* assert modulo(1, 0) == Error(Nil)
* ```
*
* ```gleam
* assert modulo(10, -1) == Ok(0)
* ```
*
* ```gleam
* assert modulo(13, by: 3) == Ok(1)
* ```
*
* ```gleam
* assert modulo(-13, by: 3) == Ok(2)
* ```
*
* ```gleam
* assert modulo(13, by: -3) == Ok(-2)
* ```
*/
export function modulo(dividend, divisor) {
if (divisor === 0) {
return new Error(undefined);
} else {
let remainder$1 = remainderInt(dividend, divisor);
let $ = remainder$1 * divisor < 0;
if ($) {
return new Ok(remainder$1 + divisor);
} else {
return new Ok(remainder$1);
}
}
}
/**
* Performs a *floored* integer division, which means that the result will
* always be rounded towards negative infinity.
*
* If you want to perform truncated integer division (rounding towards zero),
* use `int.divide()` or the `/` operator instead.
*
* Returns division of the inputs as a `Result`: If the given divisor equals
* `0`, this function returns an `Error`.
*
* ## Examples
*
* ```gleam
* assert floor_divide(1, 0) == Error(Nil)
* ```
*
* ```gleam
* assert floor_divide(5, 2) == Ok(2)
* ```
*
* ```gleam
* assert floor_divide(6, -4) == Ok(-2)
* ```
*
* ```gleam
* assert floor_divide(-99, 2) == Ok(-50)
* ```
*/
export function floor_divide(dividend, divisor) {
if (divisor === 0) {
return new Error(undefined);
} else {
let divisor$1 = divisor;
let $ = (dividend * divisor$1 < 0) && ((remainderInt(dividend, divisor$1)) !== 0);
if ($) {
return new Ok((divideInt(dividend, divisor$1)) - 1);
} else {
return new Ok(divideInt(dividend, divisor$1));
}
}
}
/**
* Adds two integers together.
*
* It's the function equivalent of the `+` operator.
* This function is useful in higher order functions or pipes.
*
* ## Examples
*
* ```gleam
* assert add(1, 2) == 3
* ```
*
* ```gleam
* import gleam/list
* assert list.fold([1, 2, 3], 0, add) == 6
* ```
*
* ```gleam
* assert 3 |> add(2) == 5
* ```
*/
export function add(a, b) {
return a + b;
}
/**
* Multiplies two integers together.
*
* It's the function equivalent of the `*` operator.
* This function is useful in higher order functions or pipes.
*
* ## Examples
*
* ```gleam
* assert multiply(2, 4) == 8
* ```
*
* ```gleam
* import gleam/list
*
* assert list.fold([2, 3, 4], 1, multiply) == 24
* ```
*
* ```gleam
* assert 3 |> multiply(2) == 6
* ```
*/
export function multiply(a, b) {
return a * b;
}
/**
* Subtracts one int from another.
*
* It's the function equivalent of the `-` operator.
* This function is useful in higher order functions or pipes.
*
* ## Examples
*
* ```gleam
* assert subtract(3, 1) == 2
* ```
*
* ```gleam
* import gleam/list
*
* assert list.fold([1, 2, 3], 10, subtract) == 4
* ```
*
* ```gleam
* assert 3 |> subtract(2) == 1
* ```
*
* ```gleam
* assert 3 |> subtract(2, _) == -1
* ```
*/
export function subtract(a, b) {
return a - b;
}
function range_loop(
loop$current,
loop$stop,
loop$increment,
loop$acc,
loop$reducer
) {
while (true) {
let current = loop$current;
let stop = loop$stop;
let increment = loop$increment;
let acc = loop$acc;
let reducer = loop$reducer;
let $ = current === stop;
if ($) {
return acc;
} else {
let acc$1 = reducer(acc, current);
let current$1 = current + increment;
loop$current = current$1;
loop$stop = stop;
loop$increment = increment;
loop$acc = acc$1;
loop$reducer = reducer;
}
}
}
/**
* Run a function for each int between ints `from` and `to`.
*
* `from` is inclusive, and `to` is exclusive.
*
* ## Examples
*
* ```gleam
* assert
* range(from: 0, to: 3, with: "", run: fn(acc, i) {
* acc <> to_string(i)
* })
* == "012"
* ```
*
* ```gleam
* assert range(from: 1, to: -2, with: [], run: list.prepend) == [-1, 0, 1]
* ```
*/
export function range(start, stop, acc, reducer) {
let _block;
let $ = start < stop;
if ($) {
_block = 1;
} else {
_block = -1;
}
let increment = _block;
return range_loop(start, stop, increment, acc, reducer);
}
+8
View File
@@ -0,0 +1,8 @@
import {
print,
print_error,
console_log as println,
console_error as println_error,
} from "../gleam_stdlib.mjs";
export { print, print_error, println, println_error };
File diff suppressed because it is too large Load Diff
+386
View File
@@ -0,0 +1,386 @@
import {
Ok,
Error,
toList,
Empty as $Empty,
prepend as listPrepend,
CustomType as $CustomType,
} from "../gleam.mjs";
export class Some extends $CustomType {
constructor($0) {
super();
this[0] = $0;
}
}
export const Option$Some = ($0) => new Some($0);
export const Option$isSome = (value) => value instanceof Some;
export const Option$Some$0 = (value) => value[0];
export class None extends $CustomType {}
export const Option$None = () => new None();
export const Option$isNone = (value) => value instanceof None;
function reverse_and_prepend(loop$prefix, loop$suffix) {
while (true) {
let prefix = loop$prefix;
let suffix = loop$suffix;
if (prefix instanceof $Empty) {
return suffix;
} else {
let first = prefix.head;
let rest = prefix.tail;
loop$prefix = rest;
loop$suffix = listPrepend(first, suffix);
}
}
}
function reverse(list) {
return reverse_and_prepend(list, toList([]));
}
function all_loop(loop$list, loop$acc) {
while (true) {
let list = loop$list;
let acc = loop$acc;
if (list instanceof $Empty) {
return new Some(reverse(acc));
} else {
let $ = list.head;
if ($ instanceof Some) {
let rest = list.tail;
let first = $[0];
loop$list = rest;
loop$acc = listPrepend(first, acc);
} else {
return new None();
}
}
}
}
/**
* Combines a list of `Option`s into a single `Option`.
* If all elements in the list are `Some` then returns a `Some` holding the list of values.
* If any element is `None` then returns `None`.
*
* ## Examples
*
* ```gleam
* assert all([Some(1), Some(2)]) == Some([1, 2])
* ```
*
* ```gleam
* assert all([Some(1), None]) == None
* ```
*/
export function all(list) {
return all_loop(list, toList([]));
}
/**
* Checks whether the `Option` is a `Some` value.
*
* ## Examples
*
* ```gleam
* assert is_some(Some(1))
* ```
*
* ```gleam
* assert !is_some(None)
* ```
*/
export function is_some(option) {
return !(option instanceof None);
}
/**
* Checks whether the `Option` is a `None` value.
*
* ## Examples
*
* ```gleam
* assert !is_none(Some(1))
* ```
*
* ```gleam
* assert is_none(None)
* ```
*/
export function is_none(option) {
return option instanceof None;
}
/**
* Converts an `Option` type to a `Result` type.
*
* ## Examples
*
* ```gleam
* assert to_result(Some(1), "some_error") == Ok(1)
* ```
*
* ```gleam
* assert to_result(None, "some_error") == Error("some_error")
* ```
*/
export function to_result(option, e) {
if (option instanceof Some) {
let a = option[0];
return new Ok(a);
} else {
return new Error(e);
}
}
/**
* Converts a `Result` type to an `Option` type.
*
* ## Examples
*
* ```gleam
* assert from_result(Ok(1)) == Some(1)
* ```
*
* ```gleam
* assert from_result(Error("some_error")) == None
* ```
*/
export function from_result(result) {
if (result instanceof Ok) {
let a = result[0];
return new Some(a);
} else {
return new None();
}
}
/**
* Extracts the value from an `Option`, returning a default value if there is none.
*
* ## Examples
*
* ```gleam
* assert unwrap(Some(1), 0) == 1
* ```
*
* ```gleam
* assert unwrap(None, 0) == 0
* ```
*/
export function unwrap(option, default$) {
if (option instanceof Some) {
let x = option[0];
return x;
} else {
return default$;
}
}
/**
* Extracts the value from an `Option`, evaluating the default function if the option is `None`.
*
* ## Examples
*
* ```gleam
* assert lazy_unwrap(Some(1), fn() { 0 }) == 1
* ```
*
* ```gleam
* assert lazy_unwrap(None, fn() { 0 }) == 0
* ```
*/
export function lazy_unwrap(option, default$) {
if (option instanceof Some) {
let x = option[0];
return x;
} else {
return default$();
}
}
/**
* Updates a value held within the `Some` of an `Option` by calling a given function
* on it.
*
* If the `Option` is a `None` rather than `Some`, the function is not called and the
* `Option` stays the same.
*
* ## Examples
*
* ```gleam
* assert map(over: Some(1), with: fn(x) { x + 1 }) == Some(2)
* ```
*
* ```gleam
* assert map(over: None, with: fn(x) { x + 1 }) == None
* ```
*/
export function map(option, fun) {
if (option instanceof Some) {
let x = option[0];
return new Some(fun(x));
} else {
return option;
}
}
/**
* Merges a nested `Option` into a single layer.
*
* ## Examples
*
* ```gleam
* assert flatten(Some(Some(1))) == Some(1)
* ```
*
* ```gleam
* assert flatten(Some(None)) == None
* ```
*
* ```gleam
* assert flatten(None) == None
* ```
*/
export function flatten(option) {
if (option instanceof Some) {
let x = option[0];
return x;
} else {
return option;
}
}
/**
* Updates a value held within the `Some` of an `Option` by calling a given function
* on it, where the given function also returns an `Option`. The two options are
* then merged together into one `Option`.
*
* If the `Option` is a `None` rather than `Some` the function is not called and the
* option stays the same.
*
* This function is the equivalent of calling `map` followed by `flatten`, and
* it is useful for chaining together multiple functions that return `Option`.
*
* ## Examples
*
* ```gleam
* assert then(Some(1), fn(x) { Some(x + 1) }) == Some(2)
* ```
*
* ```gleam
* assert then(Some(1), fn(x) { Some(#("a", x)) }) == Some(#("a", 1))
* ```
*
* ```gleam
* assert then(Some(1), fn(_) { None }) == None
* ```
*
* ```gleam
* assert then(None, fn(x) { Some(x + 1) }) == None
* ```
*/
export function then$(option, fun) {
if (option instanceof Some) {
let x = option[0];
return fun(x);
} else {
return option;
}
}
/**
* Returns the first value if it is `Some`, otherwise returns the second value.
*
* ## Examples
*
* ```gleam
* assert or(Some(1), Some(2)) == Some(1)
* ```
*
* ```gleam
* assert or(Some(1), None) == Some(1)
* ```
*
* ```gleam
* assert or(None, Some(2)) == Some(2)
* ```
*
* ```gleam
* assert or(None, None) == None
* ```
*/
export function or(first, second) {
if (first instanceof Some) {
return first;
} else {
return second;
}
}
/**
* Returns the first value if it is `Some`, otherwise evaluates the given function for a fallback value.
*
* ## Examples
*
* ```gleam
* assert lazy_or(Some(1), fn() { Some(2) }) == Some(1)
* ```
*
* ```gleam
* assert lazy_or(Some(1), fn() { None }) == Some(1)
* ```
*
* ```gleam
* assert lazy_or(None, fn() { Some(2) }) == Some(2)
* ```
*
* ```gleam
* assert lazy_or(None, fn() { None }) == None
* ```
*/
export function lazy_or(first, second) {
if (first instanceof Some) {
return first;
} else {
return second();
}
}
function values_loop(loop$list, loop$acc) {
while (true) {
let list = loop$list;
let acc = loop$acc;
if (list instanceof $Empty) {
return reverse(acc);
} else {
let $ = list.head;
if ($ instanceof Some) {
let rest = list.tail;
let first = $[0];
loop$list = rest;
loop$acc = listPrepend(first, acc);
} else {
let rest = list.tail;
loop$list = rest;
loop$acc = acc;
}
}
}
}
/**
* Given a list of `Option`s,
* returns only the values inside `Some`.
*
* ## Examples
*
* ```gleam
* assert values([Some(1), None, Some(3)]) == [1, 3]
* ```
*/
export function values(options) {
return values_loop(options, toList([]));
}
+175
View File
@@ -0,0 +1,175 @@
import { CustomType as $CustomType, isEqual } from "../gleam.mjs";
/**
* Less-than
*/
export class Lt extends $CustomType {}
export const Order$Lt = () => new Lt();
export const Order$isLt = (value) => value instanceof Lt;
/**
* Equal
*/
export class Eq extends $CustomType {}
export const Order$Eq = () => new Eq();
export const Order$isEq = (value) => value instanceof Eq;
/**
* Greater than
*/
export class Gt extends $CustomType {}
export const Order$Gt = () => new Gt();
export const Order$isGt = (value) => value instanceof Gt;
/**
* Inverts an order, so less-than becomes greater-than and greater-than
* becomes less-than.
*
* ## Examples
*
* ```gleam
* assert negate(Lt) == Gt
* ```
*
* ```gleam
* assert negate(Eq) == Eq
* ```
*
* ```gleam
* assert negate(Gt) == Lt
* ```
*/
export function negate(order) {
if (order instanceof Lt) {
return new Gt();
} else if (order instanceof Eq) {
return order;
} else {
return new Lt();
}
}
/**
* Produces a numeric representation of the order.
*
* ## Examples
*
* ```gleam
* assert to_int(Lt) == -1
* ```
*
* ```gleam
* assert to_int(Eq) == 0
* ```
*
* ```gleam
* assert to_int(Gt) == 1
* ```
*/
export function to_int(order) {
if (order instanceof Lt) {
return -1;
} else if (order instanceof Eq) {
return 0;
} else {
return 1;
}
}
/**
* Compares two `Order` values to one another, producing a new `Order`.
*
* ## Examples
*
* ```gleam
* assert compare(Eq, with: Lt) == Gt
* ```
*/
export function compare(a, b) {
let x = a;
let y = b;
if (isEqual(x, y)) {
return new Eq();
} else if (a instanceof Lt) {
return new Lt();
} else if (a instanceof Eq && b instanceof Gt) {
return new Lt();
} else {
return new Gt();
}
}
/**
* Inverts an ordering function, so less-than becomes greater-than and greater-than
* becomes less-than.
*
* ## Examples
*
* ```gleam
* import gleam/int
* import gleam/list
*
* assert list.sort([1, 5, 4], by: reverse(int.compare)) == [5, 4, 1]
* ```
*/
export function reverse(orderer) {
return (a, b) => { return orderer(b, a); };
}
/**
* Return a fallback `Order` in case the first argument is `Eq`.
*
* ## Examples
*
* ```gleam
* import gleam/int
*
* assert break_tie(in: int.compare(1, 1), with: Lt) == Lt
* ```
*
* ```gleam
* import gleam/int
*
* assert break_tie(in: int.compare(1, 0), with: Eq) == Gt
* ```
*/
export function break_tie(order, other) {
if (order instanceof Lt) {
return order;
} else if (order instanceof Eq) {
return other;
} else {
return order;
}
}
/**
* Invokes a fallback function returning an `Order` in case the first argument
* is `Eq`.
*
* This can be useful when the fallback comparison might be expensive and it
* needs to be delayed until strictly necessary.
*
* ## Examples
*
* ```gleam
* import gleam/int
*
* assert lazy_break_tie(in: int.compare(1, 1), with: fn() { Lt }) == Lt
* ```
*
* ```gleam
* import gleam/int
*
* assert lazy_break_tie(in: int.compare(1, 0), with: fn() { Eq }) == Gt
* ```
*/
export function lazy_break_tie(order, comparison) {
if (order instanceof Lt) {
return order;
} else if (order instanceof Eq) {
return comparison();
} else {
return order;
}
}
+88
View File
@@ -0,0 +1,88 @@
/**
* Returns the first element in a pair.
*
* ## Examples
*
* ```gleam
* assert first(#(1, 2)) == 1
* ```
*/
export function first(pair) {
let a = pair[0];
return a;
}
/**
* Returns the second element in a pair.
*
* ## Examples
*
* ```gleam
* assert second(#(1, 2)) == 2
* ```
*/
export function second(pair) {
let a = pair[1];
return a;
}
/**
* Returns a new pair with the elements swapped.
*
* ## Examples
*
* ```gleam
* assert swap(#(1, 2)) == #(2, 1)
* ```
*/
export function swap(pair) {
let a = pair[0];
let b = pair[1];
return [b, a];
}
/**
* Returns a new pair with the first element having had `with` applied to
* it.
*
* ## Examples
*
* ```gleam
* assert #(1, 2) |> map_first(fn(n) { n * 2 }) == #(2, 2)
* ```
*/
export function map_first(pair, fun) {
let a = pair[0];
let b = pair[1];
return [fun(a), b];
}
/**
* Returns a new pair with the second element having had `with` applied to
* it.
*
* ## Examples
*
* ```gleam
* assert #(1, 2) |> map_second(fn(n) { n * 2 }) == #(1, 4)
* ```
*/
export function map_second(pair, fun) {
let a = pair[0];
let b = pair[1];
return [a, fun(b)];
}
/**
* Returns a new pair with the given elements. This can also be done using the dedicated
* syntax instead: `new(1, 2) == #(1, 2)`.
*
* ## Examples
*
* ```gleam
* assert new(1, 2) == #(1, 2)
* ```
*/
export function new$(first, second) {
return [first, second];
}
+448
View File
@@ -0,0 +1,448 @@
import { Ok, Error, toList, Empty as $Empty, prepend as listPrepend } from "../gleam.mjs";
import * as $list from "../gleam/list.mjs";
/**
* Checks whether the result is an `Ok` value.
*
* ## Examples
*
* ```gleam
* assert is_ok(Ok(1))
* ```
*
* ```gleam
* assert !is_ok(Error(Nil))
* ```
*/
export function is_ok(result) {
if (result instanceof Ok) {
return true;
} else {
return false;
}
}
/**
* Checks whether the result is an `Error` value.
*
* ## Examples
*
* ```gleam
* assert !is_error(Ok(1))
* ```
*
* ```gleam
* assert is_error(Error(Nil))
* ```
*/
export function is_error(result) {
if (result instanceof Ok) {
return false;
} else {
return true;
}
}
/**
* Updates a value held within the `Ok` of a result by calling a given function
* on it.
*
* If the result is an `Error` rather than `Ok` the function is not called and the
* result stays the same.
*
* ## Examples
*
* ```gleam
* assert map(over: Ok(1), with: fn(x) { x + 1 }) == Ok(2)
* ```
*
* ```gleam
* assert map(over: Error(1), with: fn(x) { x + 1 }) == Error(1)
* ```
*/
export function map(result, fun) {
if (result instanceof Ok) {
let x = result[0];
return new Ok(fun(x));
} else {
return result;
}
}
/**
* Updates a value held within the `Error` of a result by calling a given function
* on it.
*
* If the result is `Ok` rather than `Error` the function is not called and the
* result stays the same.
*
* ## Examples
*
* ```gleam
* assert map_error(over: Error(1), with: fn(x) { x + 1 }) == Error(2)
* ```
*
* ```gleam
* assert map_error(over: Ok(1), with: fn(x) { x + 1 }) == Ok(1)
* ```
*/
export function map_error(result, fun) {
if (result instanceof Ok) {
return result;
} else {
let error = result[0];
return new Error(fun(error));
}
}
/**
* Merges a nested `Result` into a single layer.
*
* ## Examples
*
* ```gleam
* assert flatten(Ok(Ok(1))) == Ok(1)
* ```
*
* ```gleam
* assert flatten(Ok(Error(""))) == Error("")
* ```
*
* ```gleam
* assert flatten(Error(Nil)) == Error(Nil)
* ```
*/
export function flatten(result) {
if (result instanceof Ok) {
let x = result[0];
return x;
} else {
return result;
}
}
/**
* "Updates" an `Ok` result by passing its value to a function that yields a result,
* and returning the yielded result. (This may "replace" the `Ok` with an `Error`.)
*
* If the input is an `Error` rather than an `Ok`, the function is not called and
* the original `Error` is returned.
*
* This function is the equivalent of calling `map` followed by `flatten`, and
* it is useful for chaining together multiple functions that may fail.
*
* ## Examples
*
* ```gleam
* assert try(Ok(1), fn(x) { Ok(x + 1) }) == Ok(2)
* ```
*
* ```gleam
* assert try(Ok(1), fn(x) { Ok(#("a", x)) }) == Ok(#("a", 1))
* ```
*
* ```gleam
* assert try(Ok(1), fn(_) { Error("Oh no") }) == Error("Oh no")
* ```
*
* ```gleam
* assert try(Error(Nil), fn(x) { Ok(x + 1) }) == Error(Nil)
* ```
*/
export function try$(result, fun) {
if (result instanceof Ok) {
let x = result[0];
return fun(x);
} else {
return result;
}
}
/**
* Extracts the `Ok` value from a result, returning a default value if the result
* is an `Error`.
*
* ## Examples
*
* ```gleam
* assert unwrap(Ok(1), 0) == 1
* ```
*
* ```gleam
* assert unwrap(Error(""), 0) == 0
* ```
*/
export function unwrap(result, default$) {
if (result instanceof Ok) {
let v = result[0];
return v;
} else {
return default$;
}
}
/**
* Extracts the `Ok` value from a result, evaluating the default function if the result
* is an `Error`.
*
* ## Examples
*
* ```gleam
* assert lazy_unwrap(Ok(1), fn() { 0 }) == 1
* ```
*
* ```gleam
* assert lazy_unwrap(Error(""), fn() { 0 }) == 0
* ```
*/
export function lazy_unwrap(result, default$) {
if (result instanceof Ok) {
let v = result[0];
return v;
} else {
return default$();
}
}
/**
* Extracts the `Error` value from a result, returning a default value if the result
* is an `Ok`.
*
* ## Examples
*
* ```gleam
* assert unwrap_error(Error(1), 0) == 1
* ```
*
* ```gleam
* assert unwrap_error(Ok(""), 0) == 0
* ```
*/
export function unwrap_error(result, default$) {
if (result instanceof Ok) {
return default$;
} else {
let e = result[0];
return e;
}
}
/**
* Returns the first value if it is `Ok`, otherwise returns the second value.
*
* ## Examples
*
* ```gleam
* assert or(Ok(1), Ok(2)) == Ok(1)
* ```
*
* ```gleam
* assert or(Ok(1), Error("Error 2")) == Ok(1)
* ```
*
* ```gleam
* assert or(Error("Error 1"), Ok(2)) == Ok(2)
* ```
*
* ```gleam
* assert or(Error("Error 1"), Error("Error 2")) == Error("Error 2")
* ```
*/
export function or(first, second) {
if (first instanceof Ok) {
return first;
} else {
return second;
}
}
/**
* Returns the first value if it is `Ok`, otherwise evaluates the given function for a fallback value.
*
* If you need access to the initial error value, use `result.try_recover`.
*
* ## Examples
*
* ```gleam
* assert lazy_or(Ok(1), fn() { Ok(2) }) == Ok(1)
* ```
*
* ```gleam
* assert lazy_or(Ok(1), fn() { Error("Error 2") }) == Ok(1)
* ```
*
* ```gleam
* assert lazy_or(Error("Error 1"), fn() { Ok(2) }) == Ok(2)
* ```
*
* ```gleam
* assert lazy_or(Error("Error 1"), fn() { Error("Error 2") })
* == Error("Error 2")
* ```
*/
export function lazy_or(first, second) {
if (first instanceof Ok) {
return first;
} else {
return second();
}
}
/**
* Combines a list of results into a single result.
* If all elements in the list are `Ok` then returns an `Ok` holding the list of values.
* If any element is `Error` then returns the first error.
*
* ## Examples
*
* ```gleam
* assert all([Ok(1), Ok(2)]) == Ok([1, 2])
* ```
*
* ```gleam
* assert all([Ok(1), Error("e")]) == Error("e")
* ```
*/
export function all(results) {
return $list.try_map(results, (result) => { return result; });
}
function partition_loop(loop$results, loop$oks, loop$errors) {
while (true) {
let results = loop$results;
let oks = loop$oks;
let errors = loop$errors;
if (results instanceof $Empty) {
return [oks, errors];
} else {
let $ = results.head;
if ($ instanceof Ok) {
let rest = results.tail;
let a = $[0];
loop$results = rest;
loop$oks = listPrepend(a, oks);
loop$errors = errors;
} else {
let rest = results.tail;
let e = $[0];
loop$results = rest;
loop$oks = oks;
loop$errors = listPrepend(e, errors);
}
}
}
}
/**
* Given a list of results, returns a pair where the first element is a list
* of all the values inside `Ok` and the second element is a list with all the
* values inside `Error`. The values in both lists appear in reverse order with
* respect to their position in the original list of results.
*
* ## Examples
*
* ```gleam
* assert partition([Ok(1), Error("a"), Error("b"), Ok(2)])
* == #([2, 1], ["b", "a"])
* ```
*/
export function partition(results) {
return partition_loop(results, toList([]), toList([]));
}
/**
* Replace the value within a result
*
* ## Examples
*
* ```gleam
* assert replace(Ok(1), Nil) == Ok(Nil)
* ```
*
* ```gleam
* assert replace(Error(1), Nil) == Error(1)
* ```
*/
export function replace(result, value) {
if (result instanceof Ok) {
return new Ok(value);
} else {
return result;
}
}
/**
* Replace the error within a result
*
* ## Examples
*
* ```gleam
* assert replace_error(Error(1), Nil) == Error(Nil)
* ```
*
* ```gleam
* assert replace_error(Ok(1), Nil) == Ok(1)
* ```
*/
export function replace_error(result, error) {
if (result instanceof Ok) {
return result;
} else {
return new Error(error);
}
}
/**
* Given a list of results, returns only the values inside `Ok`.
*
* ## Examples
*
* ```gleam
* assert values([Ok(1), Error("a"), Ok(3)]) == [1, 3]
* ```
*/
export function values(results) {
return $list.filter_map(results, (result) => { return result; });
}
/**
* Updates a value held within the `Error` of a result by calling a given function
* on it, where the given function also returns a result. The two results are
* then merged together into one result.
*
* If the result is an `Ok` rather than `Error` the function is not called and the
* result stays the same.
*
* This function is useful for chaining together computations that may fail
* and trying to recover from possible errors.
*
* If you do not need access to the initial error value, use `result.lazy_or`.
*
* ## Examples
*
* ```gleam
* assert Ok(1)
* |> try_recover(with: fn(_) { Error("failed to recover") })
* == Ok(1)
* ```
*
* ```gleam
* assert Error(1)
* |> try_recover(with: fn(error) { Ok(error + 1) })
* == Ok(2)
* ```
*
* ```gleam
* assert Error(1)
* |> try_recover(with: fn(error) { Error("failed to recover") })
* == Error("failed to recover")
* ```
*/
export function try_recover(result, fun) {
if (result instanceof Ok) {
return result;
} else {
let error = result[0];
return fun(error);
}
}
+409
View File
@@ -0,0 +1,409 @@
import { CustomType as $CustomType, isEqual } from "../gleam.mjs";
import * as $dict from "../gleam/dict.mjs";
import * as $list from "../gleam/list.mjs";
import * as $result from "../gleam/result.mjs";
class Set extends $CustomType {
constructor(dict) {
super();
this.dict = dict;
}
}
const token = undefined;
/**
* Creates a new empty set.
*/
export function new$() {
return new Set($dict.new$());
}
/**
* Gets the number of members in a set.
*
* This function runs in constant time.
*
* ## Examples
*
* ```gleam
* assert new()
* |> insert(1)
* |> insert(2)
* |> size
* == 2
* ```
*/
export function size(set) {
return $dict.size(set.dict);
}
/**
* Determines whether or not the set is empty.
*
* ## Examples
*
* ```gleam
* assert new() |> is_empty
* ```
*
* ```gleam
* assert !{ new() |> insert(1) |> is_empty }
* ```
*/
export function is_empty(set) {
return isEqual(set, new$());
}
/**
* Inserts a member into the set.
*
* This function runs in logarithmic time.
*
* ## Examples
*
* ```gleam
* assert new()
* |> insert(1)
* |> insert(2)
* |> size
* == 2
* ```
*/
export function insert(set, member) {
return new Set($dict.insert(set.dict, member, token));
}
/**
* Checks whether a set contains a given member.
*
* This function runs in logarithmic time.
*
* ## Examples
*
* ```gleam
* assert new()
* |> insert(2)
* |> contains(2)
* ```
*
* ```gleam
* assert !{
* new()
* |> insert(2)
* |> contains(1)
* }
* ```
*/
export function contains(set, member) {
let _pipe = set.dict;
let _pipe$1 = $dict.get(_pipe, member);
return $result.is_ok(_pipe$1);
}
/**
* Removes a member from a set. If the set does not contain the member then
* the set is returned unchanged.
*
* This function runs in logarithmic time.
*
* ## Examples
*
* ```gleam
* assert !{
* new()
* |> insert(2)
* |> delete(2)
* |> contains(2)
* }
* ```
*/
export function delete$(set, member) {
return new Set($dict.delete$(set.dict, member));
}
/**
* Converts the set into a list of the contained members.
*
* The list has no specific ordering, any unintentional ordering may change in
* future versions of Gleam or Erlang.
*
* This function runs in linear time.
*
* ## Examples
*
* ```gleam
* assert new() |> insert(2) |> to_list == [2]
* ```
*/
export function to_list(set) {
return $dict.keys(set.dict);
}
/**
* Creates a new set of the members in a given list.
*
* This function runs in loglinear time.
*
* ## Examples
*
* ```gleam
* import gleam/int
* import gleam/list
*
* assert [1, 1, 2, 4, 3, 2]
* |> from_list
* |> to_list
* |> list.sort(by: int.compare)
* == [1, 2, 3, 4]
* ```
*/
export function from_list(members) {
let dict = $list.fold(
members,
$dict.new$(),
(m, k) => { return $dict.insert(m, k, token); },
);
return new Set(dict);
}
/**
* Combines all entries into a single value by calling a given function on each
* one.
*
* Sets are not ordered so the values are not returned in any specific order.
* Do not write code that relies on the order entries are used by this
* function as it may change in later versions of Gleam or Erlang.
*
* ## Examples
*
* ```gleam
* assert from_list([1, 3, 9])
* |> fold(0, fn(accumulator, member) { accumulator + member })
* == 13
* ```
*/
export function fold(set, initial, reducer) {
return $dict.fold(set.dict, initial, (a, k, _) => { return reducer(a, k); });
}
/**
* Creates a new set from an existing set, minus any members that a given
* function returns `False` for.
*
* This function runs in loglinear time.
*
* ## Examples
*
* ```gleam
* import gleam/int
*
* assert from_list([1, 4, 6, 3, 675, 44, 67])
* |> filter(keeping: int.is_even)
* |> to_list
* == [4, 6, 44]
* ```
*/
export function filter(set, predicate) {
return new Set($dict.filter(set.dict, (m, _) => { return predicate(m); }));
}
/**
* Creates a new set from a given set with the result of applying the given
* function to each member.
*
* ## Examples
*
* ```gleam
* assert from_list([1, 2, 3, 4])
* |> map(with: fn(x) { x * 2 })
* |> to_list
* == [2, 4, 6, 8]
* ```
*/
export function map(set, fun) {
return fold(
set,
new$(),
(acc, member) => { return insert(acc, fun(member)); },
);
}
/**
* Creates a new set from a given set with all the same entries except any
* entry found on the given list.
*
* ## Examples
*
* ```gleam
* assert from_list([1, 2, 3, 4])
* |> drop([1, 3])
* |> to_list
* == [2, 4]
* ```
*/
export function drop(set, disallowed) {
return $list.fold(disallowed, set, delete$);
}
/**
* Creates a new set from a given set, only including any members which are in
* a given list.
*
* This function runs in loglinear time.
*
* ## Examples
*
* ```gleam
* assert from_list([1, 2, 3])
* |> take([1, 3, 5])
* |> to_list
* == [1, 3]
* ```
*/
export function take(set, desired) {
return new Set($dict.take(set.dict, desired));
}
function order(first, second) {
let $ = $dict.size(first.dict) > $dict.size(second.dict);
if ($) {
return [first, second];
} else {
return [second, first];
}
}
/**
* Creates a new set that contains all members of both given sets.
*
* This function runs in loglinear time.
*
* ## Examples
*
* ```gleam
* assert union(from_list([1, 2]), from_list([2, 3])) |> to_list
* == [1, 2, 3]
* ```
*/
export function union(first, second) {
let $ = order(first, second);
let larger = $[0];
let smaller = $[1];
return fold(smaller, larger, insert);
}
/**
* Creates a new set that contains members that are present in both given sets.
*
* This function runs in loglinear time.
*
* ## Examples
*
* ```gleam
* assert intersection(from_list([1, 2]), from_list([2, 3])) |> to_list
* == [2]
* ```
*/
export function intersection(first, second) {
let $ = order(first, second);
let larger = $[0];
let smaller = $[1];
return take(larger, to_list(smaller));
}
/**
* Creates a new set that contains members that are present in the first set
* but not the second.
*
* ## Examples
*
* ```gleam
* assert difference(from_list([1, 2]), from_list([2, 3, 4])) |> to_list
* == [1]
* ```
*/
export function difference(first, second) {
return drop(first, to_list(second));
}
/**
* Determines if a set is fully contained by another.
*
* ## Examples
*
* ```gleam
* assert is_subset(from_list([1]), from_list([1, 2]))
* ```
*
* ```gleam
* assert !is_subset(from_list([1, 2, 3]), from_list([3, 4, 5]))
* ```
*/
export function is_subset(first, second) {
return isEqual(intersection(first, second), first);
}
/**
* Determines if two sets contain no common members
*
* ## Examples
*
* ```gleam
* assert is_disjoint(from_list([1, 2, 3]), from_list([4, 5, 6]))
* ```
*
* ```gleam
* assert !is_disjoint(from_list([1, 2, 3]), from_list([3, 4, 5]))
* ```
*/
export function is_disjoint(first, second) {
return isEqual(intersection(first, second), new$());
}
/**
* Creates a new set that contains members that are present in either set, but
* not both.
*
* ## Examples
*
* ```gleam
* assert symmetric_difference(from_list([1, 2, 3]), from_list([3, 4]))
* |> to_list
* == [1, 2, 4]
* ```
*/
export function symmetric_difference(first, second) {
return difference(union(first, second), intersection(first, second));
}
/**
* Calls a function for each member in a set, discarding the return
* value.
*
* Useful for producing a side effect for every item of a set.
*
* The order of elements in the iteration is an implementation detail that
* should not be relied upon.
*
* ## Examples
*
* ```gleam
* let set = from_list(["apple", "banana", "cherry"])
*
* assert each(set, io.println) == Nil
* // apple
* // banana
* // cherry
* ```
*/
export function each(set, fun) {
return fold(
set,
undefined,
(nil, member) => {
fun(member);
return nil;
},
);
}
+699
View File
@@ -0,0 +1,699 @@
import {
Ok,
Error,
Empty as $Empty,
prepend as listPrepend,
CustomType as $CustomType,
remainderInt,
divideInt,
} from "../gleam.mjs";
import * as $list from "../gleam/list.mjs";
import * as $option from "../gleam/option.mjs";
import { None, Some } from "../gleam/option.mjs";
import * as $order from "../gleam/order.mjs";
import * as $string_tree from "../gleam/string_tree.mjs";
import {
string_length as length,
lowercase,
uppercase,
less_than,
string_grapheme_slice as grapheme_slice,
string_byte_slice as unsafe_byte_slice,
crop_string as crop,
byte_size,
contains_string as contains,
starts_with,
ends_with,
pop_grapheme,
graphemes as to_graphemes,
split_once,
trim_end,
trim_start,
codepoint as unsafe_int_to_utf_codepoint,
string_to_codepoint_integer_list,
utf_codepoint_list_to_string as from_utf_codepoints,
utf_codepoint_to_int,
inspect as do_inspect,
string_remove_prefix as remove_prefix,
string_remove_suffix as remove_suffix,
} from "../gleam_stdlib.mjs";
export {
byte_size,
contains,
crop,
ends_with,
from_utf_codepoints,
length,
lowercase,
pop_grapheme,
remove_prefix,
remove_suffix,
split_once,
starts_with,
to_graphemes,
trim_end,
trim_start,
uppercase,
utf_codepoint_to_int,
};
class Leading extends $CustomType {}
class Trailing extends $CustomType {}
/**
* Determines if a `String` is empty.
*
* ## Examples
*
* ```gleam
* assert is_empty("")
* ```
*
* ```gleam
* assert !is_empty("the world")
* ```
*/
export function is_empty(str) {
return str === "";
}
/**
* Reverses a `String`.
*
* This function has to iterate across the whole `String` so it runs in linear
* time. Avoid using this in a loop.
*
* ## Examples
*
* ```gleam
* assert reverse("stressed") == "desserts"
* ```
*/
export function reverse(string) {
let _pipe = string;
let _pipe$1 = $string_tree.from_string(_pipe);
let _pipe$2 = $string_tree.reverse(_pipe$1);
return $string_tree.to_string(_pipe$2);
}
/**
* Creates a new `String` by replacing all occurrences of a given substring.
*
* ## Examples
*
* ```gleam
* assert replace("www.example.com", each: ".", with: "-") == "www-example-com"
* ```
*
* ```gleam
* assert replace("a,b,c,d,e", each: ",", with: "/") == "a/b/c/d/e"
* ```
*/
export function replace(string, pattern, substitute) {
let _pipe = string;
let _pipe$1 = $string_tree.from_string(_pipe);
let _pipe$2 = $string_tree.replace(_pipe$1, pattern, substitute);
return $string_tree.to_string(_pipe$2);
}
/**
* Compares two `String`s to see which is "larger" by comparing their graphemes.
*
* This does not compare the size or length of the given `String`s.
*
* ## Examples
*
* ```gleam
* import gleam/order
*
* assert compare("Anthony", "Anthony") == order.Eq
* ```
*
* ```gleam
* import gleam/order
*
* assert compare("A", "B") == order.Lt
* ```
*/
export function compare(a, b) {
let $ = a === b;
if ($) {
return new $order.Eq();
} else {
let $1 = less_than(a, b);
if ($1) {
return new $order.Lt();
} else {
return new $order.Gt();
}
}
}
/**
* Takes a substring given a start grapheme index and a length. Negative indexes
* are taken starting from the *end* of the string.
*
* This function runs in linear time with the size of the index and the
* length. Negative indexes are linear with the size of the input string in
* addition to the other costs.
*
* ## Examples
*
* ```gleam
* assert slice(from: "gleam", at_index: 1, length: 2) == "le"
* ```
*
* ```gleam
* assert slice(from: "gleam", at_index: 1, length: 10) == "leam"
* ```
*
* ```gleam
* assert slice(from: "gleam", at_index: 10, length: 3) == ""
* ```
*
* ```gleam
* assert slice(from: "gleam", at_index: -2, length: 2) == "am"
* ```
*
* ```gleam
* assert slice(from: "gleam", at_index: -12, length: 2) == ""
* ```
*/
export function slice(string, idx, len) {
let $ = len <= 0;
if ($) {
return "";
} else {
let $1 = idx < 0;
if ($1) {
let translated_idx = length(string) + idx;
let $2 = translated_idx < 0;
if ($2) {
return "";
} else {
return grapheme_slice(string, translated_idx, len);
}
} else {
return grapheme_slice(string, idx, len);
}
}
}
/**
* Drops *n* graphemes from the start of a `String`.
*
* This function runs in linear time with the number of graphemes to drop.
*
* ## Examples
*
* ```gleam
* assert drop_start(from: "The Lone Gunmen", up_to: 2) == "e Lone Gunmen"
* ```
*/
export function drop_start(string, num_graphemes) {
let $ = num_graphemes <= 0;
if ($) {
return string;
} else {
let prefix = grapheme_slice(string, 0, num_graphemes);
let prefix_size = byte_size(prefix);
return unsafe_byte_slice(
string,
prefix_size,
byte_size(string) - prefix_size,
);
}
}
/**
* Drops *n* graphemes from the end of a `String`.
*
* This function traverses the full string, so it runs in linear time with the
* size of the string. Avoid using this in a loop.
*
* ## Examples
*
* ```gleam
* assert drop_end(from: "Cigarette Smoking Man", up_to: 2)
* == "Cigarette Smoking M"
* ```
*/
export function drop_end(string, num_graphemes) {
let $ = num_graphemes <= 0;
if ($) {
return string;
} else {
return slice(string, 0, length(string) - num_graphemes);
}
}
function to_graphemes_loop(loop$string, loop$acc) {
while (true) {
let string = loop$string;
let acc = loop$acc;
let $ = pop_grapheme(string);
if ($ instanceof Ok) {
let grapheme = $[0][0];
let rest = $[0][1];
loop$string = rest;
loop$acc = listPrepend(grapheme, acc);
} else {
return acc;
}
}
}
/**
* Creates a list of `String`s by splitting a given string on a given substring.
*
* ## Examples
*
* ```gleam
* assert split("home/gleam/desktop/", on: "/")
* == ["home", "gleam", "desktop", ""]
* ```
*/
export function split(x, substring) {
if (substring === "") {
return to_graphemes(x);
} else {
let _pipe = x;
let _pipe$1 = $string_tree.from_string(_pipe);
let _pipe$2 = $string_tree.split(_pipe$1, substring);
return $list.map(_pipe$2, $string_tree.to_string);
}
}
/**
* Creates a new `String` by joining two `String`s together.
*
* This function typically copies both `String`s and runs in linear time, but
* the exact behaviour will depend on how the runtime you are using optimises
* your code. Benchmark and profile your code if you need to understand its
* performance better.
*
* If you are joining together large string and want to avoid copying any data
* you may want to investigate using the [`string_tree`](../gleam/string_tree.html)
* module.
*
* ## Examples
*
* ```gleam
* assert append(to: "butter", suffix: "fly") == "butterfly"
* ```
*/
export function append(first, second) {
return first + second;
}
function concat_loop(loop$strings, loop$accumulator) {
while (true) {
let strings = loop$strings;
let accumulator = loop$accumulator;
if (strings instanceof $Empty) {
return accumulator;
} else {
let string = strings.head;
let strings$1 = strings.tail;
loop$strings = strings$1;
loop$accumulator = accumulator + string;
}
}
}
/**
* Creates a new `String` by joining many `String`s together.
*
* This function copies all the `String`s and runs in linear time.
*
* ## Examples
*
* ```gleam
* assert concat(["never", "the", "less"]) == "nevertheless"
* ```
*/
export function concat(strings) {
return concat_loop(strings, "");
}
function repeat_loop(loop$times, loop$doubling_acc, loop$acc) {
while (true) {
let times = loop$times;
let doubling_acc = loop$doubling_acc;
let acc = loop$acc;
let _block;
let $ = times % 2;
if ($ === 0) {
_block = acc;
} else {
_block = acc + doubling_acc;
}
let acc$1 = _block;
let times$1 = globalThis.Math.trunc(times / 2);
let $1 = times$1 <= 0;
if ($1) {
return acc$1;
} else {
loop$times = times$1;
loop$doubling_acc = doubling_acc + doubling_acc;
loop$acc = acc$1;
}
}
}
/**
* Creates a new `String` by repeating a `String` a given number of times.
*
* This function runs in loglinear time.
*
* ## Examples
*
* ```gleam
* assert repeat("ha", times: 3) == "hahaha"
* ```
*/
export function repeat(string, times) {
let $ = times <= 0;
if ($) {
return "";
} else {
return repeat_loop(times, string, "");
}
}
function join_loop(loop$strings, loop$separator, loop$accumulator) {
while (true) {
let strings = loop$strings;
let separator = loop$separator;
let accumulator = loop$accumulator;
if (strings instanceof $Empty) {
return accumulator;
} else {
let string = strings.head;
let strings$1 = strings.tail;
loop$strings = strings$1;
loop$separator = separator;
loop$accumulator = (accumulator + separator) + string;
}
}
}
/**
* Joins many `String`s together with a given separator.
*
* This function runs in linear time.
*
* ## Examples
*
* ```gleam
* assert join(["home","evan","Desktop"], with: "/") == "home/evan/Desktop"
* ```
*/
export function join(strings, separator) {
if (strings instanceof $Empty) {
return "";
} else {
let first$1 = strings.head;
let rest = strings.tail;
return join_loop(rest, separator, first$1);
}
}
function padding(size, pad_string) {
let pad_string_length = length(pad_string);
let num_pads = divideInt(size, pad_string_length);
let extra = remainderInt(size, pad_string_length);
return repeat(pad_string, num_pads) + slice(pad_string, 0, extra);
}
/**
* Pads the start of a `String` until it has a given length.
*
* ## Examples
*
* ```gleam
* assert pad_start("121", to: 5, with: ".") == "..121"
* ```
*
* ```gleam
* assert pad_start("121", to: 3, with: ".") == "121"
* ```
*
* ```gleam
* assert pad_start("121", to: 2, with: ".") == "121"
* ```
*/
export function pad_start(string, desired_length, pad_string) {
let current_length = length(string);
let to_pad_length = desired_length - current_length;
let $ = to_pad_length <= 0;
if ($) {
return string;
} else {
return padding(to_pad_length, pad_string) + string;
}
}
/**
* Pads the end of a `String` until it has a given length.
*
* ## Examples
*
* ```gleam
* assert pad_end("123", to: 5, with: ".") == "123.."
* ```
*
* ```gleam
* assert pad_end("123", to: 3, with: ".") == "123"
* ```
*
* ```gleam
* assert pad_end("123", to: 2, with: ".") == "123"
* ```
*/
export function pad_end(string, desired_length, pad_string) {
let current_length = length(string);
let to_pad_length = desired_length - current_length;
let $ = to_pad_length <= 0;
if ($) {
return string;
} else {
return string + padding(to_pad_length, pad_string);
}
}
/**
* Removes whitespace on both sides of a `String`.
*
* Whitespace in this function is the set of nonbreakable whitespace
* codepoints, defined as Pattern_White_Space in [Unicode Standard Annex #31][1].
*
* [1]: https://unicode.org/reports/tr31/
*
* ## Examples
*
* ```gleam
* assert trim(" hats \n") == "hats"
* ```
*/
export function trim(string) {
let _pipe = string;
let _pipe$1 = trim_start(_pipe);
return trim_end(_pipe$1);
}
function do_to_utf_codepoints(string) {
let _pipe = string;
let _pipe$1 = string_to_codepoint_integer_list(_pipe);
return $list.map(_pipe$1, unsafe_int_to_utf_codepoint);
}
/**
* Converts a `String` to a `List` of `UtfCodepoint`.
*
* See <https://en.wikipedia.org/wiki/Code_point> and
* <https://en.wikipedia.org/wiki/Unicode#Codespace_and_Code_Points> for an
* explanation on code points.
*
* ## Examples
*
* ```gleam
* assert "a" |> to_utf_codepoints == [UtfCodepoint(97)]
* ```
*
* ```gleam
* // Semantically the same as:
* // ["🏳", "", "", "🌈"] or:
* // [waving_white_flag, variant_selector_16, zero_width_joiner, rainbow]
* assert "🏳️‍🌈" |> to_utf_codepoints
* == [
* UtfCodepoint(127987),
* UtfCodepoint(65039),
* UtfCodepoint(8205),
* UtfCodepoint(127752),
* ]
* ```
*/
export function to_utf_codepoints(string) {
return do_to_utf_codepoints(string);
}
/**
* Converts an integer to a `UtfCodepoint`.
*
* Returns an `Error` if the integer does not represent a valid UTF codepoint.
*/
export function utf_codepoint(value) {
let i = value;
if (i > 1_114_111) {
return new Error(undefined);
} else {
let i = value;
if ((i >= 55_296) && (i <= 57_343)) {
return new Error(undefined);
} else {
let i = value;
if (i < 0) {
return new Error(undefined);
} else {
let i = value;
return new Ok(unsafe_int_to_utf_codepoint(i));
}
}
}
}
/**
* Converts a `String` into `Option(String)` where an empty `String` becomes
* `None`.
*
* ## Examples
*
* ```gleam
* assert to_option("") == None
* ```
*
* ```gleam
* assert to_option("hats") == Some("hats")
* ```
*/
export function to_option(string) {
if (string === "") {
return new None();
} else {
return new Some(string);
}
}
/**
* Returns the first grapheme cluster in a given `String` and wraps it in a
* `Result(String, Nil)`. If the `String` is empty, it returns `Error(Nil)`.
* Otherwise, it returns `Ok(String)`.
*
* ## Examples
*
* ```gleam
* assert first("") == Error(Nil)
* ```
*
* ```gleam
* assert first("icecream") == Ok("i")
* ```
*/
export function first(string) {
let $ = pop_grapheme(string);
if ($ instanceof Ok) {
let first$1 = $[0][0];
return new Ok(first$1);
} else {
return $;
}
}
/**
* Returns the last grapheme cluster in a given `String` and wraps it in a
* `Result(String, Nil)`. If the `String` is empty, it returns `Error(Nil)`.
* Otherwise, it returns `Ok(String)`.
*
* This function traverses the full string, so it runs in linear time with the
* length of the string. Avoid using this in a loop.
*
* ## Examples
*
* ```gleam
* assert last("") == Error(Nil)
* ```
*
* ```gleam
* assert last("icecream") == Ok("m")
* ```
*/
export function last(string) {
let $ = pop_grapheme(string);
if ($ instanceof Ok) {
let $1 = $[0][1];
if ($1 === "") {
let first$1 = $[0][0];
return new Ok(first$1);
} else {
let rest = $1;
return new Ok(slice(rest, -1, 1));
}
} else {
return $;
}
}
/**
* Creates a new `String` with the first grapheme in the input `String`
* converted to uppercase and the remaining graphemes to lowercase.
*
* ## Examples
*
* ```gleam
* assert capitalise("mamouna") == "Mamouna"
* ```
*/
export function capitalise(string) {
let $ = pop_grapheme(string);
if ($ instanceof Ok) {
let first$1 = $[0][0];
let rest = $[0][1];
return append(uppercase(first$1), lowercase(rest));
} else {
return "";
}
}
/**
* Returns a `String` representation of a term in Gleam syntax.
*
* This may be occasionally useful for quick-and-dirty printing of values in
* scripts. For error reporting and other uses prefer constructing strings by
* pattern matching on the values.
*
* ## Limitations
*
* The output format of this function is not stable and could change at any
* time. The output is not suitable for parsing.
*
* This function works using runtime reflection, so the output may not be
* perfectly accurate for data structures where the runtime structure doesn't
* hold enough information to determine the original syntax. For example,
* tuples with an Erlang atom in the first position will be mistaken for Gleam
* records.
*
* ## Security and safety
*
* There is no limit to how large the strings that this function can produce.
* Be careful not to call this function with large data structures or you
* could use very large amounts of memory, potentially causing runtime
* problems.
*/
export function inspect(term) {
let _pipe = term;
let _pipe$1 = do_inspect(_pipe);
return $string_tree.to_string(_pipe$1);
}
@@ -0,0 +1,128 @@
import { toList, CustomType as $CustomType, isEqual } from "../gleam.mjs";
import * as $list from "../gleam/list.mjs";
import {
concat as from_strings,
identity as from_string,
add as append_tree,
concat,
identity as to_string,
length as byte_size,
lowercase,
uppercase,
graphemes as do_to_graphemes,
split,
string_replace as replace,
} from "../gleam_stdlib.mjs";
export {
append_tree,
byte_size,
concat,
from_string,
from_strings,
lowercase,
replace,
split,
to_string,
uppercase,
};
class All extends $CustomType {}
/**
* Create an empty `StringTree`. Useful as the start of a pipe chaining many
* trees together.
*/
export function new$() {
return from_strings(toList([]));
}
/**
* Prepends a `String` onto the start of some `StringTree`.
*
* Runs in constant time.
*/
export function prepend(tree, prefix) {
return append_tree(from_string(prefix), tree);
}
/**
* Appends a `String` onto the end of some `StringTree`.
*
* Runs in constant time.
*/
export function append(tree, second) {
return append_tree(tree, from_string(second));
}
/**
* Prepends some `StringTree` onto the start of another.
*
* Runs in constant time.
*/
export function prepend_tree(tree, prefix) {
return append_tree(prefix, tree);
}
/**
* Joins the given trees into a new tree separated with the given string.
*/
export function join(trees, sep) {
let _pipe = trees;
let _pipe$1 = $list.intersperse(_pipe, from_string(sep));
return concat(_pipe$1);
}
/**
* Converts a `StringTree` to a new one with the contents reversed.
*/
export function reverse(tree) {
let _pipe = tree;
let _pipe$1 = to_string(_pipe);
let _pipe$2 = do_to_graphemes(_pipe$1);
let _pipe$3 = $list.reverse(_pipe$2);
return from_strings(_pipe$3);
}
/**
* Compares two string trees to determine if they have the same textual
* content.
*
* Comparing two string trees using the `==` operator may return `False` even
* if they have the same content as they may have been built in different ways,
* so using this function is often preferred.
*
* ## Examples
*
* ```gleam
* assert from_strings(["a", "b"]) != from_string("ab")
* ```
*
* ```gleam
* assert is_equal(from_strings(["a", "b"]), from_string("ab"))
* ```
*/
export function is_equal(a, b) {
return isEqual(a, b);
}
/**
* Inspects a `StringTree` to determine if it is equivalent to an empty string.
*
* ## Examples
*
* ```gleam
* assert !{ from_string("ok") |> is_empty }
* ```
*
* ```gleam
* assert from_string("") |> is_empty
* ```
*
* ```gleam
* assert from_strings([]) |> is_empty
* ```
*/
export function is_empty(tree) {
return isEqual(from_string(""), tree);
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,347 @@
-module(gleam@bit_array).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]).
-define(FILEPATH, "src/gleam/bit_array.gleam").
-export([from_string/1, bit_size/1, byte_size/1, pad_to_bytes/1, concat/1, append/2, slice/3, is_utf8/1, to_string/1, base64_encode/2, base64_decode/1, base64_url_encode/2, base64_url_decode/1, base16_encode/1, base16_decode/1, inspect/1, compare/2, starts_with/2]).
-if(?OTP_RELEASE >= 27).
-define(MODULEDOC(Str), -moduledoc(Str)).
-define(DOC(Str), -doc(Str)).
-else.
-define(MODULEDOC(Str), -compile([])).
-define(DOC(Str), -compile([])).
-endif.
?MODULEDOC(" BitArrays are a sequence of binary data of any length.\n").
-file("src/gleam/bit_array.gleam", 11).
?DOC(" Converts a UTF-8 `String` type into a `BitArray`.\n").
-spec from_string(binary()) -> bitstring().
from_string(X) ->
gleam_stdlib:identity(X).
-file("src/gleam/bit_array.gleam", 17).
?DOC(" Returns an integer which is the number of bits in the bit array.\n").
-spec bit_size(bitstring()) -> integer().
bit_size(X) ->
erlang:bit_size(X).
-file("src/gleam/bit_array.gleam", 23).
?DOC(" Returns an integer which is the number of bytes in the bit array.\n").
-spec byte_size(bitstring()) -> integer().
byte_size(X) ->
erlang:byte_size(X).
-file("src/gleam/bit_array.gleam", 29).
?DOC(" Pads a bit array with zeros so that it is a whole number of bytes.\n").
-spec pad_to_bytes(bitstring()) -> bitstring().
pad_to_bytes(X) ->
gleam_stdlib:bit_array_pad_to_bytes(X).
-file("src/gleam/bit_array.gleam", 109).
?DOC(
" Creates a new bit array by joining multiple binaries.\n"
"\n"
" ## Examples\n"
"\n"
" ```gleam\n"
" assert concat([from_string(\"butter\"), from_string(\"fly\")])\n"
" == from_string(\"butterfly\")\n"
" ```\n"
).
-spec concat(list(bitstring())) -> bitstring().
concat(Bit_arrays) ->
gleam_stdlib:bit_array_concat(Bit_arrays).
-file("src/gleam/bit_array.gleam", 40).
?DOC(
" Creates a new bit array by joining two bit arrays.\n"
"\n"
" ## Examples\n"
"\n"
" ```gleam\n"
" assert append(to: from_string(\"butter\"), suffix: from_string(\"fly\"))\n"
" == from_string(\"butterfly\")\n"
" ```\n"
).
-spec append(bitstring(), bitstring()) -> bitstring().
append(First, Second) ->
gleam_stdlib:bit_array_concat([First, Second]).
-file("src/gleam/bit_array.gleam", 54).
?DOC(
" Extracts a sub-section of a bit array.\n"
"\n"
" The slice will start at given position and continue up to specified\n"
" length.\n"
" A negative length can be used to extract bytes at the end of a bit array.\n"
"\n"
" This function runs in constant time.\n"
).
-spec slice(bitstring(), integer(), integer()) -> {ok, bitstring()} |
{error, nil}.
slice(String, Position, Length) ->
gleam_stdlib:bit_array_slice(String, Position, Length).
-file("src/gleam/bit_array.gleam", 67).
-spec is_utf8_loop(bitstring()) -> boolean().
is_utf8_loop(Bits) ->
case Bits of
<<>> ->
true;
<<_/utf8, Rest/binary>> ->
is_utf8_loop(Rest);
_ ->
false
end.
-file("src/gleam/bit_array.gleam", 62).
?DOC(" Tests to see whether a bit array is valid UTF-8.\n").
-spec is_utf8(bitstring()) -> boolean().
is_utf8(Bits) ->
is_utf8_loop(Bits).
-file("src/gleam/bit_array.gleam", 88).
?DOC(
" Converts a bit array to a string.\n"
"\n"
" Returns an error if the bit array is invalid UTF-8 data.\n"
).
-spec to_string(bitstring()) -> {ok, binary()} | {error, nil}.
to_string(Bits) ->
case is_utf8(Bits) of
true ->
{ok, gleam_stdlib:identity(Bits)};
false ->
{error, nil}
end.
-file("src/gleam/bit_array.gleam", 118).
?DOC(
" Encodes a BitArray into a base 64 encoded string.\n"
"\n"
" If the bit array does not contain a whole number of bytes then it is padded\n"
" with zero bits prior to being encoded.\n"
).
-spec base64_encode(bitstring(), boolean()) -> binary().
base64_encode(Input, Padding) ->
gleam_stdlib:base64_encode(Input, Padding).
-file("src/gleam/bit_array.gleam", 122).
?DOC(" Decodes a base 64 encoded string into a `BitArray`.\n").
-spec base64_decode(binary()) -> {ok, bitstring()} | {error, nil}.
base64_decode(Encoded) ->
Padded = case erlang:byte_size(gleam_stdlib:identity(Encoded)) rem 4 of
0 ->
Encoded;
N ->
gleam@string:append(
Encoded,
gleam@string:repeat(<<"="/utf8>>, 4 - N)
)
end,
gleam_stdlib:base64_decode(Padded).
-file("src/gleam/bit_array.gleam", 140).
?DOC(
" Encodes a `BitArray` into a base 64 encoded string with URL and filename\n"
" safe alphabet.\n"
"\n"
" If the bit array does not contain a whole number of bytes then it is padded\n"
" with zero bits prior to being encoded.\n"
).
-spec base64_url_encode(bitstring(), boolean()) -> binary().
base64_url_encode(Input, Padding) ->
_pipe = Input,
_pipe@1 = gleam_stdlib:base64_encode(_pipe, Padding),
_pipe@2 = gleam@string:replace(_pipe@1, <<"+"/utf8>>, <<"-"/utf8>>),
gleam@string:replace(_pipe@2, <<"/"/utf8>>, <<"_"/utf8>>).
-file("src/gleam/bit_array.gleam", 150).
?DOC(
" Decodes a base 64 encoded string with URL and filename safe alphabet into a\n"
" `BitArray`.\n"
).
-spec base64_url_decode(binary()) -> {ok, bitstring()} | {error, nil}.
base64_url_decode(Encoded) ->
_pipe = Encoded,
_pipe@1 = gleam@string:replace(_pipe, <<"-"/utf8>>, <<"+"/utf8>>),
_pipe@2 = gleam@string:replace(_pipe@1, <<"_"/utf8>>, <<"/"/utf8>>),
base64_decode(_pipe@2).
-file("src/gleam/bit_array.gleam", 164).
?DOC(
" Encodes a `BitArray` into a base 16 encoded string.\n"
"\n"
" If the bit array does not contain a whole number of bytes then it is padded\n"
" with zero bits prior to being encoded.\n"
).
-spec base16_encode(bitstring()) -> binary().
base16_encode(Input) ->
gleam_stdlib:base16_encode(Input).
-file("src/gleam/bit_array.gleam", 170).
?DOC(" Decodes a base 16 encoded string into a `BitArray`.\n").
-spec base16_decode(binary()) -> {ok, bitstring()} | {error, nil}.
base16_decode(Input) ->
gleam_stdlib:base16_decode(Input).
-file("src/gleam/bit_array.gleam", 191).
-spec inspect_loop(bitstring(), binary()) -> binary().
inspect_loop(Input, Accumulator) ->
case Input of
<<>> ->
Accumulator;
<<X:1>> ->
<<<<Accumulator/binary, (erlang:integer_to_binary(X))/binary>>/binary,
":size(1)"/utf8>>;
<<X@1:2>> ->
<<<<Accumulator/binary, (erlang:integer_to_binary(X@1))/binary>>/binary,
":size(2)"/utf8>>;
<<X@2:3>> ->
<<<<Accumulator/binary, (erlang:integer_to_binary(X@2))/binary>>/binary,
":size(3)"/utf8>>;
<<X@3:4>> ->
<<<<Accumulator/binary, (erlang:integer_to_binary(X@3))/binary>>/binary,
":size(4)"/utf8>>;
<<X@4:5>> ->
<<<<Accumulator/binary, (erlang:integer_to_binary(X@4))/binary>>/binary,
":size(5)"/utf8>>;
<<X@5:6>> ->
<<<<Accumulator/binary, (erlang:integer_to_binary(X@5))/binary>>/binary,
":size(6)"/utf8>>;
<<X@6:7>> ->
<<<<Accumulator/binary, (erlang:integer_to_binary(X@6))/binary>>/binary,
":size(7)"/utf8>>;
<<X@7, Rest/bitstring>> ->
Suffix = case Rest of
<<>> ->
<<""/utf8>>;
_ ->
<<", "/utf8>>
end,
Accumulator@1 = <<<<Accumulator/binary,
(erlang:integer_to_binary(X@7))/binary>>/binary,
Suffix/binary>>,
inspect_loop(Rest, Accumulator@1);
_ ->
Accumulator
end.
-file("src/gleam/bit_array.gleam", 187).
?DOC(
" Converts a bit array to a string containing the decimal value of each byte.\n"
"\n"
" Use this over `string.inspect` when you have a bit array you want printed\n"
" in the array syntax even if it is valid UTF-8.\n"
"\n"
" ## Examples\n"
"\n"
" ```gleam\n"
" assert inspect(<<0, 20, 0x20, 255>>) == \"<<0, 20, 32, 255>>\"\n"
" ```\n"
"\n"
" ```gleam\n"
" assert inspect(<<100, 5:3>>) == \"<<100, 5:size(3)>>\"\n"
" ```\n"
).
-spec inspect(bitstring()) -> binary().
inspect(Input) ->
<<(inspect_loop(Input, <<"<<"/utf8>>))/binary, ">>"/utf8>>.
-file("src/gleam/bit_array.gleam", 233).
?DOC(
" Compare two bit arrays as sequences of bytes.\n"
"\n"
" ## Examples\n"
"\n"
" ```gleam\n"
" assert compare(<<1>>, <<2>>) == Lt\n"
" ```\n"
"\n"
" ```gleam\n"
" assert compare(<<\"AB\":utf8>>, <<\"AA\":utf8>>) == Gt\n"
" ```\n"
"\n"
" ```gleam\n"
" assert compare(<<1, 2:size(2)>>, with: <<1, 2:size(2)>>) == Eq\n"
" ```\n"
).
-spec compare(bitstring(), bitstring()) -> gleam@order:order().
compare(A, B) ->
case {A, B} of
{<<First_byte, First_rest/bitstring>>,
<<Second_byte, Second_rest/bitstring>>} ->
case {First_byte, Second_byte} of
{F, S} when F > S ->
gt;
{F@1, S@1} when F@1 < S@1 ->
lt;
{_, _} ->
compare(First_rest, Second_rest)
end;
{<<>>, <<>>} ->
eq;
{_, <<>>} ->
gt;
{<<>>, _} ->
lt;
{First, Second} ->
case {gleam_stdlib:bit_array_to_int_and_size(First),
gleam_stdlib:bit_array_to_int_and_size(Second)} of
{{A@1, _}, {B@1, _}} when A@1 > B@1 ->
gt;
{{A@2, _}, {B@2, _}} when A@2 < B@2 ->
lt;
{{_, Size_a}, {_, Size_b}} when Size_a > Size_b ->
gt;
{{_, Size_a@1}, {_, Size_b@1}} when Size_a@1 < Size_b@1 ->
lt;
{_, _} ->
eq
end
end.
-file("src/gleam/bit_array.gleam", 273).
?DOC(
" Checks whether the first `BitArray` starts with the second one.\n"
"\n"
" ## Examples\n"
"\n"
" ```gleam\n"
" assert starts_with(<<1, 2, 3, 4>>, <<1, 2>>)\n"
" ```\n"
).
-spec starts_with(bitstring(), bitstring()) -> boolean().
starts_with(Bits, Prefix) ->
Prefix_size = erlang:bit_size(Prefix),
case Bits of
<<Pref:Prefix_size/bitstring, _/bitstring>> when Pref =:= Prefix ->
true;
_ ->
false
end.
+334
View File
@@ -0,0 +1,334 @@
-module(gleam@bool).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]).
-define(FILEPATH, "src/gleam/bool.gleam").
-export(['and'/2, 'or'/2, negate/1, nor/2, nand/2, exclusive_or/2, exclusive_nor/2, to_string/1, guard/3, lazy_guard/3]).
-if(?OTP_RELEASE >= 27).
-define(MODULEDOC(Str), -moduledoc(Str)).
-define(DOC(Str), -doc(Str)).
-else.
-define(MODULEDOC(Str), -compile([])).
-define(DOC(Str), -compile([])).
-endif.
?MODULEDOC(
" A type with two possible values, `True` and `False`. Used to indicate whether\n"
" things are... true or false!\n"
"\n"
" It is often clearer and offers more type safety to define a custom type\n"
" than to use `Bool`. For example, rather than having a `is_teacher: Bool`\n"
" field consider having a `role: SchoolRole` field where `SchoolRole` is a custom\n"
" type that can be either `Student` or `Teacher`.\n"
).
-file("src/gleam/bool.gleam", 32).
?DOC(
" Returns the and of two bools, but it evaluates both arguments.\n"
"\n"
" It's the function equivalent of the `&&` operator.\n"
" This function is useful in higher order functions or pipes.\n"
"\n"
" ## Examples\n"
"\n"
" ```gleam\n"
" assert and(True, True)\n"
" ```\n"
"\n"
" ```gleam\n"
" assert !and(False, True)\n"
" ```\n"
"\n"
" ```gleam\n"
" assert !and(False, True)\n"
" ```\n"
"\n"
" ```gleam\n"
" assert !and(False, False)\n"
" ```\n"
).
-spec 'and'(boolean(), boolean()) -> boolean().
'and'(A, B) ->
A andalso B.
-file("src/gleam/bool.gleam", 59).
?DOC(
" Returns the or of two bools, but it evaluates both arguments.\n"
"\n"
" It's the function equivalent of the `||` operator.\n"
" This function is useful in higher order functions or pipes.\n"
"\n"
" ## Examples\n"
"\n"
" ```gleam\n"
" assert or(True, True)\n"
" ```\n"
"\n"
" ```gleam\n"
" assert or(False, True)\n"
" ```\n"
"\n"
" ```gleam\n"
" assert or(True, False)\n"
" ```\n"
"\n"
" ```gleam\n"
" assert !or(False, False)\n"
" ```\n"
).
-spec 'or'(boolean(), boolean()) -> boolean().
'or'(A, B) ->
A orelse B.
-file("src/gleam/bool.gleam", 77).
?DOC(
" Returns the opposite bool value.\n"
"\n"
" This is the same as the `!` or `not` operators in some other languages.\n"
"\n"
" ## Examples\n"
"\n"
" ```gleam\n"
" assert !negate(True)\n"
" ```\n"
"\n"
" ```gleam\n"
" assert negate(False)\n"
" ```\n"
).
-spec negate(boolean()) -> boolean().
negate(Bool) ->
not Bool.
-file("src/gleam/bool.gleam", 101).
?DOC(
" Returns the nor of two bools.\n"
"\n"
" ## Examples\n"
"\n"
" ```gleam\n"
" assert nor(False, False)\n"
" ```\n"
"\n"
" ```gleam\n"
" assert !nor(False, True)\n"
" ```\n"
"\n"
" ```gleam\n"
" assert !nor(True, False)\n"
" ```\n"
"\n"
" ```gleam\n"
" assert !nor(True, True)\n"
" ```\n"
).
-spec nor(boolean(), boolean()) -> boolean().
nor(A, B) ->
not (A orelse B).
-file("src/gleam/bool.gleam", 125).
?DOC(
" Returns the nand of two bools.\n"
"\n"
" ## Examples\n"
"\n"
" ```gleam\n"
" assert nand(False, False)\n"
" ```\n"
"\n"
" ```gleam\n"
" assert nand(False, True)\n"
" ```\n"
"\n"
" ```gleam\n"
" assert nand(True, False)\n"
" ```\n"
"\n"
" ```gleam\n"
" assert !nand(True, True)\n"
" ```\n"
).
-spec nand(boolean(), boolean()) -> boolean().
nand(A, B) ->
not (A andalso B).
-file("src/gleam/bool.gleam", 149).
?DOC(
" Returns the exclusive or of two bools.\n"
"\n"
" ## Examples\n"
"\n"
" ```gleam\n"
" assert !exclusive_or(False, False)\n"
" ```\n"
"\n"
" ```gleam\n"
" assert exclusive_or(False, True)\n"
" ```\n"
"\n"
" ```gleam\n"
" assert exclusive_or(True, False)\n"
" ```\n"
"\n"
" ```gleam\n"
" assert !exclusive_or(True, True)\n"
" ```\n"
).
-spec exclusive_or(boolean(), boolean()) -> boolean().
exclusive_or(A, B) ->
A /= B.
-file("src/gleam/bool.gleam", 173).
?DOC(
" Returns the exclusive nor of two bools.\n"
"\n"
" ## Examples\n"
"\n"
" ```gleam\n"
" assert exclusive_nor(False, False)\n"
" ```\n"
"\n"
" ```gleam\n"
" assert !exclusive_nor(False, True)\n"
" ```\n"
"\n"
" ```gleam\n"
" assert !exclusive_nor(True, False)\n"
" ```\n"
"\n"
" ```gleam\n"
" assert exclusive_nor(True, True)\n"
" ```\n"
).
-spec exclusive_nor(boolean(), boolean()) -> boolean().
exclusive_nor(A, B) ->
A =:= B.
-file("src/gleam/bool.gleam", 189).
?DOC(
" Returns a string representation of the given bool.\n"
"\n"
" ## Examples\n"
"\n"
" ```gleam\n"
" assert to_string(True) == \"True\"\n"
" ```\n"
"\n"
" ```gleam\n"
" assert to_string(False) == \"False\"\n"
" ```\n"
).
-spec to_string(boolean()) -> binary().
to_string(Bool) ->
case Bool of
false ->
<<"False"/utf8>>;
true ->
<<"True"/utf8>>
end.
-file("src/gleam/bool.gleam", 248).
?DOC(
" Run a callback function if the given bool is `False`, otherwise return a\n"
" default value.\n"
"\n"
" With a `use` expression this function can simulate the early-return pattern\n"
" found in some other programming languages.\n"
"\n"
" In a procedural language:\n"
"\n"
" ```js\n"
" if (predicate) return value;\n"
" // ...\n"
" ```\n"
"\n"
" In Gleam with a `use` expression:\n"
"\n"
" ```gleam\n"
" use <- guard(when: predicate, return: value)\n"
" // ...\n"
" ```\n"
"\n"
" Like everything in Gleam `use` is an expression, so it short circuits the\n"
" current block, not the entire function. As a result you can assign the value\n"
" to a variable:\n"
"\n"
" ```gleam\n"
" let x = {\n"
" use <- guard(when: predicate, return: value)\n"
" // ...\n"
" }\n"
" ```\n"
"\n"
" Note that unlike in procedural languages the `return` value is evaluated\n"
" even when the predicate is `False`, so it is advisable not to perform\n"
" expensive computation nor side-effects there.\n"
"\n"
"\n"
" ## Examples\n"
"\n"
" ```gleam\n"
" let name = \"\"\n"
" use <- guard(when: name == \"\", return: \"Welcome!\")\n"
" \"Hello, \" <> name\n"
" // -> \"Welcome!\"\n"
" ```\n"
"\n"
" ```gleam\n"
" let name = \"Kamaka\"\n"
" use <- guard(when: name == \"\", return: \"Welcome!\")\n"
" \"Hello, \" <> name\n"
" // -> \"Hello, Kamaka\"\n"
" ```\n"
).
-spec guard(boolean(), BTP, fun(() -> BTP)) -> BTP.
guard(Requirement, Consequence, Alternative) ->
case Requirement of
true ->
Consequence;
false ->
Alternative()
end.
-file("src/gleam/bool.gleam", 289).
?DOC(
" Runs a callback function if the given bool is `True`, otherwise runs an\n"
" alternative callback function.\n"
"\n"
" Useful when further computation should be delayed regardless of the given\n"
" bool's value.\n"
"\n"
" See [`guard`](#guard) for more info.\n"
"\n"
" ## Examples\n"
"\n"
" ```gleam\n"
" let name = \"Kamaka\"\n"
" let inquiry = fn() { \"How may we address you?\" }\n"
" use <- lazy_guard(when: name == \"\", return: inquiry)\n"
" \"Hello, \" <> name\n"
" // -> \"Hello, Kamaka\"\n"
" ```\n"
"\n"
" ```gleam\n"
" import gleam/int\n"
"\n"
" let name = \"\"\n"
" let greeting = fn() { \"Hello, \" <> name }\n"
" use <- lazy_guard(when: name == \"\", otherwise: greeting)\n"
" let number = int.random(99)\n"
" let name = \"User \" <> int.to_string(number)\n"
" \"Welcome, \" <> name\n"
" // -> \"Welcome, User 54\"\n"
" ```\n"
).
-spec lazy_guard(boolean(), fun(() -> BTQ), fun(() -> BTQ)) -> BTQ.
lazy_guard(Requirement, Consequence, Alternative) ->
case Requirement of
true ->
Consequence();
false ->
Alternative()
end.
@@ -0,0 +1,211 @@
-module(gleam@bytes_tree).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]).
-define(FILEPATH, "src/gleam/bytes_tree.gleam").
-export([concat/1, new/0, from_bit_array/1, append_tree/2, prepend/2, append/2, prepend_tree/2, from_string/1, prepend_string/2, append_string/2, concat_bit_arrays/1, from_string_tree/1, to_bit_array/1, byte_size/1]).
-export_type([bytes_tree/0]).
-if(?OTP_RELEASE >= 27).
-define(MODULEDOC(Str), -moduledoc(Str)).
-define(DOC(Str), -doc(Str)).
-else.
-define(MODULEDOC(Str), -compile([])).
-define(DOC(Str), -compile([])).
-endif.
?MODULEDOC(
" `BytesTree` is a type used for efficiently building binary content to be\n"
" written to a file or a socket. Internally it is represented as a tree so to\n"
" append or prepend to a bytes tree is a constant time operation that\n"
" allocates a new node in the tree without copying any of the content. When\n"
" writing to an output stream the tree is traversed and the content is sent\n"
" directly rather than copying it into a single buffer beforehand.\n"
"\n"
" If we append one bit array to another the bit arrays must be copied to a\n"
" new location in memory so that they can sit together. This behaviour\n"
" enables efficient reading of the data but copying can be expensive,\n"
" especially if we want to join many bit arrays together.\n"
"\n"
" BytesTree is different in that it can be joined together in constant\n"
" time using minimal memory, and then can be efficiently converted to a\n"
" bit array using the `to_bit_array` function.\n"
"\n"
" Byte trees are always byte aligned, so that a number of bits that is not\n"
" divisible by 8 will be padded with 0s.\n"
"\n"
" On Erlang this type is compatible with Erlang's iolists.\n"
).
-opaque bytes_tree() :: {bytes, bitstring()} |
{text, gleam@string_tree:string_tree()} |
{many, list(bytes_tree())}.
-file("src/gleam/bytes_tree.gleam", 98).
?DOC(
" Joins a list of bytes trees into a single one.\n"
"\n"
" Runs in constant time.\n"
).
-spec concat(list(bytes_tree())) -> bytes_tree().
concat(Trees) ->
gleam_stdlib:identity(Trees).
-file("src/gleam/bytes_tree.gleam", 35).
?DOC(
" Create an empty `BytesTree`. Useful as the start of a pipe chaining many\n"
" trees together.\n"
).
-spec new() -> bytes_tree().
new() ->
gleam_stdlib:identity([]).
-file("src/gleam/bytes_tree.gleam", 136).
?DOC(
" Creates a new bytes tree from a bit array.\n"
"\n"
" Runs in constant time.\n"
).
-spec from_bit_array(bitstring()) -> bytes_tree().
from_bit_array(Bits) ->
_pipe = Bits,
_pipe@1 = gleam_stdlib:bit_array_pad_to_bytes(_pipe),
gleam_stdlib:wrap_list(_pipe@1).
-file("src/gleam/bytes_tree.gleam", 68).
?DOC(
" Appends a bytes tree onto the end of another.\n"
"\n"
" Runs in constant time.\n"
).
-spec append_tree(bytes_tree(), bytes_tree()) -> bytes_tree().
append_tree(First, Second) ->
gleam_stdlib:iodata_append(First, Second).
-file("src/gleam/bytes_tree.gleam", 43).
?DOC(
" Prepends a bit array to the start of a bytes tree.\n"
"\n"
" Runs in constant time.\n"
).
-spec prepend(bytes_tree(), bitstring()) -> bytes_tree().
prepend(Second, First) ->
gleam_stdlib:iodata_append(from_bit_array(First), Second).
-file("src/gleam/bytes_tree.gleam", 51).
?DOC(
" Appends a bit array to the end of a bytes tree.\n"
"\n"
" Runs in constant time.\n"
).
-spec append(bytes_tree(), bitstring()) -> bytes_tree().
append(First, Second) ->
gleam_stdlib:iodata_append(First, from_bit_array(Second)).
-file("src/gleam/bytes_tree.gleam", 59).
?DOC(
" Prepends a bytes tree onto the start of another.\n"
"\n"
" Runs in constant time.\n"
).
-spec prepend_tree(bytes_tree(), bytes_tree()) -> bytes_tree().
prepend_tree(Second, First) ->
gleam_stdlib:iodata_append(First, Second).
-file("src/gleam/bytes_tree.gleam", 118).
?DOC(
" Creates a new bytes tree from a string.\n"
"\n"
" Runs in constant time when running on Erlang.\n"
" Runs in linear time otherwise.\n"
).
-spec from_string(binary()) -> bytes_tree().
from_string(String) ->
gleam_stdlib:wrap_list(String).
-file("src/gleam/bytes_tree.gleam", 80).
?DOC(
" Prepends a string onto the start of a bytes tree.\n"
"\n"
" Runs in constant time when running on Erlang.\n"
" Runs in linear time with the length of the string otherwise.\n"
).
-spec prepend_string(bytes_tree(), binary()) -> bytes_tree().
prepend_string(Second, First) ->
gleam_stdlib:iodata_append(gleam_stdlib:wrap_list(First), Second).
-file("src/gleam/bytes_tree.gleam", 89).
?DOC(
" Appends a string onto the end of a bytes tree.\n"
"\n"
" Runs in constant time when running on Erlang.\n"
" Runs in linear time with the length of the string otherwise.\n"
).
-spec append_string(bytes_tree(), binary()) -> bytes_tree().
append_string(First, Second) ->
gleam_stdlib:iodata_append(First, gleam_stdlib:wrap_list(Second)).
-file("src/gleam/bytes_tree.gleam", 106).
?DOC(
" Joins a list of bit arrays into a single bytes tree.\n"
"\n"
" Runs in constant time.\n"
).
-spec concat_bit_arrays(list(bitstring())) -> bytes_tree().
concat_bit_arrays(Bits) ->
_pipe = Bits,
_pipe@1 = gleam@list:map(_pipe, fun from_bit_array/1),
gleam_stdlib:identity(_pipe@1).
-file("src/gleam/bytes_tree.gleam", 128).
?DOC(
" Creates a new bytes tree from a string tree.\n"
"\n"
" Runs in constant time when running on Erlang.\n"
" Runs in linear time otherwise.\n"
).
-spec from_string_tree(gleam@string_tree:string_tree()) -> bytes_tree().
from_string_tree(Tree) ->
gleam_stdlib:wrap_list(Tree).
-file("src/gleam/bytes_tree.gleam", 162).
-spec to_list(list(list(bytes_tree())), list(bitstring())) -> list(bitstring()).
to_list(Stack, Acc) ->
case Stack of
[] ->
Acc;
[[] | Remaining_stack] ->
to_list(Remaining_stack, Acc);
[[{bytes, Bits} | Rest] | Remaining_stack@1] ->
to_list([Rest | Remaining_stack@1], [Bits | Acc]);
[[{text, Tree} | Rest@1] | Remaining_stack@2] ->
Bits@1 = gleam_stdlib:identity(unicode:characters_to_binary(Tree)),
to_list([Rest@1 | Remaining_stack@2], [Bits@1 | Acc]);
[[{many, Trees} | Rest@2] | Remaining_stack@3] ->
to_list([Trees, Rest@2 | Remaining_stack@3], Acc)
end.
-file("src/gleam/bytes_tree.gleam", 155).
?DOC(
" Turns a bytes tree into a bit array.\n"
"\n"
" Runs in linear time.\n"
"\n"
" When running on Erlang this function is implemented natively by the\n"
" virtual machine and is highly optimised.\n"
).
-spec to_bit_array(bytes_tree()) -> bitstring().
to_bit_array(Tree) ->
erlang:list_to_bitstring(Tree).
-file("src/gleam/bytes_tree.gleam", 186).
?DOC(
" Returns the size of the bytes tree's content in bytes.\n"
"\n"
" Runs in linear time.\n"
).
-spec byte_size(bytes_tree()) -> integer().
byte_size(Tree) ->
erlang:iolist_size(Tree).
+513
View File
@@ -0,0 +1,513 @@
-module(gleam@dict).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]).
-define(FILEPATH, "src/gleam/dict.gleam").
-export([size/1, is_empty/1, fold/3, to_list/1, new/0, from_list/1, has_key/2, get/2, insert/3, map_values/2, keys/1, values/1, filter/2, take/2, combine/3, merge/2, delete/2, drop/2, upsert/3, each/2, group/2]).
-export_type([dict/2, transient_dict/2]).
-if(?OTP_RELEASE >= 27).
-define(MODULEDOC(Str), -moduledoc(Str)).
-define(DOC(Str), -doc(Str)).
-else.
-define(MODULEDOC(Str), -compile([])).
-define(DOC(Str), -compile([])).
-endif.
-type dict(JL, JM) :: any() | {gleam_phantom, JL, JM}.
-type transient_dict(JN, JO) :: any() | {gleam_phantom, JN, JO}.
-file("src/gleam/dict.gleam", 53).
?DOC(
" Determines the number of key-value pairs in the dict.\n"
" This function runs in constant time and does not need to iterate the dict.\n"
"\n"
" ## Examples\n"
"\n"
" ```gleam\n"
" assert new() |> size == 0\n"
" ```\n"
"\n"
" ```gleam\n"
" assert new() |> insert(\"key\", \"value\") |> size == 1\n"
" ```\n"
).
-spec size(dict(any(), any())) -> integer().
size(Dict) ->
maps:size(Dict).
-file("src/gleam/dict.gleam", 67).
?DOC(
" Determines whether or not the dict is empty.\n"
"\n"
" ## Examples\n"
"\n"
" ```gleam\n"
" assert new() |> is_empty\n"
" ```\n"
"\n"
" ```gleam\n"
" assert !{ new() |> insert(\"b\", 1) |> is_empty }\n"
" ```\n"
).
-spec is_empty(dict(any(), any())) -> boolean().
is_empty(Dict) ->
maps:size(Dict) =:= 0.
-file("src/gleam/dict.gleam", 469).
?DOC(
" Combines all entries into a single value by calling a given function on each\n"
" one.\n"
"\n"
" Dicts are not ordered so the values are not returned in any specific order. Do\n"
" not write code that relies on the order entries are used by this function\n"
" as it may change in later versions of Gleam or Erlang.\n"
"\n"
" ## Examples\n"
"\n"
" ```gleam\n"
" let dict = from_list([#(\"a\", 1), #(\"b\", 3), #(\"c\", 9)])\n"
" assert fold(dict, 0, fn(accumulator, key, value) { accumulator + value })\n"
" == 13\n"
" ```\n"
"\n"
" ```gleam\n"
" import gleam/string\n"
"\n"
" let dict = from_list([#(\"a\", 1), #(\"b\", 3), #(\"c\", 9)])\n"
" assert\n"
" fold(dict, \"\", fn(accumulator, key, value) {\n"
" string.append(accumulator, key)\n"
" })\n"
" == \"abc\"\n"
" ```\n"
).
-spec fold(dict(QN, QO), QR, fun((QR, QN, QO) -> QR)) -> QR.
fold(Dict, Initial, Fun) ->
Fun@1 = fun(Key, Value, Acc) -> Fun(Acc, Key, Value) end,
maps:fold(Fun@1, Initial, Dict).
-file("src/gleam/dict.gleam", 97).
?DOC(
" Converts the dict to a list of 2-element tuples `#(key, value)`, one for\n"
" each key-value pair in the dict.\n"
"\n"
" The tuples in the list have no specific order.\n"
"\n"
" ## Examples\n"
"\n"
" Calling `to_list` on an empty `dict` returns an empty list.\n"
"\n"
" ```gleam\n"
" assert new() |> to_list == []\n"
" ```\n"
"\n"
" The ordering of elements in the resulting list is an implementation detail\n"
" that should not be relied upon.\n"
"\n"
" ```gleam\n"
" assert new()\n"
" |> insert(\"b\", 1)\n"
" |> insert(\"a\", 0)\n"
" |> insert(\"c\", 2)\n"
" |> to_list\n"
" == [#(\"a\", 0), #(\"b\", 1), #(\"c\", 2)]\n"
" ```\n"
).
-spec to_list(dict(KJ, KK)) -> list({KJ, KK}).
to_list(Dict) ->
maps:to_list(Dict).
-file("src/gleam/dict.gleam", 146).
?DOC(" Creates a fresh dict that contains no values.\n").
-spec new() -> dict(any(), any()).
new() ->
maps:new().
-file("src/gleam/dict.gleam", 111).
-spec from_list_loop(transient_dict(KT, KU), list({KT, KU})) -> dict(KT, KU).
from_list_loop(Transient, List) ->
case List of
[] ->
gleam_stdlib:identity(Transient);
[{Key, Value} | Rest] ->
from_list_loop(maps:put(Key, Value, Transient), Rest)
end.
-file("src/gleam/dict.gleam", 107).
?DOC(
" Converts a list of 2-element tuples `#(key, value)` to a dict.\n"
"\n"
" If two tuples have the same key the last one in the list will be the one\n"
" that is present in the dict.\n"
).
-spec from_list(list({KO, KP})) -> dict(KO, KP).
from_list(List) ->
maps:from_list(List).
-file("src/gleam/dict.gleam", 135).
?DOC(
" Determines whether or not a value is present in the dict for a given key.\n"
"\n"
" ## Examples\n"
"\n"
" ```gleam\n"
" assert new() |> insert(\"a\", 0) |> has_key(\"a\")\n"
" ```\n"
"\n"
" ```gleam\n"
" assert !{ new() |> insert(\"a\", 0) |> has_key(\"b\") }\n"
" ```\n"
).
-spec has_key(dict(LA, any()), LA) -> boolean().
has_key(Dict, Key) ->
maps:is_key(Key, Dict).
-file("src/gleam/dict.gleam", 165).
?DOC(
" Fetches a value from a dict for a given key.\n"
"\n"
" The dict may not have a value for the key, so the value is wrapped in a\n"
" `Result`.\n"
"\n"
" ## Examples\n"
"\n"
" ```gleam\n"
" assert new() |> insert(\"a\", 0) |> get(\"a\") == Ok(0)\n"
" ```\n"
"\n"
" ```gleam\n"
" assert new() |> insert(\"a\", 0) |> get(\"b\") == Error(Nil)\n"
" ```\n"
).
-spec get(dict(LM, LN), LM) -> {ok, LN} | {error, nil}.
get(From, Get) ->
gleam_stdlib:map_get(From, Get).
-file("src/gleam/dict.gleam", 183).
?DOC(
" Inserts a value into the dict with the given key.\n"
"\n"
" If the dict already has a value for the given key then the value is\n"
" replaced with the new value.\n"
"\n"
" ## Examples\n"
"\n"
" ```gleam\n"
" assert new() |> insert(\"a\", 0) == from_list([#(\"a\", 0)])\n"
" ```\n"
"\n"
" ```gleam\n"
" assert new() |> insert(\"a\", 0) |> insert(\"a\", 5) == from_list([#(\"a\", 5)])\n"
" ```\n"
).
-spec insert(dict(LS, LT), LS, LT) -> dict(LS, LT).
insert(Dict, Key, Value) ->
maps:put(Key, Value, Dict).
-file("src/gleam/dict.gleam", 210).
?DOC(
" Updates all values in a given dict by calling a given function on each key\n"
" and value.\n"
"\n"
" ## Examples\n"
"\n"
" ```gleam\n"
" assert from_list([#(3, 3), #(2, 4)])\n"
" |> map_values(fn(key, value) { key * value })\n"
" == from_list([#(3, 9), #(2, 8)])\n"
" ```\n"
).
-spec map_values(dict(MK, ML), fun((MK, ML) -> MO)) -> dict(MK, MO).
map_values(Dict, Fun) ->
maps:map(Fun, Dict).
-file("src/gleam/dict.gleam", 230).
?DOC(
" Gets a list of all keys in a given dict.\n"
"\n"
" Dicts are not ordered so the keys are not returned in any specific order. Do\n"
" not write code that relies on the order keys are returned by this function\n"
" as it may change in later versions of Gleam or Erlang.\n"
"\n"
" ## Examples\n"
"\n"
" ```gleam\n"
" assert from_list([#(\"a\", 0), #(\"b\", 1)]) |> keys == [\"a\", \"b\"]\n"
" ```\n"
).
-spec keys(dict(MY, any())) -> list(MY).
keys(Dict) ->
maps:keys(Dict).
-file("src/gleam/dict.gleam", 247).
?DOC(
" Gets a list of all values in a given dict.\n"
"\n"
" Dicts are not ordered so the values are not returned in any specific order. Do\n"
" not write code that relies on the order values are returned by this function\n"
" as it may change in later versions of Gleam or Erlang.\n"
"\n"
" ## Examples\n"
"\n"
" ```gleam\n"
" assert from_list([#(\"a\", 0), #(\"b\", 1)]) |> values == [0, 1]\n"
" ```\n"
).
-spec values(dict(any(), NE)) -> list(NE).
values(Dict) ->
maps:values(Dict).
-file("src/gleam/dict.gleam", 268).
?DOC(
" Creates a new dict from a given dict, minus any entries that a given function\n"
" returns `False` for.\n"
"\n"
" ## Examples\n"
"\n"
" ```gleam\n"
" assert from_list([#(\"a\", 0), #(\"b\", 1)])\n"
" |> filter(fn(key, value) { value != 0 })\n"
" == from_list([#(\"b\", 1)])\n"
" ```\n"
"\n"
" ```gleam\n"
" assert from_list([#(\"a\", 0), #(\"b\", 1)])\n"
" |> filter(fn(key, value) { True })\n"
" == from_list([#(\"a\", 0), #(\"b\", 1)])\n"
" ```\n"
).
-spec filter(dict(NI, NJ), fun((NI, NJ) -> boolean())) -> dict(NI, NJ).
filter(Dict, Predicate) ->
maps:filter(Predicate, Dict).
-file("src/gleam/dict.gleam", 313).
-spec do_take_loop(dict(OI, OJ), list(OI), transient_dict(OI, OJ)) -> dict(OI, OJ).
do_take_loop(Dict, Desired_keys, Acc) ->
case Desired_keys of
[] ->
gleam_stdlib:identity(Acc);
[Key | Rest] ->
case gleam_stdlib:map_get(Dict, Key) of
{ok, Value} ->
do_take_loop(Dict, Rest, maps:put(Key, Value, Acc));
{error, _} ->
do_take_loop(Dict, Rest, Acc)
end
end.
-file("src/gleam/dict.gleam", 304).
?DOC(
" Creates a new dict from a given dict, only including any entries for which the\n"
" keys are in a given list.\n"
"\n"
" ## Examples\n"
"\n"
" ```gleam\n"
" assert from_list([#(\"a\", 0), #(\"b\", 1)])\n"
" |> take([\"b\"])\n"
" == from_list([#(\"b\", 1)])\n"
" ```\n"
"\n"
" ```gleam\n"
" assert from_list([#(\"a\", 0), #(\"b\", 1)])\n"
" |> take([\"a\", \"b\", \"c\"])\n"
" == from_list([#(\"a\", 0), #(\"b\", 1)])\n"
" ```\n"
).
-spec take(dict(NU, NV), list(NU)) -> dict(NU, NV).
take(Dict, Desired_keys) ->
maps:with(Desired_keys, Dict).
-file("src/gleam/dict.gleam", 525).
?DOC(
" Creates a new dict from a pair of given dicts by combining their entries.\n"
"\n"
" If there are entries with the same keys in both dicts the given function is\n"
" used to determine the new value to use in the resulting dict.\n"
"\n"
" ## Examples\n"
"\n"
" ```gleam\n"
" let a = from_list([#(\"a\", 0), #(\"b\", 1)])\n"
" let b = from_list([#(\"a\", 2), #(\"c\", 3)])\n"
" assert combine(a, b, fn(one, other) { one + other })\n"
" == from_list([#(\"a\", 2), #(\"b\", 1), #(\"c\", 3)])\n"
" ```\n"
).
-spec combine(dict(RC, RD), dict(RC, RD), fun((RD, RD) -> RD)) -> dict(RC, RD).
combine(Dict, Other, Fun) ->
maps:merge_with(fun(_, L, R) -> Fun(L, R) end, Dict, Other).
-file("src/gleam/dict.gleam", 342).
?DOC(
" Creates a new dict from a pair of given dicts by combining their entries.\n"
"\n"
" If there are entries with the same keys in both dicts the entry from the\n"
" second dict takes precedence.\n"
"\n"
" ## Examples\n"
"\n"
" ```gleam\n"
" let a = from_list([#(\"a\", 0), #(\"b\", 1)])\n"
" let b = from_list([#(\"b\", 2), #(\"c\", 3)])\n"
" assert merge(a, b) == from_list([#(\"a\", 0), #(\"b\", 2), #(\"c\", 3)])\n"
" ```\n"
).
-spec merge(dict(OR, OS), dict(OR, OS)) -> dict(OR, OS).
merge(Dict, New_entries) ->
maps:merge(Dict, New_entries).
-file("src/gleam/dict.gleam", 361).
?DOC(
" Creates a new dict from a given dict with all the same entries except for the\n"
" one with a given key, if it exists.\n"
"\n"
" ## Examples\n"
"\n"
" ```gleam\n"
" assert from_list([#(\"a\", 0), #(\"b\", 1)]) |> delete(\"a\")\n"
" == from_list([#(\"b\", 1)])\n"
" ```\n"
"\n"
" ```gleam\n"
" assert from_list([#(\"a\", 0), #(\"b\", 1)]) |> delete(\"c\")\n"
" == from_list([#(\"a\", 0), #(\"b\", 1)])\n"
" ```\n"
).
-spec delete(dict(OZ, PA), OZ) -> dict(OZ, PA).
delete(Dict, Key) ->
_pipe = gleam_stdlib:identity(Dict),
_pipe@1 = maps:remove(Key, _pipe),
gleam_stdlib:identity(_pipe@1).
-file("src/gleam/dict.gleam", 398).
-spec drop_loop(transient_dict(PZ, QA), list(PZ)) -> dict(PZ, QA).
drop_loop(Transient, Disallowed_keys) ->
case Disallowed_keys of
[] ->
gleam_stdlib:identity(Transient);
[Key | Rest] ->
drop_loop(maps:remove(Key, Transient), Rest)
end.
-file("src/gleam/dict.gleam", 389).
?DOC(
" Creates a new dict from a given dict with all the same entries except any with\n"
" keys found in a given list.\n"
"\n"
" ## Examples\n"
"\n"
" ```gleam\n"
" assert from_list([#(\"a\", 0), #(\"b\", 1)]) |> drop([\"a\"])\n"
" == from_list([#(\"b\", 1)])\n"
" ```\n"
"\n"
" ```gleam\n"
" assert from_list([#(\"a\", 0), #(\"b\", 1)]) |> drop([\"c\"])\n"
" == from_list([#(\"a\", 0), #(\"b\", 1)])\n"
" ```\n"
"\n"
" ```gleam\n"
" assert from_list([#(\"a\", 0), #(\"b\", 1)]) |> drop([\"a\", \"b\", \"c\"])\n"
" == from_list([])\n"
" ```\n"
).
-spec drop(dict(PL, PM), list(PL)) -> dict(PL, PM).
drop(Dict, Disallowed_keys) ->
maps:without(Disallowed_keys, Dict).
-file("src/gleam/dict.gleam", 431).
?DOC(
" Creates a new dict with one entry inserted or updated using a given function.\n"
"\n"
" If there was not an entry in the dict for the given key then the function\n"
" gets `None` as its argument, otherwise it gets `Some(value)`.\n"
"\n"
" ## Examples\n"
"\n"
" ```gleam\n"
" let dict = from_list([#(\"a\", 0)])\n"
" let increment = fn(x) {\n"
" case x {\n"
" Some(i) -> i + 1\n"
" None -> 0\n"
" }\n"
" }\n"
"\n"
" assert upsert(dict, \"a\", increment) == from_list([#(\"a\", 1)])\n"
" ```\n"
"\n"
" ```gleam\n"
" assert upsert(dict, \"b\", increment) == from_list([#(\"a\", 0), #(\"b\", 0)])\n"
" ```\n"
).
-spec upsert(dict(QG, QH), QG, fun((gleam@option:option(QH)) -> QH)) -> dict(QG, QH).
upsert(Dict, Key, Fun) ->
case gleam_stdlib:map_get(Dict, Key) of
{ok, Value} ->
insert(Dict, Key, Fun({some, Value}));
{error, _} ->
insert(Dict, Key, Fun(none))
end.
-file("src/gleam/dict.gleam", 504).
?DOC(
" Calls a function for each key and value in a dict, discarding the return\n"
" value.\n"
"\n"
" Useful for producing a side effect for every item of a dict.\n"
"\n"
" ```gleam\n"
" import gleam/io\n"
"\n"
" let dict = from_list([#(\"a\", \"apple\"), #(\"b\", \"banana\"), #(\"c\", \"cherry\")])\n"
"\n"
" assert\n"
" each(dict, fn(k, v) {\n"
" io.println(k <> \" => \" <> v)\n"
" })\n"
" == Nil\n"
" // a => apple\n"
" // b => banana\n"
" // c => cherry\n"
" ```\n"
"\n"
" The order of elements in the iteration is an implementation detail that\n"
" should not be relied upon.\n"
).
-spec each(dict(QX, QY), fun((QX, QY) -> any())) -> nil.
each(Dict, Fun) ->
fold(
Dict,
nil,
fun(Nil, K, V) ->
Fun(K, V),
Nil
end
).
-file("src/gleam/dict.gleam", 566).
-spec group_loop(transient_dict(SE, list(SF)), fun((SF) -> SE), list(SF)) -> dict(SE, list(SF)).
group_loop(Transient, To_key, List) ->
case List of
[] ->
gleam_stdlib:identity(Transient);
[Value | Rest] ->
Key = To_key(Value),
Update = fun(Existing) -> [Value | Existing] end,
_pipe = Transient,
_pipe@1 = maps:update_with(Key, Update, [Value], _pipe),
group_loop(_pipe@1, To_key, Rest)
end.
-file("src/gleam/dict.gleam", 562).
?DOC(false).
-spec group(fun((RY) -> RZ), list(RY)) -> dict(RZ, list(RY)).
group(Key, List) ->
group_loop(gleam_stdlib:identity(maps:new()), Key, List).
@@ -0,0 +1,105 @@
-module(gleam@dynamic).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]).
-define(FILEPATH, "src/gleam/dynamic.gleam").
-export([classify/1, bool/1, string/1, float/1, int/1, bit_array/1, list/1, array/1, properties/1, nil/0]).
-export_type([dynamic_/0]).
-if(?OTP_RELEASE >= 27).
-define(MODULEDOC(Str), -moduledoc(Str)).
-define(DOC(Str), -doc(Str)).
-else.
-define(MODULEDOC(Str), -compile([])).
-define(DOC(Str), -compile([])).
-endif.
-type dynamic_() :: any().
-file("src/gleam/dynamic.gleam", 29).
?DOC(
" Return a string indicating the type of the dynamic value.\n"
"\n"
" This function may be useful for constructing error messages or logs. If you\n"
" want to turn dynamic data into well typed data then you want the\n"
" `gleam/dynamic/decode` module.\n"
"\n"
" ```gleam\n"
" assert classify(string(\"Hello\")) == \"String\"\n"
" ```\n"
).
-spec classify(dynamic_()) -> binary().
classify(Data) ->
gleam_stdlib:classify_dynamic(Data).
-file("src/gleam/dynamic.gleam", 35).
?DOC(" Create a dynamic value from a bool.\n").
-spec bool(boolean()) -> dynamic_().
bool(A) ->
gleam_stdlib:identity(A).
-file("src/gleam/dynamic.gleam", 43).
?DOC(
" Create a dynamic value from a string.\n"
"\n"
" On Erlang this will be a binary string rather than a character list.\n"
).
-spec string(binary()) -> dynamic_().
string(A) ->
gleam_stdlib:identity(A).
-file("src/gleam/dynamic.gleam", 49).
?DOC(" Create a dynamic value from a float.\n").
-spec float(float()) -> dynamic_().
float(A) ->
gleam_stdlib:identity(A).
-file("src/gleam/dynamic.gleam", 55).
?DOC(" Create a dynamic value from an int.\n").
-spec int(integer()) -> dynamic_().
int(A) ->
gleam_stdlib:identity(A).
-file("src/gleam/dynamic.gleam", 61).
?DOC(" Create a dynamic value from a bit array.\n").
-spec bit_array(bitstring()) -> dynamic_().
bit_array(A) ->
gleam_stdlib:identity(A).
-file("src/gleam/dynamic.gleam", 67).
?DOC(" Create a dynamic value from a list.\n").
-spec list(list(dynamic_())) -> dynamic_().
list(A) ->
gleam_stdlib:identity(A).
-file("src/gleam/dynamic.gleam", 76).
?DOC(
" Create a dynamic value from a list, converting it to a sequential runtime\n"
" format rather than the regular list format.\n"
"\n"
" On Erlang this will be a tuple, on JavaScript this will be an array.\n"
).
-spec array(list(dynamic_())) -> dynamic_().
array(A) ->
erlang:list_to_tuple(A).
-file("src/gleam/dynamic.gleam", 84).
?DOC(
" Create a dynamic value made of an unordered series of keys and values, where\n"
" the keys are unique.\n"
"\n"
" On Erlang this will be a map, on JavaScript this will be a Gleam dict\n"
" object.\n"
).
-spec properties(list({dynamic_(), dynamic_()})) -> dynamic_().
properties(Entries) ->
gleam_stdlib:identity(maps:from_list(Entries)).
-file("src/gleam/dynamic.gleam", 93).
?DOC(
" A dynamic value representing nothing.\n"
"\n"
" On Erlang this will be the atom `nil`, on JavaScript this will be\n"
" `undefined`.\n"
).
-spec nil() -> dynamic_().
nil() ->
gleam_stdlib:identity(nil).
File diff suppressed because it is too large Load Diff
+711
View File
@@ -0,0 +1,711 @@
-module(gleam@float).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]).
-define(FILEPATH, "src/gleam/float.gleam").
-export([parse/1, to_string/1, max/2, min/2, clamp/3, compare/2, absolute_value/1, loosely_compare/3, loosely_equals/3, ceiling/1, floor/1, negate/1, round/1, truncate/1, to_precision/2, power/2, square_root/1, sum/1, product/1, random/0, modulo/2, divide/2, add/2, multiply/2, subtract/2, logarithm/1, exponential/1]).
-if(?OTP_RELEASE >= 27).
-define(MODULEDOC(Str), -moduledoc(Str)).
-define(DOC(Str), -doc(Str)).
-else.
-define(MODULEDOC(Str), -compile([])).
-define(DOC(Str), -compile([])).
-endif.
?MODULEDOC(
" Functions for working with floats.\n"
"\n"
" ## Float representation\n"
"\n"
" Floats are represented as 64 bit floating point numbers on both the Erlang\n"
" and JavaScript runtimes. The floating point behaviour is native to their\n"
" respective runtimes, so their exact behaviour will be slightly different on\n"
" the two runtimes.\n"
"\n"
" ### Infinity and NaN\n"
"\n"
" Under the JavaScript runtime, exceeding the maximum (or minimum)\n"
" representable value for a floating point value will result in Infinity (or\n"
" -Infinity). Should you try to divide two infinities you will get NaN as a\n"
" result.\n"
"\n"
" When running on BEAM, exceeding the maximum (or minimum) representable\n"
" value for a floating point value will raise an error.\n"
"\n"
" ## Division by zero\n"
"\n"
" Gleam runs on the Erlang virtual machine, which does not follow the IEEE\n"
" 754 standard for floating point arithmetic and does not have an `Infinity`\n"
" value. In Erlang division by zero results in a crash, however Gleam does\n"
" not have partial functions and operators in core so instead division by zero\n"
" returns zero, a behaviour taken from Pony, Coq, and Lean.\n"
"\n"
" This may seem unexpected at first, but it is no less mathematically valid\n"
" than crashing or returning a special value. Division by zero is undefined\n"
" in mathematics.\n"
).
-file("src/gleam/float.gleam", 49).
?DOC(
" Attempts to parse a string as a `Float`, returning `Error(Nil)` if it was\n"
" not possible.\n"
"\n"
" ## Examples\n"
"\n"
" ```gleam\n"
" assert parse(\"2.3\") == Ok(2.3)\n"
" ```\n"
"\n"
" ```gleam\n"
" assert parse(\"ABC\") == Error(Nil)\n"
" ```\n"
).
-spec parse(binary()) -> {ok, float()} | {error, nil}.
parse(String) ->
gleam_stdlib:parse_float(String).
-file("src/gleam/float.gleam", 61).
?DOC(
" Returns the string representation of the provided `Float`.\n"
"\n"
" ## Examples\n"
"\n"
" ```gleam\n"
" assert to_string(2.3) == \"2.3\"\n"
" ```\n"
).
-spec to_string(float()) -> binary().
to_string(X) ->
gleam_stdlib:float_to_string(X).
-file("src/gleam/float.gleam", 192).
?DOC(
" Compares two `Float`s, returning the larger of the two.\n"
"\n"
" ## Examples\n"
"\n"
" ```gleam\n"
" assert max(2.0, 2.3) == 2.3\n"
" ```\n"
).
-spec max(float(), float()) -> float().
max(A, B) ->
case A > B of
true ->
A;
false ->
B
end.
-file("src/gleam/float.gleam", 177).
?DOC(
" Compares two `Float`s, returning the smaller of the two.\n"
"\n"
" ## Examples\n"
"\n"
" ```gleam\n"
" assert min(2.0, 2.3) == 2.0\n"
" ```\n"
).
-spec min(float(), float()) -> float().
min(A, B) ->
case A < B of
true ->
A;
false ->
B
end.
-file("src/gleam/float.gleam", 80).
?DOC(
" Restricts a float between two bounds.\n"
"\n"
" Note: If the `min` argument is larger than the `max` argument then they\n"
" will be swapped, so the minimum bound is always lower than the maximum\n"
" bound.\n"
"\n"
"\n"
" ## Examples\n"
"\n"
" ```gleam\n"
" assert clamp(1.2, min: 1.4, max: 1.6) == 1.4\n"
" ```\n"
"\n"
" ```gleam\n"
" assert clamp(1.2, min: 1.4, max: 0.6) == 1.2\n"
" ```\n"
).
-spec clamp(float(), float(), float()) -> float().
clamp(X, Min_bound, Max_bound) ->
case Min_bound >= Max_bound of
true ->
_pipe = X,
_pipe@1 = min(_pipe, Min_bound),
max(_pipe@1, Max_bound);
false ->
_pipe@2 = X,
_pipe@3 = min(_pipe@2, Max_bound),
max(_pipe@3, Min_bound)
end.
-file("src/gleam/float.gleam", 100).
?DOC(
" Compares two `Float`s, returning an `Order`:\n"
" `Lt` for lower than, `Eq` for equals, or `Gt` for greater than.\n"
"\n"
" ## Examples\n"
"\n"
" ```gleam\n"
" assert compare(2.0, 2.3) == Lt\n"
" ```\n"
"\n"
" To handle\n"
" [Floating Point Imprecision](https://en.wikipedia.org/wiki/Floating-point_arithmetic#Accuracy_problems)\n"
" you may use [`loosely_compare`](#loosely_compare) instead.\n"
).
-spec compare(float(), float()) -> gleam@order:order().
compare(A, B) ->
case A =:= B of
true ->
eq;
false ->
case A < B of
true ->
lt;
false ->
gt
end
end.
-file("src/gleam/float.gleam", 302).
?DOC(
" Returns the absolute value of the input as a `Float`.\n"
"\n"
" ## Examples\n"
"\n"
" ```gleam\n"
" assert absolute_value(-12.5) == 12.5\n"
" ```\n"
"\n"
" ```gleam\n"
" assert absolute_value(10.2) == 10.2\n"
" ```\n"
).
-spec absolute_value(float()) -> float().
absolute_value(X) ->
case X >= +0.0 of
true ->
X;
false ->
+0.0 - X
end.
-file("src/gleam/float.gleam", 129).
?DOC(
" Compares two `Float`s within a tolerance, returning an `Order`:\n"
" `Lt` for lower than, `Eq` for equals, or `Gt` for greater than.\n"
"\n"
" This function allows Float comparison while handling\n"
" [Floating Point Imprecision](https://en.wikipedia.org/wiki/Floating-point_arithmetic#Accuracy_problems).\n"
"\n"
" Notice: For `Float`s the tolerance won't be exact:\n"
" `5.3 - 5.0` is not exactly `0.3`.\n"
"\n"
" ## Examples\n"
"\n"
" ```gleam\n"
" assert loosely_compare(5.0, with: 5.3, tolerating: 0.5) == Eq\n"
" ```\n"
"\n"
" If you want to check only for equality you may use\n"
" [`loosely_equals`](#loosely_equals) instead.\n"
).
-spec loosely_compare(float(), float(), float()) -> gleam@order:order().
loosely_compare(A, B, Tolerance) ->
Difference = absolute_value(A - B),
case Difference =< Tolerance of
true ->
eq;
false ->
compare(A, B)
end.
-file("src/gleam/float.gleam", 160).
?DOC(
" Checks for equality of two `Float`s within a tolerance,\n"
" returning a `Bool`.\n"
"\n"
" This function allows Float comparison while handling\n"
" [Floating Point Imprecision](https://en.wikipedia.org/wiki/Floating-point_arithmetic#Accuracy_problems).\n"
"\n"
" Notice: For `Float`s the tolerance won't be exact:\n"
" `5.3 - 5.0` is not exactly `0.3`.\n"
"\n"
" ## Examples\n"
"\n"
" ```gleam\n"
" assert loosely_equals(5.0, with: 5.3, tolerating: 0.5)\n"
" ```\n"
"\n"
" ```gleam\n"
" assert !loosely_equals(5.0, with: 5.1, tolerating: 0.1)\n"
" ```\n"
).
-spec loosely_equals(float(), float(), float()) -> boolean().
loosely_equals(A, B, Tolerance) ->
Difference = absolute_value(A - B),
Difference =< Tolerance.
-file("src/gleam/float.gleam", 209).
?DOC(
" Rounds the value to the next highest whole number as a `Float`.\n"
"\n"
" ## Examples\n"
"\n"
" ```gleam\n"
" assert ceiling(2.3) == 3.0\n"
" ```\n"
).
-spec ceiling(float()) -> float().
ceiling(X) ->
math:ceil(X).
-file("src/gleam/float.gleam", 221).
?DOC(
" Rounds the value to the next lowest whole number as a `Float`.\n"
"\n"
" ## Examples\n"
"\n"
" ```gleam\n"
" assert floor(2.3) == 2.0\n"
" ```\n"
).
-spec floor(float()) -> float().
floor(X) ->
math:floor(X).
-file("src/gleam/float.gleam", 376).
?DOC(
" Returns the negative of the value provided.\n"
"\n"
" ## Examples\n"
"\n"
" ```gleam\n"
" assert negate(1.0) == -1.0\n"
" ```\n"
).
-spec negate(float()) -> float().
negate(X) ->
-1.0 * X.
-file("src/gleam/float.gleam", 236).
?DOC(
" Rounds the value to the nearest whole number as an `Int`.\n"
"\n"
" ## Examples\n"
"\n"
" ```gleam\n"
" assert round(2.3) == 2\n"
" ```\n"
"\n"
" ```gleam\n"
" assert round(2.5) == 3\n"
" ```\n"
).
-spec round(float()) -> integer().
round(X) ->
erlang:round(X).
-file("src/gleam/float.gleam", 256).
?DOC(
" Returns the value as an `Int`, truncating all decimal digits.\n"
"\n"
" ## Examples\n"
"\n"
" ```gleam\n"
" assert truncate(2.4343434847383438) == 2\n"
" ```\n"
).
-spec truncate(float()) -> integer().
truncate(X) ->
erlang:trunc(X).
-file("src/gleam/float.gleam", 273).
?DOC(
" Converts the value to a given precision as a `Float`.\n"
" The precision is the number of allowed decimal places.\n"
" Negative precisions are allowed and force rounding\n"
" to the nearest tenth, hundredth, thousandth etc.\n"
"\n"
" ## Examples\n"
"\n"
" ```gleam\n"
" assert to_precision(2.43434348473, 2) == 2.43\n"
" ```\n"
"\n"
" ```gleam\n"
" assert to_precision(547890.453444, -3) == 548000.0\n"
" ```\n"
).
-spec to_precision(float(), integer()) -> float().
to_precision(X, Precision) ->
case Precision =< 0 of
true ->
Factor = math:pow(10.0, erlang:float(- Precision)),
erlang:float(erlang:round(case Factor of
+0.0 -> +0.0;
-0.0 -> -0.0;
Gleam@denominator -> X / Gleam@denominator
end)) * Factor;
false ->
Factor@1 = math:pow(10.0, erlang:float(Precision)),
case Factor@1 of
+0.0 -> +0.0;
-0.0 -> -0.0;
Gleam@denominator@1 -> erlang:float(erlang:round(X * Factor@1))
/ Gleam@denominator@1
end
end.
-file("src/gleam/float.gleam", 334).
?DOC(
" Returns the result of the base being raised to the power of the\n"
" exponent, as a `Float`.\n"
"\n"
" ## Examples\n"
"\n"
" ```gleam\n"
" assert power(2.0, -1.0) == Ok(0.5)\n"
" ```\n"
"\n"
" ```gleam\n"
" assert power(2.0, 2.0) == Ok(4.0)\n"
" ```\n"
"\n"
" ```gleam\n"
" assert power(8.0, 1.5) == Ok(22.627416997969522)\n"
" ```\n"
"\n"
" ```gleam\n"
" assert 4.0 |> power(of: 2.0) == Ok(16.0)\n"
" ```\n"
"\n"
" ```gleam\n"
" assert power(-1.0, 0.5) == Error(Nil)\n"
" ```\n"
).
-spec power(float(), float()) -> {ok, float()} | {error, nil}.
power(Base, Exponent) ->
Fractional = (math:ceil(Exponent) - Exponent) > +0.0,
case ((Base < +0.0) andalso Fractional) orelse ((Base =:= +0.0) andalso (Exponent
< +0.0)) of
true ->
{error, nil};
false ->
{ok, math:pow(Base, Exponent)}
end.
-file("src/gleam/float.gleam", 364).
?DOC(
" Returns the square root of the input as a `Float`.\n"
"\n"
" ## Examples\n"
"\n"
" ```gleam\n"
" assert square_root(4.0) == Ok(2.0)\n"
" ```\n"
"\n"
" ```gleam\n"
" assert square_root(-16.0) == Error(Nil)\n"
" ```\n"
).
-spec square_root(float()) -> {ok, float()} | {error, nil}.
square_root(X) ->
power(X, 0.5).
-file("src/gleam/float.gleam", 392).
-spec sum_loop(list(float()), float()) -> float().
sum_loop(Numbers, Initial) ->
case Numbers of
[First | Rest] ->
sum_loop(Rest, First + Initial);
[] ->
Initial
end.
-file("src/gleam/float.gleam", 388).
?DOC(
" Sums a list of `Float`s.\n"
"\n"
" ## Example\n"
"\n"
" ```gleam\n"
" assert sum([1.0, 2.2, 3.3]) == 6.5\n"
" ```\n"
).
-spec sum(list(float())) -> float().
sum(Numbers) ->
sum_loop(Numbers, +0.0).
-file("src/gleam/float.gleam", 411).
-spec product_loop(list(float()), float()) -> float().
product_loop(Numbers, Initial) ->
case Numbers of
[First | Rest] ->
product_loop(Rest, First * Initial);
[] ->
Initial
end.
-file("src/gleam/float.gleam", 407).
?DOC(
" Multiplies a list of `Float`s and returns the product.\n"
"\n"
" ## Example\n"
"\n"
" ```gleam\n"
" assert product([2.5, 3.2, 4.2]) == 33.6\n"
" ```\n"
).
-spec product(list(float())) -> float().
product(Numbers) ->
product_loop(Numbers, 1.0).
-file("src/gleam/float.gleam", 433).
?DOC(
" Generates a random float between the given zero (inclusive) and one\n"
" (exclusive).\n"
"\n"
" On Erlang this updates the random state in the process dictionary.\n"
" See: <https://www.erlang.org/doc/man/rand.html#uniform-0>\n"
"\n"
" ## Examples\n"
"\n"
" ```gleam\n"
" random()\n"
" // -> 0.646355926896028\n"
" ```\n"
).
-spec random() -> float().
random() ->
rand:uniform().
-file("src/gleam/float.gleam", 460).
?DOC(
" Computes the modulo of a float division of inputs as a `Result`.\n"
"\n"
" Returns division of the inputs as a `Result`: If the given divisor equals\n"
" `0`, this function returns an `Error`.\n"
"\n"
" The computed value will always have the same sign as the `divisor`.\n"
"\n"
" ## Examples\n"
"\n"
" ```gleam\n"
" assert modulo(13.3, by: 3.3) == Ok(0.1)\n"
" ```\n"
"\n"
" ```gleam\n"
" assert modulo(-13.3, by: 3.3) == Ok(3.2)\n"
" ```\n"
"\n"
" ```gleam\n"
" assert modulo(13.3, by: -3.3) == Ok(-3.2)\n"
" ```\n"
"\n"
" ```gleam\n"
" assert modulo(-13.3, by: -3.3) == Ok(-0.1)\n"
" ```\n"
).
-spec modulo(float(), float()) -> {ok, float()} | {error, nil}.
modulo(Dividend, Divisor) ->
case Divisor of
+0.0 ->
{error, nil};
_ ->
{ok, Dividend - (math:floor(case Divisor of
+0.0 -> +0.0;
-0.0 -> -0.0;
Gleam@denominator -> Dividend / Gleam@denominator
end) * Divisor)}
end.
-file("src/gleam/float.gleam", 479).
?DOC(
" Returns division of the inputs as a `Result`.\n"
"\n"
" ## Examples\n"
"\n"
" ```gleam\n"
" assert divide(0.0, 1.0) == Ok(0.0)\n"
" ```\n"
"\n"
" ```gleam\n"
" assert divide(1.0, 0.0) == Error(Nil)\n"
" ```\n"
).
-spec divide(float(), float()) -> {ok, float()} | {error, nil}.
divide(A, B) ->
case B of
+0.0 ->
{error, nil};
B@1 ->
{ok, case B@1 of
+0.0 -> +0.0;
-0.0 -> -0.0;
Gleam@denominator -> A / Gleam@denominator
end}
end.
-file("src/gleam/float.gleam", 507).
?DOC(
" Adds two floats together.\n"
"\n"
" It's the function equivalent of the `+.` operator.\n"
" This function is useful in higher order functions or pipes.\n"
"\n"
" ## Examples\n"
"\n"
" ```gleam\n"
" assert add(1.0, 2.0) == 3.0\n"
" ```\n"
"\n"
" ```gleam\n"
" import gleam/list\n"
"\n"
" assert list.fold([1.0, 2.0, 3.0], 0.0, add) == 6.0\n"
" ```\n"
"\n"
" ```gleam\n"
" assert 3.0 |> add(2.0) == 5.0\n"
" ```\n"
).
-spec add(float(), float()) -> float().
add(A, B) ->
A + B.
-file("src/gleam/float.gleam", 532).
?DOC(
" Multiplies two floats together.\n"
"\n"
" It's the function equivalent of the `*.` operator.\n"
" This function is useful in higher order functions or pipes.\n"
"\n"
" ## Examples\n"
"\n"
" ```gleam\n"
" assert multiply(2.0, 4.0) == 8.0\n"
" ```\n"
"\n"
" ```gleam\n"
" import gleam/list\n"
"\n"
" assert list.fold([2.0, 3.0, 4.0], 1.0, multiply) == 24.0\n"
" ```\n"
"\n"
" ```gleam\n"
" assert 3.0 |> multiply(2.0) == 6.0\n"
" ```\n"
).
-spec multiply(float(), float()) -> float().
multiply(A, B) ->
A * B.
-file("src/gleam/float.gleam", 561).
?DOC(
" Subtracts one float from another.\n"
"\n"
" It's the function equivalent of the `-.` operator.\n"
" This function is useful in higher order functions or pipes.\n"
"\n"
" ## Examples\n"
"\n"
" ```gleam\n"
" assert subtract(3.0, 1.0) == 2.0\n"
" ```\n"
"\n"
" ```gleam\n"
" import gleam/list\n"
"\n"
" assert list.fold([1.0, 2.0, 3.0], 10.0, subtract) == 4.0\n"
" ```\n"
"\n"
" ```gleam\n"
" assert 3.0 |> subtract(_, 2.0) == 1.0\n"
" ```\n"
"\n"
" ```gleam\n"
" assert 3.0 |> subtract(2.0, _) == -1.0\n"
" ```\n"
).
-spec subtract(float(), float()) -> float().
subtract(A, B) ->
A - B.
-file("src/gleam/float.gleam", 586).
?DOC(
" Returns the natural logarithm (base e) of the given `Float` as a `Result`. If the\n"
" input is less than or equal to 0, returns `Error(Nil)`.\n"
"\n"
" ## Examples\n"
"\n"
" ```gleam\n"
" assert logarithm(1.0) == Ok(0.0)\n"
" ```\n"
"\n"
" ```gleam\n"
" assert logarithm(2.718281828459045) == Ok(1.0)\n"
" ```\n"
"\n"
" ```gleam\n"
" assert logarithm(0.0) == Error(Nil)\n"
" ```\n"
"\n"
" ```gleam\n"
" assert logarithm(-1.0) == Error(Nil)\n"
" ```\n"
).
-spec logarithm(float()) -> {ok, float()} | {error, nil}.
logarithm(X) ->
case X =< +0.0 of
true ->
{error, nil};
false ->
{ok, math:log(X)}
end.
-file("src/gleam/float.gleam", 621).
?DOC(
" Returns e (Euler's number) raised to the power of the given exponent, as\n"
" a `Float`.\n"
"\n"
" ## Examples\n"
"\n"
" ```gleam\n"
" assert exponential(0.0) == 1.0\n"
" ```\n"
"\n"
" ```gleam\n"
" assert exponential(1.0) == 2.718281828459045\n"
" ```\n"
"\n"
" ```gleam\n"
" assert exponential(-1.0) == 0.36787944117144233\n"
" ```\n"
).
-spec exponential(float()) -> float().
exponential(X) ->
math:exp(X).
@@ -0,0 +1,18 @@
-module(gleam@function).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]).
-define(FILEPATH, "src/gleam/function.gleam").
-export([identity/1]).
-if(?OTP_RELEASE >= 27).
-define(MODULEDOC(Str), -moduledoc(Str)).
-define(DOC(Str), -doc(Str)).
-else.
-define(MODULEDOC(Str), -compile([])).
-define(DOC(Str), -compile([])).
-endif.
-file("src/gleam/function.gleam", 3).
?DOC(" Takes a single argument and always returns its input value.\n").
-spec identity(CLV) -> CLV.
identity(X) ->
X.
+972
View File
@@ -0,0 +1,972 @@
-module(gleam@int).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]).
-define(FILEPATH, "src/gleam/int.gleam").
-export([absolute_value/1, to_float/1, power/2, square_root/1, parse/1, base_parse/2, to_string/1, to_base_string/2, to_base2/1, to_base8/1, to_base16/1, to_base36/1, max/2, min/2, clamp/3, compare/2, is_even/1, is_odd/1, negate/1, sum/1, product/1, random/1, divide/2, remainder/2, modulo/2, floor_divide/2, add/2, multiply/2, subtract/2, bitwise_and/2, bitwise_not/1, bitwise_or/2, bitwise_exclusive_or/2, bitwise_shift_left/2, bitwise_shift_right/2, range/4]).
-if(?OTP_RELEASE >= 27).
-define(MODULEDOC(Str), -moduledoc(Str)).
-define(DOC(Str), -doc(Str)).
-else.
-define(MODULEDOC(Str), -compile([])).
-define(DOC(Str), -compile([])).
-endif.
?MODULEDOC(
" Functions for working with integers.\n"
"\n"
" ## Division by zero\n"
"\n"
" In Erlang division by zero results in a crash, however Gleam does not have\n"
" partial functions and operators in core so instead division by zero returns\n"
" zero, a behaviour taken from Pony, Coq, and Lean.\n"
"\n"
" This may seem unexpected at first, but it is no less mathematically valid\n"
" than crashing or returning a special value. Division by zero is undefined\n"
" in mathematics.\n"
).
-file("src/gleam/int.gleam", 28).
?DOC(
" Returns the absolute value of the input.\n"
"\n"
" ## Examples\n"
"\n"
" ```gleam\n"
" assert absolute_value(-12) == 12\n"
" ```\n"
"\n"
" ```gleam\n"
" assert absolute_value(10) == 10\n"
" ```\n"
).
-spec absolute_value(integer()) -> integer().
absolute_value(X) ->
case X >= 0 of
true ->
X;
false ->
X * -1
end.
-file("src/gleam/int.gleam", 251).
?DOC(
" Takes an int and returns its value as a float.\n"
"\n"
" ## Examples\n"
"\n"
" ```gleam\n"
" assert to_float(5) == 5.0\n"
" ```\n"
"\n"
" ```gleam\n"
" assert to_float(0) == 0.0\n"
" ```\n"
"\n"
" ```gleam\n"
" assert to_float(-3) == -3.0\n"
" ```\n"
).
-spec to_float(integer()) -> float().
to_float(X) ->
erlang:float(X).
-file("src/gleam/int.gleam", 60).
?DOC(
" Returns the result of the base being raised to the power of the\n"
" exponent, as a `Float`.\n"
"\n"
" ## Examples\n"
"\n"
" ```gleam\n"
" assert power(2, -1.0) == Ok(0.5)\n"
" ```\n"
"\n"
" ```gleam\n"
" assert power(2, 2.0) == Ok(4.0)\n"
" ```\n"
"\n"
" ```gleam\n"
" assert power(8, 1.5) == Ok(22.627416997969522)\n"
" ```\n"
"\n"
" ```gleam\n"
" assert 4 |> power(of: 2.0) == Ok(16.0)\n"
" ```\n"
"\n"
" ```gleam\n"
" assert power(-1, 0.5) == Error(Nil)\n"
" ```\n"
).
-spec power(integer(), float()) -> {ok, float()} | {error, nil}.
power(Base, Exponent) ->
_pipe = Base,
_pipe@1 = erlang:float(_pipe),
gleam@float:power(_pipe@1, Exponent).
-file("src/gleam/int.gleam", 78).
?DOC(
" Returns the square root of the input as a `Float`.\n"
"\n"
" ## Examples\n"
"\n"
" ```gleam\n"
" assert square_root(4) == Ok(2.0)\n"
" ```\n"
"\n"
" ```gleam\n"
" assert square_root(-16) == Error(Nil)\n"
" ```\n"
).
-spec square_root(integer()) -> {ok, float()} | {error, nil}.
square_root(X) ->
_pipe = X,
_pipe@1 = erlang:float(_pipe),
gleam@float:square_root(_pipe@1).
-file("src/gleam/int.gleam", 98).
?DOC(
" Parses a given string as an int if possible.\n"
"\n"
" ## Examples\n"
"\n"
" ```gleam\n"
" assert parse(\"2\") == Ok(2)\n"
" ```\n"
"\n"
" ```gleam\n"
" assert parse(\"ABC\") == Error(Nil)\n"
" ```\n"
).
-spec parse(binary()) -> {ok, integer()} | {error, nil}.
parse(String) ->
gleam_stdlib:parse_int(String).
-file("src/gleam/int.gleam", 125).
?DOC(
" Parses a given string as an int in a given base if possible.\n"
" Supports only bases 2 to 36, for values outside of which this function returns an `Error(Nil)`.\n"
"\n"
" ## Examples\n"
"\n"
" ```gleam\n"
" assert base_parse(\"10\", 2) == Ok(2)\n"
" ```\n"
"\n"
" ```gleam\n"
" assert base_parse(\"30\", 16) == Ok(48)\n"
" ```\n"
"\n"
" ```gleam\n"
" assert base_parse(\"1C\", 36) == Ok(48)\n"
" ```\n"
"\n"
" ```gleam\n"
" assert base_parse(\"48\", 1) == Error(Nil)\n"
" ```\n"
"\n"
" ```gleam\n"
" assert base_parse(\"48\", 37) == Error(Nil)\n"
" ```\n"
).
-spec base_parse(binary(), integer()) -> {ok, integer()} | {error, nil}.
base_parse(String, Base) ->
case (Base >= 2) andalso (Base =< 36) of
true ->
gleam_stdlib:int_from_base_string(String, Base);
false ->
{error, nil}
end.
-file("src/gleam/int.gleam", 146).
?DOC(
" Prints a given int to a string.\n"
"\n"
" ## Examples\n"
"\n"
" ```gleam\n"
" assert to_string(2) == \"2\"\n"
" ```\n"
).
-spec to_string(integer()) -> binary().
to_string(X) ->
erlang:integer_to_binary(X).
-file("src/gleam/int.gleam", 174).
?DOC(
" Prints a given int to a string using the base number provided.\n"
" Supports only bases 2 to 36, for values outside of which this function returns an `Error(Nil)`.\n"
" For common bases (2, 8, 16, 36), use the `to_baseN` functions.\n"
"\n"
" ## Examples\n"
"\n"
" ```gleam\n"
" assert to_base_string(2, 2) == Ok(\"10\")\n"
" ```\n"
"\n"
" ```gleam\n"
" assert to_base_string(48, 16) == Ok(\"30\")\n"
" ```\n"
"\n"
" ```gleam\n"
" assert to_base_string(48, 36) == Ok(\"1C\")\n"
" ```\n"
"\n"
" ```gleam\n"
" assert to_base_string(48, 1) == Error(Nil)\n"
" ```\n"
"\n"
" ```gleam\n"
" assert to_base_string(48, 37) == Error(Nil)\n"
" ```\n"
).
-spec to_base_string(integer(), integer()) -> {ok, binary()} | {error, nil}.
to_base_string(X, Base) ->
case (Base >= 2) andalso (Base =< 36) of
true ->
{ok, erlang:integer_to_binary(X, Base)};
false ->
{error, nil}
end.
-file("src/gleam/int.gleam", 193).
?DOC(
" Prints a given int to a string using base-2.\n"
"\n"
" ## Examples\n"
"\n"
" ```gleam\n"
" assert to_base2(2) == \"10\"\n"
" ```\n"
).
-spec to_base2(integer()) -> binary().
to_base2(X) ->
erlang:integer_to_binary(X, 2).
-file("src/gleam/int.gleam", 205).
?DOC(
" Prints a given int to a string using base-8.\n"
"\n"
" ## Examples\n"
"\n"
" ```gleam\n"
" assert to_base8(15) == \"17\"\n"
" ```\n"
).
-spec to_base8(integer()) -> binary().
to_base8(X) ->
erlang:integer_to_binary(X, 8).
-file("src/gleam/int.gleam", 217).
?DOC(
" Prints a given int to a string using base-16.\n"
"\n"
" ## Examples\n"
"\n"
" ```gleam\n"
" assert to_base16(48) == \"30\"\n"
" ```\n"
).
-spec to_base16(integer()) -> binary().
to_base16(X) ->
erlang:integer_to_binary(X, 16).
-file("src/gleam/int.gleam", 229).
?DOC(
" Prints a given int to a string using base-36.\n"
"\n"
" ## Examples\n"
"\n"
" ```gleam\n"
" assert to_base36(48) == \"1C\"\n"
" ```\n"
).
-spec to_base36(integer()) -> binary().
to_base36(X) ->
erlang:integer_to_binary(X, 36).
-file("src/gleam/int.gleam", 326).
?DOC(
" Compares two ints, returning the larger of the two.\n"
"\n"
" ## Examples\n"
"\n"
" ```gleam\n"
" assert max(2, 3) == 3\n"
" ```\n"
).
-spec max(integer(), integer()) -> integer().
max(A, B) ->
case A > B of
true ->
A;
false ->
B
end.
-file("src/gleam/int.gleam", 311).
?DOC(
" Compares two ints, returning the smaller of the two.\n"
"\n"
" ## Examples\n"
"\n"
" ```gleam\n"
" assert min(2, 3) == 2\n"
" ```\n"
).
-spec min(integer(), integer()) -> integer().
min(A, B) ->
case A < B of
true ->
A;
false ->
B
end.
-file("src/gleam/int.gleam", 269).
?DOC(
" Restricts an int between two bounds.\n"
"\n"
" Note: If the `min` argument is larger than the `max` argument then they\n"
" will be swapped, so the minimum bound is always lower than the maximum\n"
" bound.\n"
"\n"
" ## Examples\n"
"\n"
" ```gleam\n"
" assert clamp(40, min: 50, max: 60) == 50\n"
" ```\n"
"\n"
" ```gleam\n"
" assert clamp(40, min: 50, max: 30) == 40\n"
" ```\n"
).
-spec clamp(integer(), integer(), integer()) -> integer().
clamp(X, Min_bound, Max_bound) ->
case Min_bound >= Max_bound of
true ->
_pipe = X,
_pipe@1 = min(_pipe, Min_bound),
max(_pipe@1, Max_bound);
false ->
_pipe@2 = X,
_pipe@3 = min(_pipe@2, Max_bound),
max(_pipe@3, Min_bound)
end.
-file("src/gleam/int.gleam", 292).
?DOC(
" Compares two ints, returning an order.\n"
"\n"
" ## Examples\n"
"\n"
" ```gleam\n"
" assert compare(2, 3) == Lt\n"
" ```\n"
"\n"
" ```gleam\n"
" assert compare(4, 3) == Gt\n"
" ```\n"
"\n"
" ```gleam\n"
" assert compare(3, 3) == Eq\n"
" ```\n"
).
-spec compare(integer(), integer()) -> gleam@order:order().
compare(A, B) ->
case A =:= B of
true ->
eq;
false ->
case A < B of
true ->
lt;
false ->
gt
end
end.
-file("src/gleam/int.gleam", 345).
?DOC(
" Returns whether the value provided is even.\n"
"\n"
" ## Examples\n"
"\n"
" ```gleam\n"
" assert is_even(2)\n"
" ```\n"
"\n"
" ```gleam\n"
" assert !is_even(3)\n"
" ```\n"
).
-spec is_even(integer()) -> boolean().
is_even(X) ->
(X rem 2) =:= 0.
-file("src/gleam/int.gleam", 361).
?DOC(
" Returns whether the value provided is odd.\n"
"\n"
" ## Examples\n"
"\n"
" ```gleam\n"
" assert is_odd(3)\n"
" ```\n"
"\n"
" ```gleam\n"
" assert !is_odd(2)\n"
" ```\n"
).
-spec is_odd(integer()) -> boolean().
is_odd(X) ->
(X rem 2) /= 0.
-file("src/gleam/int.gleam", 373).
?DOC(
" Returns the negative of the value provided.\n"
"\n"
" ## Examples\n"
"\n"
" ```gleam\n"
" assert negate(1) == -1\n"
" ```\n"
).
-spec negate(integer()) -> integer().
negate(X) ->
-1 * X.
-file("src/gleam/int.gleam", 389).
-spec sum_loop(list(integer()), integer()) -> integer().
sum_loop(Numbers, Initial) ->
case Numbers of
[First | Rest] ->
sum_loop(Rest, First + Initial);
[] ->
Initial
end.
-file("src/gleam/int.gleam", 385).
?DOC(
" Sums a list of ints.\n"
"\n"
" ## Example\n"
"\n"
" ```gleam\n"
" assert sum([1, 2, 3]) == 6\n"
" ```\n"
).
-spec sum(list(integer())) -> integer().
sum(Numbers) ->
sum_loop(Numbers, 0).
-file("src/gleam/int.gleam", 408).
-spec product_loop(list(integer()), integer()) -> integer().
product_loop(Numbers, Initial) ->
case Numbers of
[First | Rest] ->
product_loop(Rest, First * Initial);
[] ->
Initial
end.
-file("src/gleam/int.gleam", 404).
?DOC(
" Multiplies a list of ints and returns the product.\n"
"\n"
" ## Example\n"
"\n"
" ```gleam\n"
" assert product([2, 3, 4]) == 24\n"
" ```\n"
).
-spec product(list(integer())) -> integer().
product(Numbers) ->
product_loop(Numbers, 1).
-file("src/gleam/int.gleam", 436).
?DOC(
" Generates a random int between zero and the given maximum.\n"
"\n"
" The lower number is inclusive, the upper number is exclusive.\n"
"\n"
" ## Examples\n"
"\n"
" ```gleam\n"
" random(10)\n"
" // -> 4\n"
" ```\n"
"\n"
" ```gleam\n"
" random(1)\n"
" // -> 0\n"
" ```\n"
"\n"
" ```gleam\n"
" random(-1)\n"
" // -> -1\n"
" ```\n"
).
-spec random(integer()) -> integer().
random(Max) ->
_pipe = (rand:uniform() * erlang:float(Max)),
_pipe@1 = math:floor(_pipe),
erlang:round(_pipe@1).
-file("src/gleam/int.gleam", 465).
?DOC(
" Performs a truncated integer division.\n"
"\n"
" Returns division of the inputs as a `Result`: If the given divisor equals\n"
" `0`, this function returns an `Error`.\n"
"\n"
" ## Examples\n"
"\n"
" ```gleam\n"
" assert divide(0, 1) == Ok(0)\n"
" ```\n"
"\n"
" ```gleam\n"
" assert divide(1, 0) == Error(Nil)\n"
" ```\n"
"\n"
" ```gleam\n"
" assert divide(5, 2) == Ok(2)\n"
" ```\n"
"\n"
" ```gleam\n"
" assert divide(-99, 2) == Ok(-49)\n"
" ```\n"
).
-spec divide(integer(), integer()) -> {ok, integer()} | {error, nil}.
divide(Dividend, Divisor) ->
case Divisor of
0 ->
{error, nil};
Divisor@1 ->
{ok, case Divisor@1 of
0 -> 0;
Gleam@denominator -> Dividend div Gleam@denominator
end}
end.
-file("src/gleam/int.gleam", 510).
?DOC(
" Computes the remainder of an integer division of inputs as a `Result`.\n"
"\n"
" Returns division of the inputs as a `Result`: If the given divisor equals\n"
" `0`, this function returns an `Error`.\n"
"\n"
" Most of the time you will want to use the `%` operator instead of this\n"
" function.\n"
"\n"
" ## Examples\n"
"\n"
" ```gleam\n"
" assert remainder(3, 2) == Ok(1)\n"
" ```\n"
"\n"
" ```gleam\n"
" assert remainder(1, 0) == Error(Nil)\n"
" ```\n"
"\n"
" ```gleam\n"
" assert remainder(10, -1) == Ok(0)\n"
" ```\n"
"\n"
" ```gleam\n"
" assert remainder(13, by: 3) == Ok(1)\n"
" ```\n"
"\n"
" ```gleam\n"
" assert remainder(-13, by: 3) == Ok(-1)\n"
" ```\n"
"\n"
" ```gleam\n"
" assert remainder(13, by: -3) == Ok(1)\n"
" ```\n"
"\n"
" ```gleam\n"
" assert remainder(-13, by: -3) == Ok(-1)\n"
" ```\n"
).
-spec remainder(integer(), integer()) -> {ok, integer()} | {error, nil}.
remainder(Dividend, Divisor) ->
case Divisor of
0 ->
{error, nil};
Divisor@1 ->
{ok, case Divisor@1 of
0 -> 0;
Gleam@denominator -> Dividend rem Gleam@denominator
end}
end.
-file("src/gleam/int.gleam", 551).
?DOC(
" Computes the modulo of an integer division of inputs as a `Result`.\n"
"\n"
" Returns division of the inputs as a `Result`: If the given divisor equals\n"
" `0`, this function returns an `Error`.\n"
"\n"
" Note that this is different from `int.remainder` and `%` in that the\n"
" computed value will always have the same sign as the `divisor`.\n"
"\n"
" ## Examples\n"
"\n"
" ```gleam\n"
" assert modulo(3, 2) == Ok(1)\n"
" ```\n"
"\n"
" ```gleam\n"
" assert modulo(1, 0) == Error(Nil)\n"
" ```\n"
"\n"
" ```gleam\n"
" assert modulo(10, -1) == Ok(0)\n"
" ```\n"
"\n"
" ```gleam\n"
" assert modulo(13, by: 3) == Ok(1)\n"
" ```\n"
"\n"
" ```gleam\n"
" assert modulo(-13, by: 3) == Ok(2)\n"
" ```\n"
"\n"
" ```gleam\n"
" assert modulo(13, by: -3) == Ok(-2)\n"
" ```\n"
).
-spec modulo(integer(), integer()) -> {ok, integer()} | {error, nil}.
modulo(Dividend, Divisor) ->
case Divisor of
0 ->
{error, nil};
_ ->
Remainder = case Divisor of
0 -> 0;
Gleam@denominator -> Dividend rem Gleam@denominator
end,
case (Remainder * Divisor) < 0 of
true ->
{ok, Remainder + Divisor};
false ->
{ok, Remainder}
end
end.
-file("src/gleam/int.gleam", 591).
?DOC(
" Performs a *floored* integer division, which means that the result will\n"
" always be rounded towards negative infinity.\n"
"\n"
" If you want to perform truncated integer division (rounding towards zero),\n"
" use `int.divide()` or the `/` operator instead.\n"
"\n"
" Returns division of the inputs as a `Result`: If the given divisor equals\n"
" `0`, this function returns an `Error`.\n"
"\n"
" ## Examples\n"
"\n"
" ```gleam\n"
" assert floor_divide(1, 0) == Error(Nil)\n"
" ```\n"
"\n"
" ```gleam\n"
" assert floor_divide(5, 2) == Ok(2)\n"
" ```\n"
"\n"
" ```gleam\n"
" assert floor_divide(6, -4) == Ok(-2)\n"
" ```\n"
"\n"
" ```gleam\n"
" assert floor_divide(-99, 2) == Ok(-50)\n"
" ```\n"
).
-spec floor_divide(integer(), integer()) -> {ok, integer()} | {error, nil}.
floor_divide(Dividend, Divisor) ->
case Divisor of
0 ->
{error, nil};
Divisor@1 ->
case ((Dividend * Divisor@1) < 0) andalso ((case Divisor@1 of
0 -> 0;
Gleam@denominator -> Dividend rem Gleam@denominator
end) /= 0) of
true ->
{ok, (case Divisor@1 of
0 -> 0;
Gleam@denominator@1 -> Dividend div Gleam@denominator@1
end) - 1};
false ->
{ok, case Divisor@1 of
0 -> 0;
Gleam@denominator@2 -> Dividend div Gleam@denominator@2
end}
end
end.
-file("src/gleam/int.gleam", 622).
?DOC(
" Adds two integers together.\n"
"\n"
" It's the function equivalent of the `+` operator.\n"
" This function is useful in higher order functions or pipes.\n"
"\n"
" ## Examples\n"
"\n"
" ```gleam\n"
" assert add(1, 2) == 3\n"
" ```\n"
"\n"
" ```gleam\n"
" import gleam/list\n"
" assert list.fold([1, 2, 3], 0, add) == 6\n"
" ```\n"
"\n"
" ```gleam\n"
" assert 3 |> add(2) == 5\n"
" ```\n"
).
-spec add(integer(), integer()) -> integer().
add(A, B) ->
A + B.
-file("src/gleam/int.gleam", 647).
?DOC(
" Multiplies two integers together.\n"
"\n"
" It's the function equivalent of the `*` operator.\n"
" This function is useful in higher order functions or pipes.\n"
"\n"
" ## Examples\n"
"\n"
" ```gleam\n"
" assert multiply(2, 4) == 8\n"
" ```\n"
"\n"
" ```gleam\n"
" import gleam/list\n"
"\n"
" assert list.fold([2, 3, 4], 1, multiply) == 24\n"
" ```\n"
"\n"
" ```gleam\n"
" assert 3 |> multiply(2) == 6\n"
" ```\n"
).
-spec multiply(integer(), integer()) -> integer().
multiply(A, B) ->
A * B.
-file("src/gleam/int.gleam", 676).
?DOC(
" Subtracts one int from another.\n"
"\n"
" It's the function equivalent of the `-` operator.\n"
" This function is useful in higher order functions or pipes.\n"
"\n"
" ## Examples\n"
"\n"
" ```gleam\n"
" assert subtract(3, 1) == 2\n"
" ```\n"
"\n"
" ```gleam\n"
" import gleam/list\n"
"\n"
" assert list.fold([1, 2, 3], 10, subtract) == 4\n"
" ```\n"
"\n"
" ```gleam\n"
" assert 3 |> subtract(2) == 1\n"
" ```\n"
"\n"
" ```gleam\n"
" assert 3 |> subtract(2, _) == -1\n"
" ```\n"
).
-spec subtract(integer(), integer()) -> integer().
subtract(A, B) ->
A - B.
-file("src/gleam/int.gleam", 695).
?DOC(
" Calculates the bitwise AND of its arguments.\n"
"\n"
" Most the time you should use the bit array syntaxes instead of manipulating\n"
" bits as ints with bitwise functions.\n"
"\n"
" ## Target specific behaviour\n"
"\n"
" The exact behaviour of this function depends on the target platform.\n"
" On Erlang it is equivalent to bitwise operations on ints, on JavaScript it\n"
" is equivalent to bitwise operations on big-ints. If you need to avoid the\n"
" overhead of big-ints on JavaScript use bit arrays or another package that\n"
" provides faster bitwise operations.\n"
).
-spec bitwise_and(integer(), integer()) -> integer().
bitwise_and(X, Y) ->
erlang:'band'(X, Y).
-file("src/gleam/int.gleam", 712).
?DOC(
" Calculates the bitwise NOT of its argument.\n"
"\n"
" Most the time you should use the bit array syntaxes instead of manipulating\n"
" bits as ints with bitwise functions.\n"
"\n"
" ## Target specific behaviour\n"
"\n"
" The exact behaviour of this function depends on the target platform.\n"
" On Erlang it is equivalent to bitwise operations on ints, on JavaScript it\n"
" is equivalent to bitwise operations on big-ints. If you need to avoid the\n"
" overhead of big-ints on JavaScript use bit arrays or another package that\n"
" provides faster bitwise operations.\n"
).
-spec bitwise_not(integer()) -> integer().
bitwise_not(X) ->
erlang:'bnot'(X).
-file("src/gleam/int.gleam", 729).
?DOC(
" Calculates the bitwise OR of its arguments.\n"
"\n"
" Most the time you should use the bit array syntaxes instead of manipulating\n"
" bits as ints with bitwise functions.\n"
"\n"
" ## Target specific behaviour\n"
"\n"
" The exact behaviour of this function depends on the target platform.\n"
" On Erlang it is equivalent to bitwise operations on ints, on JavaScript it\n"
" is equivalent to bitwise operations on big-ints. If you need to avoid the\n"
" overhead of big-ints on JavaScript use bit arrays or another package that\n"
" provides faster bitwise operations.\n"
).
-spec bitwise_or(integer(), integer()) -> integer().
bitwise_or(X, Y) ->
erlang:'bor'(X, Y).
-file("src/gleam/int.gleam", 746).
?DOC(
" Calculates the bitwise XOR of its arguments.\n"
"\n"
" Most the time you should use the bit array syntaxes instead of manipulating\n"
" bits as ints with bitwise functions.\n"
"\n"
" ## Target specific behaviour\n"
"\n"
" The exact behaviour of this function depends on the target platform.\n"
" On Erlang it is equivalent to bitwise operations on ints, on JavaScript it\n"
" is equivalent to bitwise operations on big-ints. If you need to avoid the\n"
" overhead of big-ints on JavaScript use bit arrays or another package that\n"
" provides faster bitwise operations.\n"
).
-spec bitwise_exclusive_or(integer(), integer()) -> integer().
bitwise_exclusive_or(X, Y) ->
erlang:'bxor'(X, Y).
-file("src/gleam/int.gleam", 763).
?DOC(
" Calculates the result of an arithmetic left bitshift.\n"
"\n"
" Most the time you should use the bit array syntaxes instead of manipulating\n"
" bits as ints with bitwise functions.\n"
"\n"
" ## Target specific behaviour\n"
"\n"
" The exact behaviour of this function depends on the target platform.\n"
" On Erlang it is equivalent to bitwise operations on ints, on JavaScript it\n"
" is equivalent to bitwise operations on big-ints. If you need to avoid the\n"
" overhead of big-ints on JavaScript use bit arrays or another package that\n"
" provides faster bitwise operations.\n"
).
-spec bitwise_shift_left(integer(), integer()) -> integer().
bitwise_shift_left(X, Y) ->
erlang:'bsl'(X, Y).
-file("src/gleam/int.gleam", 780).
?DOC(
" Calculates the result of an arithmetic right bitshift.\n"
"\n"
" Most the time you should use the bit array syntaxes instead of manipulating\n"
" bits as ints with bitwise functions.\n"
"\n"
" ## Target specific behaviour\n"
"\n"
" The exact behaviour of this function depends on the target platform.\n"
" On Erlang it is equivalent to bitwise operations on ints, on JavaScript it\n"
" is equivalent to bitwise operations on big-ints. If you need to avoid the\n"
" overhead of big-ints on JavaScript use bit arrays or another package that\n"
" provides faster bitwise operations.\n"
).
-spec bitwise_shift_right(integer(), integer()) -> integer().
bitwise_shift_right(X, Y) ->
erlang:'bsr'(X, Y).
-file("src/gleam/int.gleam", 813).
-spec range_loop(
integer(),
integer(),
integer(),
CM,
fun((CM, integer()) -> CM)
) -> CM.
range_loop(Current, Stop, Increment, Acc, Reducer) ->
case Current =:= Stop of
true ->
Acc;
false ->
Acc@1 = Reducer(Acc, Current),
Current@1 = Current + Increment,
range_loop(Current@1, Stop, Increment, Acc@1, Reducer)
end.
-file("src/gleam/int.gleam", 800).
?DOC(
" Run a function for each int between ints `from` and `to`.\n"
"\n"
" `from` is inclusive, and `to` is exclusive.\n"
"\n"
" ## Examples\n"
"\n"
" ```gleam\n"
" assert\n"
" range(from: 0, to: 3, with: \"\", run: fn(acc, i) {\n"
" acc <> to_string(i)\n"
" })\n"
" == \"012\"\n"
" ```\n"
"\n"
" ```gleam\n"
" assert range(from: 1, to: -2, with: [], run: list.prepend) == [-1, 0, 1]\n"
" ```\n"
).
-spec range(integer(), integer(), CL, fun((CL, integer()) -> CL)) -> CL.
range(Start, Stop, Acc, Reducer) ->
Increment = case Start < Stop of
true ->
1;
false ->
-1
end,
range_loop(Start, Stop, Increment, Acc, Reducer).
+76
View File
@@ -0,0 +1,76 @@
-module(gleam@io).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]).
-define(FILEPATH, "src/gleam/io.gleam").
-export([print/1, print_error/1, println/1, println_error/1]).
-if(?OTP_RELEASE >= 27).
-define(MODULEDOC(Str), -moduledoc(Str)).
-define(DOC(Str), -doc(Str)).
-else.
-define(MODULEDOC(Str), -compile([])).
-define(DOC(Str), -compile([])).
-endif.
-file("src/gleam/io.gleam", 14).
?DOC(
" Writes a string to standard output (stdout).\n"
"\n"
" If you want your output to be printed on its own line see `println`.\n"
"\n"
" ## Example\n"
"\n"
" ```gleam\n"
" assert io.print(\"Hi mum\") == Nil\n"
" // Hi mum\n"
" ```\n"
).
-spec print(binary()) -> nil.
print(String) ->
gleam_stdlib:print(String).
-file("src/gleam/io.gleam", 29).
?DOC(
" Writes a string to standard error (stderr).\n"
"\n"
" If you want your output to be printed on its own line see `println_error`.\n"
"\n"
" ## Example\n"
"\n"
" ```gleam\n"
" assert io.print_error(\"Hi pop\") == Nil\n"
" // Hi pop\n"
" ```\n"
).
-spec print_error(binary()) -> nil.
print_error(String) ->
gleam_stdlib:print_error(String).
-file("src/gleam/io.gleam", 42).
?DOC(
" Writes a string to standard output (stdout), appending a newline to the end.\n"
"\n"
" ## Example\n"
"\n"
" ```gleam\n"
" assert io.println(\"Hi mum\") == Nil\n"
" // Hi mum\n"
" ```\n"
).
-spec println(binary()) -> nil.
println(String) ->
gleam_stdlib:println(String).
-file("src/gleam/io.gleam", 55).
?DOC(
" Writes a string to standard error (stderr), appending a newline to the end.\n"
"\n"
" ## Example\n"
"\n"
" ```gleam\n"
" assert io.println_error(\"Hi pop\") == Nil\n"
" // Hi pop\n"
" ```\n"
).
-spec println_error(binary()) -> nil.
println_error(String) ->
gleam_stdlib:println_error(String).
File diff suppressed because it is too large Load Diff
+381
View File
@@ -0,0 +1,381 @@
-module(gleam@option).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]).
-define(FILEPATH, "src/gleam/option.gleam").
-export([all/1, is_some/1, is_none/1, to_result/2, from_result/1, unwrap/2, lazy_unwrap/2, map/2, flatten/1, then/2, 'or'/2, lazy_or/2, values/1]).
-export_type([option/1]).
-if(?OTP_RELEASE >= 27).
-define(MODULEDOC(Str), -moduledoc(Str)).
-define(DOC(Str), -doc(Str)).
-else.
-define(MODULEDOC(Str), -compile([])).
-define(DOC(Str), -compile([])).
-endif.
-type option(EL) :: {some, EL} | none.
-file("src/gleam/option.gleam", 57).
-spec reverse_and_prepend(list(FA), list(FA)) -> list(FA).
reverse_and_prepend(Prefix, Suffix) ->
case Prefix of
[] ->
Suffix;
[First | Rest] ->
reverse_and_prepend(Rest, [First | Suffix])
end.
-file("src/gleam/option.gleam", 42).
-spec all_loop(list(option(ER)), list(ER)) -> option(list(ER)).
all_loop(List, Acc) ->
case List of
[] ->
{some, lists:reverse(Acc)};
[none | _] ->
none;
[{some, First} | Rest] ->
all_loop(Rest, [First | Acc])
end.
-file("src/gleam/option.gleam", 38).
?DOC(
" Combines a list of `Option`s into a single `Option`.\n"
" If all elements in the list are `Some` then returns a `Some` holding the list of values.\n"
" If any element is `None` then returns `None`.\n"
"\n"
" ## Examples\n"
"\n"
" ```gleam\n"
" assert all([Some(1), Some(2)]) == Some([1, 2])\n"
" ```\n"
"\n"
" ```gleam\n"
" assert all([Some(1), None]) == None\n"
" ```\n"
).
-spec all(list(option(EM))) -> option(list(EM)).
all(List) ->
all_loop(List, []).
-file("src/gleam/option.gleam", 76).
?DOC(
" Checks whether the `Option` is a `Some` value.\n"
"\n"
" ## Examples\n"
"\n"
" ```gleam\n"
" assert is_some(Some(1))\n"
" ```\n"
"\n"
" ```gleam\n"
" assert !is_some(None)\n"
" ```\n"
).
-spec is_some(option(any())) -> boolean().
is_some(Option) ->
Option /= none.
-file("src/gleam/option.gleam", 92).
?DOC(
" Checks whether the `Option` is a `None` value.\n"
"\n"
" ## Examples\n"
"\n"
" ```gleam\n"
" assert !is_none(Some(1))\n"
" ```\n"
"\n"
" ```gleam\n"
" assert is_none(None)\n"
" ```\n"
).
-spec is_none(option(any())) -> boolean().
is_none(Option) ->
Option =:= none.
-file("src/gleam/option.gleam", 108).
?DOC(
" Converts an `Option` type to a `Result` type.\n"
"\n"
" ## Examples\n"
"\n"
" ```gleam\n"
" assert to_result(Some(1), \"some_error\") == Ok(1)\n"
" ```\n"
"\n"
" ```gleam\n"
" assert to_result(None, \"some_error\") == Error(\"some_error\")\n"
" ```\n"
).
-spec to_result(option(FI), FL) -> {ok, FI} | {error, FL}.
to_result(Option, E) ->
case Option of
{some, A} ->
{ok, A};
none ->
{error, E}
end.
-file("src/gleam/option.gleam", 127).
?DOC(
" Converts a `Result` type to an `Option` type.\n"
"\n"
" ## Examples\n"
"\n"
" ```gleam\n"
" assert from_result(Ok(1)) == Some(1)\n"
" ```\n"
"\n"
" ```gleam\n"
" assert from_result(Error(\"some_error\")) == None\n"
" ```\n"
).
-spec from_result({ok, FO} | {error, any()}) -> option(FO).
from_result(Result) ->
case Result of
{ok, A} ->
{some, A};
{error, _} ->
none
end.
-file("src/gleam/option.gleam", 146).
?DOC(
" Extracts the value from an `Option`, returning a default value if there is none.\n"
"\n"
" ## Examples\n"
"\n"
" ```gleam\n"
" assert unwrap(Some(1), 0) == 1\n"
" ```\n"
"\n"
" ```gleam\n"
" assert unwrap(None, 0) == 0\n"
" ```\n"
).
-spec unwrap(option(FT), FT) -> FT.
unwrap(Option, Default) ->
case Option of
{some, X} ->
X;
none ->
Default
end.
-file("src/gleam/option.gleam", 165).
?DOC(
" Extracts the value from an `Option`, evaluating the default function if the option is `None`.\n"
"\n"
" ## Examples\n"
"\n"
" ```gleam\n"
" assert lazy_unwrap(Some(1), fn() { 0 }) == 1\n"
" ```\n"
"\n"
" ```gleam\n"
" assert lazy_unwrap(None, fn() { 0 }) == 0\n"
" ```\n"
).
-spec lazy_unwrap(option(FV), fun(() -> FV)) -> FV.
lazy_unwrap(Option, Default) ->
case Option of
{some, X} ->
X;
none ->
Default()
end.
-file("src/gleam/option.gleam", 188).
?DOC(
" Updates a value held within the `Some` of an `Option` by calling a given function\n"
" on it.\n"
"\n"
" If the `Option` is a `None` rather than `Some`, the function is not called and the\n"
" `Option` stays the same.\n"
"\n"
" ## Examples\n"
"\n"
" ```gleam\n"
" assert map(over: Some(1), with: fn(x) { x + 1 }) == Some(2)\n"
" ```\n"
"\n"
" ```gleam\n"
" assert map(over: None, with: fn(x) { x + 1 }) == None\n"
" ```\n"
).
-spec map(option(FX), fun((FX) -> FZ)) -> option(FZ).
map(Option, Fun) ->
case Option of
{some, X} ->
{some, Fun(X)};
none ->
none
end.
-file("src/gleam/option.gleam", 211).
?DOC(
" Merges a nested `Option` into a single layer.\n"
"\n"
" ## Examples\n"
"\n"
" ```gleam\n"
" assert flatten(Some(Some(1))) == Some(1)\n"
" ```\n"
"\n"
" ```gleam\n"
" assert flatten(Some(None)) == None\n"
" ```\n"
"\n"
" ```gleam\n"
" assert flatten(None) == None\n"
" ```\n"
).
-spec flatten(option(option(GB))) -> option(GB).
flatten(Option) ->
case Option of
{some, X} ->
X;
none ->
none
end.
-file("src/gleam/option.gleam", 246).
?DOC(
" Updates a value held within the `Some` of an `Option` by calling a given function\n"
" on it, where the given function also returns an `Option`. The two options are\n"
" then merged together into one `Option`.\n"
"\n"
" If the `Option` is a `None` rather than `Some` the function is not called and the\n"
" option stays the same.\n"
"\n"
" This function is the equivalent of calling `map` followed by `flatten`, and\n"
" it is useful for chaining together multiple functions that return `Option`.\n"
"\n"
" ## Examples\n"
"\n"
" ```gleam\n"
" assert then(Some(1), fn(x) { Some(x + 1) }) == Some(2)\n"
" ```\n"
"\n"
" ```gleam\n"
" assert then(Some(1), fn(x) { Some(#(\"a\", x)) }) == Some(#(\"a\", 1))\n"
" ```\n"
"\n"
" ```gleam\n"
" assert then(Some(1), fn(_) { None }) == None\n"
" ```\n"
"\n"
" ```gleam\n"
" assert then(None, fn(x) { Some(x + 1) }) == None\n"
" ```\n"
).
-spec then(option(GF), fun((GF) -> option(GH))) -> option(GH).
then(Option, Fun) ->
case Option of
{some, X} ->
Fun(X);
none ->
none
end.
-file("src/gleam/option.gleam", 273).
?DOC(
" Returns the first value if it is `Some`, otherwise returns the second value.\n"
"\n"
" ## Examples\n"
"\n"
" ```gleam\n"
" assert or(Some(1), Some(2)) == Some(1)\n"
" ```\n"
"\n"
" ```gleam\n"
" assert or(Some(1), None) == Some(1)\n"
" ```\n"
"\n"
" ```gleam\n"
" assert or(None, Some(2)) == Some(2)\n"
" ```\n"
"\n"
" ```gleam\n"
" assert or(None, None) == None\n"
" ```\n"
).
-spec 'or'(option(GK), option(GK)) -> option(GK).
'or'(First, Second) ->
case First of
{some, _} ->
First;
none ->
Second
end.
-file("src/gleam/option.gleam", 300).
?DOC(
" Returns the first value if it is `Some`, otherwise evaluates the given function for a fallback value.\n"
"\n"
" ## Examples\n"
"\n"
" ```gleam\n"
" assert lazy_or(Some(1), fn() { Some(2) }) == Some(1)\n"
" ```\n"
"\n"
" ```gleam\n"
" assert lazy_or(Some(1), fn() { None }) == Some(1)\n"
" ```\n"
"\n"
" ```gleam\n"
" assert lazy_or(None, fn() { Some(2) }) == Some(2)\n"
" ```\n"
"\n"
" ```gleam\n"
" assert lazy_or(None, fn() { None }) == None\n"
" ```\n"
).
-spec lazy_or(option(GO), fun(() -> option(GO))) -> option(GO).
lazy_or(First, Second) ->
case First of
{some, _} ->
First;
none ->
Second()
end.
-file("src/gleam/option.gleam", 320).
-spec values_loop(list(option(GW)), list(GW)) -> list(GW).
values_loop(List, Acc) ->
case List of
[] ->
lists:reverse(Acc);
[none | Rest] ->
values_loop(Rest, Acc);
[{some, First} | Rest@1] ->
values_loop(Rest@1, [First | Acc])
end.
-file("src/gleam/option.gleam", 316).
?DOC(
" Given a list of `Option`s,\n"
" returns only the values inside `Some`.\n"
"\n"
" ## Examples\n"
"\n"
" ```gleam\n"
" assert values([Some(1), None, Some(3)]) == [1, 3]\n"
" ```\n"
).
-spec values(list(option(GS))) -> list(GS).
values(Options) ->
values_loop(Options, []).
+188
View File
@@ -0,0 +1,188 @@
-module(gleam@order).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]).
-define(FILEPATH, "src/gleam/order.gleam").
-export([negate/1, to_int/1, compare/2, reverse/1, break_tie/2, lazy_break_tie/2]).
-export_type([order/0]).
-if(?OTP_RELEASE >= 27).
-define(MODULEDOC(Str), -moduledoc(Str)).
-define(DOC(Str), -doc(Str)).
-else.
-define(MODULEDOC(Str), -compile([])).
-define(DOC(Str), -compile([])).
-endif.
-type order() :: lt | eq | gt.
-file("src/gleam/order.gleam", 32).
?DOC(
" Inverts an order, so less-than becomes greater-than and greater-than\n"
" becomes less-than.\n"
"\n"
" ## Examples\n"
"\n"
" ```gleam\n"
" assert negate(Lt) == Gt\n"
" ```\n"
"\n"
" ```gleam\n"
" assert negate(Eq) == Eq\n"
" ```\n"
"\n"
" ```gleam\n"
" assert negate(Gt) == Lt\n"
" ```\n"
).
-spec negate(order()) -> order().
negate(Order) ->
case Order of
lt ->
gt;
eq ->
eq;
gt ->
lt
end.
-file("src/gleam/order.gleam", 56).
?DOC(
" Produces a numeric representation of the order.\n"
"\n"
" ## Examples\n"
"\n"
" ```gleam\n"
" assert to_int(Lt) == -1\n"
" ```\n"
"\n"
" ```gleam\n"
" assert to_int(Eq) == 0\n"
" ```\n"
"\n"
" ```gleam\n"
" assert to_int(Gt) == 1\n"
" ```\n"
).
-spec to_int(order()) -> integer().
to_int(Order) ->
case Order of
lt ->
-1;
eq ->
0;
gt ->
1
end.
-file("src/gleam/order.gleam", 72).
?DOC(
" Compares two `Order` values to one another, producing a new `Order`.\n"
"\n"
" ## Examples\n"
"\n"
" ```gleam\n"
" assert compare(Eq, with: Lt) == Gt\n"
" ```\n"
).
-spec compare(order(), order()) -> order().
compare(A, B) ->
case {A, B} of
{X, Y} when X =:= Y ->
eq;
{lt, _} ->
lt;
{eq, gt} ->
lt;
{_, _} ->
gt
end.
-file("src/gleam/order.gleam", 92).
?DOC(
" Inverts an ordering function, so less-than becomes greater-than and greater-than\n"
" becomes less-than.\n"
"\n"
" ## Examples\n"
"\n"
" ```gleam\n"
" import gleam/int\n"
" import gleam/list\n"
"\n"
" assert list.sort([1, 5, 4], by: reverse(int.compare)) == [5, 4, 1]\n"
" ```\n"
).
-spec reverse(fun((I, I) -> order())) -> fun((I, I) -> order()).
reverse(Orderer) ->
fun(A, B) -> Orderer(B, A) end.
-file("src/gleam/order.gleam", 112).
?DOC(
" Return a fallback `Order` in case the first argument is `Eq`.\n"
"\n"
" ## Examples\n"
"\n"
" ```gleam\n"
" import gleam/int\n"
"\n"
" assert break_tie(in: int.compare(1, 1), with: Lt) == Lt\n"
" ```\n"
"\n"
" ```gleam\n"
" import gleam/int\n"
"\n"
" assert break_tie(in: int.compare(1, 0), with: Eq) == Gt\n"
" ```\n"
).
-spec break_tie(order(), order()) -> order().
break_tie(Order, Other) ->
case Order of
lt ->
Order;
gt ->
Order;
eq ->
Other
end.
-file("src/gleam/order.gleam", 139).
?DOC(
" Invokes a fallback function returning an `Order` in case the first argument\n"
" is `Eq`.\n"
"\n"
" This can be useful when the fallback comparison might be expensive and it\n"
" needs to be delayed until strictly necessary.\n"
"\n"
" ## Examples\n"
"\n"
" ```gleam\n"
" import gleam/int\n"
"\n"
" assert lazy_break_tie(in: int.compare(1, 1), with: fn() { Lt }) == Lt\n"
" ```\n"
"\n"
" ```gleam\n"
" import gleam/int\n"
"\n"
" assert lazy_break_tie(in: int.compare(1, 0), with: fn() { Eq }) == Gt\n"
" ```\n"
).
-spec lazy_break_tie(order(), fun(() -> order())) -> order().
lazy_break_tie(Order, Comparison) ->
case Order of
lt ->
Order;
gt ->
Order;
eq ->
Comparison()
end.
+104
View File
@@ -0,0 +1,104 @@
-module(gleam@pair).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]).
-define(FILEPATH, "src/gleam/pair.gleam").
-export([first/1, second/1, swap/1, map_first/2, map_second/2, new/2]).
-if(?OTP_RELEASE >= 27).
-define(MODULEDOC(Str), -moduledoc(Str)).
-define(DOC(Str), -doc(Str)).
-else.
-define(MODULEDOC(Str), -compile([])).
-define(DOC(Str), -compile([])).
-endif.
-file("src/gleam/pair.gleam", 9).
?DOC(
" Returns the first element in a pair.\n"
"\n"
" ## Examples\n"
"\n"
" ```gleam\n"
" assert first(#(1, 2)) == 1\n"
" ```\n"
).
-spec first({CLY, any()}) -> CLY.
first(Pair) ->
{A, _} = Pair,
A.
-file("src/gleam/pair.gleam", 22).
?DOC(
" Returns the second element in a pair.\n"
"\n"
" ## Examples\n"
"\n"
" ```gleam\n"
" assert second(#(1, 2)) == 2\n"
" ```\n"
).
-spec second({any(), CMB}) -> CMB.
second(Pair) ->
{_, A} = Pair,
A.
-file("src/gleam/pair.gleam", 35).
?DOC(
" Returns a new pair with the elements swapped.\n"
"\n"
" ## Examples\n"
"\n"
" ```gleam\n"
" assert swap(#(1, 2)) == #(2, 1)\n"
" ```\n"
).
-spec swap({CMC, CMD}) -> {CMD, CMC}.
swap(Pair) ->
{A, B} = Pair,
{B, A}.
-file("src/gleam/pair.gleam", 49).
?DOC(
" Returns a new pair with the first element having had `with` applied to\n"
" it.\n"
"\n"
" ## Examples\n"
"\n"
" ```gleam\n"
" assert #(1, 2) |> map_first(fn(n) { n * 2 }) == #(2, 2)\n"
" ```\n"
).
-spec map_first({CME, CMF}, fun((CME) -> CMG)) -> {CMG, CMF}.
map_first(Pair, Fun) ->
{A, B} = Pair,
{Fun(A), B}.
-file("src/gleam/pair.gleam", 63).
?DOC(
" Returns a new pair with the second element having had `with` applied to\n"
" it.\n"
"\n"
" ## Examples\n"
"\n"
" ```gleam\n"
" assert #(1, 2) |> map_second(fn(n) { n * 2 }) == #(1, 4)\n"
" ```\n"
).
-spec map_second({CMH, CMI}, fun((CMI) -> CMJ)) -> {CMH, CMJ}.
map_second(Pair, Fun) ->
{A, B} = Pair,
{A, Fun(B)}.
-file("src/gleam/pair.gleam", 77).
?DOC(
" Returns a new pair with the given elements. This can also be done using the dedicated\n"
" syntax instead: `new(1, 2) == #(1, 2)`.\n"
"\n"
" ## Examples\n"
"\n"
" ```gleam\n"
" assert new(1, 2) == #(1, 2)\n"
" ```\n"
).
-spec new(CMK, CML) -> {CMK, CML}.
new(First, Second) ->
{First, Second}.
+500
View File
@@ -0,0 +1,500 @@
-module(gleam@result).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]).
-define(FILEPATH, "src/gleam/result.gleam").
-export([is_ok/1, is_error/1, map/2, map_error/2, flatten/1, 'try'/2, unwrap/2, lazy_unwrap/2, unwrap_error/2, 'or'/2, lazy_or/2, all/1, partition/1, replace/2, replace_error/2, values/1, try_recover/2]).
-if(?OTP_RELEASE >= 27).
-define(MODULEDOC(Str), -moduledoc(Str)).
-define(DOC(Str), -doc(Str)).
-else.
-define(MODULEDOC(Str), -compile([])).
-define(DOC(Str), -compile([])).
-endif.
?MODULEDOC(
" Result represents the result of something that may succeed or not.\n"
" `Ok` means it was successful, `Error` means it was not successful.\n"
).
-file("src/gleam/result.gleam", 18).
?DOC(
" Checks whether the result is an `Ok` value.\n"
"\n"
" ## Examples\n"
"\n"
" ```gleam\n"
" assert is_ok(Ok(1))\n"
" ```\n"
"\n"
" ```gleam\n"
" assert !is_ok(Error(Nil))\n"
" ```\n"
).
-spec is_ok({ok, any()} | {error, any()}) -> boolean().
is_ok(Result) ->
case Result of
{error, _} ->
false;
{ok, _} ->
true
end.
-file("src/gleam/result.gleam", 37).
?DOC(
" Checks whether the result is an `Error` value.\n"
"\n"
" ## Examples\n"
"\n"
" ```gleam\n"
" assert !is_error(Ok(1))\n"
" ```\n"
"\n"
" ```gleam\n"
" assert is_error(Error(Nil))\n"
" ```\n"
).
-spec is_error({ok, any()} | {error, any()}) -> boolean().
is_error(Result) ->
case Result of
{ok, _} ->
false;
{error, _} ->
true
end.
-file("src/gleam/result.gleam", 60).
?DOC(
" Updates a value held within the `Ok` of a result by calling a given function\n"
" on it.\n"
"\n"
" If the result is an `Error` rather than `Ok` the function is not called and the\n"
" result stays the same.\n"
"\n"
" ## Examples\n"
"\n"
" ```gleam\n"
" assert map(over: Ok(1), with: fn(x) { x + 1 }) == Ok(2)\n"
" ```\n"
"\n"
" ```gleam\n"
" assert map(over: Error(1), with: fn(x) { x + 1 }) == Error(1)\n"
" ```\n"
).
-spec map({ok, CMV} | {error, CMW}, fun((CMV) -> CMZ)) -> {ok, CMZ} |
{error, CMW}.
map(Result, Fun) ->
case Result of
{ok, X} ->
{ok, Fun(X)};
{error, E} ->
{error, E}
end.
-file("src/gleam/result.gleam", 83).
?DOC(
" Updates a value held within the `Error` of a result by calling a given function\n"
" on it.\n"
"\n"
" If the result is `Ok` rather than `Error` the function is not called and the\n"
" result stays the same.\n"
"\n"
" ## Examples\n"
"\n"
" ```gleam\n"
" assert map_error(over: Error(1), with: fn(x) { x + 1 }) == Error(2)\n"
" ```\n"
"\n"
" ```gleam\n"
" assert map_error(over: Ok(1), with: fn(x) { x + 1 }) == Ok(1)\n"
" ```\n"
).
-spec map_error({ok, CNC} | {error, CND}, fun((CND) -> CNG)) -> {ok, CNC} |
{error, CNG}.
map_error(Result, Fun) ->
case Result of
{ok, X} ->
{ok, X};
{error, Error} ->
{error, Fun(Error)}
end.
-file("src/gleam/result.gleam", 109).
?DOC(
" Merges a nested `Result` into a single layer.\n"
"\n"
" ## Examples\n"
"\n"
" ```gleam\n"
" assert flatten(Ok(Ok(1))) == Ok(1)\n"
" ```\n"
"\n"
" ```gleam\n"
" assert flatten(Ok(Error(\"\"))) == Error(\"\")\n"
" ```\n"
"\n"
" ```gleam\n"
" assert flatten(Error(Nil)) == Error(Nil)\n"
" ```\n"
).
-spec flatten({ok, {ok, CNJ} | {error, CNK}} | {error, CNK}) -> {ok, CNJ} |
{error, CNK}.
flatten(Result) ->
case Result of
{ok, X} ->
X;
{error, Error} ->
{error, Error}
end.
-file("src/gleam/result.gleam", 143).
?DOC(
" \"Updates\" an `Ok` result by passing its value to a function that yields a result,\n"
" and returning the yielded result. (This may \"replace\" the `Ok` with an `Error`.)\n"
"\n"
" If the input is an `Error` rather than an `Ok`, the function is not called and\n"
" the original `Error` is returned.\n"
"\n"
" This function is the equivalent of calling `map` followed by `flatten`, and\n"
" it is useful for chaining together multiple functions that may fail.\n"
"\n"
" ## Examples\n"
"\n"
" ```gleam\n"
" assert try(Ok(1), fn(x) { Ok(x + 1) }) == Ok(2)\n"
" ```\n"
"\n"
" ```gleam\n"
" assert try(Ok(1), fn(x) { Ok(#(\"a\", x)) }) == Ok(#(\"a\", 1))\n"
" ```\n"
"\n"
" ```gleam\n"
" assert try(Ok(1), fn(_) { Error(\"Oh no\") }) == Error(\"Oh no\")\n"
" ```\n"
"\n"
" ```gleam\n"
" assert try(Error(Nil), fn(x) { Ok(x + 1) }) == Error(Nil)\n"
" ```\n"
).
-spec 'try'({ok, CNR} | {error, CNS}, fun((CNR) -> {ok, CNV} | {error, CNS})) -> {ok,
CNV} |
{error, CNS}.
'try'(Result, Fun) ->
case Result of
{ok, X} ->
Fun(X);
{error, E} ->
{error, E}
end.
-file("src/gleam/result.gleam", 166).
?DOC(
" Extracts the `Ok` value from a result, returning a default value if the result\n"
" is an `Error`.\n"
"\n"
" ## Examples\n"
"\n"
" ```gleam\n"
" assert unwrap(Ok(1), 0) == 1\n"
" ```\n"
"\n"
" ```gleam\n"
" assert unwrap(Error(\"\"), 0) == 0\n"
" ```\n"
).
-spec unwrap({ok, COA} | {error, any()}, COA) -> COA.
unwrap(Result, Default) ->
case Result of
{ok, V} ->
V;
{error, _} ->
Default
end.
-file("src/gleam/result.gleam", 186).
?DOC(
" Extracts the `Ok` value from a result, evaluating the default function if the result\n"
" is an `Error`.\n"
"\n"
" ## Examples\n"
"\n"
" ```gleam\n"
" assert lazy_unwrap(Ok(1), fn() { 0 }) == 1\n"
" ```\n"
"\n"
" ```gleam\n"
" assert lazy_unwrap(Error(\"\"), fn() { 0 }) == 0\n"
" ```\n"
).
-spec lazy_unwrap({ok, COE} | {error, any()}, fun(() -> COE)) -> COE.
lazy_unwrap(Result, Default) ->
case Result of
{ok, V} ->
V;
{error, _} ->
Default()
end.
-file("src/gleam/result.gleam", 206).
?DOC(
" Extracts the `Error` value from a result, returning a default value if the result\n"
" is an `Ok`.\n"
"\n"
" ## Examples\n"
"\n"
" ```gleam\n"
" assert unwrap_error(Error(1), 0) == 1\n"
" ```\n"
"\n"
" ```gleam\n"
" assert unwrap_error(Ok(\"\"), 0) == 0\n"
" ```\n"
).
-spec unwrap_error({ok, any()} | {error, COJ}, COJ) -> COJ.
unwrap_error(Result, Default) ->
case Result of
{ok, _} ->
Default;
{error, E} ->
E
end.
-file("src/gleam/result.gleam", 233).
?DOC(
" Returns the first value if it is `Ok`, otherwise returns the second value.\n"
"\n"
" ## Examples\n"
"\n"
" ```gleam\n"
" assert or(Ok(1), Ok(2)) == Ok(1)\n"
" ```\n"
"\n"
" ```gleam\n"
" assert or(Ok(1), Error(\"Error 2\")) == Ok(1)\n"
" ```\n"
"\n"
" ```gleam\n"
" assert or(Error(\"Error 1\"), Ok(2)) == Ok(2)\n"
" ```\n"
"\n"
" ```gleam\n"
" assert or(Error(\"Error 1\"), Error(\"Error 2\")) == Error(\"Error 2\")\n"
" ```\n"
).
-spec 'or'({ok, COM} | {error, CON}, {ok, COM} | {error, CON}) -> {ok, COM} |
{error, CON}.
'or'(First, Second) ->
case First of
{ok, _} ->
First;
{error, _} ->
Second
end.
-file("src/gleam/result.gleam", 263).
?DOC(
" Returns the first value if it is `Ok`, otherwise evaluates the given function for a fallback value.\n"
"\n"
" If you need access to the initial error value, use `result.try_recover`.\n"
"\n"
" ## Examples\n"
"\n"
" ```gleam\n"
" assert lazy_or(Ok(1), fn() { Ok(2) }) == Ok(1)\n"
" ```\n"
"\n"
" ```gleam\n"
" assert lazy_or(Ok(1), fn() { Error(\"Error 2\") }) == Ok(1)\n"
" ```\n"
"\n"
" ```gleam\n"
" assert lazy_or(Error(\"Error 1\"), fn() { Ok(2) }) == Ok(2)\n"
" ```\n"
"\n"
" ```gleam\n"
" assert lazy_or(Error(\"Error 1\"), fn() { Error(\"Error 2\") })\n"
" == Error(\"Error 2\")\n"
" ```\n"
).
-spec lazy_or({ok, COU} | {error, COV}, fun(() -> {ok, COU} | {error, COV})) -> {ok,
COU} |
{error, COV}.
lazy_or(First, Second) ->
case First of
{ok, _} ->
First;
{error, _} ->
Second()
end.
-file("src/gleam/result.gleam", 287).
?DOC(
" Combines a list of results into a single result.\n"
" If all elements in the list are `Ok` then returns an `Ok` holding the list of values.\n"
" If any element is `Error` then returns the first error.\n"
"\n"
" ## Examples\n"
"\n"
" ```gleam\n"
" assert all([Ok(1), Ok(2)]) == Ok([1, 2])\n"
" ```\n"
"\n"
" ```gleam\n"
" assert all([Ok(1), Error(\"e\")]) == Error(\"e\")\n"
" ```\n"
).
-spec all(list({ok, CPC} | {error, CPD})) -> {ok, list(CPC)} | {error, CPD}.
all(Results) ->
gleam@list:try_map(Results, fun(Result) -> Result end).
-file("src/gleam/result.gleam", 307).
-spec partition_loop(list({ok, CPR} | {error, CPS}), list(CPR), list(CPS)) -> {list(CPR),
list(CPS)}.
partition_loop(Results, Oks, Errors) ->
case Results of
[] ->
{Oks, Errors};
[{ok, A} | Rest] ->
partition_loop(Rest, [A | Oks], Errors);
[{error, E} | Rest@1] ->
partition_loop(Rest@1, Oks, [E | Errors])
end.
-file("src/gleam/result.gleam", 303).
?DOC(
" Given a list of results, returns a pair where the first element is a list\n"
" of all the values inside `Ok` and the second element is a list with all the\n"
" values inside `Error`. The values in both lists appear in reverse order with\n"
" respect to their position in the original list of results.\n"
"\n"
" ## Examples\n"
"\n"
" ```gleam\n"
" assert partition([Ok(1), Error(\"a\"), Error(\"b\"), Ok(2)])\n"
" == #([2, 1], [\"b\", \"a\"])\n"
" ```\n"
).
-spec partition(list({ok, CPK} | {error, CPL})) -> {list(CPK), list(CPL)}.
partition(Results) ->
partition_loop(Results, [], []).
-file("src/gleam/result.gleam", 327).
?DOC(
" Replace the value within a result\n"
"\n"
" ## Examples\n"
"\n"
" ```gleam\n"
" assert replace(Ok(1), Nil) == Ok(Nil)\n"
" ```\n"
"\n"
" ```gleam\n"
" assert replace(Error(1), Nil) == Error(1)\n"
" ```\n"
).
-spec replace({ok, any()} | {error, CQA}, CQD) -> {ok, CQD} | {error, CQA}.
replace(Result, Value) ->
case Result of
{ok, _} ->
{ok, Value};
{error, Error} ->
{error, Error}
end.
-file("src/gleam/result.gleam", 346).
?DOC(
" Replace the error within a result\n"
"\n"
" ## Examples\n"
"\n"
" ```gleam\n"
" assert replace_error(Error(1), Nil) == Error(Nil)\n"
" ```\n"
"\n"
" ```gleam\n"
" assert replace_error(Ok(1), Nil) == Ok(1)\n"
" ```\n"
).
-spec replace_error({ok, CQG} | {error, any()}, CQK) -> {ok, CQG} | {error, CQK}.
replace_error(Result, Error) ->
case Result of
{ok, X} ->
{ok, X};
{error, _} ->
{error, Error}
end.
-file("src/gleam/result.gleam", 361).
?DOC(
" Given a list of results, returns only the values inside `Ok`.\n"
"\n"
" ## Examples\n"
"\n"
" ```gleam\n"
" assert values([Ok(1), Error(\"a\"), Ok(3)]) == [1, 3]\n"
" ```\n"
).
-spec values(list({ok, CQN} | {error, any()})) -> list(CQN).
values(Results) ->
gleam@list:filter_map(Results, fun(Result) -> Result end).
-file("src/gleam/result.gleam", 397).
?DOC(
" Updates a value held within the `Error` of a result by calling a given function\n"
" on it, where the given function also returns a result. The two results are\n"
" then merged together into one result.\n"
"\n"
" If the result is an `Ok` rather than `Error` the function is not called and the\n"
" result stays the same.\n"
"\n"
" This function is useful for chaining together computations that may fail\n"
" and trying to recover from possible errors.\n"
"\n"
" If you do not need access to the initial error value, use `result.lazy_or`.\n"
"\n"
" ## Examples\n"
"\n"
" ```gleam\n"
" assert Ok(1)\n"
" |> try_recover(with: fn(_) { Error(\"failed to recover\") })\n"
" == Ok(1)\n"
" ```\n"
"\n"
" ```gleam\n"
" assert Error(1)\n"
" |> try_recover(with: fn(error) { Ok(error + 1) })\n"
" == Ok(2)\n"
" ```\n"
"\n"
" ```gleam\n"
" assert Error(1)\n"
" |> try_recover(with: fn(error) { Error(\"failed to recover\") })\n"
" == Error(\"failed to recover\")\n"
" ```\n"
).
-spec try_recover(
{ok, CQT} | {error, CQU},
fun((CQU) -> {ok, CQT} | {error, CQX})
) -> {ok, CQT} | {error, CQX}.
try_recover(Result, Fun) ->
case Result of
{ok, Value} ->
{ok, Value};
{error, Error} ->
Fun(Error)
end.
+430
View File
@@ -0,0 +1,430 @@
-module(gleam@set).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]).
-define(FILEPATH, "src/gleam/set.gleam").
-export([new/0, size/1, is_empty/1, insert/2, contains/2, delete/2, to_list/1, from_list/1, fold/3, filter/2, map/2, drop/2, take/2, union/2, intersection/2, difference/2, is_subset/2, is_disjoint/2, symmetric_difference/2, each/2]).
-export_type([set/1]).
-if(?OTP_RELEASE >= 27).
-define(MODULEDOC(Str), -moduledoc(Str)).
-define(DOC(Str), -doc(Str)).
-else.
-define(MODULEDOC(Str), -compile([])).
-define(DOC(Str), -compile([])).
-endif.
-opaque set(CVK) :: {set, gleam@dict:dict(CVK, list(nil))}.
-file("src/gleam/set.gleam", 32).
?DOC(" Creates a new empty set.\n").
-spec new() -> set(any()).
new() ->
{set, maps:new()}.
-file("src/gleam/set.gleam", 50).
?DOC(
" Gets the number of members in a set.\n"
"\n"
" This function runs in constant time.\n"
"\n"
" ## Examples\n"
"\n"
" ```gleam\n"
" assert new()\n"
" |> insert(1)\n"
" |> insert(2)\n"
" |> size\n"
" == 2\n"
" ```\n"
).
-spec size(set(any())) -> integer().
size(Set) ->
maps:size(erlang:element(2, Set)).
-file("src/gleam/set.gleam", 66).
?DOC(
" Determines whether or not the set is empty.\n"
"\n"
" ## Examples\n"
"\n"
" ```gleam\n"
" assert new() |> is_empty\n"
" ```\n"
"\n"
" ```gleam\n"
" assert !{ new() |> insert(1) |> is_empty }\n"
" ```\n"
).
-spec is_empty(set(any())) -> boolean().
is_empty(Set) ->
Set =:= new().
-file("src/gleam/set.gleam", 84).
?DOC(
" Inserts a member into the set.\n"
"\n"
" This function runs in logarithmic time.\n"
"\n"
" ## Examples\n"
"\n"
" ```gleam\n"
" assert new()\n"
" |> insert(1)\n"
" |> insert(2)\n"
" |> size\n"
" == 2\n"
" ```\n"
).
-spec insert(set(CVS), CVS) -> set(CVS).
insert(Set, Member) ->
{set, gleam@dict:insert(erlang:element(2, Set), Member, [])}.
-file("src/gleam/set.gleam", 108).
?DOC(
" Checks whether a set contains a given member.\n"
"\n"
" This function runs in logarithmic time.\n"
"\n"
" ## Examples\n"
"\n"
" ```gleam\n"
" assert new()\n"
" |> insert(2)\n"
" |> contains(2)\n"
" ```\n"
"\n"
" ```gleam\n"
" assert !{\n"
" new()\n"
" |> insert(2)\n"
" |> contains(1)\n"
" }\n"
" ```\n"
).
-spec contains(set(CVV), CVV) -> boolean().
contains(Set, Member) ->
_pipe = erlang:element(2, Set),
_pipe@1 = gleam_stdlib:map_get(_pipe, Member),
gleam@result:is_ok(_pipe@1).
-file("src/gleam/set.gleam", 130).
?DOC(
" Removes a member from a set. If the set does not contain the member then\n"
" the set is returned unchanged.\n"
"\n"
" This function runs in logarithmic time.\n"
"\n"
" ## Examples\n"
"\n"
" ```gleam\n"
" assert !{\n"
" new()\n"
" |> insert(2)\n"
" |> delete(2)\n"
" |> contains(2)\n"
" }\n"
" ```\n"
).
-spec delete(set(CVX), CVX) -> set(CVX).
delete(Set, Member) ->
{set, gleam@dict:delete(erlang:element(2, Set), Member)}.
-file("src/gleam/set.gleam", 147).
?DOC(
" Converts the set into a list of the contained members.\n"
"\n"
" The list has no specific ordering, any unintentional ordering may change in\n"
" future versions of Gleam or Erlang.\n"
"\n"
" This function runs in linear time.\n"
"\n"
" ## Examples\n"
"\n"
" ```gleam\n"
" assert new() |> insert(2) |> to_list == [2]\n"
" ```\n"
).
-spec to_list(set(CWA)) -> list(CWA).
to_list(Set) ->
maps:keys(erlang:element(2, Set)).
-file("src/gleam/set.gleam", 168).
?DOC(
" Creates a new set of the members in a given list.\n"
"\n"
" This function runs in loglinear time.\n"
"\n"
" ## Examples\n"
"\n"
" ```gleam\n"
" import gleam/int\n"
" import gleam/list\n"
"\n"
" assert [1, 1, 2, 4, 3, 2]\n"
" |> from_list\n"
" |> to_list\n"
" |> list.sort(by: int.compare)\n"
" == [1, 2, 3, 4]\n"
" ```\n"
).
-spec from_list(list(CWD)) -> set(CWD).
from_list(Members) ->
Dict = gleam@list:fold(
Members,
maps:new(),
fun(M, K) -> gleam@dict:insert(M, K, []) end
),
{set, Dict}.
-file("src/gleam/set.gleam", 191).
?DOC(
" Combines all entries into a single value by calling a given function on each\n"
" one.\n"
"\n"
" Sets are not ordered so the values are not returned in any specific order.\n"
" Do not write code that relies on the order entries are used by this\n"
" function as it may change in later versions of Gleam or Erlang.\n"
"\n"
" ## Examples\n"
"\n"
" ```gleam\n"
" assert from_list([1, 3, 9])\n"
" |> fold(0, fn(accumulator, member) { accumulator + member })\n"
" == 13\n"
" ```\n"
).
-spec fold(set(CWG), CWI, fun((CWI, CWG) -> CWI)) -> CWI.
fold(Set, Initial, Reducer) ->
gleam@dict:fold(
erlang:element(2, Set),
Initial,
fun(A, K, _) -> Reducer(A, K) end
).
-file("src/gleam/set.gleam", 215).
?DOC(
" Creates a new set from an existing set, minus any members that a given\n"
" function returns `False` for.\n"
"\n"
" This function runs in loglinear time.\n"
"\n"
" ## Examples\n"
"\n"
" ```gleam\n"
" import gleam/int\n"
"\n"
" assert from_list([1, 4, 6, 3, 675, 44, 67])\n"
" |> filter(keeping: int.is_even)\n"
" |> to_list\n"
" == [4, 6, 44]\n"
" ```\n"
).
-spec filter(set(CWJ), fun((CWJ) -> boolean())) -> set(CWJ).
filter(Set, Predicate) ->
{set,
gleam@dict:filter(erlang:element(2, Set), fun(M, _) -> Predicate(M) end)}.
-file("src/gleam/set.gleam", 234).
?DOC(
" Creates a new set from a given set with the result of applying the given\n"
" function to each member.\n"
"\n"
" ## Examples\n"
"\n"
" ```gleam\n"
" assert from_list([1, 2, 3, 4])\n"
" |> map(with: fn(x) { x * 2 })\n"
" |> to_list\n"
" == [2, 4, 6, 8]\n"
" ```\n"
).
-spec map(set(CWM), fun((CWM) -> CWO)) -> set(CWO).
map(Set, Fun) ->
fold(Set, new(), fun(Acc, Member) -> insert(Acc, Fun(Member)) end).
-file("src/gleam/set.gleam", 252).
?DOC(
" Creates a new set from a given set with all the same entries except any\n"
" entry found on the given list.\n"
"\n"
" ## Examples\n"
"\n"
" ```gleam\n"
" assert from_list([1, 2, 3, 4])\n"
" |> drop([1, 3])\n"
" |> to_list\n"
" == [2, 4]\n"
" ```\n"
).
-spec drop(set(CWQ), list(CWQ)) -> set(CWQ).
drop(Set, Disallowed) ->
gleam@list:fold(Disallowed, Set, fun delete/2).
-file("src/gleam/set.gleam", 270).
?DOC(
" Creates a new set from a given set, only including any members which are in\n"
" a given list.\n"
"\n"
" This function runs in loglinear time.\n"
"\n"
" ## Examples\n"
"\n"
" ```gleam\n"
" assert from_list([1, 2, 3])\n"
" |> take([1, 3, 5])\n"
" |> to_list\n"
" == [1, 3]\n"
" ```\n"
).
-spec take(set(CWU), list(CWU)) -> set(CWU).
take(Set, Desired) ->
{set, gleam@dict:take(erlang:element(2, Set), Desired)}.
-file("src/gleam/set.gleam", 290).
-spec order(set(CXC), set(CXC)) -> {set(CXC), set(CXC)}.
order(First, Second) ->
case maps:size(erlang:element(2, First)) > maps:size(
erlang:element(2, Second)
) of
true ->
{First, Second};
false ->
{Second, First}
end.
-file("src/gleam/set.gleam", 285).
?DOC(
" Creates a new set that contains all members of both given sets.\n"
"\n"
" This function runs in loglinear time.\n"
"\n"
" ## Examples\n"
"\n"
" ```gleam\n"
" assert union(from_list([1, 2]), from_list([2, 3])) |> to_list\n"
" == [1, 2, 3]\n"
" ```\n"
).
-spec union(set(CWY), set(CWY)) -> set(CWY).
union(First, Second) ->
{Larger, Smaller} = order(First, Second),
fold(Smaller, Larger, fun insert/2).
-file("src/gleam/set.gleam", 308).
?DOC(
" Creates a new set that contains members that are present in both given sets.\n"
"\n"
" This function runs in loglinear time.\n"
"\n"
" ## Examples\n"
"\n"
" ```gleam\n"
" assert intersection(from_list([1, 2]), from_list([2, 3])) |> to_list\n"
" == [2]\n"
" ```\n"
).
-spec intersection(set(CXH), set(CXH)) -> set(CXH).
intersection(First, Second) ->
{Larger, Smaller} = order(First, Second),
take(Larger, to_list(Smaller)).
-file("src/gleam/set.gleam", 326).
?DOC(
" Creates a new set that contains members that are present in the first set\n"
" but not the second.\n"
"\n"
" ## Examples\n"
"\n"
" ```gleam\n"
" assert difference(from_list([1, 2]), from_list([2, 3, 4])) |> to_list\n"
" == [1]\n"
" ```\n"
).
-spec difference(set(CXL), set(CXL)) -> set(CXL).
difference(First, Second) ->
drop(First, to_list(Second)).
-file("src/gleam/set.gleam", 345).
?DOC(
" Determines if a set is fully contained by another.\n"
"\n"
" ## Examples\n"
"\n"
" ```gleam\n"
" assert is_subset(from_list([1]), from_list([1, 2]))\n"
" ```\n"
"\n"
" ```gleam\n"
" assert !is_subset(from_list([1, 2, 3]), from_list([3, 4, 5]))\n"
" ```\n"
).
-spec is_subset(set(CXP), set(CXP)) -> boolean().
is_subset(First, Second) ->
intersection(First, Second) =:= First.
-file("src/gleam/set.gleam", 361).
?DOC(
" Determines if two sets contain no common members\n"
"\n"
" ## Examples\n"
"\n"
" ```gleam\n"
" assert is_disjoint(from_list([1, 2, 3]), from_list([4, 5, 6]))\n"
" ```\n"
"\n"
" ```gleam\n"
" assert !is_disjoint(from_list([1, 2, 3]), from_list([3, 4, 5]))\n"
" ```\n"
).
-spec is_disjoint(set(CXS), set(CXS)) -> boolean().
is_disjoint(First, Second) ->
intersection(First, Second) =:= new().
-file("src/gleam/set.gleam", 376).
?DOC(
" Creates a new set that contains members that are present in either set, but\n"
" not both.\n"
"\n"
" ## Examples\n"
"\n"
" ```gleam\n"
" assert symmetric_difference(from_list([1, 2, 3]), from_list([3, 4]))\n"
" |> to_list\n"
" == [1, 2, 4]\n"
" ```\n"
).
-spec symmetric_difference(set(CXV), set(CXV)) -> set(CXV).
symmetric_difference(First, Second) ->
difference(union(First, Second), intersection(First, Second)).
-file("src/gleam/set.gleam", 405).
?DOC(
" Calls a function for each member in a set, discarding the return\n"
" value.\n"
"\n"
" Useful for producing a side effect for every item of a set.\n"
"\n"
" The order of elements in the iteration is an implementation detail that\n"
" should not be relied upon.\n"
"\n"
" ## Examples\n"
"\n"
" ```gleam\n"
" let set = from_list([\"apple\", \"banana\", \"cherry\"])\n"
"\n"
" assert each(set, io.println) == Nil\n"
" // apple\n"
" // banana\n"
" // cherry\n"
" ```\n"
).
-spec each(set(CXZ), fun((CXZ) -> any())) -> nil.
each(Set, Fun) ->
fold(
Set,
nil,
fun(Nil, Member) ->
Fun(Member),
Nil
end
).

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